]> git.karo-electronics.de Git - linux-beck.git/blobdiff - drivers/gpio/gpiolib.c
Merge branch 'gpio-irq-validmask' of /home/linus/linux-pinctrl into devel
[linux-beck.git] / drivers / gpio / gpiolib.c
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 */