diff options
author | Mika Westerberg <mika.westerberg@linux.intel.com> | 2016-09-20 15:15:21 +0300 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2016-09-23 14:49:50 +0200 |
commit | 79b804cb6af4f128b2c53f0887c02537a7eb5824 (patch) | |
tree | 7eac13032ae91d1d9d24d264fde22a654c744a57 /drivers/gpio/gpiolib.c | |
parent | 29b4817d4018df78086157ea3a55c1d9424a7cfc (diff) |
gpiolib: Make it possible to exclude GPIOs from IRQ domain
When using GPIO irqchip helpers to setup irqchip for a gpiolib based
driver, it is not possible to select which GPIOs to add to the IRQ domain.
Instead it just adds all GPIOs which is not always desired. For example
there might be GPIOs that for some reason cannot generated normal
interrupts at all.
To support this we add a flag irq_need_valid_mask to struct gpio_chip. When
this flag is set the core allocates irq_valid_mask that holds one bit for
each GPIO the chip has. By default all bits are set but drivers can
manipulate this using set_bit() and clear_bit() accordingly.
Then when gpiochip_irqchip_add() is called, this mask is checked and all
GPIOs with bit is set are added to the IRQ domain created for the GPIO
chip.
Suggested-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 66 |
1 files changed, 63 insertions, 3 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 53ff25ac66d8..23be4daefeed 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -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); @@ -1401,6 +1408,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 @@ -1442,9 +1483,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); @@ -1551,9 +1595,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); } @@ -1562,6 +1609,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); } /** @@ -1597,6 +1646,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; @@ -1646,13 +1696,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); @@ -1664,6 +1718,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 */ |