]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'gpio-irq-validmask' of /home/linus/linux-pinctrl into devel
authorLinus Walleij <linus.walleij@linaro.org>
Fri, 23 Sep 2016 12:51:18 +0000 (14:51 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Fri, 23 Sep 2016 12:51:18 +0000 (14:51 +0200)
Documentation/gpio/driver.txt
drivers/gpio/gpiolib.c
include/linux/gpio/driver.h

index 6cb35a78eff4c5c50073a2cdb4dac603a3a16b79..368d5a294d8937d7c0ea0fa157525667f8549c7f 100644 (file)
@@ -262,6 +262,12 @@ symbol:
   to the container using container_of().
   (See Documentation/driver-model/design-patterns.txt)
 
+  If there is a need to exclude certain GPIOs from the IRQ domain, one can
+  set .irq_need_valid_mask of the gpiochip before gpiochip_add_data() is
+  called. This allocates .irq_valid_mask with as many bits set as there are
+  GPIOs in the chip. Drivers can exclude GPIOs by clearing bits from this
+  mask. The mask must be filled in before gpiochip_irqchip_add() is called.
+
 * gpiochip_set_chained_irqchip(): sets up a chained irq handler for a
   gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler
   data. (Notice handler data, since the irqchip data is likely used by the
index 043128d3e831daf391e544b67c2f3db23f8c2146..19a665f8d455f36114d683ea264ecf8ec43ff4e7 100644 (file)
@@ -71,6 +71,8 @@ LIST_HEAD(gpio_devices);
 
 static void gpiochip_free_hogs(struct gpio_chip *chip);
 static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
+static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip);
+static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip);
 
 static bool gpiolib_initialized;
 
@@ -1167,6 +1169,10 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
        if (status)
                goto err_remove_from_list;
 
+       status = gpiochip_irqchip_init_valid_mask(chip);
+       if (status)
+               goto err_remove_from_list;
+
        status = of_gpiochip_add(chip);
        if (status)
                goto err_remove_chip;
@@ -1192,6 +1198,7 @@ err_remove_chip:
        acpi_gpiochip_remove(chip);
        gpiochip_free_hogs(chip);
        of_gpiochip_remove(chip);
+       gpiochip_irqchip_free_valid_mask(chip);
 err_remove_from_list:
        spin_lock_irqsave(&gpio_lock, flags);
        list_del(&gdev->list);
@@ -1397,6 +1404,40 @@ static struct gpio_chip *find_chip_by_name(const char *name)
  * The following is irqchip helper code for gpiochips.
  */
 
+static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
+{
+       int i;
+
+       if (!gpiochip->irq_need_valid_mask)
+               return 0;
+
+       gpiochip->irq_valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio),
+                                          sizeof(long), GFP_KERNEL);
+       if (!gpiochip->irq_valid_mask)
+               return -ENOMEM;
+
+       /* Assume by default all GPIOs are valid */
+       for (i = 0; i < gpiochip->ngpio; i++)
+               set_bit(i, gpiochip->irq_valid_mask);
+
+       return 0;
+}
+
+static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
+{
+       kfree(gpiochip->irq_valid_mask);
+       gpiochip->irq_valid_mask = NULL;
+}
+
+static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
+                                      unsigned int offset)
+{
+       /* No mask means all valid */
+       if (likely(!gpiochip->irq_valid_mask))
+               return true;
+       return test_bit(offset, gpiochip->irq_valid_mask);
+}
+
 /**
  * gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip
  * @gpiochip: the gpiochip to set the irqchip chain to
@@ -1438,9 +1479,12 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
        }
 
        /* Set the parent IRQ for all affected IRQs */
-       for (offset = 0; offset < gpiochip->ngpio; offset++)
+       for (offset = 0; offset < gpiochip->ngpio; offset++) {
+               if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
+                       continue;
                irq_set_parent(irq_find_mapping(gpiochip->irqdomain, offset),
                               parent_irq);
+       }
 }
 EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
 
@@ -1547,9 +1591,12 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
 
        /* Remove all IRQ mappings and delete the domain */
        if (gpiochip->irqdomain) {
-               for (offset = 0; offset < gpiochip->ngpio; offset++)
+               for (offset = 0; offset < gpiochip->ngpio; offset++) {
+                       if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
+                               continue;
                        irq_dispose_mapping(
                                irq_find_mapping(gpiochip->irqdomain, offset));
+               }
                irq_domain_remove(gpiochip->irqdomain);
        }
 
@@ -1558,6 +1605,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
                gpiochip->irqchip->irq_release_resources = NULL;
                gpiochip->irqchip = NULL;
        }
+
+       gpiochip_irqchip_free_valid_mask(gpiochip);
 }
 
 /**
@@ -1593,6 +1642,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
                          struct lock_class_key *lock_key)
 {
        struct device_node *of_node;
+       bool irq_base_set = false;
        unsigned int offset;
        unsigned irq_base = 0;
 
@@ -1656,13 +1706,17 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
         * necessary to allocate descriptors for all IRQs.
         */
        for (offset = 0; offset < gpiochip->ngpio; offset++) {
+               if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
+                       continue;
                irq_base = irq_create_mapping(gpiochip->irqdomain, offset);
-               if (offset == 0)
+               if (!irq_base_set) {
                        /*
                         * Store the base into the gpiochip to be used when
                         * unmapping the irqs.
                         */
                        gpiochip->irq_base = irq_base;
+                       irq_base_set = true;
+               }
        }
 
        acpi_gpiochip_request_interrupts(gpiochip);
@@ -1674,6 +1728,12 @@ EXPORT_SYMBOL_GPL(_gpiochip_irqchip_add);
 #else /* CONFIG_GPIOLIB_IRQCHIP */
 
 static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
+static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
+{
+       return 0;
+}
+static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
+{ }
 
 #endif /* CONFIG_GPIOLIB_IRQCHIP */
 
index 216e6f275aa850dd76d13f015e9611e4cf2dd1a7..1f0be7213e6dfd6a03da165da1e32d93eab1e7df 100644 (file)
@@ -112,6 +112,10 @@ enum single_ended_mode {
  *     initialization, provided by GPIO driver
  * @irq_parent: GPIO IRQ chip parent/bank linux irq number,
  *     provided by GPIO driver
+ * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all
+ *     bits set to one
+ * @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to
+ *     be included in IRQ domain of the chip
  * @lock_key: per GPIO IRQ chip lockdep class
  *
  * A gpio_chip can help platforms abstract various sources of GPIOs so
@@ -190,6 +194,8 @@ struct gpio_chip {
        irq_flow_handler_t      irq_handler;
        unsigned int            irq_default_type;
        int                     irq_parent;
+       bool                    irq_need_valid_mask;
+       unsigned long           *irq_valid_mask;
        struct lock_class_key   *lock_key;
 #endif