summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-pca953x.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 09:46:29 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 09:46:29 -0700
commitf01603979a4afaad7504a728918b678d572cda9e (patch)
treec88ced8221cbc6eba9e1ea8a421d7cae7c9f8cd5 /drivers/gpio/gpio-pca953x.c
parent3002b7a31894cfa0e57080f7e0961b5fee788aa3 (diff)
parent3c92506d86785967fd7e7933e04491b9276c2f00 (diff)
Merge tag 'gpio-updates-for-v6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "We have a single new driver, support for a bunch of new models, improvements in drivers and core gpiolib code as well device-tree bindings changes. Summary: New driver: - IMX System Controller Unit GPIOs GPIO core: - add fdinfo output for the GPIO character device file descriptors (allows user-space to determine which processes own which GPIO lines) - improvements to OF GPIO code - new quirk for Asus UM325UAZ in gpiolib-acpi - new quirk for Freescale SPI in gpiolib-of Driver improvements: - add a new macro that reduces the amount of boilerplate code in ISA drivers and use it in relevant drivers - support two new models in gpio-pca953x - support new model in gpio-f7188x - convert more drivers to use immutable irq chips - other minor tweaks Device-tree bindings: - add DT bindings for gpio-imx-scu - convert Xilinx GPIO bindings to YAML - reference the properties from the SPI peripheral device-tree bindings instead of providing custom ones in the GPIO controller document - add parsing of GPIO hog nodes to the DT bindings for gpio-mpfs-gpio - relax the node name requirements in gpio-stmpe - add new models for gpio-rcar and gpio-pxa95xx - add a new vendor prefix: Diodes (for Diodes, Inc.) Misc: - pulled in the immutable branch from the x86 platform drivers tree including support for a new simatic board that depends on GPIO changes" * tag 'gpio-updates-for-v6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (36 commits) gpio: tc3589x: Make irqchip immutable gpiolib: cdev: add fdinfo output for line request file descriptors gpio: twl4030: Reorder functions which allows to drop a forward declaraion gpiolib: fix OOB access in quirk callbacks gpiolib: of: factor out conversion from OF flags gpiolib: rework quirk handling in of_find_gpio() gpiolib: of: make Freescale SPI quirk similar to all others gpiolib: of: do not ignore requested index when applying quirks gpio: ws16c48: Ensure number of irq matches number of base gpio: 104-idio-16: Ensure number of irq matches number of base gpio: 104-idi-48: Ensure number of irq matches number of base gpio: 104-dio-48e: Ensure number of irq matches number of base counter: 104-quad-8: Ensure number of irq matches number of base isa: Introduce the module_isa_driver_with_irq helper macro gpio: pca953x: Add support for PCAL6534 gpio: pca953x: Swap if statements to save later complexity gpio: pca953x: Fix pca953x_gpio_set_pull_up_down() dt-bindings: gpio: pca95xx: add entry for pcal6534 and PI4IOE5V6534Q dt-bindings: vendor-prefixes: add Diodes gpio: mt7621: Switch to use platform_get_irq() function ...
Diffstat (limited to 'drivers/gpio/gpio-pca953x.c')
-rw-r--r--drivers/gpio/gpio-pca953x.c177
1 files changed, 139 insertions, 38 deletions
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index cf9bf3fcaee2..ebe1943b85dd 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -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), },