Merge tag 'gpio-updates-for-v6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / drivers / gpio / gpio-pca953x.c
index cf9bf3fcaee2653e586b2b2e29e655b4a27465b5..ebe1943b85dd99f77a7c8883c2759ef666abc703 100644 (file)
@@ -66,6 +66,7 @@
 #define PCA_LATCH_INT          (PCA_PCAL | PCA_INT)
 #define PCA953X_TYPE           BIT(12)
 #define PCA957X_TYPE           BIT(13)
+#define PCAL653X_TYPE          BIT(14)
 #define PCA_TYPE_MASK          GENMASK(15, 12)
 
 #define PCA_CHIP_TYPE(x)       ((x) & PCA_TYPE_MASK)
@@ -89,8 +90,10 @@ static const struct i2c_device_id pca953x_id[] = {
        { "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
        { "pca9698", 40 | PCA953X_TYPE, },
 
+       { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
        { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
        { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, },
+       { "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, },
        { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
        { "pcal9554b", 8  | PCA953X_TYPE | PCA_LATCH_INT, },
        { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
@@ -211,6 +214,10 @@ struct pca953x_chip {
        struct regulator *regulator;
 
        const struct pca953x_reg_config *regs;
+
+       u8 (*recalc_addr)(struct pca953x_chip *chip, int reg, int off);
+       bool (*check_reg)(struct pca953x_chip *chip, unsigned int reg,
+                         u32 checkbank);
 };
 
 static int pca953x_bank_shift(struct pca953x_chip *chip)
@@ -288,18 +295,67 @@ static bool pca953x_check_register(struct pca953x_chip *chip, unsigned int reg,
        return true;
 }
 
+/*
+ * Unfortunately, whilst the PCAL6534 chip (and compatibles) broadly follow the
+ * same register layout as the PCAL6524, the spacing of the registers has been
+ * fundamentally altered by compacting them and thus does not obey the same
+ * rules, including being able to use bit shifting to determine bank. These
+ * chips hence need special handling here.
+ */
+static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg,
+                                   u32 checkbank)
+{
+       int bank;
+       int offset;
+
+       if (reg >= 0x30) {
+               /*
+                * Reserved block between 14h and 2Fh does not align on
+                * expected bank boundaries like other devices.
+                */
+               int temp = reg - 0x30;
+
+               bank = temp / NBANK(chip);
+               offset = temp - (bank * NBANK(chip));
+               bank += 8;
+       } else if (reg >= 0x54) {
+               /*
+                * Handle lack of reserved registers after output port
+                * configuration register to form a bank.
+                */
+               int temp = reg - 0x54;
+
+               bank = temp / NBANK(chip);
+               offset = temp - (bank * NBANK(chip));
+               bank += 16;
+       } else {
+               bank = reg / NBANK(chip);
+               offset = reg - (bank * NBANK(chip));
+       }
+
+       /* Register is not in the matching bank. */
+       if (!(BIT(bank) & checkbank))
+               return false;
+
+       /* Register is not within allowed range of bank. */
+       if (offset >= NBANK(chip))
+               return false;
+
+       return true;
+}
+
 static bool pca953x_readable_register(struct device *dev, unsigned int reg)
 {
        struct pca953x_chip *chip = dev_get_drvdata(dev);
        u32 bank;
 
-       if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
-               bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT |
-                      PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG;
-       } else {
+       if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
                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 (chip->driver_data & PCA_PCAL) {
@@ -308,7 +364,7 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg)
                        PCAL9xxx_BANK_IRQ_STAT;
        }
 
-       return pca953x_check_register(chip, reg, bank);
+       return chip->check_reg(chip, reg, bank);
 }
 
 static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
@@ -316,19 +372,19 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
        struct pca953x_chip *chip = dev_get_drvdata(dev);
        u32 bank;
 
-       if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
-               bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY |
-                       PCA953x_BANK_CONFIG;
-       } else {
+       if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
                bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY |
                        PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD;
+       } else {
+               bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY |
+                       PCA953x_BANK_CONFIG;
        }
 
        if (chip->driver_data & PCA_PCAL)
                bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN |
                        PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK;
 
-       return pca953x_check_register(chip, reg, bank);
+       return chip->check_reg(chip, reg, bank);
 }
 
 static bool pca953x_volatile_register(struct device *dev, unsigned int reg)
@@ -336,15 +392,15 @@ static bool pca953x_volatile_register(struct device *dev, unsigned int reg)
        struct pca953x_chip *chip = dev_get_drvdata(dev);
        u32 bank;
 
-       if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
-               bank = PCA953x_BANK_INPUT;
-       else
+       if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE)
                bank = PCA957x_BANK_INPUT;
+       else
+               bank = PCA953x_BANK_INPUT;
 
        if (chip->driver_data & PCA_PCAL)
                bank |= PCAL9xxx_BANK_IRQ_STAT;
 
-       return pca953x_check_register(chip, reg, bank);
+       return chip->check_reg(chip, reg, bank);
 }
 
 static const struct regmap_config pca953x_i2c_regmap = {
@@ -389,9 +445,42 @@ static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off)
        return regaddr;
 }
 
