From: Fenglin Wu <fengl...@codeaurora.org>

Add support for qcom,gpios-disallowed property which is used to exclude
PMIC GPIOs not owned by the APSS processor from the pinctrl device.

Signed-off-by: Fenglin Wu <fengl...@codeaurora.org>
---
 .../devicetree/bindings/pinctrl/qcom,pmic-gpio.txt |  12 ++
 drivers/pinctrl/qcom/pinctrl-spmi-gpio.c           | 202 +++++++++++++++++----
 2 files changed, 176 insertions(+), 38 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt 
b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
index 8d893a8..435efe8 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -43,6 +43,17 @@ PMIC's from Qualcomm.
                    the first cell will be used to define gpio number and the
                    second denotes the flags for this gpio
 
+- qcom,gpios-disallowed:
+       Usage: optional
+       Value type: <prop-encoded-array>
+       Definition: Array of the GPIO hardware numbers corresponding to GPIOs
+                   which the APSS processor is not allowed to configure.
+                   The hardware numbers are indexed from 1.
+                   The interrupt resources for these GPIOs must not be defined
+                   in "interrupts" and "interrupt-names" properties.
+                   GPIOs defined in this array won't be registered as pins
+                   in the pinctrl device or gpios in the gpio chip.
+
 Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
 a general description of GPIO and interrupt bindings.
 
@@ -206,6 +217,7 @@ Example:
 
                gpio-controller;
                #gpio-cells = <2>;
