diff options
Diffstat (limited to 'drivers/gpio/gpio-pca953x.c')
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 207 |
1 files changed, 171 insertions, 36 deletions
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 13cc120cf11f..b46927f55038 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -38,6 +38,10 @@ #define PCA953X_INVERT 0x02 #define PCA953X_DIRECTION 0x03 +#define TCA6418_INPUT 0x14 +#define TCA6418_OUTPUT 0x17 +#define TCA6418_DIRECTION 0x23 + #define REG_ADDR_MASK GENMASK(5, 0) #define REG_ADDR_EXT BIT(6) #define REG_ADDR_AI BIT(7) @@ -76,7 +80,8 @@ #define PCA953X_TYPE BIT(12) #define PCA957X_TYPE BIT(13) #define PCAL653X_TYPE BIT(14) -#define PCA_TYPE_MASK GENMASK(15, 12) +#define TCA6418_TYPE BIT(16) +#define PCA_TYPE_MASK GENMASK(16, 12) #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) @@ -115,6 +120,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, + { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, @@ -204,6 +210,13 @@ static const struct pca953x_reg_config pca957x_regs = { .invert = PCA957X_INVRT, }; +static const struct pca953x_reg_config tca6418_regs = { + .direction = TCA6418_DIRECTION, + .output = TCA6418_OUTPUT, + .input = TCA6418_INPUT, + .invert = 0xFF, /* Does not apply */ +}; + struct pca953x_chip { unsigned gpio_start; struct mutex i2c_lock; @@ -215,6 +228,8 @@ struct pca953x_chip { DECLARE_BITMAP(irq_stat, MAX_LINE); DECLARE_BITMAP(irq_trig_raise, MAX_LINE); DECLARE_BITMAP(irq_trig_fall, MAX_LINE); + DECLARE_BITMAP(irq_trig_level_high, MAX_LINE); + DECLARE_BITMAP(irq_trig_level_low, MAX_LINE); #endif atomic_t wakeup_path; @@ -235,6 +250,22 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); } +/* + * Helper function to get the correct bit mask for a given offset and chip type. + * The TCA6418's input, output, and direction banks have a peculiar bit order: + * the first byte uses reversed bit order, while the second byte uses standard order. + */ +static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int offset) +{ + unsigned int bit_pos_in_bank = offset % BANK_SZ; + int msb = BANK_SZ - 1; + + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE && offset <= msb) + return BIT(msb - bit_pos_in_bank); + + return BIT(bit_pos_in_bank); +} + #define PCA953x_BANK_INPUT BIT(0) #define PCA953x_BANK_OUTPUT BIT(1) #define PCA953x_BANK_POLARITY BIT(2) @@ -351,18 +382,43 @@ static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg, return true; } +/* TCA6418 breaks the PCA953x register order rule */ +static bool tca6418_check_register(struct pca953x_chip *chip, unsigned int reg, + u32 access_type_mask) +{ + /* Valid Input Registers - BIT(0) for readable access */ + if (reg >= TCA6418_INPUT && reg < (TCA6418_INPUT + NBANK(chip))) + return (access_type_mask & BIT(0)); + + /* Valid Output Registers - BIT(1) for writeable access */ + if (reg >= TCA6418_OUTPUT && reg < (TCA6418_OUTPUT + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + /* Valid Direction Registers - BIT(2) for volatile access */ + if (reg >= TCA6418_DIRECTION && reg < (TCA6418_DIRECTION + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + return false; +} + 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) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(0) to indicate read access */ + return tca6418_check_register(chip, reg, BIT(0)); + default: bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } if (chip->driver_data & PCA_PCAL) { @@ -379,12 +435,18 @@ 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) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(1) for write access */ + return tca6418_check_register(chip, reg, BIT(1)); + default: bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } if (chip->driver_data & PCA_PCAL) @@ -399,10 +461,17 @@ 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) == PCA957X_TYPE) + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_INPUT; - else + break; + case TCA6418_TYPE: + /* BIT(2) for volatile access */ + return tca6418_check_register(chip, reg, BIT(2)); + default: bank = PCA953x_BANK_INPUT; + break; + } if (chip->driver_data & PCA_PCAL) bank |= PCAL9xxx_BANK_IRQ_STAT; @@ -487,6 +556,16 @@ static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off) return pinctrl + addr + (off / BANK_SZ); } +static u8 tca6418_recalc_addr(struct pca953x_chip *chip, int reg_base, int offset) +{ + /* + * reg_base will be TCA6418_INPUT, TCA6418_OUTPUT, or TCA6418_DIRECTION + * offset is the global GPIO line offset (0-17) + * BANK_SZ is 8 for TCA6418 (8 bits per register bank) + */ + return reg_base + (offset / BANK_SZ); +} + static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val) { u8 regaddr = chip->recalc_addr(chip, reg, 0); @@ -527,11 +606,14 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); guard(mutex)(&chip->i2c_lock); - return regmap_write_bits(chip->regmap, dirreg, bit, bit); + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return regmap_update_bits(chip->regmap, dirreg, bit, 0); + + return regmap_update_bits(chip->regmap, dirreg, bit, bit); } static int pca953x_gpio_direction_output(struct gpio_chip *gc, @@ -540,25 +622,31 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, struct pca953x_chip *chip = gpiochip_get_data(gc); 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); + u8 bit = pca953x_get_bit_mask(chip, off); int ret; guard(mutex)(&chip->i2c_lock); /* set output level */ - ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + ret = regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); if (ret) return ret; - /* then direction */ - return regmap_write_bits(chip->regmap, dirreg, bit, 0); + /* + * then direction + * (in/out logic is inverted on TCA6418) + */ + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return regmap_update_bits(chip->regmap, dirreg, bit, bit); + + return regmap_update_bits(chip->regmap, dirreg, bit, 0); } static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 inreg = chip->recalc_addr(chip, chip->regs->input, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; @@ -575,18 +663,18 @@ static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off, { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); guard(mutex)(&chip->i2c_lock); - return regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + return regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); } static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; @@ -595,7 +683,14 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) if (ret < 0) return ret; - if (reg_val & bit) + /* (in/out logic is inverted on TCA6418) */ + if (reg_val & bit) { + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; + } + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -656,9 +751,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, /* Configure pull-up/pull-down */ if (param == PIN_CONFIG_BIAS_PULL_UP) - ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, bit); else if (param == PIN_CONFIG_BIAS_PULL_DOWN) - ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, 0); else ret = 0; if (ret) @@ -666,9 +761,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, /* Disable/Enable pull-up/pull-down */ if (param == PIN_CONFIG_BIAS_DISABLE) - return regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + return regmap_update_bits(chip->regmap, pull_en_reg, bit, 0); else - return regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + return regmap_update_bits(chip->regmap, pull_en_reg, bit, bit); } static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, @@ -694,10 +789,10 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->direction_input = pca953x_gpio_direction_input; gc->direction_output = pca953x_gpio_direction_output; gc->get = pca953x_gpio_get_value; - gc->set_rv = pca953x_gpio_set_value; + gc->set = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->get_multiple = pca953x_gpio_get_multiple; - gc->set_multiple_rv = pca953x_gpio_set_multiple; + gc->set_multiple = pca953x_gpio_set_multiple; gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; @@ -774,6 +869,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) pca953x_read_regs(chip, chip->regs->direction, reg_direction); bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); + bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); + bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio); bitmap_complement(reg_direction, reg_direction, gc->ngpio); bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); @@ -791,13 +888,15 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) struct device *dev = &chip->client->dev; irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (!(type & IRQ_TYPE_EDGE_BOTH)) { + if (!(type & IRQ_TYPE_SENSE_MASK)) { dev_err(dev, "irq %d: unsupported type %d\n", d->irq, type); return -EINVAL; } assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING); assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING); + assign_bit(hwirq, chip->irq_trig_level_low, type & IRQ_TYPE_LEVEL_LOW); + assign_bit(hwirq, chip->irq_trig_level_high, type & IRQ_TYPE_LEVEL_HIGH); return 0; } @@ -810,6 +909,8 @@ static void pca953x_irq_shutdown(struct irq_data *d) clear_bit(hwirq, chip->irq_trig_raise); clear_bit(hwirq, chip->irq_trig_fall); + clear_bit(hwirq, chip->irq_trig_level_low); + clear_bit(hwirq, chip->irq_trig_level_high); } static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p) @@ -840,6 +941,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin DECLARE_BITMAP(cur_stat, MAX_LINE); DECLARE_BITMAP(new_stat, MAX_LINE); DECLARE_BITMAP(trigger, MAX_LINE); + DECLARE_BITMAP(edges, MAX_LINE); int ret; ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); @@ -857,13 +959,26 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_copy(chip->irq_stat, new_stat, gc->ngpio); - if (bitmap_empty(trigger, gc->ngpio)) - return false; + if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) && + bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) { + if (bitmap_empty(trigger, gc->ngpio)) + return false; + } bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio); bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio); - bitmap_or(new_stat, old_stat, cur_stat, gc->ngpio); - bitmap_and(pending, new_stat, trigger, gc->ngpio); + bitmap_or(edges, old_stat, cur_stat, gc->ngpio); + bitmap_and(pending, edges, trigger, gc->ngpio); + + bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio); + bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); + bitmap_or(pending, pending, cur_stat, gc->ngpio); + + bitmap_complement(cur_stat, new_stat, gc->ngpio); + bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); + bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); + bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); + bitmap_or(pending, pending, old_stat, gc->ngpio); return !bitmap_empty(pending, gc->ngpio); } @@ -952,7 +1067,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), chip); if (ret) - return dev_err_probe(dev, client->irq, "failed to request irq\n"); + return dev_err_probe(dev, ret, "failed to request irq\n"); return 0; } @@ -1095,12 +1210,22 @@ static int pca953x_probe(struct i2c_client *client) regmap_config = &pca953x_i2c_regmap; } - if (PCA_CHIP_TYPE(chip->driver_data) == PCAL653X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCAL653X_TYPE: chip->recalc_addr = pcal6534_recalc_addr; chip->check_reg = pcal6534_check_register; - } else { + break; + case TCA6418_TYPE: + chip->recalc_addr = tca6418_recalc_addr; + /* + * We don't assign chip->check_reg = tca6418_check_register directly here. + * Instead, the wrappers handle the dispatch based on PCA_CHIP_TYPE. + */ + break; + default: chip->recalc_addr = pca953x_recalc_addr; chip->check_reg = pca953x_check_register; + break; } chip->regmap = devm_regmap_init_i2c(client, regmap_config); @@ -1129,15 +1254,22 @@ static int pca953x_probe(struct i2c_client *client) lockdep_set_subclass(&chip->i2c_lock, i2c_adapter_depth(client->adapter)); - /* initialize cached registers from their original values. + /* + * 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) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: chip->regs = &pca957x_regs; ret = device_pca957x_init(chip); - } else { + break; + case TCA6418_TYPE: + chip->regs = &tca6418_regs; + break; + default: chip->regs = &pca953x_regs; ret = device_pca95xx_init(chip); + break; } if (ret) return ret; @@ -1303,6 +1435,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), }, { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, + { .compatible = "ti,tca6418", .data = (void *)(18 | TCA6418_TYPE | PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca9535", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, @@ -1333,7 +1466,9 @@ static int __init pca953x_init(void) { return i2c_add_driver(&pca953x_driver); } -/* register after i2c postcore initcall and before + +/* + * register after i2c postcore initcall and before * subsys initcalls that may rely on these GPIOs */ subsys_initcall(pca953x_init); |