pinctrl: intel: Allow custom GPIO base for pad groups
[linux-2.6-microblaze.git] / drivers / pinctrl / intel / pinctrl-intel.c
index 12a1af4..4c2369a 100644 (file)
@@ -806,22 +806,63 @@ static const struct gpio_chip intel_gpio_chip = {
        .set_config = gpiochip_generic_config,
 };
 
+/**
+ * intel_gpio_to_pin() - Translate from GPIO offset to pin number
+ * @pctrl: Pinctrl structure
+ * @offset: GPIO offset from gpiolib
+ * @commmunity: Community is filled here if not %NULL
+ * @padgrp: Pad group is filled here if not %NULL
+ *
+ * When coming through gpiolib irqchip, the GPIO offset is not
+ * automatically translated to pinctrl pin number. This function can be
+ * used to find out the corresponding pinctrl pin.
+ */
+static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned offset,
+                            const struct intel_community **community,
+                            const struct intel_padgroup **padgrp)
+{
+       int i;
+
+       for (i = 0; i < pctrl->ncommunities; i++) {
+               const struct intel_community *comm = &pctrl->communities[i];
+               int j;
+
+               for (j = 0; j < comm->ngpps; j++) {
+                       const struct intel_padgroup *pgrp = &comm->gpps[j];
+
+                       if (pgrp->gpio_base < 0)
+                               continue;
+
+                       if (offset >= pgrp->gpio_base &&
+                           offset < pgrp->gpio_base + pgrp->size) {
+                               int pin;
+
+                               pin = pgrp->base + offset - pgrp->gpio_base;
+                               if (community)
+                                       *community = comm;
+                               if (padgrp)
+                                       *padgrp = pgrp;
+
+                               return pin;
+                       }
+               }
+       }
+
+       return -EINVAL;
+}
+
 static void intel_gpio_irq_ack(struct irq_data *d)
 {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
        const struct intel_community *community;
-       unsigned pin = irqd_to_hwirq(d);
+       const struct intel_padgroup *padgrp;
+       int pin;
 
-       community = intel_get_community(pctrl, pin);
-       if (community) {
-               const struct intel_padgroup *padgrp;
+       pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
+       if (pin >= 0) {
                unsigned gpp, gpp_offset, is_offset;
 
-               padgrp = intel_community_get_padgroup(community, pin);
-               if (!padgrp)
-                       return;
-
                gpp = padgrp->reg_num;
                gpp_offset = padgroup_offset(padgrp, pin);
                is_offset = community->is_offset + gpp * 4;
@@ -837,19 +878,15 @@ static void intel_gpio_irq_enable(struct irq_data *d)
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
        const struct intel_community *community;
-       unsigned pin = irqd_to_hwirq(d);
+       const struct intel_padgroup *padgrp;
+       int pin;
 
-       community = intel_get_community(pctrl, pin);
-       if (community) {
-               const struct intel_padgroup *padgrp;
+       pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
+       if (pin >= 0) {
                unsigned gpp, gpp_offset, is_offset;
                unsigned long flags;
                u32 value;
 
-               padgrp = intel_community_get_padgroup(community, pin);
-               if (!padgrp)
-                       return;
-
                gpp = padgrp->reg_num;
                gpp_offset = padgroup_offset(padgrp, pin);
                is_offset = community->is_offset + gpp * 4;
@@ -870,20 +907,16 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
        const struct intel_community *community;
-       unsigned pin = irqd_to_hwirq(d);
+       const struct intel_padgroup *padgrp;
+       int pin;
 
-       community = intel_get_community(pctrl, pin);
-       if (community) {
-               const struct intel_padgroup *padgrp;
+       pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), &community, &padgrp);
+       if (pin >= 0) {
                unsigned gpp, gpp_offset;
                unsigned long flags;
                void __iomem *reg;
                u32 value;
 
-               padgrp = intel_community_get_padgroup(community, pin);
-               if (!padgrp)
-                       return;
-
                gpp = padgrp->reg_num;
                gpp_offset = padgroup_offset(padgrp, pin);
 
@@ -914,7 +947,7 @@ static int intel_gpio_irq_type(struct irq_data *d, unsigned type)
 {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
-       unsigned pin = irqd_to_hwirq(d);
+       unsigned pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL);
        unsigned long flags;
        void __iomem *reg;
        u32 value;
@@ -969,7 +1002,7 @@ static int intel_gpio_irq_wake(struct irq_data *d, unsigned int on)
 {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct intel_pinctrl *pctrl = gpiochip_get_data(gc);
-       unsigned pin = irqd_to_hwirq(d);
+       unsigned pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL);
 
        if (on)
                enable_irq_wake(pctrl->irq);
@@ -1000,14 +1033,10 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl,
                pending &= enabled;
 
                for_each_set_bit(gpp_offset, &pending, padgrp->size) {
-                       unsigned padno, irq;
-
-                       padno = padgrp->base - community->pin_base + gpp_offset;
-                       if (padno >= community->npins)
-                               break;
+                       unsigned irq;
 
                        irq = irq_find_mapping(gc->irq.domain,
-                                              community->pin_base + padno);
+                                              padgrp->gpio_base + gpp_offset);
                        generic_handle_irq(irq);
 
                        ret |= IRQ_HANDLED;
@@ -1044,13 +1073,56 @@ static struct irq_chip intel_gpio_irqchip = {
        .flags = IRQCHIP_MASK_ON_SUSPEND,
 };
 
+static int intel_gpio_add_pin_ranges(struct intel_pinctrl *pctrl,
+                                    const struct intel_community *community)
+{
+       int ret, i;
+
+       for (i = 0; i < community->ngpps; i++) {
+               const struct intel_padgroup *gpp = &community->gpps[i];
+
+               if (gpp->gpio_base < 0)
+                       continue;
+
+               ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
+                                            gpp->gpio_base, gpp->base,
+                                            gpp->size);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static unsigned intel_gpio_ngpio(const struct intel_pinctrl *pctrl)
+{
+       const struct intel_community *community;
+       unsigned ngpio = 0;
+       int i, j;
+
+       for (i = 0; i < pctrl->ncommunities; i++) {
+               community = &pctrl->communities[i];
+               for (j = 0; j < community->ngpps; j++) {
+                       const struct intel_padgroup *gpp = &community->gpps[j];
+
+                       if (gpp->gpio_base < 0)
+                               continue;
+
+                       if (gpp->gpio_base + gpp->size > ngpio)
+                               ngpio = gpp->gpio_base + gpp->size;
+               }
+       }
+
+       return ngpio;
+}
+
 static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
 {
-       int ret;
+       int ret, i;
 
        pctrl->chip = intel_gpio_chip;
 
-       pctrl->chip.ngpio = pctrl->soc->npins;
+       pctrl->chip.ngpio = intel_gpio_ngpio(pctrl);
        pctrl->chip.label = dev_name(pctrl->dev);
        pctrl->chip.parent = pctrl->dev;
        pctrl->chip.base = -1;
@@ -1062,11 +1134,14 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq)
                return ret;
        }
 
-       ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
-                                    0, 0, pctrl->soc->npins);
-       if (ret) {
-               dev_err(pctrl->dev, "failed to add GPIO pin range\n");
-               return ret;
+       for (i = 0; i < pctrl->ncommunities; i++) {
+               struct intel_community *community = &pctrl->communities[i];
+
+               ret = intel_gpio_add_pin_ranges(pctrl, community);
+               if (ret) {
+                       dev_err(pctrl->dev, "failed to add GPIO pin range\n");
+                       return ret;
+               }
        }
 
        /*
@@ -1126,6 +1201,9 @@ static int intel_pinctrl_add_padgroups(struct intel_pinctrl *pctrl,
                if (gpps[i].size > 32)
                        return -EINVAL;
 
+               if (!gpps[i].gpio_base)
+                       gpps[i].gpio_base = gpps[i].base;
+
                gpps[i].padown_num = padown_num;
 
                /*