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;
};