+/*
+ * The PCAL6534 and compatible chips have altered bank alignment that doesn't
+ * fit within the bit shifting scheme used for other devices.
+ */
+static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off)
+{
+       int addr;
+       int pinctrl;
+
+       addr = (reg & PCAL_GPIO_MASK) * NBANK(chip);
+
+       switch (reg) {
+       case PCAL953X_OUT_STRENGTH:
+       case PCAL953X_IN_LATCH:
+       case PCAL953X_PULL_EN:
+       case PCAL953X_PULL_SEL:
+       case PCAL953X_INT_MASK:
+       case PCAL953X_INT_STAT:
+       case PCAL953X_OUT_CONF:
+               pinctrl = ((reg & PCAL_PINCTRL_MASK) >> 1) + 0x20;
+               break;
+       case PCAL6524_INT_EDGE:
+       case PCAL6524_INT_CLR:
+       case PCAL6524_IN_STATUS:
+       case PCAL6524_OUT_INDCONF:
+       case PCAL6524_DEBOUNCE:
+               pinctrl = ((reg & PCAL_PINCTRL_MASK) >> 1) + 0x1c;
+               break;
+       }
+
+       return pinctrl + addr + (off / BANK_SZ);
+}
+
 static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
 {
-       u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
+       u8 regaddr = chip->recalc_addr(chip, reg, 0);
        u8 value[MAX_BANK];
        int i, ret;
 
@@ -409,7 +498,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long
 
 static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *val)
 {
-       u8 regaddr = pca953x_recalc_addr(chip, reg, 0);
+       u8 regaddr = chip->recalc_addr(chip, reg, 0);
        u8 value[MAX_BANK];
        int i, ret;
 
@@ -428,7 +517,7 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *
 static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 {
        struct pca953x_chip *chip = gpiochip_get_data(gc);
-       u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
+       u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
        u8 bit = BIT(off % BANK_SZ);
        int ret;
 
@@ -442,8 +531,8 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
                unsigned off, int val)
 {
        struct pca953x_chip *chip = gpiochip_get_data(gc);
-       u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
-       u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
+       u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
+       u8 outreg = chip->recalc_addr(chip, chip->regs->output, off);
        u8 bit = BIT(off % BANK_SZ);
        int ret;
 
@@ -463,7 +552,7 @@ exit:
 static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
 {
        struct pca953x_chip *chip = gpiochip_get_data(gc);
-       u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off);
+       u8 inreg = chip->recalc_addr(chip, chip->regs->input, off);
        u8 bit = BIT(off % BANK_SZ);
        u32 reg_val;
        int ret;
@@ -480,7 +569,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
 static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
 {
        struct pca953x_chip *chip = gpiochip_get_data(gc);
-       u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off);
+       u8 outreg = chip->recalc_addr(chip, chip->regs->output, off);
        u8 bit = BIT(off % BANK_SZ);
 
        mutex_lock(&chip->i2c_lock);
@@ -491,7 +580,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
 static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
 {
        struct pca953x_chip *chip = gpiochip_get_data(gc);
-       u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off);
+       u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
        u8 bit = BIT(off % BANK_SZ);
        u32 reg_val;
        int ret;
@@ -548,8 +637,10 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
                                         unsigned int offset,
                                         unsigned long config)
 {
-       u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset);
-       u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset);
+       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 bit = BIT(offset % BANK_SZ);
        int ret;
 
@@ -563,9 +654,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
        mutex_lock(&chip->i2c_lock);
 
        /* Configure pull-up/pull-down */
-       if (config == PIN_CONFIG_BIAS_PULL_UP)
+       if (param == PIN_CONFIG_BIAS_PULL_UP)
                ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit);
-       else if (config == PIN_CONFIG_BIAS_PULL_DOWN)
+       else if (param == PIN_CONFIG_BIAS_PULL_DOWN)
                ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0);
        else
                ret = 0;
@@ -573,7 +664,7 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip,
                goto exit;
 
        /* Disable/Enable pull-up/pull-down */
-       if (config == PIN_CONFIG_BIAS_DISABLE)
+       if (param == PIN_CONFIG_BIAS_DISABLE)
                ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0);
        else
                ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit);
@@ -912,13 +1003,13 @@ static int device_pca95xx_init(struct pca953x_chip *chip, u32 invert)
        u8 regaddr;
        int ret;
 
-       regaddr = pca953x_recalc_addr(chip, chip->regs->output, 0);
+       regaddr = chip->recalc_addr(chip, chip->regs->output, 0);
        ret = regcache_sync_region(chip->regmap, regaddr,
                                   regaddr + NBANK(chip) - 1);
        if (ret)
                goto out;
 
-       regaddr = pca953x_recalc_addr(chip, chip->regs->direction, 0);
+       regaddr = chip->recalc_addr(chip, chip->regs->direction, 0);
        ret = regcache_sync_region(chip->regmap, regaddr,
                                   regaddr + NBANK(chip) - 1);
        if (ret)
@@ -1037,6 +1128,14 @@ static int pca953x_probe(struct i2c_client *client,
                regmap_config = &pca953x_i2c_regmap;
        }
 
+       if (PCA_CHIP_TYPE(chip->driver_data) == PCAL653X_TYPE) {
+               chip->recalc_addr = pcal6534_recalc_addr;
+               chip->check_reg = pcal6534_check_register;
+       } else {
+               chip->recalc_addr = pca953x_recalc_addr;
+               chip->check_reg = pca953x_check_register;
+       }
+
        chip->regmap = devm_regmap_init_i2c(client, regmap_config);
        if (IS_ERR(chip->regmap)) {
                ret = PTR_ERR(chip->regmap);
@@ -1068,13 +1167,12 @@ 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) == PCA953X_TYPE) {
-               chip->regs = &pca953x_regs;
-               ret = device_pca95xx_init(chip, invert);
-       } else {
+       if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
                chip->regs = &pca957x_regs;
                ret = device_pca957x_init(chip, invert);
+       } else {
+               chip->regs = &pca953x_regs;
+               ret = device_pca95xx_init(chip, invert);
        }
        if (ret)
                goto err_exit;
@@ -1125,14 +1223,14 @@ static int pca953x_regcache_sync(struct device *dev)
         * The ordering between direction and output is important,
         * sync these registers first and only then sync the rest.
         */
-       regaddr = pca953x_recalc_addr(chip, chip->regs->direction, 0);
+       regaddr = chip->recalc_addr(chip, chip->regs->direction, 0);
        ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1);
        if (ret) {
                dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret);
                return ret;
        }
 
-       regaddr = pca953x_recalc_addr(chip, chip->regs->output, 0);
+       regaddr = chip->recalc_addr(chip, chip->regs->output, 0);
        ret = regcache_sync_region(chip->regmap, regaddr, regaddr + NBANK(chip) - 1);
        if (ret) {
                dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret);
@@ -1141,7 +1239,7 @@ static int pca953x_regcache_sync(struct device *dev)
 
 #ifdef CONFIG_GPIO_PCA953X_IRQ
        if (chip->driver_data & PCA_PCAL) {
-               regaddr = pca953x_recalc_addr(chip, PCAL953X_IN_LATCH, 0);
+               regaddr = chip->recalc_addr(chip, PCAL953X_IN_LATCH, 0);
                ret = regcache_sync_region(chip->regmap, regaddr,
                                           regaddr + NBANK(chip) - 1);
                if (ret) {
@@ -1150,7 +1248,7 @@ static int pca953x_regcache_sync(struct device *dev)
                        return ret;
                }
 
-               regaddr = pca953x_recalc_addr(chip, PCAL953X_INT_MASK, 0);
+               regaddr = chip->recalc_addr(chip, PCAL953X_INT_MASK, 0);
                ret = regcache_sync_region(chip->regmap, regaddr,
                                           regaddr + NBANK(chip) - 1);
                if (ret) {
@@ -1214,6 +1312,7 @@ static int pca953x_resume(struct device *dev)
 #endif
 
 /* convenience to stop overlong match-table lines */
+#define OF_653X(__nrgpio, __int) ((void *)(__nrgpio | PCAL653X_TYPE | __int))
 #define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int)
 #define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
 
@@ -1236,8 +1335,10 @@ 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_953X(8, PCA_LATCH_INT), },
        { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
        { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
+       { .compatible = "nxp,pcal6534", .data = OF_653X(34, PCA_LATCH_INT), },
        { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), },
        { .compatible = "nxp,pcal9554b", .data = OF_953X( 8, PCA_LATCH_INT), },
        { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), },