gpio: pca953x: Perform basic regmap conversion
authorMarek Vasut <marek.vasut@gmail.com>
Wed, 12 Dec 2018 01:39:58 +0000 (02:39 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Fri, 14 Dec 2018 14:19:11 +0000 (15:19 +0100)
Convert the driver to use regmap to access the chips. Due to the convoluted
register mapping scheme, implement read/write/volatile check functions that
untangle the mess and perform check accordingly. This patch does not zap the
internal register cache of the PCA953x driver, nor does it push the regmap
access down into the gpiochip accessors to simplify the review. All that is
in subsequent patches.

Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Cc: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpio-pca953x.c

index 7e66f46b41b2b75e44ce2f25f0dc6ab38503e6ee..389fa891f342a3f0cd83b13d3a03e74ad247aa0b 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_data/pca953x.h>
+#include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
@@ -30,6 +31,8 @@
 #define PCA953X_INVERT         0x02
 #define PCA953X_DIRECTION      0x03
 
+#define REG_ADDR_MASK          0x3f
+#define REG_ADDR_EXT           0x40
 #define REG_ADDR_AI            0x80
 
 #define PCA957X_IN             0x00
@@ -141,6 +144,7 @@ struct pca953x_chip {
        u8 reg_output[MAX_BANK];
        u8 reg_direction[MAX_BANK];
        struct mutex i2c_lock;
+       struct regmap *regmap;
 
 #ifdef CONFIG_GPIO_PCA953X_IRQ
        struct mutex irq_lock;
@@ -164,6 +168,141 @@ 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_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
+ *   - PCA953x standard
+ *     Input port                      0x00 + 0 * bank_size    R
+ *     Output port                     0x00 + 1 * bank_size    RW
+ *     Polarity Inversion port         0x00 + 2 * bank_size    RW
+ *     Configuration port              0x00 + 3 * bank_size    RW
+ *   - PCA957x with mixed up registers
+ *     Input port                      0x00 + 0 * bank_size    R
+ *     Polarity Inversion port         0x00 + 1 * bank_size    RW
+ *     Bus hold port                   0x00 + 2 * bank_size    RW
+ *     Configuration port              0x00 + 4 * bank_size    RW
+ *     Output port                     0x00 + 5 * bank_size    RW
+ *
+ * - Extended set, above 0x40, often chip specific.
+ *   - PCAL6524/PCAL9555A with custom PCAL IRQ handling:
+ *     Input latch register            0x40 + 2 * bank_size    RW
+ *     Interrupt mask register         0x40 + 5 * bank_size    RW
+ *     Interrupt status register       0x40 + 6 * bank_size    R
+ *
+ * - Registers with bit 0x80 set, the AI bit
+ *   The bit is cleared and the registers fall into one of the
+ *   categories above.
+ */
+
+static bool pca953x_check_register(struct pca953x_chip *chip, unsigned int reg,
+                                  u32 checkbank)
+{
+       int bank_shift = pca953x_bank_shift(chip);
+       int bank = (reg & REG_ADDR_MASK) >> bank_shift;
+       int offset = reg & (BIT(bank_shift) - 1);
+
+       /* Special PCAL extended register check. */
+       if (reg & REG_ADDR_EXT) {
+               if (!(chip->driver_data & PCA_PCAL))
+                       return false;
+               bank += 8;
+       }
+
+       /* 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 {
+               bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT |
+                      PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG |
+                      PCA957x_BANK_BUSHOLD;
+       }
+
+       if (chip->driver_data & PCA_PCAL) {
+               bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK |
+                       PCAL9xxx_BANK_IRQ_STAT;
+       }
+
+       return pca953x_check_register(chip, reg, bank);
+}
+
+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 {
+               bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY |
+                       PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD;
+       }
+
+       if (chip->driver_data & PCA_PCAL)
+               bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK;
+
+       return pca953x_check_register(chip, reg, bank);
+}
+
+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
+               bank = PCA957x_BANK_INPUT;
+
+       if (chip->driver_data & PCA_PCAL)
+               bank |= PCAL9xxx_BANK_IRQ_STAT;
+
+       return pca953x_check_register(chip, reg, bank);
+}
+
+const struct regmap_config pca953x_i2c_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .readable_reg = pca953x_readable_register,
+       .writeable_reg = pca953x_writeable_register,
+       .volatile_reg = pca953x_volatile_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .max_register = 0x7f,
+};
+
 static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off,
                              bool write, bool addrinc)
 {
@@ -193,8 +332,7 @@ static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
        u8 regaddr = pca953x_recalc_addr(chip, reg, off, false, false);
        int ret;
 
-       ret = i2c_smbus_read_byte_data(chip->client, regaddr);
-       *val = ret;
+       ret = regmap_read(chip->regmap, regaddr, val);
        if (ret < 0) {
                dev_err(&chip->client->dev, "failed reading register\n");
                return ret;
@@ -209,7 +347,7 @@ static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
        u8 regaddr = pca953x_recalc_addr(chip, reg, off, true, false);
        int ret;
 
-       ret = i2c_smbus_write_byte_data(chip->client, regaddr, val);
+       ret = regmap_write(chip->regmap, regaddr, val);
        if (ret < 0) {
                dev_err(&chip->client->dev, "failed writing register\n");
                return ret;
@@ -223,8 +361,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
        u8 regaddr = pca953x_recalc_addr(chip, reg, 0, true, true);
        int ret;
 
-       ret = i2c_smbus_write_i2c_block_data(chip->client, regaddr,
-                                            NBANK(chip), val);
+       ret = regmap_bulk_write(chip->regmap, regaddr, val, NBANK(chip));
        if (ret < 0) {
                dev_err(&chip->client->dev, "failed writing register\n");
                return ret;
@@ -238,8 +375,7 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
        u8 regaddr = pca953x_recalc_addr(chip, reg, 0, false, true);
        int ret;
 
-       ret = i2c_smbus_read_i2c_block_data(chip->client, regaddr,
-                                           NBANK(chip), val);
+       ret = regmap_bulk_read(chip->regmap, regaddr, val, NBANK(chip));
        if (ret < 0) {
                dev_err(&chip->client->dev, "failed reading register\n");
                return ret;
@@ -793,6 +929,14 @@ static int pca953x_probe(struct i2c_client *client,
                }
        }
 
+       i2c_set_clientdata(client, chip);
+
+       chip->regmap = devm_regmap_init_i2c(client, &pca953x_i2c_regmap);
+       if (IS_ERR(chip->regmap)) {
+               ret = PTR_ERR(chip->regmap);
+               goto err_exit;
+       }
+
        mutex_init(&chip->i2c_lock);
        /*
         * In case we have an i2c-mux controlled by a GPIO provided by an
@@ -843,7 +987,6 @@ static int pca953x_probe(struct i2c_client *client,
                        dev_warn(&client->dev, "setup failed, %d\n", ret);
        }
 
-       i2c_set_clientdata(client, chip);
        return 0;
 
 err_exit: