The driver supports 8 chip types, 6 of which have extended registers with various functions, e.g. pull-up and pull-down bias for the gpio lines or interrupt masking. To allow supporting these functions, the driver has to be able to calculate the addresses of these registers. Replace the register maps with an enum for each chip type. These do not contain the same numeric values as the old defines, but the new address calculating functions (in the next patch) use them appropriately. Add currently used registers to struct pca953x_reg_config. Create a reg_config struct for each chip type. Signed-off-by: Levente Révész <levente.revesz@xxxxxxxxxx> --- drivers/gpio/gpio-pca953x.c | 422 ++++++++++++++++++++++++++++-------- 1 file changed, 333 insertions(+), 89 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index d6099fc18b01..3d4c3efeaf35 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -25,24 +25,10 @@ #include <asm/unaligned.h> -#define PCA953X_INPUT 0x00 -#define PCA953X_OUTPUT 0x01 -#define PCA953X_INVERT 0x02 -#define PCA953X_DIRECTION 0x03 - #define REG_ADDR_MASK GENMASK(5, 0) #define REG_ADDR_EXT BIT(6) #define REG_ADDR_AI BIT(7) -#define PCA957X_IN 0x00 -#define PCA957X_INVRT 0x01 -#define PCA957X_BKEN 0x02 -#define PCA957X_PUPD 0x03 -#define PCA957X_CFG 0x04 -#define PCA957X_OUT 0x05 -#define PCA957X_MSK 0x06 -#define PCA957X_INTS 0x07 - #define PCAL953X_OUT_STRENGTH 0x20 #define PCAL953X_IN_LATCH 0x22 #define PCAL953X_PULL_EN 0x23 @@ -191,25 +177,152 @@ MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids); #define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ) +enum pca953x_reg { + PCA953X_REG_INPUT, + PCA953X_REG_OUTPUT, + PCA953X_REG_INVERT, + PCA953X_REG_DIRECTION, +}; + +enum pca950x_reg { + PCA950X_REG_INPUT, + PCA950X_REG_OUTPUT, + PCA950X_REG_INVERT, + PCA950X_REG_DIRECTION, + PCA950X_REG_INT_MASK, +}; + +enum pcal953x_reg { + PCAL953X_REG_INPUT, + PCAL953X_REG_OUTPUT, + PCAL953X_REG_INVERT, + PCAL953X_REG_DIRECTION, + PCAL953X_REG_DRIVE_STRENGTH, + PCAL953X_REG_INPUT_LATCH, + PCAL953X_REG_PULL_EN, + PCAL953X_REG_PULL_SEL, + PCAL953X_REG_INT_MASK, + PCAL953X_REG_INT_STATUS, + PCAL953X_REG_DRIVE_MODE, +}; + +enum pcal65xx_reg { + PCAL65XX_REG_INPUT, + PCAL65XX_REG_OUTPUT, + PCAL65XX_REG_INVERT, + PCAL65XX_REG_DIRECTION, + PCAL65XX_REG_DRIVE_STRENGTH, + PCAL65XX_REG_INPUT_LATCH, + PCAL65XX_REG_PULL_EN, + PCAL65XX_REG_PULL_SEL, + PCAL65XX_REG_INT_MASK, + PCAL65XX_REG_INT_STATUS, + PCAL65XX_REG_DRIVE_MODE, + PCAL65XX_REG_INT_EDGE, + PCAL65XX_REG_INT_CLEAR, + PCAL65XX_REG_INPUT_STATUS, + PCAL65XX_REG_INDIV_DRIVE_MODE, + PCAL65XX_REG_DEBOUNCE_EN, + PCAL65XX_REG_DEBOUNCE_COUNT, +}; + +#define PCAL65XX_MAX_NUM_DEBOUNCE_PORTS 2 + +enum pca957x_reg { + PCA957X_REG_INPUT, + PCA957X_REG_INVERT, + PCA957X_REG_PULL_EN, + PCA957X_REG_PULL_SEL, + PCA957X_REG_DIRECTION, + PCA957X_REG_OUTPUT, + PCA957X_REG_INT_MASK, + PCA957X_REG_INT_STATUS, +}; + +enum xra120x_reg { + XRA120X_REG_INPUT, + XRA120X_REG_OUTPUT, + XRA120X_REG_INVERT, + XRA120X_REG_DIRECTION, + XRA120X_REG_PULL_EN, + XRA120X_REG_INT_MASK, + XRA120X_REG_TRISTATE, + XRA120X_REG_INT_STATUS, + XRA120X_REG_RISING_MASK, + XRA120X_REG_FALLING_MASK, + XRA120X_REG_DEBOUNCE_EN, +}; + struct pca953x_reg_config { int direction; int output; int input; int invert; + int input_latch; + int int_mask; + int int_status; + int pull_en; + int pull_sel; }; static const struct pca953x_reg_config pca953x_regs = { - .direction = PCA953X_DIRECTION, - .output = PCA953X_OUTPUT, - .input = PCA953X_INPUT, - .invert = PCA953X_INVERT, + .direction = PCA953X_REG_DIRECTION, + .output = PCA953X_REG_OUTPUT, + .input = PCA953X_REG_INPUT, + .invert = PCA953X_REG_INVERT, +}; + +static const struct pca953x_reg_config pca950x_regs = { + .direction = PCA950X_REG_DIRECTION, + .output = PCA950X_REG_OUTPUT, + .input = PCA950X_REG_INPUT, + .invert = PCA950X_REG_INVERT, + .int_mask = PCA950X_REG_INT_MASK, }; static const struct pca953x_reg_config pca957x_regs = { - .direction = PCA957X_CFG, - .output = PCA957X_OUT, - .input = PCA957X_IN, - .invert = PCA957X_INVRT, + .direction = PCA957X_REG_DIRECTION, + .output = PCA957X_REG_OUTPUT, + .input = PCA957X_REG_INPUT, + .invert = PCA957X_REG_INVERT, + .int_mask = PCA957X_REG_INT_MASK, + .int_status = PCA957X_REG_INT_STATUS, + .pull_en = PCA957X_REG_PULL_EN, + .pull_sel = PCA957X_REG_PULL_SEL, +}; + +static const struct pca953x_reg_config xra120x_regs = { + .direction = XRA120X_REG_DIRECTION, + .output = XRA120X_REG_OUTPUT, + .input = XRA120X_REG_INPUT, + .invert = XRA120X_REG_INVERT, + .int_mask = XRA120X_REG_INT_MASK, + .int_status = XRA120X_REG_INT_STATUS, + .pull_en = XRA120X_REG_PULL_EN, +}; + +static const struct pca953x_reg_config pcal953x_regs = { + .direction = PCAL953X_REG_DIRECTION, + .output = PCAL953X_REG_OUTPUT, + .input = PCAL953X_REG_INPUT, + .invert = PCAL953X_REG_INVERT, + .input_latch = PCAL953X_REG_INPUT_LATCH, + .int_mask = PCAL953X_REG_INT_MASK, + .int_status = PCAL953X_REG_INT_STATUS, + .pull_en = PCAL953X_REG_PULL_EN, + .pull_sel = PCAL953X_REG_PULL_SEL, +}; + +static const struct pca953x_reg_config pcal65xx_regs = { + .direction = PCAL65XX_REG_DIRECTION, + .output = PCAL65XX_REG_OUTPUT, + .input = PCAL65XX_REG_INPUT, + .invert = PCAL65XX_REG_INVERT, + .input_latch = PCAL65XX_REG_INPUT_LATCH, + .int_mask = PCAL65XX_REG_INT_MASK, + .int_status = PCAL65XX_REG_INT_STATUS, + .pull_en = PCAL65XX_REG_PULL_EN, + .pull_sel = PCAL65XX_REG_PULL_SEL, }; struct pca953x_chip { @@ -257,23 +370,6 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); } -#define PCA953x_BANK_INPUT BIT(0) -#define PCA953x_BANK_OUTPUT BIT(1) -#define PCA953x_BANK_POLARITY BIT(2) -#define PCA953x_BANK_CONFIG BIT(3) - -#define PCA957x_BANK_INPUT BIT(0) -#define PCA957x_BANK_POLARITY BIT(1) -#define PCA957x_BANK_BUSHOLD BIT(2) -#define PCA957x_BANK_CONFIG BIT(4) -#define PCA957x_BANK_OUTPUT BIT(5) - -#define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2) -#define PCAL9xxx_BANK_PULL_EN BIT(8 + 3) -#define PCAL9xxx_BANK_PULL_SEL BIT(8 + 4) -#define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5) -#define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6) - /* * We care about the following registers: * - Standard set, below 0x40, each port can be replicated up to 8 times @@ -373,63 +469,189 @@ static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg, return true; } -static bool pca953x_readable_register(struct device *dev, unsigned int reg) +static bool pca953x_readable_register(struct device *dev, unsigned int reg_addr) { struct pca953x_chip *chip = dev_get_drvdata(dev); - u32 bank; - - if (chip->type == TYPE_PCA957X) { - bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT | - PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | - PCA957x_BANK_BUSHOLD; - } else { - bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT | - PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; - } - - if (pca953x_is_pcal_type(chip)) { - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | - PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK | - PCAL9xxx_BANK_IRQ_STAT; + u32 registers; + + switch (chip->type) { + case TYPE_PCA950X: + registers = BIT(PCA950X_REG_INPUT) | + BIT(PCA950X_REG_OUTPUT) | + BIT(PCA950X_REG_INVERT) | + BIT(PCA950X_REG_DIRECTION) | + BIT(PCA950X_REG_INT_MASK); + break; + case TYPE_PCAL953X: + registers = BIT(PCAL953X_REG_INPUT) | + BIT(PCAL953X_REG_OUTPUT) | + BIT(PCAL953X_REG_INVERT) | + BIT(PCAL953X_REG_DIRECTION) | + BIT(PCAL953X_REG_DRIVE_STRENGTH) | + BIT(PCAL953X_REG_INPUT_LATCH) | + BIT(PCAL953X_REG_PULL_EN) | + BIT(PCAL953X_REG_PULL_SEL) | + BIT(PCAL953X_REG_INT_MASK) | + BIT(PCAL953X_REG_INT_STATUS) | + BIT(PCAL953X_REG_DRIVE_MODE); + break; + case TYPE_PCAL652X: + case TYPE_PCAL653X: + registers = BIT(PCAL65XX_REG_INPUT) | + BIT(PCAL65XX_REG_OUTPUT) | + BIT(PCAL65XX_REG_INVERT) | + BIT(PCAL65XX_REG_DIRECTION) | + BIT(PCAL65XX_REG_DRIVE_STRENGTH) | + BIT(PCAL65XX_REG_INPUT_LATCH) | + BIT(PCAL65XX_REG_PULL_EN) | + BIT(PCAL65XX_REG_PULL_SEL) | + BIT(PCAL65XX_REG_INT_MASK) | + BIT(PCAL65XX_REG_INT_STATUS) | + BIT(PCAL65XX_REG_DRIVE_MODE) | + BIT(PCAL65XX_REG_INT_EDGE) | + BIT(PCAL65XX_REG_INPUT_STATUS) | + BIT(PCAL65XX_REG_INDIV_DRIVE_MODE) | + BIT(PCAL65XX_REG_DEBOUNCE_EN) | + BIT(PCAL65XX_REG_DEBOUNCE_COUNT); + break; + case TYPE_PCA957X: + registers = BIT(PCA957X_REG_INPUT) | + BIT(PCA957X_REG_INVERT) | + BIT(PCA957X_REG_PULL_EN) | + BIT(PCA957X_REG_PULL_SEL) | + BIT(PCA957X_REG_DIRECTION) | + BIT(PCA957X_REG_OUTPUT) | + BIT(PCA957X_REG_INT_MASK) | + BIT(PCA957X_REG_INT_STATUS); + break; + case TYPE_XRA120X: + registers = BIT(XRA120X_REG_INPUT) | + BIT(XRA120X_REG_OUTPUT) | + BIT(XRA120X_REG_INVERT) | + BIT(XRA120X_REG_DIRECTION) | + BIT(XRA120X_REG_OUTPUT) | + BIT(XRA120X_REG_PULL_EN) | + BIT(XRA120X_REG_INT_MASK) | + BIT(XRA120X_REG_TRISTATE) | + BIT(XRA120X_REG_INT_STATUS) | + BIT(XRA120X_REG_RISING_MASK) | + BIT(XRA120X_REG_FALLING_MASK) | + BIT(XRA120X_REG_DEBOUNCE_EN); + break; + default: + registers = BIT(PCA953X_REG_INPUT) | + BIT(PCA953X_REG_OUTPUT) | + BIT(PCA953X_REG_INVERT) | + BIT(PCAL953X_REG_DIRECTION); + break; } - return chip->check_reg(chip, reg, bank); + return chip->check_reg(chip, reg_addr, registers); } -static bool pca953x_writeable_register(struct device *dev, unsigned int reg) +static bool pca953x_writeable_register(struct device *dev, unsigned int reg_addr) { struct pca953x_chip *chip = dev_get_drvdata(dev); - u32 bank; - - if (chip->type == TYPE_PCA957X) { - bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | - PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { - bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | - PCA953x_BANK_CONFIG; + u32 registers; + + switch (chip->type) { + case TYPE_PCA950X: + registers = BIT(PCA950X_REG_OUTPUT) | + BIT(PCA950X_REG_INVERT) | + BIT(PCA950X_REG_DIRECTION) | + BIT(PCA950X_REG_INT_MASK); + break; + case TYPE_PCAL953X: + registers = BIT(PCAL953X_REG_OUTPUT) | + BIT(PCAL953X_REG_INVERT) | + BIT(PCAL953X_REG_DIRECTION) | + BIT(PCAL953X_REG_DRIVE_STRENGTH) | + BIT(PCAL953X_REG_INPUT_LATCH) | + BIT(PCAL953X_REG_PULL_EN) | + BIT(PCAL953X_REG_PULL_SEL) | + BIT(PCAL953X_REG_INT_MASK) | + BIT(PCAL953X_REG_DRIVE_MODE); + break; + case TYPE_PCAL652X: + case TYPE_PCAL653X: + registers = BIT(PCAL65XX_REG_OUTPUT) | + BIT(PCAL65XX_REG_INVERT) | + BIT(PCAL65XX_REG_DIRECTION) | + BIT(PCAL65XX_REG_DRIVE_STRENGTH) | + BIT(PCAL65XX_REG_INPUT_LATCH) | + BIT(PCAL65XX_REG_PULL_EN) | + BIT(PCAL65XX_REG_PULL_SEL) | + BIT(PCAL65XX_REG_INT_MASK) | + BIT(PCAL65XX_REG_DRIVE_MODE) | + BIT(PCAL65XX_REG_INT_EDGE) | + BIT(PCAL65XX_REG_INT_CLEAR) | + BIT(PCAL65XX_REG_INDIV_DRIVE_MODE) | + BIT(PCAL65XX_REG_DEBOUNCE_EN) | + BIT(PCAL65XX_REG_DEBOUNCE_COUNT); + break; + case TYPE_PCA957X: + registers = BIT(PCA957X_REG_INVERT) | + BIT(PCA957X_REG_PULL_EN) | + BIT(PCA957X_REG_PULL_SEL) | + BIT(PCA957X_REG_DIRECTION) | + BIT(PCA957X_REG_OUTPUT) | + BIT(PCA957X_REG_INT_MASK); + break; + case TYPE_XRA120X: + registers = BIT(XRA120X_REG_OUTPUT) | + BIT(XRA120X_REG_INVERT) | + BIT(XRA120X_REG_DIRECTION) | + BIT(XRA120X_REG_OUTPUT) | + BIT(XRA120X_REG_PULL_EN) | + BIT(XRA120X_REG_INT_MASK) | + BIT(XRA120X_REG_TRISTATE) | + BIT(XRA120X_REG_RISING_MASK) | + BIT(XRA120X_REG_FALLING_MASK) | + BIT(XRA120X_REG_DEBOUNCE_EN); + break; + default: + registers = BIT(PCA953X_REG_OUTPUT) | + BIT(PCA953X_REG_INVERT) | + BIT(PCAL953X_REG_DIRECTION); + break; } - if (pca953x_is_pcal_type(chip)) - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | - PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK; - - return chip->check_reg(chip, reg, bank); + return chip->check_reg(chip, reg_addr, registers); } static bool pca953x_volatile_register(struct device *dev, unsigned int reg) { struct pca953x_chip *chip = dev_get_drvdata(dev); - u32 bank; - - if (chip->type == TYPE_PCA957X) - bank = PCA957x_BANK_INPUT; - else - bank = PCA953x_BANK_INPUT; + u32 registers; - if (pca953x_is_pcal_type(chip)) - bank |= PCAL9xxx_BANK_IRQ_STAT; + switch (chip->type) { + case TYPE_PCA950X: + registers = BIT(PCA950X_REG_INPUT); + break; + case TYPE_PCAL953X: + registers = BIT(PCAL953X_REG_INPUT) | + BIT(PCAL953X_REG_INT_STATUS); + break; + case TYPE_PCAL652X: + case TYPE_PCAL653X: + registers = BIT(PCAL65XX_REG_INPUT) | + BIT(PCAL65XX_REG_INT_STATUS) | + BIT(PCAL65XX_REG_INPUT_STATUS); + break; + case TYPE_PCA957X: + registers = BIT(PCA957X_REG_INPUT) | + BIT(PCA957X_REG_INT_STATUS); + break; + case TYPE_XRA120X: + registers = BIT(XRA120X_REG_INPUT) | + BIT(XRA120X_REG_INT_STATUS); + break; + default: + registers = BIT(PCA953X_REG_INPUT); + break; + } - return chip->check_reg(chip, reg, bank); + return chip->check_reg(chip, reg, registers); } static const struct regmap_config pca953x_i2c_regmap = { @@ -667,8 +889,8 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, { enum pin_config_param param = pinconf_to_config_param(config); - u8 pull_en_reg = chip->recalc_addr(chip, PCAL953X_PULL_EN, offset); - u8 pull_sel_reg = chip->recalc_addr(chip, PCAL953X_PULL_SEL, offset); + u8 pull_en_reg = chip->recalc_addr(chip, chip->regs->pull_en, offset); + u8 pull_sel_reg = chip->recalc_addr(chip, chip->regs->pull_sel, offset); u8 bit = BIT(offset % BANK_SZ); int ret; @@ -794,12 +1016,12 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) if (pca953x_is_pcal_type(chip)) { /* Enable latch on interrupt-enabled inputs */ - pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); + pca953x_write_regs(chip, chip->regs->input_latch, chip->irq_mask); 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); } /* Switch direction to input if needed */ @@ -876,7 +1098,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin if (pca953x_is_pcal_type(chip)) { /* Read the current interrupt status from the device */ - ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger); + ret = pca953x_read_regs(chip, chip->regs->int_status, trigger); if (ret) return false; @@ -1068,7 +1290,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert) for (i = 0; i < NBANK(chip); i++) bitmap_set_value8(val, 0x02, i * BANK_SZ); - ret = pca953x_write_regs(chip, PCA957X_BKEN, val); + ret = pca953x_write_regs(chip, chip->regs->pull_en, val); if (ret) goto out; @@ -1158,6 +1380,30 @@ static int pca953x_probe(struct i2c_client *client) regmap_config = &pca953x_i2c_regmap; } + switch (chip->type) { + case TYPE_PCA950X: + chip->regs = &pca950x_regs; + break; + case TYPE_PCAL953X: + chip->regs = &pcal953x_regs; + break; + case TYPE_PCAL652X: + chip->regs = &pcal65xx_regs; + break; + case TYPE_PCAL653X: + chip->regs = &pcal65xx_regs; + break; + case TYPE_PCA957X: + chip->regs = &pca957x_regs; + break; + case TYPE_XRA120X: + chip->regs = &xra120x_regs; + break; + default: + chip->regs = &pca953x_regs; + break; + } + if (chip->type == TYPE_PCAL653X) { chip->recalc_addr = pcal6534_recalc_addr; chip->check_reg = pcal6534_check_register; @@ -1198,10 +1444,8 @@ static int pca953x_probe(struct i2c_client *client) * we can't share this chip with another i2c master. */ if (chip->type == TYPE_PCA957X) { - chip->regs = &pca957x_regs; ret = device_pca957x_init(chip, invert); } else { - chip->regs = &pca953x_regs; ret = device_pca95xx_init(chip, invert); } if (ret) @@ -1269,7 +1513,7 @@ 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); + regaddr = chip->recalc_addr(chip, chip->regs->input_latch, 0); ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1); if (ret) { @@ -1278,7 +1522,7 @@ static int pca953x_regcache_sync(struct device *dev) return ret; } - regaddr = chip->recalc_addr(chip, PCAL953X_INT_MASK, 0); + regaddr = chip->recalc_addr(chip, chip->regs->int_mask, 0); ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1); if (ret) { -- 2.38.1