+               qcom,gpios-disallowed = <1 20>;
 
                pm8921_gpio_keys: gpio-keys {
                        volume-keys {
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c 
b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index 664b641..74821af 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -96,6 +96,7 @@
  * struct pmic_gpio_pad - keep current GPIO settings
  * @base: Address base in SPMI device.
  * @irq: IRQ number which this GPIO generate.
+ * @gpio_idx: The index in GPIO's hardware number space (1-based)
  * @is_enabled: Set to false when GPIO should be put in high Z state.
  * @out_value: Cached pin output value
  * @have_buffer: Set to true if GPIO output could be configured in push-pull,
@@ -112,6 +113,7 @@
 struct pmic_gpio_pad {
        u16             base;
        int             irq;
+       int             gpio_idx;
        bool            is_enabled;
        bool            out_value;
        bool            have_buffer;
@@ -130,6 +132,7 @@ struct pmic_gpio_state {
        struct regmap   *map;
        struct pinctrl_dev *ctrl;
        struct gpio_chip chip;
+       const char **gpio_groups;
 };
 
 static const struct pinconf_generic_params pmic_gpio_bindings[] = {
@@ -231,7 +234,9 @@ static int pmic_gpio_get_function_groups(struct pinctrl_dev 
*pctldev,
                                         const char *const **groups,
                                         unsigned *const num_qgroups)
 {
-       *groups = pmic_gpio_groups;
+       struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
+
+       *groups = state->gpio_groups;
        *num_qgroups = pctldev->desc->npins;
        return 0;
 }
@@ -455,7 +460,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev 
*pctldev,
 
        pad = pctldev->desc->pins[pin].drv_data;
 
-       seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
+       seq_printf(s, " gpio%-2d:", pad->gpio_idx);
 
        val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
 
@@ -546,13 +551,29 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip,
                              const struct of_phandle_args *gpio_desc,
                              u32 *flags)
 {
+       int i;
+       struct pmic_gpio_state *state = gpiochip_get_data(chip);
+       struct pinctrl_desc *desc = state->ctrl->desc;
+       struct pmic_gpio_pad *pad;
+
        if (chip->of_gpio_n_cells < 2)
                return -EINVAL;
 
        if (flags)
                *flags = gpio_desc->args[1];
 
-       return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
+       for (i = 0; i < chip->ngpio; i++) {
+               pad = desc->pins[i].drv_data;
+               if (pad->gpio_idx == gpio_desc->args[0]) {
+                       dev_dbg(state->dev, "gpio%-2d xlate to pin%-2d\n",
+                                               gpio_desc->args[0], i);
+                       return i;
+               }
+       }
+
+       dev_err(state->dev, "Couldn't find pin for gpio %d\n",
+                               gpio_desc->args[0]);
+       return -ENODEV;
 }
 
 static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
@@ -688,43 +709,124 @@ static int pmic_gpio_probe(struct platform_device *pdev)
        struct pinctrl_desc *pctrldesc;
        struct pmic_gpio_pad *pad, *pads;
        struct pmic_gpio_state *state;
-       int ret, npins, i;
-       u32 reg;
+       int ret, npins, ngpios, i, j, pin_idx;
+       int disallowed_count = 0;
+       u32 reg[2], start, size;
+       u32 *disallowed = NULL;
 
-       ret = of_property_read_u32(dev->of_node, "reg", &reg);
+       ret = of_property_read_u32_array(dev->of_node, "reg", reg, 2);
        if (ret < 0) {
-               dev_err(dev, "missing base address");
+               dev_err(dev, "reg property reading failed\n");
                return ret;
        }
+       start = reg[0];
+       size = reg[1];
+
+       ngpios = size / PMIC_GPIO_ADDRESS_RANGE;
+       if (ngpios == 0) {
+               dev_err(dev, "no gpios assigned\n");
+               return -ENODEV;
+       }
 
-       npins = platform_irq_count(pdev);
-       if (!npins)
+       if (ngpios > ARRAY_SIZE(pmic_gpio_groups)) {
+               dev_err(dev, "reg property defines %d gpios, but only %d are 
allowed\n",
+                               ngpios, (int)ARRAY_SIZE(pmic_gpio_groups));
                return -EINVAL;
-       if (npins < 0)
-               return npins;
+       }
+
+       if (of_find_property(dev->of_node, "qcom,gpios-disallowed",
+                                       &disallowed_count)) {
+               disallowed_count /= sizeof(u32);
+               if (disallowed_count == 0) {
+                       dev_err(dev, "No data in gpios-disallowed\n");
+                       return -EINVAL;
+               }
 
-       BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
+               disallowed = kcalloc(disallowed_count, sizeof(u32), GFP_KERNEL);
+               if (disallowed == NULL)
+                       return -ENOMEM;
+
+               ret = of_property_read_u32_array(dev->of_node,
+                               "qcom,gpios-disallowed",
+                               disallowed, disallowed_count);
+               if (ret < 0) {
+                       dev_err(dev, "qcom,gpios-disallowed property reading 
failed, ret=%d\n",
+                                                               ret);
+                       goto err_free;
+               }
+
+               for (i = 0; i < disallowed_count; i++) {
+                       if (disallowed[i] >= ngpios + PMIC_GPIO_PHYSICAL_OFFSET
+                               || disallowed[i] < PMIC_GPIO_PHYSICAL_OFFSET) {
+                               dev_err(dev, "invalid gpio = %d specified in 
qcom,gpios-disallowed, supported values: %d to %d\n",
+                                       disallowed[i],
+                                       PMIC_GPIO_PHYSICAL_OFFSET,
+                                       ngpios - 1 + PMIC_GPIO_PHYSICAL_OFFSET);
+                               ret = -EINVAL;
+                               goto err_free;
+                       }
+                       for (j = 0; j < i; j++) {
+                               if (disallowed[i] == disallowed[j]) {
+                                       dev_err(dev, "duplicate gpio = %d 
listed in qcom,gpios-disallowed\n",
+                                                       disallowed[i]);
+                                       ret = -EINVAL;
+                                       goto err_free;
+                               }
+                       }
+                       dev_dbg(dev, "gpio %d NOT supported\n", disallowed[i]);
+               }
+       } else {
+               disallowed_count = 0;
+       }
+
+       npins = ngpios - disallowed_count;
+       if (npins <= 0) {
+               dev_err(dev, "No pins assigned\n");
+               ret = -ENODEV;
+               goto err_free;
+       }
+       if (platform_irq_count(pdev) != npins) {
+               dev_err(dev, "%d IRQs defined but %d expected\n",
+                               platform_irq_count(pdev), npins);
+               ret = -EINVAL;
+               goto err_free;
+       }
 
        state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return -ENOMEM;
+       if (!state) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
 
        platform_set_drvdata(pdev, state);
 
        state->dev = &pdev->dev;
        state->map = dev_get_regmap(dev->parent, NULL);
 
+       state->gpio_groups = devm_kcalloc(dev, sizeof(*state->gpio_groups),
+                                               npins, GFP_KERNEL);
+       if (!state->gpio_groups) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
        pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
-       if (!pindesc)
-               return -ENOMEM;
+       if (!pindesc) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
 
        pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
-       if (!pads)
-               return -ENOMEM;
+       if (!pads) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
 
        pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
-       if (!pctrldesc)
-               return -ENOMEM;
+       if (!pctrldesc) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
 
        pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
        pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
@@ -738,22 +840,42 @@ static int pmic_gpio_probe(struct platform_device *pdev)
 #ifdef CONFIG_DEBUG_FS
        pctrldesc->custom_conf_items = pmic_conf_items;
 #endif
+       for (pin_idx = 0, i = 0; i < ngpios; i++) {
+               for (j = 0; j < disallowed_count; j++) {
+                       if (i + PMIC_GPIO_PHYSICAL_OFFSET == disallowed[j])
+                               break;
+               }
+               if (j != disallowed_count)
+                       continue;
 
-       for (i = 0; i < npins; i++, pindesc++) {
-               pad = &pads[i];
+               pad = &pads[pin_idx];
                pindesc->drv_data = pad;
-               pindesc->number = i;
+               pindesc->number = pin_idx;
                pindesc->name = pmic_gpio_groups[i];
 
-               pad->irq = platform_get_irq(pdev, i);
-               if (pad->irq < 0)
-                       return pad->irq;
+               pad->gpio_idx = i + PMIC_GPIO_PHYSICAL_OFFSET;
+               pad->irq = platform_get_irq(pdev, pin_idx);
+               if (pad->irq < 0) {
+                       dev_err(state->dev,
+                               "failed to get irq for gpio %d (pin %d), 
ret=%d\n",
+                                       pad->gpio_idx, pin_idx, pad->irq);
+                       ret = pad->irq;
+                       goto err_free;
+               }
+               /* Every pin is a group */
+               state->gpio_groups[pin_idx] = pmic_gpio_groups[i];
 
-               pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
+               pad->base = start + i * PMIC_GPIO_ADDRESS_RANGE;
 
                ret = pmic_gpio_populate(state, pad);
-               if (ret < 0)
-                       return ret;
+               if (ret < 0) {
+                       dev_err(state->dev,
+                               "failed to populate gpio %d, ret=%d\n",
+                                                       i, ret);
+                       goto err_free;
+               }
+               pindesc++;
+               pin_idx++;
        }
 
        state->chip = pmic_gpio_gpio_template;
@@ -765,25 +887,29 @@ static int pmic_gpio_probe(struct platform_device *pdev)
        state->chip.can_sleep = false;
 
        state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
-       if (IS_ERR(state->ctrl))
-               return PTR_ERR(state->ctrl);
+       if (IS_ERR(state->ctrl)) {
+               ret = PTR_ERR(state->ctrl);
+               dev_err(state->dev, "failed to register pinctrl device, 
ret=%d\n",
+                                                       ret);
+               goto err_free;
+       }
 
        ret = gpiochip_add_data(&state->chip, state);
        if (ret) {
-               dev_err(state->dev, "can't add gpio chip\n");
-               return ret;
+               dev_err(state->dev, "can't add gpio chip, ret=%d\n", ret);
+               goto err_free;
        }
 
        ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
        if (ret) {
-               dev_err(dev, "failed to add pin range\n");
-               goto err_range;
+               dev_err(dev, "failed to add pin range\n, ret=%d\n", ret);
+               gpiochip_remove(&state->chip);
+               goto err_free;
        }
 
-       return 0;
+err_free:
+       kfree(disallowed);
 
-err_range:
-       gpiochip_remove(&state->chip);
        return ret;
 }
 
-- 
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

Reply via email to