Merge tag 'regmap-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 5 Mar 2019 03:16:09 +0000 (19:16 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 5 Mar 2019 03:16:09 +0000 (19:16 -0800)
Pull regmap updates from Mark Brown:
 "There are only two changes here:

   - fix for conflicting attributes on the rbtree node structure

   - implementation of main status register support in the interrupt
     code which supports chips that have a register to cut down on the
     number of per-interrupt status registers that need to be checked
     when handling interrupts"

* tag 'regmap-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: Remove attribute packed from struct 'regcache_rbtree_node'
  regmap: regmap-irq: Add main status register support

drivers/base/regmap/regcache-rbtree.c
drivers/base/regmap/regmap-irq.c
include/linux/regmap.h

index 2e8f0144f9ab3cb031983538aeb235ef984b70a3..9cbb4b0cd01bf3939760f42cc760e73c25a9028a 100644 (file)
@@ -33,7 +33,7 @@ struct regcache_rbtree_node {
        unsigned int blklen;
        /* the actual rbtree node holding this block */
        struct rb_node node;
-} __attribute__ ((packed));
+};
 
 struct regcache_rbtree_ctx {
        struct rb_root root;
index 330c1f7e96656a7f651ee9ca9b336b699cbb335e..5059748afd4c5391f057c1ad7429bfb2d1c0f3a3 100644 (file)
@@ -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;
@@ -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;
@@ -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 &&
-           data->irq_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;
@@ -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)
index 1781b6cb793c4cf7541d54d0379551feaf6d541c..daeec7dbd65c2d7912c8fe761559bef7bac1a777 100644 (file)
@@ -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.
@@ -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;