diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index a6db605707b0..a691553a0d6f 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -30,6 +30,9 @@ struct regmap_irq_chip_data { int irq; int wake_count; + unsigned int mask_base; + unsigned int unmask_base; + void *status_reg_buf; unsigned int *main_status_buf; unsigned int *status_buf; @@ -39,34 +42,16 @@ struct regmap_irq_chip_data { unsigned int *type_buf; unsigned int *type_buf_def; unsigned int **virt_buf; + unsigned int **config_buf; unsigned int irq_reg_stride; - unsigned int type_reg_stride; - bool clear_status:1; + unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data, + unsigned int base, int index); + + unsigned int clear_status:1; }; -static int sub_irq_reg(struct regmap_irq_chip_data *data, - unsigned int base_reg, int i) -{ - const struct regmap_irq_chip *chip = data->chip; - struct regmap *map = data->map; - struct regmap_irq_sub_irq_map *subreg; - unsigned int offset; - int reg = 0; - - if (!chip->sub_reg_offsets || !chip->not_fixed_stride) { - /* Assume linear mapping */ - reg = base_reg + (i * map->reg_stride * data->irq_reg_stride); - } else { - subreg = &chip->sub_reg_offsets[i]; - offset = subreg->offset[0]; - reg = base_reg + offset; - } - - return reg; -} - static inline const struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, int irq) @@ -74,6 +59,20 @@ struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, return &data->chip->irqs[irq]; } +static bool regmap_irq_can_bulk_read_status(struct regmap_irq_chip_data *data) +{ + struct regmap *map = data->map; + + /* + * While possible that a user-defined ->get_irq_reg() callback might + * be linear enough to support bulk reads, most of the time it won't. + * Therefore only allow them if the default callback is being used. + */ + return data->irq_reg_stride == 1 && map->reg_stride == 1 && + data->get_irq_reg == regmap_irq_get_irq_reg_linear && + !map->use_single_read; +} + static void regmap_irq_lock(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); @@ -81,23 +80,12 @@ static void regmap_irq_lock(struct irq_data *data) mutex_lock(&d->lock); } -static int regmap_irq_update_bits(struct regmap_irq_chip_data *d, - unsigned int reg, unsigned int mask, - unsigned int val) -{ - if (d->chip->mask_writeonly) - return regmap_write_bits(d->map, reg, mask, val); - else - return regmap_update_bits(d->map, reg, mask, val); -} - static void regmap_irq_sync_unlock(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; int i, j, ret; u32 reg; - u32 unmask_offset; u32 val; if (d->chip->runtime_pm) { @@ -109,7 +97,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (d->clear_status) { for (i = 0; i < d->chip->num_regs; i++) { - reg = sub_irq_reg(d, d->chip->status_base, i); + reg = d->get_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &val); if (ret) @@ -126,44 +114,32 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - if (!d->chip->mask_base) - continue; - - reg = sub_irq_reg(d, d->chip->mask_base, i); - if (d->chip->mask_invert) { - ret = regmap_irq_update_bits(d, reg, - d->mask_buf_def[i], ~d->mask_buf[i]); - } else if (d->chip->unmask_base) { - /* set mask with mask_base register */ - ret = regmap_irq_update_bits(d, reg, - d->mask_buf_def[i], ~d->mask_buf[i]); - if (ret < 0) - dev_err(d->map->dev, - "Failed to sync unmasks in %x\n", + if (d->mask_base) { + reg = d->get_irq_reg(d, d->mask_base, i); + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], d->mask_buf[i]); + if (ret) + dev_err(d->map->dev, "Failed to sync masks in %x\n", reg); - unmask_offset = d->chip->unmask_base - - d->chip->mask_base; - /* clear mask with unmask_base register */ - ret = regmap_irq_update_bits(d, - reg + unmask_offset, - d->mask_buf_def[i], - d->mask_buf[i]); - } else { - ret = regmap_irq_update_bits(d, reg, - d->mask_buf_def[i], d->mask_buf[i]); } - if (ret != 0) - dev_err(d->map->dev, "Failed to sync masks in %x\n", - reg); - reg = sub_irq_reg(d, d->chip->wake_base, i); + if (d->unmask_base) { + reg = d->get_irq_reg(d, d->unmask_base, i); + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], ~d->mask_buf[i]); + if (ret) + dev_err(d->map->dev, "Failed to sync masks in %x\n", + reg); + } + + reg = d->get_irq_reg(d, d->chip->wake_base, i); if (d->wake_buf) { if (d->chip->wake_invert) - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], ~d->wake_buf[i]); else - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->wake_buf[i]); if (ret != 0) @@ -180,7 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * it'll be ignored in irq handler, then may introduce irq storm */ if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { - reg = sub_irq_reg(d, d->chip->ack_base, i); + reg = d->get_irq_reg(d, d->chip->ack_base, i); /* some chips ack by write 0 */ if (d->chip->ack_invert) @@ -204,12 +180,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) for (i = 0; i < d->chip->num_type_reg; i++) { if (!d->type_buf_def[i]) continue; - reg = sub_irq_reg(d, d->chip->type_base, i); + reg = d->get_irq_reg(d, d->chip->type_base, i); if (d->chip->type_invert) - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->type_buf_def[i], ~d->type_buf[i]); else - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->type_buf_def[i], d->type_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync type in %x\n", @@ -220,8 +196,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (d->chip->num_virt_regs) { for (i = 0; i < d->chip->num_virt_regs; i++) { for (j = 0; j < d->chip->num_regs; j++) { - reg = sub_irq_reg(d, d->chip->virt_reg_base[i], - j); + reg = d->get_irq_reg(d, d->chip->virt_reg_base[i], + j); ret = regmap_write(map, reg, d->virt_buf[i][j]); if (ret != 0) dev_err(d->map->dev, @@ -231,6 +207,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data) } } + for (i = 0; i < d->chip->num_config_bases; i++) { + for (j = 0; j < d->chip->num_config_regs; j++) { + reg = d->get_irq_reg(d, d->chip->config_base[i], j); + ret = regmap_write(map, reg, d->config_buf[i][j]); + if (ret) + dev_err(d->map->dev, + "Failed to write config %x: %d\n", + reg, ret); + } + } + if (d->chip->runtime_pm) pm_runtime_put(map->dev); @@ -253,22 +240,19 @@ static void regmap_irq_enable(struct irq_data *data) struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); unsigned int reg = irq_data->reg_offset / map->reg_stride; - unsigned int mask, type; - - type = irq_data->type.type_falling_val | irq_data->type.type_rising_val; + unsigned int mask; /* * The type_in_mask flag means that the underlying hardware uses - * separate mask bits for rising and falling edge interrupts, but - * we want to make them into a single virtual interrupt with - * configurable edge. + * separate mask bits for each interrupt trigger type, but we want + * to have a single logical interrupt with a configurable type. * - * If the interrupt we're enabling defines the falling or rising - * masks then instead of using the regular mask bits for this - * interrupt, use the value previously written to the type buffer - * at the corresponding offset in regmap_irq_set_type(). + * If the interrupt we're enabling defines any supported types + * then instead of using the regular mask bits for this interrupt, + * use the value previously written to the type buffer at the + * corresponding offset in regmap_irq_set_type(). */ - if (d->chip->type_in_mask && type) + if (d->chip->type_in_mask && irq_data->type.types_supported) mask = d->type_buf[reg] & irq_data->mask; else mask = irq_data->mask; @@ -293,7 +277,7 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - int reg; + int reg, ret; const struct regmap_irq_type *t = &irq_data->type; if ((t->types_supported & type) != type) @@ -333,9 +317,19 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; } - if (d->chip->set_type_virt) - return d->chip->set_type_virt(d->virt_buf, type, data->hwirq, - reg); + if (d->chip->set_type_virt) { + ret = d->chip->set_type_virt(d->virt_buf, type, data->hwirq, + reg); + if (ret) + return ret; + } + + if (d->chip->set_type_config) { + ret = d->chip->set_type_config(d->config_buf, type, + irq_data, reg); + if (ret) + return ret; + } return 0; } @@ -376,14 +370,17 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, const struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; struct regmap_irq_sub_irq_map *subreg; + unsigned int reg; int i, ret = 0; if (!chip->sub_reg_offsets) { - /* Assume linear mapping */ - ret = regmap_read(map, chip->status_base + - (b * map->reg_stride * data->irq_reg_stride), - &data->status_buf[b]); + reg = data->get_irq_reg(data, chip->status_base, b); + ret = regmap_read(map, reg, &data->status_buf[b]); } else { + /* + * Note we can't use ->get_irq_reg() here because the offsets + * in 'subreg' are *not* interchangeable with indices. + */ subreg = &chip->sub_reg_offsets[b]; for (i = 0; i < subreg->num_regs; i++) { unsigned int offset = subreg->offset[i]; @@ -449,10 +446,18 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) * sake of simplicity. and add bulk reads only if needed */ for (i = 0; i < chip->num_main_regs; i++) { - ret = regmap_read(map, chip->main_status + - (i * map->reg_stride - * data->irq_reg_stride), - &data->main_status_buf[i]); + /* + * For not_fixed_stride, don't use ->get_irq_reg(). + * It would produce an incorrect result. + */ + if (data->chip->not_fixed_stride) + reg = chip->main_status + + i * map->reg_stride * data->irq_reg_stride; + else + reg = data->get_irq_reg(data, + chip->main_status, i); + + ret = regmap_read(map, reg, &data->main_status_buf[i]); if (ret) { dev_err(map->dev, "Failed to read IRQ status %d\n", @@ -481,8 +486,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - } else if (!map->use_single_read && map->reg_stride == 1 && - data->irq_reg_stride == 1) { + } else if (regmap_irq_can_bulk_read_status(data)) { u8 *buf8 = data->status_reg_buf; u16 *buf16 = data->status_reg_buf; @@ -518,7 +522,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } else { for (i = 0; i < data->chip->num_regs; i++) { - unsigned int reg = sub_irq_reg(data, + unsigned int reg = data->get_irq_reg(data, data->chip->status_base, i); ret = regmap_read(map, reg, &data->status_buf[i]); @@ -546,7 +550,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = sub_irq_reg(data, data->chip->ack_base, i); + reg = data->get_irq_reg(data, data->chip->ack_base, i); if (chip->ack_invert) ret = regmap_write(map, reg, @@ -606,6 +610,91 @@ static const struct irq_domain_ops regmap_domain_ops = { .xlate = irq_domain_xlate_onetwocell, }; +/** + * regmap_irq_get_irq_reg_linear() - Linear IRQ register mapping callback. + * @data: Data for the &struct regmap_irq_chip + * @base: Base register + * @index: Register index + * + * Returns the register address corresponding to the given @base and @index + * by the formula ``base + index * regmap_stride * irq_reg_stride``. + */ +unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data, + unsigned int base, int index) +{ + const struct regmap_irq_chip *chip = data->chip; + struct regmap *map = data->map; + + /* + * FIXME: This is for backward compatibility and should be removed + * when not_fixed_stride is dropped (it's only used by qcom-pm8008). + */ + if (chip->not_fixed_stride && chip->sub_reg_offsets) { + struct regmap_irq_sub_irq_map *subreg; + + subreg = &chip->sub_reg_offsets[0]; + return base + subreg->offset[0]; + } + + return base + index * map->reg_stride * chip->irq_reg_stride; +} +EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear); + +/** + * regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback. + * @buf: Buffer containing configuration register values, this is a 2D array of + * `num_config_bases` rows, each of `num_config_regs` elements. + * @type: The requested IRQ type. + * @irq_data: The IRQ being configured. + * @idx: Index of the irq's config registers within each array `buf[i]` + * + * This is a &struct regmap_irq_chip->set_type_config callback suitable for + * chips with one config register. Register values are updated according to + * the &struct regmap_irq_type data associated with an IRQ. + */ +int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type, + const struct regmap_irq *irq_data, int idx) +{ + const struct regmap_irq_type *t = &irq_data->type; + + if (t->type_reg_mask) + buf[0][idx] &= ~t->type_reg_mask; + else + buf[0][idx] &= ~(t->type_falling_val | + t->type_rising_val | + t->type_level_low_val | + t->type_level_high_val); + + switch (type) { + case IRQ_TYPE_EDGE_FALLING: + buf[0][idx] |= t->type_falling_val; + break; + + case IRQ_TYPE_EDGE_RISING: + buf[0][idx] |= t->type_rising_val; + break; + + case IRQ_TYPE_EDGE_BOTH: + buf[0][idx] |= (t->type_falling_val | + t->type_rising_val); + break; + + case IRQ_TYPE_LEVEL_HIGH: + buf[0][idx] |= t->type_level_high_val; + break; + + case IRQ_TYPE_LEVEL_LOW: + buf[0][idx] |= t->type_level_low_val; + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple); + /** * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling * @@ -634,7 +723,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, int ret = -ENOMEM; int num_type_reg; u32 reg; - u32 unmask_offset; if (chip->num_regs <= 0) return -EINVAL; @@ -651,11 +739,19 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, } if (chip->not_fixed_stride) { + dev_warn(map->dev, "not_fixed_stride is deprecated; use ->get_irq_reg() instead"); + for (i = 0; i < chip->num_regs; i++) if (chip->sub_reg_offsets[i].num_regs != 1) return -EINVAL; } + if (chip->num_type_reg) + dev_warn(map->dev, "type registers are deprecated; use config registers instead"); + + if (chip->num_virt_regs || chip->virt_reg_base || chip->set_type_virt) + dev_warn(map->dev, "virtual registers are deprecated; use config registers instead"); + if (irq_base) { irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { @@ -671,30 +767,30 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (chip->num_main_regs) { d->main_status_buf = kcalloc(chip->num_main_regs, - sizeof(unsigned int), + sizeof(*d->main_status_buf), GFP_KERNEL); if (!d->main_status_buf) goto err_alloc; } - d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), + d->status_buf = kcalloc(chip->num_regs, sizeof(*d->status_buf), GFP_KERNEL); if (!d->status_buf) goto err_alloc; - d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), + d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf), GFP_KERNEL); if (!d->mask_buf) goto err_alloc; - d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int), + d->mask_buf_def = kcalloc(chip->num_regs, sizeof(*d->mask_buf_def), GFP_KERNEL); if (!d->mask_buf_def) goto err_alloc; if (chip->wake_base) { - d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int), + d->wake_buf = kcalloc(chip->num_regs, sizeof(*d->wake_buf), GFP_KERNEL); if (!d->wake_buf) goto err_alloc; @@ -703,11 +799,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg; if (num_type_reg) { d->type_buf_def = kcalloc(num_type_reg, - sizeof(unsigned int), GFP_KERNEL); + sizeof(*d->type_buf_def), GFP_KERNEL); if (!d->type_buf_def) goto err_alloc; - d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int), + d->type_buf = kcalloc(num_type_reg, sizeof(*d->type_buf), GFP_KERNEL); if (!d->type_buf) goto err_alloc; @@ -724,13 +820,31 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, for (i = 0; i < chip->num_virt_regs; i++) { d->virt_buf[i] = kcalloc(chip->num_regs, - sizeof(unsigned int), + sizeof(**d->virt_buf), GFP_KERNEL); if (!d->virt_buf[i]) goto err_alloc; } } + if (chip->num_config_bases && chip->num_config_regs) { + /* + * Create config_buf[num_config_bases][num_config_regs] + */ + d->config_buf = kcalloc(chip->num_config_bases, + sizeof(*d->config_buf), GFP_KERNEL); + if (!d->config_buf) + goto err_alloc; + + for (i = 0; i < chip->num_config_regs; i++) { + d->config_buf[i] = kcalloc(chip->num_config_regs, + sizeof(**d->config_buf), + GFP_KERNEL); + if (!d->config_buf[i]) + goto err_alloc; + } + } + d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; d->irq = irq; @@ -738,18 +852,53 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, d->chip = chip; d->irq_base = irq_base; + if (chip->mask_base && chip->unmask_base && + !chip->mask_unmask_non_inverted) { + /* + * Chips that specify both mask_base and unmask_base used to + * get inverted mask behavior by default, with no way to ask + * for the normal, non-inverted behavior. This "inverted by + * default" behavior is deprecated, but we have to support it + * until existing drivers have been fixed. + * + * Existing drivers should be updated by swapping mask_base + * and unmask_base and setting mask_unmask_non_inverted=true. + * New drivers should always set the flag. + */ + dev_warn(map->dev, "mask_base and unmask_base are inverted, please fix it"); + + /* Might as well warn about mask_invert while we're at it... */ + if (chip->mask_invert) + dev_warn(map->dev, "mask_invert=true ignored"); + + d->mask_base = chip->unmask_base; + d->unmask_base = chip->mask_base; + } else if (chip->mask_invert) { + /* + * Swap the roles of mask_base and unmask_base if the bits are + * inverted. This is deprecated, drivers should use unmask_base + * directly. + */ + dev_warn(map->dev, "mask_invert=true is deprecated; please switch to unmask_base"); + + d->mask_base = chip->unmask_base; + d->unmask_base = chip->mask_base; + } else { + d->mask_base = chip->mask_base; + d->unmask_base = chip->unmask_base; + } + if (chip->irq_reg_stride) d->irq_reg_stride = chip->irq_reg_stride; else d->irq_reg_stride = 1; - if (chip->type_reg_stride) - d->type_reg_stride = chip->type_reg_stride; + if (chip->get_irq_reg) + d->get_irq_reg = chip->get_irq_reg; else - d->type_reg_stride = 1; + d->get_irq_reg = regmap_irq_get_irq_reg_linear; - if (!map->use_single_read && map->reg_stride == 1 && - d->irq_reg_stride == 1) { + if (regmap_irq_can_bulk_read_status(d)) { d->status_reg_buf = kmalloc_array(chip->num_regs, map->format.val_bytes, GFP_KERNEL); @@ -766,35 +915,34 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - if (!chip->mask_base) - continue; - reg = sub_irq_reg(d, d->chip->mask_base, i); + if (d->mask_base) { + reg = d->get_irq_reg(d, d->mask_base, i); + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], d->mask_buf[i]); + if (ret) { + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", + reg, ret); + goto err_alloc; + } + } - if (chip->mask_invert) - ret = regmap_irq_update_bits(d, reg, - d->mask_buf[i], ~d->mask_buf[i]); - else if (d->chip->unmask_base) { - unmask_offset = d->chip->unmask_base - - d->chip->mask_base; - ret = regmap_irq_update_bits(d, - reg + unmask_offset, - d->mask_buf[i], - d->mask_buf[i]); - } else - ret = regmap_irq_update_bits(d, reg, - d->mask_buf[i], d->mask_buf[i]); - if (ret != 0) { - dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", - reg, ret); - goto err_alloc; + if (d->unmask_base) { + reg = d->get_irq_reg(d, d->unmask_base, i); + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], ~d->mask_buf[i]); + if (ret) { + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", + reg, ret); + goto err_alloc; + } } if (!chip->init_ack_masked) continue; /* Ack masked but set interrupts */ - reg = sub_irq_reg(d, d->chip->status_base, i); + reg = d->get_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &d->status_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status: %d\n", @@ -806,7 +954,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, d->status_buf[i] = ~d->status_buf[i]; if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = sub_irq_reg(d, d->chip->ack_base, i); + reg = d->get_irq_reg(d, d->chip->ack_base, i); if (chip->ack_invert) ret = regmap_write(map, reg, ~(d->status_buf[i] & d->mask_buf[i])); @@ -831,14 +979,14 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (d->wake_buf) { for (i = 0; i < chip->num_regs; i++) { d->wake_buf[i] = d->mask_buf_def[i]; - reg = sub_irq_reg(d, d->chip->wake_base, i); + reg = d->get_irq_reg(d, d->chip->wake_base, i); if (chip->wake_invert) - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], 0); else - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->wake_buf[i]); if (ret != 0) { @@ -851,7 +999,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (chip->num_type_reg && !chip->type_in_mask) { for (i = 0; i < chip->num_type_reg; ++i) { - reg = sub_irq_reg(d, d->chip->type_base, i); + reg = d->get_irq_reg(d, d->chip->type_base, i); ret = regmap_read(map, reg, &d->type_buf_def[i]); @@ -907,6 +1055,11 @@ err_alloc: kfree(d->virt_buf[i]); kfree(d->virt_buf); } + if (d->config_buf) { + for (i = 0; i < chip->num_config_bases; i++) + kfree(d->config_buf[i]); + kfree(d->config_buf); + } kfree(d); return ret; } @@ -947,7 +1100,7 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip); void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) { unsigned int virq; - int hwirq; + int i, hwirq; if (!d) return; @@ -977,6 +1130,11 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) kfree(d->mask_buf); kfree(d->status_reg_buf); kfree(d->status_buf); + if (d->config_buf) { + for (i = 0; i < d->chip->num_config_bases; i++) + kfree(d->config_buf[i]); + kfree(d->config_buf); + } kfree(d); } EXPORT_SYMBOL_GPL(regmap_del_irq_chip); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index d5b08f4f0dc0..7cf2157134ac 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1440,6 +1440,8 @@ struct regmap_irq_sub_irq_map { unsigned int *offset; }; +struct regmap_irq_chip_data; + /** * struct regmap_irq_chip - Description of a generic regmap irq_chip. * @@ -1467,32 +1469,50 @@ struct regmap_irq_sub_irq_map { * main_status set. * * @status_base: Base status register address. - * @mask_base: Base mask register address. - * @mask_writeonly: Base mask register is write only. - * @unmask_base: Base unmask register address. for chips who have - * separate mask and unmask registers + * @mask_base: Base mask register address. Mask bits are set to 1 when an + * interrupt is masked, 0 when unmasked. + * @unmask_base: Base unmask register address. Unmask bits are set to 1 when + * an interrupt is unmasked and 0 when masked. * @ack_base: Base ack address. If zero then the chip is clear on read. * Using zero value is possible with @use_ack bit. * @wake_base: Base address for wake enables. If zero unsupported. - * @type_base: Base address for irq type. If zero unsupported. - * @virt_reg_base: Base addresses for extra config regs. + * @type_base: Base address for irq type. If zero unsupported. Deprecated, + * use @config_base instead. + * @virt_reg_base: Base addresses for extra config regs. Deprecated, use + * @config_base instead. + * @config_base: Base address for IRQ type config regs. If null unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. * @init_ack_masked: Ack all masked interrupts once during initalization. * @mask_invert: Inverted mask register: cleared bits are masked out. + * Deprecated; prefer describing an inverted mask register as + * an unmask register. + * @mask_unmask_non_inverted: Controls mask bit inversion for chips that set + * both @mask_base and @unmask_base. If false, mask and unmask bits are + * inverted (which is deprecated behavior); if true, bits will not be + * inverted and the registers keep their normal behavior. Note that if + * you use only one of @mask_base or @unmask_base, this flag has no + * effect and is unnecessary. Any new drivers that set both @mask_base + * and @unmask_base should set this to true to avoid relying on the + * deprecated behavior. * @use_ack: Use @ack register even if it is zero. * @ack_invert: Inverted ack register: cleared bits for ack. * @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts. * @wake_invert: Inverted wake register: cleared bits are wake enabled. - * @type_invert: Invert the type flags. - * @type_in_mask: Use the mask registers for controlling irq type. For - * interrupts defining type_rising/falling_mask use mask_base - * for edge configuration and never update bits in type_base. + * @type_invert: Invert the type flags. Deprecated, use config registers + * instead. + * @type_in_mask: Use the mask registers for controlling irq type. Use this if + * the hardware provides separate bits for rising/falling edge + * or low/high level interrupts and they should be combined into + * a single logical interrupt. Use &struct regmap_irq_type data + * to define the mask bit for each irq type. * @clear_on_unmask: For chips with interrupts cleared on read: read the status * registers before unmasking interrupts to clear any bits * set when they were masked. * @not_fixed_stride: Used when chip peripherals are not laid out with fixed - * stride. Must be used with sub_reg_offsets containing the - * offsets to each peripheral. + * stride. Must be used with sub_reg_offsets containing the + * offsets to each peripheral. Deprecated; the same thing + * can be accomplished with a @get_irq_reg callback, without + * the need for a @sub_reg_offsets table. * @status_invert: Inverted status register: cleared bits are active interrupts. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * @@ -1500,17 +1520,28 @@ struct regmap_irq_sub_irq_map { * @irqs: Descriptors for individual IRQs. Interrupt numbers are * assigned based on the index in the array of the interrupt. * @num_irqs: Number of descriptors. - * @num_type_reg: Number of type registers. + * @num_type_reg: Number of type registers. Deprecated, use config registers + * instead. * @num_virt_regs: Number of non-standard irq configuration registers. - * If zero unsupported. - * @type_reg_stride: Stride to use for chips where type registers are not - * contiguous. + * If zero unsupported. Deprecated, use config registers + * instead. + * @num_config_bases: Number of config base registers. + * @num_config_regs: Number of config registers for each config base register. * @handle_pre_irq: Driver specific callback to handle interrupt from device * before regmap_irq_handler process the interrupts. * @handle_post_irq: Driver specific callback to handle interrupt from device * after handling the interrupts in regmap_irq_handler(). * @set_type_virt: Driver specific callback to extend regmap_irq_set_type() - * and configure virt regs. + * and configure virt regs. Deprecated, use @set_type_config + * callback and config registers instead. + * @set_type_config: Callback used for configuring irq types. + * @get_irq_reg: Callback for mapping (base register, index) pairs to register + * addresses. The base register will be one of @status_base, + * @mask_base, etc., @main_status, or any of @config_base. + * The index will be in the range [0, num_main_regs[ for the + * main status base, [0, num_type_settings[ for any config + * register base, and [0, num_regs[ for any other base. + * If unspecified then regmap_irq_get_irq_reg_linear() is used. * @irq_drv_data: Driver specific IRQ data which is passed as parameter when * driver specific pre/post interrupt handler is called. * @@ -1533,20 +1564,21 @@ struct regmap_irq_chip { unsigned int wake_base; unsigned int type_base; unsigned int *virt_reg_base; + const unsigned int *config_base; unsigned int irq_reg_stride; - bool mask_writeonly:1; - bool init_ack_masked:1; - bool mask_invert:1; - bool use_ack:1; - bool ack_invert:1; - bool clear_ack:1; - bool wake_invert:1; - bool runtime_pm:1; - bool type_invert:1; - bool type_in_mask:1; - bool clear_on_unmask:1; - bool not_fixed_stride:1; - bool status_invert:1; + unsigned int init_ack_masked:1; + unsigned int mask_invert:1; + unsigned int mask_unmask_non_inverted:1; + unsigned int use_ack:1; + unsigned int ack_invert:1; + unsigned int clear_ack:1; + unsigned int wake_invert:1; + unsigned int runtime_pm:1; + unsigned int type_invert:1; + unsigned int type_in_mask:1; + unsigned int clear_on_unmask:1; + unsigned int not_fixed_stride:1; + unsigned int status_invert:1; int num_regs; @@ -1555,16 +1587,24 @@ struct regmap_irq_chip { int num_type_reg; int num_virt_regs; - unsigned int type_reg_stride; + int num_config_bases; + int num_config_regs; int (*handle_pre_irq)(void *irq_drv_data); int (*handle_post_irq)(void *irq_drv_data); int (*set_type_virt)(unsigned int **buf, unsigned int type, unsigned long hwirq, int reg); + int (*set_type_config)(unsigned int **buf, unsigned int type, + const struct regmap_irq *irq_data, int idx); + unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data, + unsigned int base, int index); void *irq_drv_data; }; -struct regmap_irq_chip_data; +unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data, + unsigned int base, int index); +int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type, + const struct regmap_irq *irq_data, int idx); int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int irq_base, const struct regmap_irq_chip *chip,