From 55ac85e942c6783e728964861df36fc80e8ced93 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 19 Dec 2012 19:42:28 +0530 Subject: regmap: irq: enable wake support by default regmap-irq framework is used vastly by mfd drivers and some of devices like TPS65910, TPS80036 do not support the wake base register to enable wake. Currently wake in regmap-irq only supported if client driver passes the wake base register. As the regmap-irq is mostly used by mfd devices and it is require to have wake support from these devices in most of use cases, enabling wake support by default in regmap-irq. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5972ad958544..91294935130e 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -129,16 +129,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - if (!d->chip->wake_base) - return -EINVAL; - if (on) { - d->wake_buf[irq_data->reg_offset / map->reg_stride] - &= ~irq_data->mask; + if (d->wake_buf) + d->wake_buf[irq_data->reg_offset / map->reg_stride] + &= ~irq_data->mask; d->wake_count++; } else { - d->wake_buf[irq_data->reg_offset / map->reg_stride] - |= irq_data->mask; + if (d->wake_buf) + d->wake_buf[irq_data->reg_offset / map->reg_stride] + |= irq_data->mask; d->wake_count--; } @@ -316,11 +315,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; - if (!chip->wake_base) { - d->irq_chip.irq_set_wake = NULL; - d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND | - IRQCHIP_SKIP_SET_WAKE; - } d->irq = irq; d->map = map; d->chip = chip; -- cgit v1.2.3 From bbae92ca49f77898277576e3377b09e1391a3271 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jan 2013 13:58:33 +0000 Subject: regmap: irq: Factor register read out of the IRQ parsing loop In preparation for adding back support for block reads. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 91294935130e..329be9836716 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -170,13 +170,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - /* - * Ignore masked IRQs and ack if we need to; we ack early so - * there is no race between handling and acknowleding the - * interrupt. We assume that typically few of the interrupts - * will fire simultaneously so don't worry about overhead from - * doing a write per register. - */ for (i = 0; i < data->chip->num_regs; i++) { ret = regmap_read(map, chip->status_base + (i * map->reg_stride * data->irq_reg_stride), @@ -189,7 +182,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) pm_runtime_put(map->dev); return IRQ_NONE; } + } + /* + * Ignore masked IRQs and ack if we need to; we ack early so + * there is no race between handling and acknowleding the + * interrupt. We assume that typically few of the interrupts + * will fire simultaneously so don't worry about overhead from + * doing a write per register. + */ + for (i = 0; i < data->chip->num_regs; i++) { data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { -- cgit v1.2.3 From a7440eaa90cf2659920b9b28973cc1a13a2b331f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jan 2013 14:27:15 +0000 Subject: regmap: irq: Use a bulk read for interrupt status where possible 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 --- drivers/base/regmap/regmap-irq.c | 67 +++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 329be9836716..aaf599a320b3 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -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); } -- cgit v1.2.3 From 33be49324f7f3e9dff10d3d07a5c78ee82f8d54e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 16:32:54 +0000 Subject: regmap: irq: Fix sync of wake statuses to hardware This wasn't implemented but happened to work on test systems due to lack of wake mask inversion support. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index aaf599a320b3..be5579964be0 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -88,6 +88,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (ret != 0) 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); + if (d->wake_buf) { + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], d->wake_buf[i]); + if (ret != 0) + dev_err(d->map->dev, + "Failed to sync wakes in %x: %d\n", + reg, ret); + } } if (d->chip->runtime_pm) -- cgit v1.2.3 From 9442490a02867088bcd5ed6231fa3b35a733c2b8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 16:35:07 +0000 Subject: regmap: irq: Support wake IRQ mask inversion Support devices which have an enable rather than mask register for wake sources. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 21 +++++++++++++++++---- include/linux/regmap.h | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index be5579964be0..4706c63d0bc6 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -92,8 +92,14 @@ static void regmap_irq_sync_unlock(struct irq_data *data) reg = d->chip->wake_base + (i * map->reg_stride * d->irq_reg_stride); if (d->wake_buf) { - ret = regmap_update_bits(d->map, reg, - d->mask_buf_def[i], d->wake_buf[i]); + if (d->chip->wake_invert) + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], + ~d->wake_buf[i]); + else + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], + d->wake_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync wakes in %x: %d\n", @@ -419,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->wake_buf[i] = d->mask_buf_def[i]; reg = chip->wake_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(map, reg, d->wake_buf[i], - d->wake_buf[i]); + + if (chip->wake_invert) + ret = regmap_update_bits(map, reg, + d->mask_buf_def[i], + 0); + else + ret = regmap_update_bits(map, reg, + d->mask_buf_def[i], + d->wake_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf942c9..8c0b50df3d59 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -381,6 +381,7 @@ struct regmap_irq_chip { unsigned int wake_base; unsigned int irq_reg_stride; unsigned int mask_invert; + unsigned int wake_invert; bool runtime_pm; int num_regs; -- cgit v1.2.3