Loading drivers/base/regmap/regmap-irq.c +95 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ struct regmap_irq_chip_data { int wake_count; void *status_reg_buf; unsigned int *main_status_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; Loading Loading @@ -329,6 +330,33 @@ static const struct irq_chip regmap_irq_chip = { .irq_set_wake = regmap_irq_set_wake, }; static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, unsigned int b) { const struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; struct regmap_irq_sub_irq_map *subreg; 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]); } else { subreg = &chip->sub_reg_offsets[b]; 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 (ret) break; } } return ret; } static irqreturn_t regmap_irq_thread(int irq, void *d) { struct regmap_irq_chip_data *data = d; Loading @@ -352,11 +380,65 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } /* * Read in the statuses, using a single bulk read if possible * in order to reduce the I/O overheads. * Read only registers with active IRQs if the chip has 'main status * register'. Else read in the statuses, using a single bulk read if * possible in order to reduce the I/O overheads. */ if (!map->use_single_read && map->reg_stride == 1 && if (chip->num_main_regs) { unsigned int max_main_bits; unsigned long size; size = chip->num_regs * sizeof(unsigned int); max_main_bits = (chip->num_main_status_bits) ? chip->num_main_status_bits : chip->num_regs; /* Clear the status buf as we don't read all status regs */ memset(data->status_buf, 0, size); /* We could support bulk read for main status registers * but I don't expect to see devices with really many main * status registers so let's only support single reads for the * 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]); if (ret) { dev_err(map->dev, "Failed to read IRQ status %d\n", ret); goto exit; } } /* Read sub registers with active IRQs */ for (i = 0; i < chip->num_main_regs; i++) { unsigned int b; const unsigned long mreg = data->main_status_buf[i]; for_each_set_bit(b, &mreg, map->format.val_bytes * 8) { if (i * map->format.val_bytes * 8 + b > max_main_bits) break; ret = read_sub_irq_data(data, b); if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status %d\n", ret); if (chip->runtime_pm) pm_runtime_put(map->dev); goto exit; } } } } else if (!map->use_single_read && map->reg_stride == 1 && data->irq_reg_stride == 1) { u8 *buf8 = data->status_reg_buf; u16 *buf16 = data->status_reg_buf; u32 *buf32 = data->status_reg_buf; Loading Loading @@ -521,6 +603,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; if (chip->num_main_regs) { d->main_status_buf = kcalloc(chip->num_main_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->main_status_buf) goto err_alloc; } d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->status_buf) Loading include/linux/regmap.h +31 −0 Original line number Diff line number Diff line Loading @@ -1131,11 +1131,37 @@ struct regmap_irq { .reg_offset = (_id) / (_reg_bits), \ } #define REGMAP_IRQ_MAIN_REG_OFFSET(arr) \ { .num_regs = ARRAY_SIZE((arr)), .offset = &(arr)[0] } struct regmap_irq_sub_irq_map { unsigned int num_regs; unsigned int *offset; }; /** * struct regmap_irq_chip - Description of a generic regmap irq_chip. * * @name: Descriptive name for IRQ controller. * * @main_status: Base main status register address. For chips which have * interrupts arranged in separate sub-irq blocks with own IRQ * registers and which have a main IRQ registers indicating * sub-irq blocks with unhandled interrupts. For such chips fill * sub-irq register information in status_base, mask_base and * ack_base. * @num_main_status_bits: Should be given to chips where number of meaningfull * main status bits differs from num_regs. * @sub_reg_offsets: arrays of mappings from main register bits to sub irq * registers. First item in array describes the registers * for first main status bit. Second array for second bit etc. * Offset is given as sub register status offset to * status_base. Should contain num_regs arrays. * Can be provided for chips with more complex mapping than * 1.st bit to 1.st sub-reg, 2.nd bit to 2.nd sub-reg, ... * @num_main_regs: Number of 'main status' irq registers for chips which have * main_status set. * * @status_base: Base status register address. * @mask_base: Base mask register address. * @mask_writeonly: Base mask register is write only. Loading Loading @@ -1181,6 +1207,11 @@ struct regmap_irq { struct regmap_irq_chip { const char *name; unsigned int main_status; unsigned int num_main_status_bits; struct regmap_irq_sub_irq_map *sub_reg_offsets; int num_main_regs; unsigned int status_base; unsigned int mask_base; unsigned int unmask_base; Loading Loading
drivers/base/regmap/regmap-irq.c +95 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ struct regmap_irq_chip_data { int wake_count; void *status_reg_buf; unsigned int *main_status_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; Loading Loading @@ -329,6 +330,33 @@ static const struct irq_chip regmap_irq_chip = { .irq_set_wake = regmap_irq_set_wake, }; static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, unsigned int b) { const struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; struct regmap_irq_sub_irq_map *subreg; 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]); } else { subreg = &chip->sub_reg_offsets[b]; 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 (ret) break; } } return ret; } static irqreturn_t regmap_irq_thread(int irq, void *d) { struct regmap_irq_chip_data *data = d; Loading @@ -352,11 +380,65 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } /* * Read in the statuses, using a single bulk read if possible * in order to reduce the I/O overheads. * Read only registers with active IRQs if the chip has 'main status * register'. Else read in the statuses, using a single bulk read if * possible in order to reduce the I/O overheads. */ if (!map->use_single_read && map->reg_stride == 1 && if (chip->num_main_regs) { unsigned int max_main_bits; unsigned long size; size = chip->num_regs * sizeof(unsigned int); max_main_bits = (chip->num_main_status_bits) ? chip->num_main_status_bits : chip->num_regs; /* Clear the status buf as we don't read all status regs */ memset(data->status_buf, 0, size); /* We could support bulk read for main status registers * but I don't expect to see devices with really many main * status registers so let's only support single reads for the * 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]); if (ret) { dev_err(map->dev, "Failed to read IRQ status %d\n", ret); goto exit; } } /* Read sub registers with active IRQs */ for (i = 0; i < chip->num_main_regs; i++) { unsigned int b; const unsigned long mreg = data->main_status_buf[i]; for_each_set_bit(b, &mreg, map->format.val_bytes * 8) { if (i * map->format.val_bytes * 8 + b > max_main_bits) break; ret = read_sub_irq_data(data, b); if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status %d\n", ret); if (chip->runtime_pm) pm_runtime_put(map->dev); goto exit; } } } } else if (!map->use_single_read && map->reg_stride == 1 && data->irq_reg_stride == 1) { u8 *buf8 = data->status_reg_buf; u16 *buf16 = data->status_reg_buf; u32 *buf32 = data->status_reg_buf; Loading Loading @@ -521,6 +603,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; if (chip->num_main_regs) { d->main_status_buf = kcalloc(chip->num_main_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->main_status_buf) goto err_alloc; } d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->status_buf) Loading
include/linux/regmap.h +31 −0 Original line number Diff line number Diff line Loading @@ -1131,11 +1131,37 @@ struct regmap_irq { .reg_offset = (_id) / (_reg_bits), \ } #define REGMAP_IRQ_MAIN_REG_OFFSET(arr) \ { .num_regs = ARRAY_SIZE((arr)), .offset = &(arr)[0] } struct regmap_irq_sub_irq_map { unsigned int num_regs; unsigned int *offset; }; /** * struct regmap_irq_chip - Description of a generic regmap irq_chip. * * @name: Descriptive name for IRQ controller. * * @main_status: Base main status register address. For chips which have * interrupts arranged in separate sub-irq blocks with own IRQ * registers and which have a main IRQ registers indicating * sub-irq blocks with unhandled interrupts. For such chips fill * sub-irq register information in status_base, mask_base and * ack_base. * @num_main_status_bits: Should be given to chips where number of meaningfull * main status bits differs from num_regs. * @sub_reg_offsets: arrays of mappings from main register bits to sub irq * registers. First item in array describes the registers * for first main status bit. Second array for second bit etc. * Offset is given as sub register status offset to * status_base. Should contain num_regs arrays. * Can be provided for chips with more complex mapping than * 1.st bit to 1.st sub-reg, 2.nd bit to 2.nd sub-reg, ... * @num_main_regs: Number of 'main status' irq registers for chips which have * main_status set. * * @status_base: Base status register address. * @mask_base: Base mask register address. * @mask_writeonly: Base mask register is write only. Loading Loading @@ -1181,6 +1207,11 @@ struct regmap_irq { struct regmap_irq_chip { const char *name; unsigned int main_status; unsigned int num_main_status_bits; struct regmap_irq_sub_irq_map *sub_reg_offsets; int num_main_regs; unsigned int status_base; unsigned int mask_base; unsigned int unmask_base; Loading