diff options
Diffstat (limited to 'drivers/gpio/gpio-pca953x.c')
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 142 |
1 files changed, 117 insertions, 25 deletions
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1fca8dd7824f..bd2e96c34f82 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -89,6 +89,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "max7310", 8 | PCA953X_TYPE, }, @@ -107,6 +108,84 @@ static const struct i2c_device_id pca953x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pca953x_id); +#ifdef CONFIG_GPIO_PCA953X_IRQ + +#include <linux/dmi.h> +#include <linux/gpio.h> +#include <linux/list.h> + +static const struct dmi_system_id pca953x_dmi_acpi_irq_info[] = { + { + /* + * On Intel Galileo Gen 2 board the IRQ pin of one of + * the I²C GPIO expanders, which has GpioInt() resource, + * is provided as an absolute number instead of being + * relative. Since first controller (gpio-sch.c) and + * second (gpio-dwapb.c) are at the fixed bases, we may + * safely refer to the number in the global space to get + * an IRQ out of it. + */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"), + }, + }, + {} +}; + +#ifdef CONFIG_ACPI +static int pca953x_acpi_get_pin(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_gpio *agpio; + int *pin = data; + + if (acpi_gpio_get_irq_resource(ares, &agpio)) + *pin = agpio->pin_table[0]; + return 1; +} + +static int pca953x_acpi_find_pin(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + int pin = -ENOENT, ret; + LIST_HEAD(r); + + ret = acpi_dev_get_resources(adev, &r, pca953x_acpi_get_pin, &pin); + acpi_dev_free_resource_list(&r); + if (ret < 0) + return ret; + + return pin; +} +#else +static inline int pca953x_acpi_find_pin(struct device *dev) { return -ENXIO; } +#endif + +static int pca953x_acpi_get_irq(struct device *dev) +{ + int pin, ret; + + pin = pca953x_acpi_find_pin(dev); + if (pin < 0) + return pin; + + dev_info(dev, "Applying ACPI interrupt quirk (GPIO %d)\n", pin); + + if (!gpio_is_valid(pin)) + return -EINVAL; + + ret = gpio_request(pin, "pca953x interrupt"); + if (ret) + return ret; + + ret = gpio_to_irq(pin); + + /* When pin is used as an IRQ, no need to keep it requested */ + gpio_free(pin); + + return ret; +} +#endif + static const struct acpi_device_id pca953x_acpi_ids[] = { { "INT3491", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { } @@ -322,6 +401,7 @@ static const struct regmap_config pca953x_ai_i2c_regmap = { .writeable_reg = pca953x_writeable_register, .volatile_reg = pca953x_volatile_register, + .disable_locking = true, .cache_type = REGCACHE_RBTREE, .max_register = 0x7f, }; @@ -623,8 +703,6 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) DECLARE_BITMAP(reg_direction, MAX_LINE); int level; - pca953x_read_regs(chip, chip->regs->direction, reg_direction); - if (chip->driver_data & PCA_PCAL) { /* Enable latch on interrupt-enabled inputs */ pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); @@ -635,7 +713,11 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) pca953x_write_regs(chip, PCAL953X_INT_MASK, irq_mask); } + /* Switch direction to input if needed */ + pca953x_read_regs(chip, chip->regs->direction, reg_direction); + bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); + bitmap_complement(reg_direction, reg_direction, gc->ngpio); bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); /* Look for any newly setup interrupt */ @@ -734,14 +816,16 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) struct gpio_chip *gc = &chip->gpio_chip; DECLARE_BITMAP(pending, MAX_LINE); int level; + bool ret; - if (!pca953x_irq_pending(chip, pending)) - return IRQ_NONE; + mutex_lock(&chip->i2c_lock); + ret = pca953x_irq_pending(chip, pending); + mutex_unlock(&chip->i2c_lock); for_each_set_bit(level, pending, gc->ngpio) handle_nested_irq(irq_find_mapping(gc->irq.domain, level)); - return IRQ_HANDLED; + return IRQ_RETVAL(ret); } static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) @@ -750,8 +834,15 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) struct irq_chip *irq_chip = &chip->irq_chip; DECLARE_BITMAP(reg_direction, MAX_LINE); DECLARE_BITMAP(irq_stat, MAX_LINE); + struct gpio_irq_chip *girq; int ret; + if (dmi_first_match(pca953x_dmi_acpi_irq_info)) { + ret = pca953x_acpi_get_irq(&client->dev); + if (ret > 0) + client->irq = ret; + } + if (!client->irq) return 0; @@ -774,17 +865,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) bitmap_and(chip->irq_stat, irq_stat, reg_direction, chip->gpio_chip.ngpio); mutex_init(&chip->irq_lock); - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, pca953x_irq_handler, - IRQF_ONESHOT | IRQF_SHARED, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - return ret; - } - - irq_chip->name = dev_name(&chip->client->dev); + irq_chip->name = dev_name(&client->dev); irq_chip->irq_mask = pca953x_irq_mask; irq_chip->irq_unmask = pca953x_irq_unmask; irq_chip->irq_set_wake = pca953x_irq_set_wake; @@ -793,17 +874,27 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) irq_chip->irq_set_type = pca953x_irq_set_type; irq_chip->irq_shutdown = pca953x_irq_shutdown; - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip, - irq_base, handle_simple_irq, - IRQ_TYPE_NONE); + girq = &chip->gpio_chip.irq; + girq->chip = irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + girq->first = irq_base; /* FIXME: get rid of this */ + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pca953x_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(&client->dev), chip); if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); return ret; } - gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq); - return 0; } @@ -990,11 +1081,11 @@ static int pca953x_probe(struct i2c_client *client, if (ret) goto err_exit; - ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); + ret = pca953x_irq_setup(chip, irq_base); if (ret) goto err_exit; - ret = pca953x_irq_setup(chip, irq_base); + ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); if (ret) goto err_exit; @@ -1145,6 +1236,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "maxim,max7310", .data = OF_953X( 8, 0), }, |