diff options
Diffstat (limited to 'drivers/base/regmap/regmap-irq.c')
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 126 |
1 files changed, 97 insertions, 29 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 19db764ffa4a..760296a4b606 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -38,6 +38,7 @@ struct regmap_irq_chip_data { unsigned int *wake_buf; unsigned int *type_buf; unsigned int *type_buf_def; + unsigned int **virt_buf; unsigned int irq_reg_stride; unsigned int type_reg_stride; @@ -45,6 +46,27 @@ struct regmap_irq_chip_data { bool 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) @@ -73,7 +95,7 @@ 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, ret; + int i, j, ret; u32 reg; u32 unmask_offset; u32 val; @@ -87,8 +109,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 = d->chip->status_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &val); if (ret) @@ -108,8 +129,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (!d->chip->mask_base) continue; - reg = d->chip->mask_base + - (i * map->reg_stride * d->irq_reg_stride); + 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]); @@ -136,8 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) dev_err(d->map->dev, "Failed to sync masks in %x\n", reg); - reg = d->chip->wake_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->wake_base, i); if (d->wake_buf) { if (d->chip->wake_invert) ret = regmap_irq_update_bits(d, reg, @@ -161,8 +180,8 @@ 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 = d->chip->ack_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->ack_base, i); + /* some chips ack by write 0 */ if (d->chip->ack_invert) ret = regmap_write(map, reg, ~d->mask_buf[i]); @@ -187,8 +206,7 @@ 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 = d->chip->type_base + - (i * map->reg_stride * d->type_reg_stride); + reg = sub_irq_reg(d, d->chip->type_base, i); if (d->chip->type_invert) ret = regmap_irq_update_bits(d, reg, d->type_buf_def[i], ~d->type_buf[i]); @@ -201,6 +219,20 @@ 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); + ret = regmap_write(map, reg, d->virt_buf[i][j]); + if (ret != 0) + dev_err(d->map->dev, + "Failed to write virt 0x%x: %d\n", + reg, ret); + } + } + } + if (d->chip->runtime_pm) pm_runtime_put(map->dev); @@ -301,6 +333,11 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) default: return -EINVAL; } + + if (d->chip->set_type_virt) + return d->chip->set_type_virt(d->virt_buf, type, data->hwirq, + reg); + return 0; } @@ -352,8 +389,15 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, for (i = 0; i < subreg->num_regs; i++) { unsigned int offset = subreg->offset[i]; - ret = regmap_read(map, chip->status_base + offset, - &data->status_buf[offset]); + if (chip->not_fixed_stride) + ret = regmap_read(map, + chip->status_base + offset, + &data->status_buf[b]); + else + ret = regmap_read(map, + chip->status_base + offset, + &data->status_buf[offset]); + if (ret) break; } @@ -474,10 +518,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } else { for (i = 0; i < data->chip->num_regs; i++) { - ret = regmap_read(map, chip->status_base + - (i * map->reg_stride - * data->irq_reg_stride), - &data->status_buf[i]); + unsigned int reg = sub_irq_reg(data, + data->chip->status_base, i); + ret = regmap_read(map, reg, &data->status_buf[i]); if (ret != 0) { dev_err(map->dev, @@ -499,8 +542,8 @@ 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 = chip->ack_base + - (i * map->reg_stride * data->irq_reg_stride); + reg = sub_irq_reg(data, data->chip->ack_base, i); + if (chip->ack_invert) ret = regmap_write(map, reg, ~data->status_buf[i]); @@ -605,6 +648,12 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, return -EINVAL; } + if (chip->not_fixed_stride) { + for (i = 0; i < chip->num_regs; i++) + if (chip->sub_reg_offsets[i].num_regs != 1) + return -EINVAL; + } + if (irq_base) { irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { @@ -662,6 +711,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, goto err_alloc; } + if (chip->num_virt_regs) { + /* + * Create virt_buf[chip->num_extra_config_regs][chip->num_regs] + */ + d->virt_buf = kcalloc(chip->num_virt_regs, sizeof(*d->virt_buf), + GFP_KERNEL); + if (!d->virt_buf) + goto err_alloc; + + for (i = 0; i < chip->num_virt_regs; i++) { + d->virt_buf[i] = kcalloc(chip->num_regs, + sizeof(unsigned int), + GFP_KERNEL); + if (!d->virt_buf[i]) + goto err_alloc; + } + } + d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; d->irq = irq; @@ -700,8 +767,8 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (!chip->mask_base) continue; - reg = chip->mask_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->mask_base, i); + if (chip->mask_invert) ret = regmap_irq_update_bits(d, reg, d->mask_buf[i], ~d->mask_buf[i]); @@ -725,8 +792,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, continue; /* Ack masked but set interrupts */ - reg = chip->status_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_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", @@ -735,8 +801,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, } if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = chip->ack_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_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])); @@ -765,8 +830,7 @@ 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 = chip->wake_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->wake_base, i); if (chip->wake_invert) ret = regmap_irq_update_bits(d, reg, @@ -786,8 +850,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 = chip->type_base + - (i * map->reg_stride * d->type_reg_stride); + reg = sub_irq_reg(d, d->chip->type_base, i); ret = regmap_read(map, reg, &d->type_buf_def[i]); @@ -838,6 +901,11 @@ err_alloc: kfree(d->mask_buf); kfree(d->status_buf); kfree(d->status_reg_buf); + if (d->virt_buf) { + for (i = 0; i < chip->num_virt_regs; i++) + kfree(d->virt_buf[i]); + kfree(d->virt_buf); + } kfree(d); return ret; } |