diff --git a/Documentation/devicetree/bindings/spmi/msm-qpnp-gpio.txt b/Documentation/devicetree/bindings/spmi/msm-qpnp-gpio.txt new file mode 100644 index 00000000000..f07d3c2a10c --- /dev/null +++ b/Documentation/devicetree/bindings/spmi/msm-qpnp-gpio.txt @@ -0,0 +1,142 @@ +* msm-qpnp-gpio + +msm-qpnp-gpio is a GPIO chip driver for the MSM SPMI implementation. +It creates a spmi_device for every spmi-dev-container block of device_nodes. +These device_nodes contained within specify the PMIC GPIO number associated +with each GPIO chip. The driver will map these to Linux GPIO numbers. + +[PMIC GPIO Device Declarations] + +-Root Node- + +Required properties : + - spmi-dev-container : Used to specify the following child nodes as part of the + same SPMI device. + - gpio-controller : Specify as gpio-contoller. All child nodes will belong to this + gpio_chip. + - #gpio-cells: We encode a PMIC GPIO number and a 32-bit flag field to + specify the gpio configuration. This must be set to '2'. + - #address-cells: Specify one address field. This must be set to '1'. + - #size-cells: Specify one size-cell. This must be set to '1'. + - compatible = "qcom,qpnp-gpio" : Specify driver matching for this driver. + +-Child Nodes- + +Required properties : + - reg : Specify the spmi offset and size for this gpio device. + - qcom,qpnp-gpio-num : Specify the PMIC GPIO number for this gpio device. + +Optional properties : + - qcom,qpnp-gpio-cfg : Specify the PMIC gpio configuration. + The format of this configuration is specified in a tuple of 9 entries. + These entries should be specified in the same order as the entries listed + in this following discription. + + @direction: indicates whether the gpio should be input, output, or + both. + QPNP_GPIO_DIR_OUT = 1, + QPNP_GPIO_DIR_IN = 2, + QPNP_GPIO_DIR_BOTH = 3 + + @output_type: indicates gpio should be configured as CMOS or open + drain. + QPNP_GPIO_OUT_BUF_OPEN_DRAIN = 1, + QPNP_GPIO_OUT_BUF_CMOS = 0 + + @output_value: The gpio output value of the gpio line - 0 or 1 + @pull: Indicates whether a pull up or pull down should be + applied. If a pullup is required the current strength + needs to be specified. Current values of 30uA, 1.5uA, + 31.5uA, 1.5uA with 30uA boost are supported. + QPNP_GPIO_PULL_UP_30 = 0, + QPNP_GPIO_PULL_UP_1P5 = 1, + QPNP_GPIO_PULL_UP_31P5 = 2, + QPNP_GPIO_PULL_UP_1P5_30 = 3, + QPNP_GPIO_PULL_DN = 4, + QPNP_GPIO_PULL_NO = 5 + + @vin_sel: specifies the voltage level when the output is set to 1. + For an input gpio specifies the voltage level at which + the input is interpreted as a logical 1. + QPNP_GPIO_VIN0 = 0, + QPNP_GPIO_VIN1 = 1, + QPNP_GPIO_VIN2 = 2, + QPNP_GPIO_VIN3 = 3, + QPNP_GPIO_VIN4 = 4, + QPNP_GPIO_VIN5 = 5, + QPNP_GPIO_VIN6 = 6, + QPNP_GPIO_VIN7 = 7 + + @out_strength: the amount of current supplied for an output gpio. + QPNP_GPIO_OUT_STRENGTH_HIGH = 1, + QPNP_GPIO_OUT_STRENGTH_MED = 2, + QPNP_GPIO_OUT_STRENGTH_LOW = 3 + + @source_sel: choose alternate function for the gpio. Certain gpios + can be paired (shorted) with each other. Some gpio pin + can act as alternate functions. + QPNP_GPIO_FUNC_NORMAL = 0, + QPNP_GPIO_FUNC_PAIRED = 1 + QPNP_GPIO_FUNC_1 = 2, + QPNP_GPIO_FUNC_3 = 3, + QPNP_GPIO_DTEST1 = 4, + QPNP_GPIO_DTEST2 = 5, + QPNP_GPIO_DTEST3 = 6, + QPNP_GPIO_DTEST4 = 7 + + @inv_int_pol: Invert polarity before feeding the line to the interrupt + module in pmic. This feature will almost be never used + since the pm8xxx interrupt block can detect both edges + and both levels. + @master_en: 1 = Enable features within the GPIO block based on + configurations. + 0 = Completely disable the GPIO block and let the pin + float with high impedance regardless of other settings. + +[PMIC GPIO clients] + +Required properties : + - gpios : Contains 3 fields of the form <&gpio_controller pmic_gpio_num flags> + +[Example] + +qpnp: qcom,spmi@fc4c0000 { + #address-cells = <1>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <3>; + + qcom,pm8941@0 { + spmi-slave-container; + reg = <0x0>; + #address-cells = <1>; + #size-cells = <1>; + + pm8941_gpios: gpios { + spmi-dev-container; + compatible = "qcom,qpnp-gpio"; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <1>; + + qcom,pm8941_gpio1@0xc000 { + reg = <0xc000 0x100>; + qcom,qpnp-gpio-num = <62>; + }; + + qcom,pm8941_gpio2@0xc100 { + reg = <0xc100 0x100>; + qcom,qpnp-gpio-num = <20>; + qcom,qpnp-gpio-cfg = <0x1 0x1 0x1 0x2 0x3 0x2 0x0 0x0 0x1>; + }; + }; + + qcom,testgpio@1000 { + compatible = "qcom,qpnp-testgpio"; + reg = <0x1000 0x1000>; + gpios = <&pm8941_gpios 62 0x0 &pm8941_gpios 20 0x1>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/spmi/msm-spmi.txt b/Documentation/devicetree/bindings/spmi/msm-spmi.txt index 8fe415bd958..d50037feceb 100644 --- a/Documentation/devicetree/bindings/spmi/msm-spmi.txt +++ b/Documentation/devicetree/bindings/spmi/msm-spmi.txt @@ -1,13 +1,28 @@ * SPMI -The spmi Device Tree support interprets up to two levels of Device Tree +The SPMI Device Tree support interprets up to three levels of Device Tree topology. The first level is required and specifies only a slave address. The second level is optional and allows for the specification of different -offsets within the same 16-bit address space underneath a particular SPMI -slave ID. Within the second level, any number of address ranges can be -associated with a particular device within that 16-bit range. +device nodes within the same 16-bit address space underneath a particular +SPMI slave ID. Within the second level, any number of address ranges can be +associated with a particular device within that 16-bit range. An additional +flag allows for the possiblity to specify that all device nodes should +have their resources dedicated to only one spmi_device. This flag can +be specified at the second level, or an optional third level. By default +without this flag, one spmi_device is created for each device_node. -First level +[Root Node] + +Recommended properties : + - interrupt-controller : Used to specify the root node as the + interrupt controller for SPMI devices. + - #interrupt-cells : The number of cells used to express one interrupt. + +Notes : + - It is considered an error to include either spmi-container-dev or + spmi-slave-dev in the Root Node. + +[First Level Nodes] Required properites : @@ -22,51 +37,124 @@ Recommended properties : - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. -Second level +[Second Level Nodes] Required properties : - spmi-slave-container: Used by the parser to understand that this is the - second level of the tree. + second level of the tree that includes device nodes associated with the + same slave_id. - reg: where a is < 65536 and b is a size. Each device supports an arbitrary number of address ranges. - compatible : "qcom," prefixed string to match against the driver. Recommended properties : - - interrupts : where a is the slave ID, b is is the peripheral ID, + - interrupts : where a is the slave ID, b is the peripheral ID, c is the device interrupt number (0-7). Each device supports any arbitrary number of interrupts. - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. -Example: +Optional properties : + + - spmi-dev-container: This specifies that all the device nodes specified for + this slave_id should have their resources coalesced into only one + spmi_device. + +[Third Level Nodes] + +Required properties : + + - spmi-dev-container: This specifies that all the device nodes specified for + this slave_id should have their resources coalesced into only one + spmi_device. + - reg: where a is < 65536 and b is a size. Each device supports an + arbitrary number of address ranges. + - compatible : "qcom," prefixed string to match against the driver. + +Recommended properties : + + - interrupts : where a is the slave ID, b is the peripheral ID, + c is the device interrupt number (0-7). Each device supports any arbitrary + number of interrupts. + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + +Notes : + - It is considered an error to include spmi-slave-dev at this level. + +[Example] / { - qcom,spmi@fc4c0000 { + qpnp: qcom,spmi@fc4c0000 { #address-cells = <1>; #size-cells = <0>; - interrupt-parent = <&qpnpint>; - pmic8941@d { + interrupt-controller; + #interrupt-cells = <3>; + + testint@f { + interrupt-parent = <&qpnp>; + compatible = "qcom,qpnp-testint"; + reg = <0xf>; + interrupts = <0x3 0x15 0x0 0x3 0x15 0x02 0x1 0x47 0x0>; + + }; + + pm8941@0 { + spmi-slave-container; + reg = <0x0>; #address-cells = <1>; #size-cells = <1>; - reg = <0xd>; + + pm8941_gpios: gpios { + spmi-dev-container; + compatible = "qcom,qpnp-gpio"; + gpio-controller; + #gpio-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + + pm8941_gpio1@0xc000 { + compatible = "qcom,qpnp-gpio"; + reg = <0xc000 0x100>; + qcom,qpnp_gpio = <1>; + interrupt-parent = <&qpnp>; + interrupts = <0x3 0x15 0x02 0x1 0x47 0x0>; + }; + + pm8941_gpio2@0xc100 { + compatible = "qcom,qpnp-gpio"; + reg = <0xc100 0x100>; + qcom,qpnp_gpio = <2>; + interrupt-parent = <&qpnp>; + interrupts = <0x3 0x15 0x0>; + }; + }; + + testgpio@0x1000 { + compatible = "qcom,qpnp-testgpio"; + reg = <0x1000 0x1000>; + qpnp-gpios = <&pm8941_gpios 0x0>; + }; + }; + pm8841@2 { spmi-slave-container; - - coincell@2800 { - compatible = "qcom,qpnp-coincell"; - reg = <0x2800 0x4000>; - interrupts = <0xd 0x28 0x6 0xd 0x28 0x3>; - - }; - pon@800 { - compatible = "qcom,qpnp-pon"; - reg = <0x800 0x4000>; - }; - }; - customer_dev@2 { - compatible = "qcom,qpnp-pon"; reg = <0x2>; - interrupts = <0x2 0x08 0x1 0x2 0x8 0x3>; + #address-cells = <1>; + #size-cells = <1>; + spmi-dev-container; + compatible = "qcom,qpnp-gpio"; + + pm8841_gpio1@0xc000 { + reg = <0xc000 0x100>; + qcom,qpnp_gpio = <1>; + }; + + pm8841_gpio2@0xc100 { + reg = <0xc100 0x100>; + qcom,qpnp_gpio = <2>; + }; }; + }; }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8f3263e6033..8328ee2a3e7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -477,4 +477,12 @@ config GPIO_PM8XXX_RPC This option enables support for on-chip GPIO found on Qualcomm PM8xxx PMICs through RPC. +config GPIO_QPNP + depends on ARCH_MSMCOPPER + depends on OF_SPMI + depends on MSM_QPNP_INT + tristate "Qualcomm QPNP GPIO support" + help + Say 'y' here to include support for the Qualcomm QPNP gpio + support. QPNP is a SPMI based PMIC implementation. endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 83972f1192e..db19ac89e6d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o obj-$(CONFIG_GPIO_PM8XXX) += pm8xxx-gpio.o obj-$(CONFIG_GPIO_PM8XXX_MPP) += pm8xxx-mpp.o obj-$(CONFIG_GPIO_PM8XXX_RPC) += gpio-pm8xxx-rpc.o +obj-$(CONFIG_GPIO_QPNP) += qpnp-gpio.o diff --git a/drivers/gpio/qpnp-gpio.c b/drivers/gpio/qpnp-gpio.c new file mode 100644 index 00000000000..b09b040c756 --- /dev/null +++ b/drivers/gpio/qpnp-gpio.c @@ -0,0 +1,706 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define Q_REG_ADDR(q_spec, reg_index) \ + ((q_spec)->offset + reg_index) + +#define Q_REG_STATUS1 0x8 +#define Q_NUM_CTL_REGS 5 + +/* control register base address offsets */ +#define Q_REG_IO_CTL1 0x42 +#define Q_REG_INPUT_CTL1 0x43 +#define Q_REG_OUTPUT_CTL1 0x44 +#define Q_REG_OUTPUT_CTL2 0x45 +#define Q_REG_EN_CTL1 0x46 + +/* control register regs array indices */ +#define Q_REG_I_IO_CTL1 0 +#define Q_REG_I_INPUT_CTL1 1 +#define Q_REG_I_OUTPUT_CTL1 2 +#define Q_REG_I_OUTPUT_CTL2 3 +#define Q_REG_I_EN_CTL1 4 + +/* control register configuration */ +#define Q_REG_VIN_SHIFT 0 +#define Q_REG_VIN_MASK 0x7 +#define Q_REG_PULL_SHIFT 4 +#define Q_REG_PULL_MASK 0x70 +#define Q_REG_INPUT_EN_SHIFT 7 +#define Q_REG_INPUT_EN_MASK 0x80 +#define Q_REG_OUT_STRENGTH_SHIFT 0 +#define Q_REG_OUT_STRENGTH_MASK 0x3 +#define Q_REG_OUT_TYPE_SHIFT 6 +#define Q_REG_OUT_TYPE_MASK 0x40 +#define Q_REG_OUT_INVERT_SHIFT 0 +#define Q_REG_OUT_INVERT_MASK 0x1 +#define Q_REG_SRC_SEL_SHIFT 1 +#define Q_REG_SRC_SEL_MASK 0xE +#define Q_REG_OUTPUT_EN_SHIFT 7 +#define Q_REG_OUTPUT_EN_MASK 0x80 +#define Q_REG_MASTER_EN_SHIFT 7 +#define Q_REG_MASTER_EN_MASK 0x80 + + +struct qpnp_gpio_spec { + uint8_t slave; /* 0-15 */ + uint16_t offset; /* 0-255 */ + uint32_t gpio_chip_idx; /* offset from gpio_chip base */ + int irq; /* logical IRQ number */ + u8 regs[Q_NUM_CTL_REGS]; /* Control regs */ +}; + +struct qpnp_gpio_chip { + struct gpio_chip gpio_chip; + struct spmi_device *spmi; + struct qpnp_gpio_spec **pmic_gpios; + struct qpnp_gpio_spec **chip_gpios; + uint32_t pmic_gpio_lowest; + uint32_t pmic_gpio_highest; + struct device_node *int_ctrl; + struct list_head chip_list; +}; + +static LIST_HEAD(qpnp_gpio_chips); +static DEFINE_MUTEX(qpnp_gpio_chips_lock); + +static inline void qpnp_pmic_gpio_set_spec(struct qpnp_gpio_chip *q_chip, + uint32_t pmic_gpio, + struct qpnp_gpio_spec *spec) +{ + q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest] = spec; +} + +static inline struct qpnp_gpio_spec *qpnp_pmic_gpio_get_spec( + struct qpnp_gpio_chip *q_chip, + uint32_t pmic_gpio) +{ + if (pmic_gpio < q_chip->pmic_gpio_lowest || + pmic_gpio > q_chip->pmic_gpio_highest) + return NULL; + + return q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest]; +} + +static inline struct qpnp_gpio_spec *qpnp_chip_gpio_get_spec( + struct qpnp_gpio_chip *q_chip, + uint32_t chip_gpio) +{ + if (chip_gpio > q_chip->gpio_chip.ngpio) + return NULL; + + return q_chip->chip_gpios[chip_gpio]; +} + +static inline void qpnp_chip_gpio_set_spec(struct qpnp_gpio_chip *q_chip, + uint32_t chip_gpio, + struct qpnp_gpio_spec *spec) +{ + q_chip->chip_gpios[chip_gpio] = spec; +} + +int qpnp_gpio_config(int gpio, struct qpnp_gpio_cfg *param) +{ + int rc, chip_offset; + struct qpnp_gpio_chip *q_chip; + struct qpnp_gpio_spec *q_spec = NULL; + struct gpio_chip *gpio_chip; + + if (param == NULL) + return -EINVAL; + + mutex_lock(&qpnp_gpio_chips_lock); + list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) { + gpio_chip = &q_chip->gpio_chip; + if (gpio >= gpio_chip->base + && gpio < gpio_chip->base + gpio_chip->ngpio) { + chip_offset = gpio - gpio_chip->base; + q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset); + if (WARN_ON(!q_spec)) { + mutex_unlock(&qpnp_gpio_chips_lock); + return -ENODEV; + } + break; + } + } + mutex_unlock(&qpnp_gpio_chips_lock); + if (!q_spec) { + pr_err("gpio %d not handled by any pmic\n", gpio); + return -EINVAL; + } + + q_spec->regs[Q_REG_I_IO_CTL1] = (param->vin_sel << + Q_REG_VIN_SHIFT) & Q_REG_VIN_MASK; + q_spec->regs[Q_REG_I_IO_CTL1] |= (param->pull << + Q_REG_PULL_SHIFT) & Q_REG_PULL_MASK; + q_spec->regs[Q_REG_I_INPUT_CTL1] = ((param->direction & + QPNP_GPIO_DIR_IN) ? ((1 << Q_REG_INPUT_EN_SHIFT)) : 0); + + if (param->direction & QPNP_GPIO_DIR_OUT) { + q_spec->regs[Q_REG_I_OUTPUT_CTL1] = (param->out_strength + << Q_REG_OUT_STRENGTH_SHIFT) & Q_REG_OUT_STRENGTH_MASK; + q_spec->regs[Q_REG_I_OUTPUT_CTL1] |= (param->output_type + << Q_REG_OUT_TYPE_SHIFT) & Q_REG_OUT_TYPE_MASK; + } else { + q_spec->regs[Q_REG_I_OUTPUT_CTL1] = 0; + } + + if (param->direction & QPNP_GPIO_DIR_OUT) { + q_spec->regs[Q_REG_I_OUTPUT_CTL2] = (param->inv_int_pol + << Q_REG_OUT_INVERT_SHIFT) & Q_REG_OUT_INVERT_MASK; + q_spec->regs[Q_REG_I_OUTPUT_CTL2] |= (param->src_select + << Q_REG_SRC_SEL_SHIFT) & Q_REG_SRC_SEL_MASK; + q_spec->regs[Q_REG_I_OUTPUT_CTL2] |= (1 << + Q_REG_OUTPUT_EN_SHIFT) & Q_REG_OUTPUT_EN_MASK; + } else { + q_spec->regs[Q_REG_I_OUTPUT_CTL2] = 0; + } + + q_spec->regs[Q_REG_I_EN_CTL1] = (param->master_en << + Q_REG_MASTER_EN_SHIFT) & Q_REG_MASTER_EN_MASK; + + rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave, + Q_REG_ADDR(q_spec, Q_REG_IO_CTL1), + &q_spec->regs[Q_REG_I_IO_CTL1], Q_NUM_CTL_REGS); + if (rc) + dev_err(&q_chip->spmi->dev, "%s: unable to write master" + " enable\n", __func__); + + return rc; +} +EXPORT_SYMBOL(qpnp_gpio_config); + +int qpnp_gpio_map_gpio(uint16_t slave_id, uint32_t pmic_gpio) +{ + struct qpnp_gpio_chip *q_chip; + struct qpnp_gpio_spec *q_spec = NULL; + + mutex_lock(&qpnp_gpio_chips_lock); + list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) { + if (q_chip->spmi->sid != slave_id) + continue; + if (q_chip->pmic_gpio_lowest <= pmic_gpio && + q_chip->pmic_gpio_highest >= pmic_gpio) { + q_spec = qpnp_pmic_gpio_get_spec(q_chip, pmic_gpio); + mutex_unlock(&qpnp_gpio_chips_lock); + if (WARN_ON(!q_spec)) + return -ENODEV; + return q_chip->gpio_chip.base + q_spec->gpio_chip_idx; + } + } + mutex_unlock(&qpnp_gpio_chips_lock); + return -EINVAL; +} +EXPORT_SYMBOL(qpnp_gpio_map_gpio); + +static int qpnp_gpio_to_irq(struct gpio_chip *gpio_chip, unsigned offset) +{ + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev); + struct qpnp_gpio_spec *q_spec; + + q_spec = qpnp_chip_gpio_get_spec(q_chip, offset); + if (!q_spec) + return -EINVAL; + + return q_spec->irq; +} + +static int qpnp_gpio_get(struct gpio_chip *gpio_chip, unsigned offset) +{ + int rc, ret_val; + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev); + struct qpnp_gpio_spec *q_spec = NULL; + u8 buf[1]; + + if (WARN_ON(!q_chip)) + return -ENODEV; + + q_spec = qpnp_chip_gpio_get_spec(q_chip, offset); + if (WARN_ON(!q_spec)) + return -ENODEV; + + /* gpio val is from RT status iff input is enabled */ + if (q_spec->regs[Q_REG_I_INPUT_CTL1] & Q_REG_INPUT_EN_MASK) { + /* INT_RT_STS */ + rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave, + Q_REG_ADDR(q_spec, Q_REG_STATUS1), + &buf[0], 1); + return buf[0]; + + } else { + ret_val = (q_spec->regs[Q_REG_I_OUTPUT_CTL2] & + Q_REG_OUT_INVERT_MASK) >> Q_REG_OUT_INVERT_SHIFT; + return ret_val; + } + + return 0; +} + +static int __qpnp_gpio_set(struct qpnp_gpio_chip *q_chip, + struct qpnp_gpio_spec *q_spec, int value) +{ + int rc; + + if (!q_chip || !q_spec) + return -EINVAL; + + q_spec->regs[Q_REG_I_OUTPUT_CTL2] &= ~(1 << Q_REG_OUT_INVERT_SHIFT); + + if (value) + q_spec->regs[Q_REG_I_OUTPUT_CTL2] |= + (1 << Q_REG_OUT_INVERT_SHIFT); + + rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave, + Q_REG_ADDR(q_spec, Q_REG_OUTPUT_CTL2), + &q_spec->regs[Q_REG_I_OUTPUT_CTL2], 1); + if (rc) + dev_err(&q_chip->spmi->dev, "%s: spmi write failed\n", + __func__); + return rc; +} + + +static void qpnp_gpio_set(struct gpio_chip *gpio_chip, + unsigned offset, int value) +{ + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev); + struct qpnp_gpio_spec *q_spec; + + if (WARN_ON(!q_chip)) + return; + + q_spec = qpnp_chip_gpio_get_spec(q_chip, offset); + if (WARN_ON(!q_spec)) + return; + + __qpnp_gpio_set(q_chip, q_spec, value); +} + +static int qpnp_gpio_set_direction(struct qpnp_gpio_chip *q_chip, + struct qpnp_gpio_spec *q_spec, int direction) +{ + int rc; + + if (!q_chip || !q_spec) + return -EINVAL; + + if (direction & QPNP_GPIO_DIR_IN) { + q_spec->regs[Q_REG_I_INPUT_CTL1] |= + (1 << Q_REG_INPUT_EN_SHIFT); + q_spec->regs[Q_REG_I_OUTPUT_CTL2] &= + ~(1 << Q_REG_OUTPUT_EN_SHIFT); + } else { + q_spec->regs[Q_REG_I_INPUT_CTL1] &= + ~(1 << Q_REG_INPUT_EN_SHIFT); + q_spec->regs[Q_REG_I_OUTPUT_CTL2] |= + (1 << Q_REG_OUTPUT_EN_SHIFT); + } + + rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave, + Q_REG_ADDR(q_spec, Q_REG_INPUT_CTL1), + &q_spec->regs[Q_REG_I_INPUT_CTL1], 3); + return rc; +} + +static int qpnp_gpio_direction_input(struct gpio_chip *gpio_chip, + unsigned offset) +{ + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev); + struct qpnp_gpio_spec *q_spec; + + if (WARN_ON(!q_chip)) + return -ENODEV; + + q_spec = qpnp_chip_gpio_get_spec(q_chip, offset); + if (WARN_ON(!q_spec)) + return -ENODEV; + + return qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_IN); +} + +static int qpnp_gpio_direction_output(struct gpio_chip *gpio_chip, + unsigned offset, + int val) +{ + int rc; + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev); + struct qpnp_gpio_spec *q_spec; + + if (WARN_ON(!q_chip)) + return -ENODEV; + + q_spec = qpnp_chip_gpio_get_spec(q_chip, offset); + if (WARN_ON(!q_spec)) + return -ENODEV; + + rc = __qpnp_gpio_set(q_chip, q_spec, val); + if (rc) + return rc; + + rc = qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_OUT); + + return rc; +} + +static int qpnp_gpio_of_gpio_xlate(struct gpio_chip *gpio_chip, + struct device_node *np, + const void *gpio_spec, u32 *flags) +{ + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev); + struct qpnp_gpio_spec *q_spec; + const __be32 *gpio = gpio_spec; + u32 n = be32_to_cpup(gpio); + + if (WARN_ON(gpio_chip->of_gpio_n_cells < 2)) { + pr_err("%s: of_gpio_n_cells < 2\n", __func__); + return -EINVAL; + } + + q_spec = qpnp_pmic_gpio_get_spec(q_chip, n); + if (!q_spec) { + pr_err("%s: no such PMIC gpio %u in device topology\n", + __func__, n); + return -EINVAL; + } + + if (flags) + *flags = be32_to_cpu(gpio[1]); + + return q_spec->gpio_chip_idx; +} + +static int qpnp_gpio_config_default(struct spmi_device *spmi, + const __be32 *prop, int gpio) +{ + struct qpnp_gpio_cfg param; + int rc; + + dev_dbg(&spmi->dev, "%s: p[0]: 0x%x p[1]: 0x%x p[2]: 0x%x p[3]:" + " 0x%x p[4]: 0x%x p[5]: 0x%x p[6]: 0x%x p[7]: 0x%x" + " p[8]: 0x%x\n", __func__, + be32_to_cpup(&prop[0]), be32_to_cpup(&prop[1]), + be32_to_cpup(&prop[2]), be32_to_cpup(&prop[3]), + be32_to_cpup(&prop[4]), be32_to_cpup(&prop[5]), + be32_to_cpup(&prop[6]), be32_to_cpup(&prop[7]), + be32_to_cpup(&prop[8])); + + param.direction = be32_to_cpup(&prop[0]); + param.output_type = be32_to_cpup(&prop[1]); + param.output_value = be32_to_cpup(&prop[2]); + param.pull = be32_to_cpup(&prop[3]); + param.vin_sel = be32_to_cpup(&prop[4]); + param.out_strength = be32_to_cpup(&prop[5]); + param.src_select = be32_to_cpup(&prop[6]); + param.inv_int_pol = be32_to_cpup(&prop[7]); + param.master_en = be32_to_cpup(&prop[8]); + + rc = qpnp_gpio_config(gpio, ¶m); + if (rc) + dev_err(&spmi->dev, "%s: unable to set default config for" + " gpio %d\n", __func__, gpio); + return rc; +} + +static int qpnp_gpio_free_chip(struct qpnp_gpio_chip *q_chip) +{ + struct spmi_device *spmi = q_chip->spmi; + int rc, i; + + if (q_chip->chip_gpios) + for (i = 0; i < spmi->num_dev_node; i++) + kfree(q_chip->chip_gpios[i]); + + mutex_lock(&qpnp_gpio_chips_lock); + list_del(&q_chip->chip_list); + mutex_unlock(&qpnp_gpio_chips_lock); + rc = gpiochip_remove(&q_chip->gpio_chip); + if (rc) + dev_err(&q_chip->spmi->dev, "%s: unable to remove gpio\n", + __func__); + kfree(q_chip->chip_gpios); + kfree(q_chip->pmic_gpios); + kfree(q_chip); + return rc; +} + +static int qpnp_gpio_probe(struct spmi_device *spmi) +{ + struct qpnp_gpio_chip *q_chip; + struct resource *res; + struct qpnp_gpio_spec *q_spec; + const __be32 *prop; + int i, rc, ret, gpio, len; + int lowest_gpio = INT_MAX, highest_gpio = INT_MIN; + u32 intspec[3]; + + q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL); + if (!q_chip) { + dev_err(&spmi->dev, "%s: Can't allocate gpio_chip\n", + __func__); + return -ENOMEM; + } + q_chip->spmi = spmi; + dev_set_drvdata(&spmi->dev, q_chip); + + mutex_lock(&qpnp_gpio_chips_lock); + list_add(&q_chip->chip_list, &qpnp_gpio_chips); + mutex_unlock(&qpnp_gpio_chips_lock); + + /* first scan through nodes to find the range required for allocation */ + for (i = 0; i < spmi->num_dev_node; i++) { + prop = of_get_property(spmi->dev_node[i].of_node, + "qcom,qpnp-gpio-num", &len); + if (!prop) { + dev_err(&spmi->dev, "%s: unable to get" + " qcom,qpnp-gpio-num property\n", __func__); + ret = -EINVAL; + goto err_probe; + } else if (len != sizeof(__be32)) { + dev_err(&spmi->dev, "%s: Invalid qcom,qpnp-gpio-num" + " property\n", __func__); + ret = -EINVAL; + goto err_probe; + } + + gpio = be32_to_cpup(prop); + if (gpio < lowest_gpio) + lowest_gpio = gpio; + if (gpio > highest_gpio) + highest_gpio = gpio; + } + + if (highest_gpio < lowest_gpio) { + dev_err(&spmi->dev, "%s: no device nodes specified in" + " topology\n", __func__); + ret = -EINVAL; + goto err_probe; + } else if (lowest_gpio == 0) { + dev_err(&spmi->dev, "%s: 0 is not a valid PMIC GPIO\n", + __func__); + ret = -EINVAL; + goto err_probe; + } + + q_chip->pmic_gpio_lowest = lowest_gpio; + q_chip->pmic_gpio_highest = highest_gpio; + + /* allocate gpio lookup tables */ + q_chip->pmic_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) * + highest_gpio - lowest_gpio + 1, + GFP_KERNEL); + q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) * + spmi->num_dev_node, GFP_KERNEL); + if (!q_chip->pmic_gpios || !q_chip->chip_gpios) { + dev_err(&spmi->dev, "%s: unable to allocate memory\n", + __func__); + ret = -ENOMEM; + goto err_probe; + } + + /* get interrupt controller device_node */ + q_chip->int_ctrl = of_irq_find_parent(spmi->dev.of_node); + if (!q_chip->int_ctrl) { + dev_err(&spmi->dev, "%s: Can't find interrupt parent\n", + __func__); + ret = -EINVAL; + goto err_probe; + } + + /* now scan through again and populate the lookup table */ + for (i = 0; i < spmi->num_dev_node; i++) { + res = qpnp_get_resource(spmi, i, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&spmi->dev, "%s: node %s is missing has no" + " base address definition\n", + __func__, spmi->dev_node[i].of_node->full_name); + } + + prop = of_get_property(spmi->dev_node[i].of_node, + "qcom,qpnp-gpio-num", &len); + if (!prop) { + dev_err(&spmi->dev, "%s: unable to get" + " qcom,qpnp-gpio-num property\n", __func__); + ret = -EINVAL; + goto err_probe; + } else if (len != sizeof(__be32)) { + dev_err(&spmi->dev, "%s: Invalid qcom,qpnp-gpio-num" + " property\n", __func__); + ret = -EINVAL; + goto err_probe; + } + gpio = be32_to_cpup(prop); + + q_spec = kzalloc(sizeof(struct qpnp_gpio_spec), + GFP_KERNEL); + if (!q_spec) { + dev_err(&spmi->dev, "%s: unable to allocate" + " memory\n", + __func__); + ret = -ENOMEM; + goto err_probe; + } + + q_spec->slave = spmi->sid; + q_spec->offset = res->start; + q_spec->gpio_chip_idx = i; + + /* call into irq_domain to get irq mapping */ + intspec[0] = q_chip->spmi->sid; + intspec[1] = (q_spec->offset >> 8) & 0xFF; + intspec[2] = 0; + q_spec->irq = irq_create_of_mapping(q_chip->int_ctrl, + intspec, 3); + if (!q_spec->irq) { + dev_err(&spmi->dev, "%s: invalid irq for gpio" + " %u\n", __func__, gpio); + ret = -EINVAL; + goto err_probe; + } + /* initialize lookup table entries */ + qpnp_pmic_gpio_set_spec(q_chip, gpio, q_spec); + qpnp_chip_gpio_set_spec(q_chip, i, q_spec); + } + + q_chip->gpio_chip.base = -1; + q_chip->gpio_chip.ngpio = spmi->num_dev_node; + q_chip->gpio_chip.label = "qpnp-gpio"; + q_chip->gpio_chip.direction_input = qpnp_gpio_direction_input; + q_chip->gpio_chip.direction_output = qpnp_gpio_direction_output; + q_chip->gpio_chip.to_irq = qpnp_gpio_to_irq; + q_chip->gpio_chip.get = qpnp_gpio_get; + q_chip->gpio_chip.set = qpnp_gpio_set; + q_chip->gpio_chip.dev = &spmi->dev; + q_chip->gpio_chip.of_xlate = qpnp_gpio_of_gpio_xlate; + q_chip->gpio_chip.of_gpio_n_cells = 2; + q_chip->gpio_chip.can_sleep = 0; + + rc = gpiochip_add(&q_chip->gpio_chip); + if (rc) { + dev_err(&spmi->dev, "%s: Can't add gpio chip, rc = %d\n", + __func__, rc); + ret = rc; + goto err_probe; + } + + /* now configure gpio defaults if they exist */ + for (i = 0; i < spmi->num_dev_node; i++) { + q_spec = qpnp_chip_gpio_get_spec(q_chip, i); + if (WARN_ON(!q_spec)) + return -ENODEV; + + /* It's not an error to not config a default */ + prop = of_get_property(spmi->dev_node[i].of_node, + "qcom,qpnp-gpio-cfg", &len); + /* 9 data values constitute one tuple */ + if (prop && (len != (9 * sizeof(__be32)))) { + dev_err(&spmi->dev, "%s: invalid format for" + " qcom,qpnp-gpio-cfg property\n", + __func__); + ret = -EINVAL; + goto err_probe; + } else if (prop) { + rc = qpnp_gpio_config_default(spmi, prop, + q_chip->gpio_chip.base + i); + if (rc) { + ret = rc; + goto err_probe; + } + } else { + /* initialize with hardware defaults */ + rc = spmi_ext_register_readl( + q_chip->spmi->ctrl, q_spec->slave, + Q_REG_ADDR(q_spec, Q_REG_IO_CTL1), + &q_spec->regs[Q_REG_I_IO_CTL1], + Q_NUM_CTL_REGS); + q_spec->regs[Q_REG_I_EN_CTL1] |= + (1 << Q_REG_MASTER_EN_SHIFT); + rc = spmi_ext_register_writel( + q_chip->spmi->ctrl, q_spec->slave, + Q_REG_ADDR(q_spec, Q_REG_EN_CTL1), + &q_spec->regs[Q_REG_EN_CTL1], 1); + if (rc) { + dev_err(&spmi->dev, "%s: spmi write" + " failed\n", __func__); + ret = rc; + goto err_probe; + } + } + } + + dev_dbg(&spmi->dev, "%s: gpio_chip registered between %d-%u\n", + __func__, q_chip->gpio_chip.base, + (q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1); + return 0; + +err_probe: + qpnp_gpio_free_chip(q_chip); + return ret; +} + +static int qpnp_gpio_remove(struct spmi_device *spmi) +{ + struct qpnp_gpio_chip *q_chip = dev_get_drvdata(&spmi->dev); + + return qpnp_gpio_free_chip(q_chip); +} + +static struct of_device_id spmi_match_table[] = { + { .compatible = "qcom,qpnp-gpio", + }, + {} +}; + +static const struct spmi_device_id qpnp_gpio_id[] = { + { "qcom,qpnp-gpio", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spmi, qpnp_gpio_id); + +static struct spmi_driver qpnp_gpio_driver = { + .driver = { + .name = "qcom,qpnp-gpio", + .of_match_table = spmi_match_table, + }, + .probe = qpnp_gpio_probe, + .remove = qpnp_gpio_remove, + .id_table = qpnp_gpio_id, +}; + +static int __init qpnp_gpio_init(void) +{ + return spmi_driver_register(&qpnp_gpio_driver); +} + +static void __exit qpnp_gpio_exit(void) +{ +} + +MODULE_AUTHOR( + "Michael Bohan "); +MODULE_DESCRIPTION("QPNP PMIC gpio driver"); +MODULE_LICENSE("GPLv2"); + +module_init(qpnp_gpio_init); +module_exit(qpnp_gpio_exit); diff --git a/drivers/of/of_spmi.c b/drivers/of/of_spmi.c index 2032b462708..112497ccb74 100644 --- a/drivers/of/of_spmi.c +++ b/drivers/of/of_spmi.c @@ -18,6 +18,7 @@ #include #include #include +#include struct of_spmi_dev_info { struct spmi_controller *ctrl; @@ -30,14 +31,49 @@ struct of_spmi_res_info { uint32_t num_irq; }; -static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg) +/* + * Initialize r_info structure for safe usage + */ +static inline void of_spmi_init_resource(struct of_spmi_res_info *r_info, + struct device_node *node) +{ + r_info->node = node; + r_info->num_reg = 0; + r_info->num_irq = 0; +} + +/* + * Allocate dev_node array for spmi_device + */ +static inline int of_spmi_alloc_device_store(struct of_spmi_dev_info *d_info, + uint32_t num_dev_node) +{ + d_info->b_info.num_dev_node = num_dev_node; + d_info->b_info.dev_node = kzalloc(sizeof(struct spmi_resource) * + num_dev_node, GFP_KERNEL); + if (!d_info->b_info.dev_node) + return -ENOMEM; + + return 0; +} + +/* + * Calculate the number of resources to allocate + * + * The caller is responsible for initializing the of_spmi_res_info structure. + */ +static void of_spmi_sum_node_resources(struct of_spmi_res_info *r_info, + bool has_reg) { struct of_irq oirq; uint64_t size; uint32_t flags; + int i = 0; - while (of_irq_map_one(r_info->node, r_info->num_irq, &oirq) == 0) - r_info->num_irq++; + while (of_irq_map_one(r_info->node, i, &oirq) == 0) + i++; + + r_info->num_irq += i; if (!has_reg) return; @@ -48,16 +84,32 @@ static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg) * parent buses have a size-cell of 0. But SPMI does have a * size-cell of 0. */ - while (of_get_address(r_info->node, r_info->num_reg, - &size, &flags) != NULL) - r_info->num_reg++; + i = 0; + while (of_get_address(r_info->node, i, &size, &flags) != NULL) + i++; + + r_info->num_reg += i; } -/** - * Allocate resources for a child of a spmi-slave node. +/* + * free spmi_resource for the spmi_device */ -static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info, - struct of_spmi_res_info *r_info) +static void of_spmi_free_device_resources(struct of_spmi_dev_info *d_info) +{ + int i; + + for (i = 0; i < d_info->b_info.num_dev_node; i++) + kfree(d_info->b_info.dev_node[i].resource); + + kfree(d_info->b_info.dev_node); +} + +/* + * Gather node resources and populate + */ +static void of_spmi_populate_node_resources(struct of_spmi_dev_info *d_info, + struct of_spmi_res_info *r_info, + int idx) { uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg; @@ -67,13 +119,10 @@ static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info, uint64_t size; uint32_t flags; - if (num_irq || num_reg) { - res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); - if (!res) - return -ENOMEM; + res = d_info->b_info.dev_node[idx].resource; + d_info->b_info.dev_node[idx].of_node = r_info->node; - d_info->b_info.num_resources = num_reg + num_irq; - d_info->b_info.resource = res; + if ((num_irq || num_reg) && (res != NULL)) { for (i = 0; i < num_reg; i++, res++) { /* Addresses are always 16 bits */ addrp = of_get_address(r_info->node, i, &size, &flags); @@ -85,10 +134,34 @@ static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info, WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) != num_irq); } +} + +/* + * Allocate enough memory to handle the resources associated with the + * device_node. The number of device nodes included in this allocation + * depends on whether the spmi-dev-container flag is specified or not. + */ +static int of_spmi_allocate_node_resources(struct of_spmi_dev_info *d_info, + struct of_spmi_res_info *r_info, + uint32_t idx) +{ + uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg; + struct resource *res = NULL; + + if (num_irq || num_reg) { + res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); + if (!res) + return -ENOMEM; + } + d_info->b_info.dev_node[idx].num_resources = num_reg + num_irq; + d_info->b_info.dev_node[idx].resource = res; return 0; } +/* + * create a single spmi_device + */ static int of_spmi_create_device(struct of_spmi_dev_info *d_info, struct device_node *node) { @@ -117,6 +190,71 @@ static int of_spmi_create_device(struct of_spmi_dev_info *d_info, return 0; } +/* + * Walks all children of a node containing the spmi-dev-container + * binding. This special type of spmi_device can include resources + * from more than one device node. + */ +static void of_spmi_walk_dev_container(struct of_spmi_dev_info *d_info, + struct device_node *container) +{ + struct of_spmi_res_info r_info = {}; + struct spmi_controller *ctrl = d_info->ctrl; + struct device_node *node; + int rc, i, num_dev_node = 0; + + /* first count the total number of device_nodes */ + for_each_child_of_node(container, node) + num_dev_node++; + + rc = of_spmi_alloc_device_store(d_info, num_dev_node); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to allocate" + " device resources\n", __func__); + return; + } + + /* allocate resources per device_node */ + i = 0; + for_each_child_of_node(container, node) { + of_spmi_init_resource(&r_info, node); + of_spmi_sum_node_resources(&r_info, 1); + rc = of_spmi_allocate_node_resources(d_info, &r_info, i); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to allocate" + " resources\n", __func__); + of_spmi_free_device_resources(d_info); + return; + } + i++; + } + + /** + * Now we need to cycle through again and actually populate + * the resources for each node. + */ + i = 0; + for_each_child_of_node(container, node) { + of_spmi_init_resource(&r_info, node); + of_spmi_sum_node_resources(&r_info, 1); + of_spmi_populate_node_resources(d_info, &r_info, i); + i++; + } + + rc = of_spmi_create_device(d_info, container); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to create device for" + " node %s\n", __func__, container->full_name); + of_spmi_free_device_resources(d_info); + return; + } +} + +/* + * Walks all children of a node containing the spmi-slave-container + * binding. This indicates that all spmi_devices created from this + * point all share the same slave_id. + */ static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info, struct device_node *container) { @@ -127,59 +265,133 @@ static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info, for_each_child_of_node(container, node) { struct of_spmi_res_info r_info; - r_info.node = node; - of_spmi_sum_resources(&r_info, 1); + /** + * Check to see if this node contains children which + * should be all created as the same spmi_device. + */ + if (of_get_property(node, "spmi-dev-container", NULL)) { + of_spmi_walk_dev_container(d_info, node); + continue; + } - rc = of_spmi_allocate_resources(d_info, &r_info); + rc = of_spmi_alloc_device_store(d_info, 1); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to allocate" + " device resources\n", __func__); + goto slave_err; + } + + of_spmi_init_resource(&r_info, node); + of_spmi_sum_node_resources(&r_info, 1); + + rc = of_spmi_allocate_node_resources(d_info, &r_info, 0); if (rc) { dev_err(&ctrl->dev, "%s: unable to allocate" " resources\n", __func__); - return; + goto slave_err; } + + of_spmi_populate_node_resources(d_info, &r_info, 0); + rc = of_spmi_create_device(d_info, node); if (rc) { dev_err(&ctrl->dev, "%s: unable to create device for" " node %s\n", __func__, node->full_name); - return; + goto slave_err; } } + return; + +slave_err: + of_spmi_free_device_resources(d_info); } int of_spmi_register_devices(struct spmi_controller *ctrl) { - struct device_node *node; + struct device_node *node = ctrl->dev.of_node; /* Only register child devices if the ctrl has a node pointer set */ - if (!ctrl->dev.of_node) + if (!node) return -ENODEV; + if (of_get_property(node, "spmi-slave-container", NULL)) { + dev_err(&ctrl->dev, "%s: structural error: spmi-slave-container" + " is prohibited at the root level\n", __func__); + return -EINVAL; + } else if (of_get_property(node, "spmi-dev-container", NULL)) { + dev_err(&ctrl->dev, "%s: structural error: spmi-dev-container" + " is prohibited at the root level\n", __func__); + return -EINVAL; + } + + /** + * Make best effort to launch as many nodes as possible. If there are + * syntax errors, we will simply ignore that subtree and keep going. + */ for_each_child_of_node(ctrl->dev.of_node, node) { struct of_spmi_dev_info d_info = {}; const __be32 *slave_id; - int len, rc; + int len, rc, have_dev_container = 0; slave_id = of_get_property(node, "reg", &len); if (!slave_id) { - dev_err(&ctrl->dev, "of_spmi: invalid sid " - "on %s\n", node->full_name); + dev_err(&ctrl->dev, "%s: invalid sid " + "on %s\n", __func__, node->full_name); continue; } d_info.b_info.slave_id = be32_to_cpup(slave_id); d_info.ctrl = ctrl; + if (of_get_property(node, "spmi-dev-container", NULL)) + have_dev_container = 1; if (of_get_property(node, "spmi-slave-container", NULL)) { - of_spmi_walk_slave_container(&d_info, node); - continue; + if (have_dev_container) + of_spmi_walk_dev_container(&d_info, node); + else + of_spmi_walk_slave_container(&d_info, node); } else { struct of_spmi_res_info r_info; - r_info.node = node; - of_spmi_sum_resources(&r_info, 0); - rc = of_spmi_allocate_resources(&d_info, &r_info); - if (rc) + /** + * A dev container at the second level without a slave + * container is considered an error. + */ + if (have_dev_container) { + dev_err(&ctrl->dev, "%s: structural error," + " node %s has spmi-dev-container without" + " specifying spmi-slave-container\n", + __func__, node->full_name); continue; - of_spmi_create_device(&d_info, node); + } + + rc = of_spmi_alloc_device_store(&d_info, 1); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to allocate" + " device resources\n", __func__); + continue; + } + + of_spmi_init_resource(&r_info, node); + of_spmi_sum_node_resources(&r_info, 0); + rc = of_spmi_allocate_node_resources(&d_info, + &r_info, 0); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to allocate" + " resources\n", __func__); + of_spmi_free_device_resources(&d_info); + continue; + } + + of_spmi_populate_node_resources(&d_info, &r_info, 0); + + rc = of_spmi_create_device(&d_info, node); + if (rc) { + dev_err(&ctrl->dev, "%s: unable to create" + " device\n", __func__); + of_spmi_free_device_resources(&d_info); + continue; + } } } diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 2842cd80881..20a290a304e 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -233,9 +233,9 @@ struct spmi_device *spmi_new_device(struct spmi_controller *ctrl, spmidev->name = info->name; spmidev->sid = info->slave_id; spmidev->dev.of_node = info->of_node; - spmidev->resource = info->resource; - spmidev->num_resources = info->num_resources; spmidev->dev.platform_data = (void *)info->platform_data; + spmidev->num_dev_node = info->num_dev_node; + spmidev->dev_node = info->dev_node; rc = spmi_add_device(spmidev); if (rc < 0) { diff --git a/include/linux/of_spmi.h b/include/linux/of_spmi.h index d07ce635adc..fe09dec4538 100644 --- a/include/linux/of_spmi.h +++ b/include/linux/of_spmi.h @@ -14,6 +14,17 @@ #include #ifdef CONFIG_OF_SPMI +/** + * of_spmi_register_devices() - Register devices in the SPMI Device Tree + * @ctrl: spmi_controller which devices should be registered to. + * + * This routine scans the SPMI Device Tree, allocating resources and + * creating spmi_devices according to the SPMI bus Device Tree + * hierarchy. Details of this hierarchy can be found in + * Documentation/devicetree/bindings/spmi. This routine is normally + * called from the probe routine of the driver registering as a + * spmi_controller. + */ int of_spmi_register_devices(struct spmi_controller *ctrl); #else static int of_spmi_register_devices(struct spmi_controller *ctrl) diff --git a/include/linux/qpnp/gpio.h b/include/linux/qpnp/gpio.h new file mode 100644 index 00000000000..0447a084b1e --- /dev/null +++ b/include/linux/qpnp/gpio.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#define QPNP_GPIO_DIR_OUT 1 +#define QPNP_GPIO_DIR_IN 2 +#define QPNP_GPIO_DIR_BOTH (QPNP_GPIO_DIR_OUT | QPNP_GPIO_DIR_IN) + +#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN 1 +#define QPNP_GPIO_OUT_BUF_CMOS 0 + +#define QPNP_GPIO_VIN0 0 +#define QPNP_GPIO_VIN1 1 +#define QPNP_GPIO_VIN2 2 +#define QPNP_GPIO_VIN3 3 +#define QPNP_GPIO_VIN4 4 +#define QPNP_GPIO_VIN5 5 +#define QPNP_GPIO_VIN6 6 +#define QPNP_GPIO_VIN7 7 + +#define QPNP_GPIO_PULL_UP_30 0 +#define QPNP_GPIO_PULL_UP_1P5 1 +#define QPNP_GPIO_PULL_UP_31P5 2 +#define QPNP_GPIO_PULL_UP_1P5_30 3 +#define QPNP_GPIO_PULL_DN 4 +#define QPNP_GPIO_PULL_NO 5 + +#define QPNP_GPIO_OUT_STRENGTH_LOW 1 +#define QPNP_GPIO_OUT_STRENGTH_MED 2 +#define QPNP_GPIO_OUT_STRENGTH_HIGH 3 + +#define QPNP_GPIO_FUNC_NORMAL 0 +#define QPNP_GPIO_FUNC_PAIRED 1 +#define QPNP_GPIO_FUNC_1 2 +#define QPNP_GPIO_FUNC_2 3 +#define QPNP_GPIO_DTEST1 4 +#define QPNP_GPIO_DTEST2 5 +#define QPNP_GPIO_DTEST3 6 +#define QPNP_GPIO_DTEST4 7 + +/** + * struct qpnp_gpio_cfg - structure to specify gpio configurtion values + * @direction: indicates whether the gpio should be input, output, or + * both. Should be of the type QPNP_GPIO_DIR_* + * @output_type: indicates gpio should be configured as CMOS or open + * drain. Should be of the type QPNP_GPIO_OUT_BUF_* + * @output_value: The gpio output value of the gpio line - 0 or 1 + * @pull: Indicates whether a pull up or pull down should be + * applied. If a pullup is required the current strength + * needs to be specified. Current values of 30uA, 1.5uA, + * 31.5uA, 1.5uA with 30uA boost are supported. This value + * should be one of the QPNP_GPIO_PULL_* + * @vin_sel: specifies the voltage level when the output is set to 1. + * For an input gpio specifies the voltage level at which + * the input is interpreted as a logical 1. + * @out_strength: the amount of current supplied for an output gpio, + * should be of the type QPNP_GPIO_STRENGTH_* + * @source_sel: choose alternate function for the gpio. Certain gpios + * can be paired (shorted) with each other. Some gpio pin + * can act as alternate functions. This parameter should + * be of type QPNP_GPIO_FUNC_* + * @inv_int_pol: Invert polarity before feeding the line to the interrupt + * module in pmic. This feature will almost be never used + * since the pm8xxx interrupt block can detect both edges + * and both levels. + * @master_en: 1 = Enable features within the GPIO block based on + * configurations. + * 0 = Completely disable the GPIO block and let the pin + * float with high impedance regardless of other settings. + */ +struct qpnp_gpio_cfg { + int direction; + int output_type; + int output_value; + int pull; + int vin_sel; + int out_strength; + int src_select; + int inv_int_pol; + int master_en; +}; + +/** + * qpnp_gpio_config - Apply gpio configuration for Linux gpio + * @gpio: Linux gpio number to configure. + * @param: parameters to configure. + * + * This routine takes a Linux gpio number that corresponds with a + * PMIC gpio and applies the configuration specified in 'param'. + * This gpio number can be ascertained by of_get_gpio_flags() or + * the qpnp_gpio_map_gpio() API. + */ +int qpnp_gpio_config(int gpio, struct qpnp_gpio_cfg *param); + +/** + * qpnp_gpio_map_gpio - Obtain Linux GPIO number from device spec + * @slave_id: slave_id of the spmi_device for the gpio in question. + * @pmic_gpio: PMIC gpio number to lookup. + * + * This routine is used in legacy configurations that do not support + * Device Tree. If you are using Device Tree, you should not use this. + * For such cases, use of_get_gpio() instead. + */ +int qpnp_gpio_map_gpio(uint16_t slave_id, uint32_t pmic_gpio); diff --git a/include/linux/spmi.h b/include/linux/spmi.h index ffcb24eb2db..927978af32b 100644 --- a/include/linux/spmi.h +++ b/include/linux/spmi.h @@ -87,25 +87,37 @@ struct spmi_driver { }; #define to_spmi_driver(d) container_of(d, struct spmi_driver, driver) +/** + * struct spmi_resource: spmi_resource for one device_node + * @num_resources: number of resources for this device node + * @resources: array of resources for this device_node + * @of_node: device_node of the resource in question + */ +struct spmi_resource { + struct resource *resource; + u32 num_resources; + struct device_node *of_node; +}; + /** * Client/device handle (struct spmi_device): * ------------------------------------------ - * This is the client/device handle returned when a SPMI device - * is registered with a controller. - * Pointer to this structure is used by client-driver as a handle. - * @dev: driver model representation of the device. - * @name: name of driver to use with this device. - * @ctrl: SPMI controller managing the bus hosting this device. - * @resource: array of resources for this device_node - * @num_resources: number of resources for this device node - * @sid: slave identifier. + * This is the client/device handle returned when a SPMI device + * is registered with a controller. + * Pointer to this structure is used by client-driver as a handle. + * @dev: Driver model representation of the device. + * @name: Name of driver to use with this device. + * @ctrl: SPMI controller managing the bus hosting this device. + * @dev_node: array of SPMI resources - one entry per device_node. + * @num_dev_node: number of device_node structures. + * @sid: Slave Identifier. */ struct spmi_device { struct device dev; const char *name; struct spmi_controller *ctrl; - struct resource *resource; - u32 num_resources; + struct spmi_resource *dev_node; + u32 num_dev_node; u8 sid; }; #define to_spmi_device(d) container_of(d, struct spmi_device, dev) @@ -115,16 +127,16 @@ struct spmi_device { * @slave_id: slave identifier. * @spmi_device: device to be registered with the SPMI framework. * @of_node: pointer to the OpenFirmware device node. - * @num_resources: number of resources for this device node - * @resource: array of resources for this device_node + * @dev_node: one spmi_resource for each device_node. + * @num_dev_node: number of device_node structures. * @platform_data: goes to spmi_device.dev.platform_data */ struct spmi_boardinfo { char name[SPMI_NAME_SIZE]; uint8_t slave_id; struct device_node *of_node; - u32 num_resources; - struct resource *resource; + struct spmi_resource *dev_node; + u32 num_dev_node; const void *platform_data; };