This change is necessary for a following patch, which introduces an interrupt mask register different from what is already in the driver. The driver already handles an interrupt mask register for pcal chips, PCAL953x_INT_MASK. Extend this implementation to support interrupt mask registers at other addresses. Add bit flag PCA_HAS_INT_MASK, which is set for each chip with an interrupt mask register (including pcal chips). Define a convenience bitmask PCA_MASKED_INT. Add an int_mask member to struct pca953x_reg_config. This way interrupt mask handling code can work with registers at different addresses. Add separate pca953x_reg_config for pcal953x chips. This differs from the pca953x_regs in the new int_mask field. Signed-off-by: Levente Révész <levente.revesz@xxxxxxxxxx> --- Changes in v2 1. New function pca953x_has_int_mask_reg(). 2. In pca953x_probe() replaced if-else with a switch. 3. Fix recalc_addr inconsistency pointed out by Martyn Welch Question: This patch uses the PCA_HAS_INT_MASK bit to encode if the chip has interrupt register mask. An alternatice approach would be to create a new chip type for PCA953X_TYPE chips with the mask register. What do you think? drivers/gpio/gpio-pca953x.c | 84 ++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1725c1000445..2cf9541057a8 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -62,6 +62,8 @@ #define PCAL_PINCTRL_MASK GENMASK(6, 5) #define PCA_INT BIT(8) +#define PCA_HAS_INT_MASK BIT(9) +#define PCA_MASKED_INT (PCA_INT | PCA_HAS_INT_MASK) #define PCA953X_TYPE (0x00 << 12) #define PCAL953X_TYPE (0x01 << 12) #define PCAL653X_TYPE (0x02 << 12) @@ -89,13 +91,13 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, - { "pcal6408", 8 | PCAL953X_TYPE | PCA_INT, }, - { "pcal6416", 16 | PCAL953X_TYPE | PCA_INT, }, - { "pcal6524", 24 | PCAL953X_TYPE | PCA_INT, }, - { "pcal6534", 34 | PCAL653X_TYPE | PCA_INT, }, - { "pcal9535", 16 | PCAL953X_TYPE | PCA_INT, }, - { "pcal9554b", 8 | PCAL953X_TYPE | PCA_INT, }, - { "pcal9555a", 16 | PCAL953X_TYPE | PCA_INT, }, + { "pcal6408", 8 | PCAL953X_TYPE | PCA_MASKED_INT, }, + { "pcal6416", 16 | PCAL953X_TYPE | PCA_MASKED_INT, }, + { "pcal6524", 24 | PCAL953X_TYPE | PCA_MASKED_INT, }, + { "pcal6534", 34 | PCAL653X_TYPE | PCA_MASKED_INT, }, + { "pcal9535", 16 | PCAL953X_TYPE | PCA_MASKED_INT, }, + { "pcal9554b", 8 | PCAL953X_TYPE | PCA_MASKED_INT, }, + { "pcal9555a", 16 | PCAL953X_TYPE | PCA_MASKED_INT, }, { "max7310", 8 | PCA953X_TYPE, }, { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, @@ -176,6 +178,7 @@ struct pca953x_reg_config { int output; int input; int invert; + int int_mask; }; static const struct pca953x_reg_config pca953x_regs = { @@ -185,6 +188,14 @@ static const struct pca953x_reg_config pca953x_regs = { .invert = PCA953X_INVERT, }; +static const struct pca953x_reg_config pcal953x_regs = { + .direction = PCA953X_DIRECTION, + .output = PCA953X_OUTPUT, + .input = PCA953X_INPUT, + .invert = PCA953X_INVERT, + .int_mask = PCAL953X_INT_MASK, +}; + static const struct pca953x_reg_config pca957x_regs = { .direction = PCA957X_CFG, .output = PCA957X_OUT, @@ -235,6 +246,11 @@ static inline bool pca953x_has_interrupt(const struct pca953x_chip *chip) return chip->driver_data & PCA_INT; } +static inline bool pca953x_has_int_mask_reg(const struct pca953x_chip *chip) +{ + return chip->driver_data & PCA_HAS_INT_MASK; +} + #define PCA953x_BANK_INPUT BIT(0) #define PCA953x_BANK_OUTPUT BIT(1) #define PCA953x_BANK_POLARITY BIT(2) @@ -790,14 +806,16 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) DECLARE_BITMAP(reg_direction, MAX_LINE); int level; - if (pca953x_is_pcal_type(chip)) { - /* Enable latch on interrupt-enabled inputs */ - pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); - + if (pca953x_has_int_mask_reg(chip)) { bitmap_complement(irq_mask, chip->irq_mask, gc->ngpio); /* Unmask enabled interrupts */ - pca953x_write_regs(chip, PCAL953X_INT_MASK, irq_mask); + pca953x_write_regs(chip, chip->regs->int_mask, irq_mask); + } + + if (pca953x_is_pcal_type(chip)) { + /* Enable latch on interrupt-enabled inputs */ + pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); } /* Switch direction to input if needed */ @@ -1193,12 +1211,20 @@ static int pca953x_probe(struct i2c_client *client, /* initialize cached registers from their original values. * we can't share this chip with another i2c master. */ - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { - chip->regs = &pca957x_regs; - ret = device_pca957x_init(chip, invert); - } else { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA953X_TYPE: chip->regs = &pca953x_regs; ret = device_pca95xx_init(chip, invert); + break; + case PCAL953X_TYPE: + case PCAL653X_TYPE: + chip->regs = &pcal953x_regs; + ret = device_pca95xx_init(chip, invert); + break; + case PCA957X_TYPE: + chip->regs = &pca957x_regs; + ret = device_pca957x_init(chip, invert); + break; } if (ret) goto err_exit; @@ -1264,21 +1290,23 @@ static int pca953x_regcache_sync(struct device *dev) } #ifdef CONFIG_GPIO_PCA953X_IRQ - if (pca953x_is_pcal_type(chip)) { - regaddr = chip->recalc_addr(chip, PCAL953X_IN_LATCH, 0); + if (pca953x_has_int_mask_reg(chip)) { + regaddr = chip->recalc_addr(chip, chip->regs->int_mask, 0); ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1); if (ret) { - dev_err(dev, "Failed to sync INT latch registers: %d\n", + dev_err(dev, "Failed to sync INT mask registers: %d\n", ret); return ret; } + } - regaddr = chip->recalc_addr(chip, PCAL953X_INT_MASK, 0); + if (pca953x_is_pcal_type(chip)) { + regaddr = chip->recalc_addr(chip, PCAL953X_IN_LATCH, 0); ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1); if (ret) { - dev_err(dev, "Failed to sync INT mask registers: %d\n", + dev_err(dev, "Failed to sync INT latch registers: %d\n", ret); return ret; } @@ -1362,13 +1390,13 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, - { .compatible = "nxp,pcal6408", .data = OF_L953X( 8, PCA_INT), }, - { .compatible = "nxp,pcal6416", .data = OF_L953X(16, PCA_INT), }, - { .compatible = "nxp,pcal6524", .data = OF_L953X(24, PCA_INT), }, - { .compatible = "nxp,pcal6534", .data = OF_L653X(34, PCA_INT), }, - { .compatible = "nxp,pcal9535", .data = OF_L953X(16, PCA_INT), }, - { .compatible = "nxp,pcal9554b", .data = OF_L953X( 8, PCA_INT), }, - { .compatible = "nxp,pcal9555a", .data = OF_L953X(16, PCA_INT), }, + { .compatible = "nxp,pcal6408", .data = OF_L953X( 8, PCA_MASKED_INT), }, + { .compatible = "nxp,pcal6416", .data = OF_L953X(16, PCA_MASKED_INT), }, + { .compatible = "nxp,pcal6524", .data = OF_L953X(24, PCA_MASKED_INT), }, + { .compatible = "nxp,pcal6534", .data = OF_L653X(34, PCA_MASKED_INT), }, + { .compatible = "nxp,pcal9535", .data = OF_L953X(16, PCA_MASKED_INT), }, + { .compatible = "nxp,pcal9554b", .data = OF_L953X( 8, PCA_MASKED_INT), }, + { .compatible = "nxp,pcal9555a", .data = OF_L953X(16, PCA_MASKED_INT), }, { .compatible = "maxim,max7310", .data = OF_953X( 8, 0), }, { .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), }, -- 2.37.3