|
@@ -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 */
|
|
|
|