regmap: irq: Use a bulk read for interrupt status where possible
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 3 Jan 2013 14:27:15 +0000 (14:27 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 4 Jan 2013 20:53:42 +0000 (20:53 +0000)
If the interrupt status registers are a single block of registers and the
chip supports bulk reads then do a single bulk read rather than pay the
extra I/O cost. This restores the original behaviour which was lost when
support for register striding was added.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
drivers/base/regmap/regmap-irq.c

index 329be98..aaf599a 100644 (file)
@@ -34,6 +34,7 @@ struct regmap_irq_chip_data {
        int irq;
        int wake_count;
 
+       void *status_reg_buf;
        unsigned int *status_buf;
        unsigned int *mask_buf;
        unsigned int *mask_buf_def;
@@ -170,18 +171,60 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
                }
        }
 
-       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]);
+       /*
+        * Read in the statuses, using a single bulk read if possible
+        * in order to reduce the I/O overheads.
+        */
+       if (!map->use_single_rw && 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;
 
+               BUG_ON(!data->status_reg_buf);
+
+               ret = regmap_bulk_read(map, chip->status_base,
+                                      data->status_reg_buf,
+                                      chip->num_regs);
                if (ret != 0) {
                        dev_err(map->dev, "Failed to read IRQ status: %d\n",
-                                       ret);
-                       if (chip->runtime_pm)
-                               pm_runtime_put(map->dev);
+                               ret);
                        return IRQ_NONE;
                }
+
+               for (i = 0; i < data->chip->num_regs; i++) {
+                       switch (map->format.val_bytes) {
+                       case 1:
+                               data->status_buf[i] = buf8[i];
+                               break;
+                       case 2:
+                               data->status_buf[i] = buf16[i];
+                               break;
+                       case 4:
+                               data->status_buf[i] = buf32[i];
+                               break;
+                       default:
+                               BUG();
+                               return IRQ_NONE;
+                       }
+               }
+
+       } 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]);
+
+                       if (ret != 0) {
+                               dev_err(map->dev,
+                                       "Failed to read IRQ status: %d\n",
+                                       ret);
+                               if (chip->runtime_pm)
+                                       pm_runtime_put(map->dev);
+                               return IRQ_NONE;
+                       }
+               }
        }
 
        /*
@@ -327,6 +370,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        else
                d->irq_reg_stride = 1;
 
+       if (!map->use_single_rw && map->reg_stride == 1 &&
+           d->irq_reg_stride == 1) {
+               d->status_reg_buf = kmalloc(map->format.val_bytes *
+                                           chip->num_regs, GFP_KERNEL);
+               if (!d->status_reg_buf)
+                       goto err_alloc;
+       }
+
        mutex_init(&d->lock);
 
        for (i = 0; i < chip->num_irqs; i++)
@@ -397,6 +448,7 @@ err_alloc:
        kfree(d->mask_buf_def);
        kfree(d->mask_buf);
        kfree(d->status_buf);
+       kfree(d->status_reg_buf);
        kfree(d);
        return ret;
 }
@@ -418,6 +470,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
        kfree(d->wake_buf);
        kfree(d->mask_buf_def);
        kfree(d->mask_buf);
+       kfree(d->status_reg_buf);
        kfree(d->status_buf);
        kfree(d);
 }