staticconststruct dmi_system_id pca953x_dmi_acpi_irq_info[] = {
{ /* * On Intel Galileo Gen 2 board the IRQ pin of one of * the I²C GPIO expanders, which has GpioInt() resource, * is provided as an absolute number instead of being * relative. Since first controller (gpio-sch.c) and * second (gpio-dwapb.c) are at the fixed bases, we may * safely refer to the number in the global space to get * an IRQ out of it.
*/
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"),
},
},
{}
}; #endif
/* * 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.
*/ staticinline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsignedint offset)
{ unsignedint bit_pos_in_bank = offset % BANK_SZ; int msb = BANK_SZ - 1;
/* * 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 * Pull-up/pull-down enable reg 0x40 + 3 * bank_size RW * Pull-up/pull-down select reg 0x40 + 4 * 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.
*/
staticbool pca953x_check_register(struct pca953x_chip *chip, unsignedint 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)) returnfalse;
bank += 8;
}
/* Register is not in the matching bank. */ if (!(BIT(bank) & checkbank)) returnfalse;
/* Register is not within allowed range of bank. */ if (offset >= NBANK(chip)) returnfalse;
returntrue;
}
/* * 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.
*/ staticbool pcal6534_check_register(struct pca953x_chip *chip, unsignedint reg,
u32 checkbank)
{ int bank_shift; int bank; int offset;
if (reg >= 0x54) { /* * Handle lack of reserved registers after output port * configuration register to form a bank.
*/
reg -= 0x54;
bank_shift = 16;
} elseif (reg >= 0x30) { /* * Reserved block between 14h and 2Fh does not align on * expected bank boundaries like other devices.
*/
reg -= 0x30;
bank_shift = 8;
} else {
bank_shift = 0;
}
static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off)
{ int bank_shift = pca953x_bank_shift(chip); int addr = (reg & PCAL_GPIO_MASK) << bank_shift; int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
u8 regaddr = pinctrl | addr | (off / BANK_SZ);
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:
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; default:
pinctrl = 0; break;
}
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);
}
staticint pca953x_write_regs(struct pca953x_chip *chip, int reg, unsignedlong *val)
{
u8 regaddr = chip->recalc_addr(chip, reg, 0);
u8 value[MAX_BANK]; int i, ret;
for (i = 0; i < NBANK(chip); i++)
value[i] = bitmap_get_value8(val, i * BANK_SZ);
ret = regmap_bulk_write(chip->regmap, regaddr, value, NBANK(chip)); if (ret < 0) {
dev_err(&chip->client->dev, "failed writing register: %d\n", ret); return ret;
}
return 0;
}
staticint pca953x_read_regs(struct pca953x_chip *chip, int reg, unsignedlong *val)
{
u8 regaddr = chip->recalc_addr(chip, reg, 0);
u8 value[MAX_BANK]; int i, ret;
ret = regmap_bulk_read(chip->regmap, regaddr, value, NBANK(chip)); if (ret < 0) {
dev_err(&chip->client->dev, "failed reading register: %d\n", ret); return ret;
}
for (i = 0; i < NBANK(chip); i++)
bitmap_set_value8(val, value[i], i * BANK_SZ);
/* set output level */
ret = regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); if (ret) return ret;
/* * 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);
switch (pinconf_to_config_param(config)) { case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_DISABLE: return pca953x_gpio_set_pull_up_down(chip, offset, config); default: return -ENOTSUPP;
}
}
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);
if (dmi_first_match(pca953x_dmi_acpi_irq_info)) {
ret = pca953x_acpi_get_irq(dev); if (ret > 0)
client->irq = ret;
}
if (!client->irq) return 0;
if (irq_base == -1) return 0;
if (!(chip->driver_data & PCA_INT)) return 0;
ret = pca953x_read_regs(chip, chip->regs->input, irq_stat); if (ret) return ret;
/* * There is no way to know which GPIO line generated the * interrupt. We have to rely on the previous read for * this purpose.
*/
pca953x_read_regs(chip, chip->regs->direction, reg_direction);
bitmap_and(chip->irq_stat, irq_stat, reg_direction, gc->ngpio);
mutex_init(&chip->irq_lock);
girq = &chip->gpio_chip.irq;
gpio_irq_chip_set_chip(girq, &pca953x_irq_chip); /* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_simple_irq;
girq->threaded = true;
girq->first = irq_base; /* FIXME: get rid of this */
ret = devm_request_threaded_irq(dev, client->irq, NULL, pca953x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED, dev_name(dev),
chip); if (ret) return dev_err_probe(dev, ret, "failed to request irq\n");
/* * See if we need to de-assert a reset pin. * * There is no known ACPI-enabled platforms that are * using "reset" GPIO. Otherwise any of those platform * must use _DSD method with corresponding property.
*/
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) return dev_err_probe(dev, PTR_ERR(reset_gpio), "Failed to get reset gpio\n");
}
chip->client = client;
chip->driver_data = (uintptr_t)i2c_get_match_data(client); if (!chip->driver_data) return -ENODEV;
ret = pca953x_get_and_enable_regulator(chip); if (ret) return ret;
switch (PCA_CHIP_TYPE(chip->driver_data)) { case PCAL653X_TYPE:
chip->recalc_addr = pcal6534_recalc_addr;
chip->check_reg = pcal6534_check_register; 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); if (IS_ERR(chip->regmap)) return PTR_ERR(chip->regmap);
regcache_mark_dirty(chip->regmap);
mutex_init(&chip->i2c_lock); /* * In case we have an i2c-mux controlled by a GPIO provided by an * expander using the same driver higher on the device tree, read the * i2c adapter nesting depth and use the retrieved value as lockdep * subclass for chip->i2c_lock. * * REVISIT: This solution is not complete. It protects us from lockdep * false positives when the expander controlling the i2c-mux is on * a different level on the device tree, but not when it's on the same * level on a different branch (in which case the subclass number * would be the same). * * TODO: Once a correct solution is developed, a similar fix should be * applied to all other i2c-controlled GPIO expanders (and potentially * regmap-i2c).
*/
lockdep_set_subclass(&chip->i2c_lock,
i2c_adapter_depth(client->adapter));
/* * initialize cached registers from their original values. * we can't share this chip with another i2c master.
*/ switch (PCA_CHIP_TYPE(chip->driver_data)) { case PCA957X_TYPE:
chip->regs = &pca957x_regs;
ret = device_pca957x_init(chip); break; case TCA6418_TYPE:
chip->regs = &tca6418_regs; break; default:
chip->regs = &pca953x_regs;
ret = device_pca95xx_init(chip); break;
} if (ret) return ret;
ret = pca953x_irq_setup(chip, irq_base); if (ret) return ret;
/* * The ordering between direction and output is important, * sync these registers first and only then sync the rest.
*/
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 = 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); return ret;
}
#ifdef CONFIG_GPIO_PCA953X_IRQ if (chip->driver_data & PCA_PCAL) {
regaddr = chip->recalc_addr(chip, PCAL953X_IN_LATCH, 0);
ret = regcache_sync_region(chip->regmap, regaddr,
regaddr + NBANK(chip) - 1); if (ret) {
dev_err(dev, "Failed to sync INT latch registers: %d\n",
ret); return ret;
}
regaddr = chip->recalc_addr(chip, PCAL953X_INT_MASK, 0);
ret = regcache_sync_region(chip->regmap, regaddr,
regaddr + NBANK(chip) - 1); if (ret) {
dev_err(dev, "Failed to sync INT mask registers: %d\n",
ret); return ret;
}
} #endif
return 0;
}
staticint pca953x_restore_context(struct pca953x_chip *chip)
{ int ret;
guard(mutex)(&chip->i2c_lock);
if (chip->client->irq > 0)
enable_irq(chip->client->irq);
regcache_cache_only(chip->regmap, false);
regcache_mark_dirty(chip->regmap);
ret = pca953x_regcache_sync(chip); if (ret) return ret;
/* Disable IRQ to prevent early triggering while regmap "cache only" is on */ if (chip->client->irq > 0)
disable_irq(chip->client->irq);
regcache_cache_only(chip->regmap, true);
}
if (!atomic_read(&chip->wakeup_path)) {
ret = regulator_enable(chip->regulator); if (ret) {
dev_err(dev, "Failed to enable regulator: %d\n", ret); return 0;
}
}
ret = pca953x_restore_context(chip); if (ret)
dev_err(dev, "Failed to restore register map: %d\n", ret);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.