From 4d879514e73f3e6b27617d9898c83c9939462dda Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 27 Jul 2012 14:54:15 +0100 Subject: regmap: Don't lock in regmap_reinit_cache() When bus->fast_io is set, the locking here is done with spinlocks. This is currently true for the regmap-mmio bus implementation. While holding a spinlock we can't go to sleep, various operations like removing the debugfs entries or re-initializing the cache will sleep, therefore, shift the locking up to the user. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index c241ae2f2f10..52069d29ff12 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -659,13 +659,12 @@ EXPORT_SYMBOL_GPL(devm_regmap_init); * new cache. This can be used to restore the cache to defaults or to * update the cache configuration to reflect runtime discovery of the * hardware. + * + * No explicit locking is done here, the user needs to ensure that + * this function will not race with other calls to regmap. */ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) { - int ret; - - map->lock(map); - regcache_exit(map); regmap_debugfs_exit(map); @@ -681,11 +680,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->cache_bypass = false; map->cache_only = false; - ret = regcache_init(map, config); - - map->unlock(map); - - return ret; + return regcache_init(map, config); } EXPORT_SYMBOL_GPL(regmap_reinit_cache); -- cgit v1.2.3 From 16032624f511b2fac0671cba5e7da40aa7e73a66 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 27 Jul 2012 13:01:54 -0600 Subject: regmap: fix some error messages to take account of irq_reg_stride A number of places in the code were printing error messages that included the address of a register, but were not calculating the register address in the same way as the access to the register. Use a temporary to solve this. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index a89734621e51..c7e5b18f4edc 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -59,6 +59,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; + u32 reg; /* * If there's been a change in the mask write it back to the @@ -66,13 +67,13 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - ret = regmap_update_bits(d->map, d->chip->mask_base + - (i * map->reg_stride * - d->irq_reg_stride), + reg = d->chip->mask_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", - d->chip->mask_base + (i * map->reg_stride)); + reg); } /* If we've changed our wakeup count propagate it to the parent */ @@ -144,6 +145,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) struct regmap *map = data->map; int ret, i; bool handled = false; + u32 reg; /* * Ignore masked IRQs and ack if we need to; we ack early so @@ -166,14 +168,12 @@ 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) { - ret = regmap_write(map, chip->ack_base + - (i * map->reg_stride * - data->irq_reg_stride), - data->status_buf[i]); + reg = chip->ack_base + + (i * map->reg_stride * data->irq_reg_stride); + ret = regmap_write(map, reg, data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", - chip->ack_base + (i * map->reg_stride), - ret); + reg, ret); } } @@ -238,6 +238,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, struct regmap_irq_chip_data *d; int i; int ret = -ENOMEM; + u32 reg; for (i = 0; i < chip->num_irqs; i++) { if (chip->irqs[i].reg_offset % map->reg_stride) @@ -303,12 +304,12 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + (i * map->reg_stride - * d->irq_reg_stride), - d->mask_buf[i]); + reg = chip->mask_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_write(map, reg, d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", - chip->mask_base + (i * map->reg_stride), ret); + reg, ret); goto err_alloc; } } -- cgit v1.2.3 From 0eb46ad0c8d60943c1f46cef795fc537fbffd177 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Aug 2012 20:29:14 +0100 Subject: regmap: irq: Only update mask bits when doing initial mask Don't write the full register, it's possible there's bits other than the masks in the same register which we shouldn't be changing. Signed-off-by: Mark Brown Tested-by: Stephen Warren --- drivers/base/regmap/regmap-irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index c7e5b18f4edc..4a52743b60cd 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -306,7 +306,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->mask_buf[i] = d->mask_buf_def[i]; reg = chip->mask_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_write(map, reg, d->mask_buf[i]); + ret = regmap_update_bits(map, reg, + d->mask_buf[i], d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); -- cgit v1.2.3 From 7ac140ec426ed304237205be77f99eedfc1186b5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Aug 2012 11:40:47 -0600 Subject: regmap: store irq_chip inside regmap_irq_chip_data This will allow later patches to adjust portions of the irq_chip individually for each regmap_irq_chip that is created. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 4a52743b60cd..72a6f31e8223 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -22,6 +22,7 @@ struct regmap_irq_chip_data { struct mutex lock; + struct irq_chip irq_chip; struct regmap *map; const struct regmap_irq_chip *chip; @@ -129,7 +130,7 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } -static struct irq_chip regmap_irq_chip = { +static const struct irq_chip regmap_irq_chip = { .name = "regmap", .irq_bus_lock = regmap_irq_lock, .irq_bus_sync_unlock = regmap_irq_sync_unlock, @@ -197,7 +198,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq, struct regmap_irq_chip_data *data = h->host_data; irq_set_chip_data(virq, data); - irq_set_chip_and_handler(virq, ®map_irq_chip, handle_edge_irq); + irq_set_chip_and_handler(virq, &data->irq_chip, handle_edge_irq); irq_set_nested_thread(virq, 1); /* ARM needs us to explicitly flag the IRQ as valid @@ -285,6 +286,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_alloc; } + d->irq_chip = regmap_irq_chip; d->irq = irq; d->map = map; d->chip = chip; -- cgit v1.2.3 From ca142750f8ac3d01e45909e624ca783779894640 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Aug 2012 11:40:48 -0600 Subject: regmap: name irq_chip based on regmap_irq_chip's name This is intended to give each irq_chip a useful name, rather than hard- coding them all as "regmap". Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 72a6f31e8223..bf1203cbd122 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -131,7 +131,6 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) } static const struct irq_chip regmap_irq_chip = { - .name = "regmap", .irq_bus_lock = regmap_irq_lock, .irq_bus_sync_unlock = regmap_irq_sync_unlock, .irq_disable = regmap_irq_disable, @@ -287,6 +286,7 @@ 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; d->irq = irq; d->map = map; d->chip = chip; -- cgit v1.2.3 From 685879f4b2036e58c1a0cdaaee2b155d3c965461 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Aug 2012 11:40:49 -0600 Subject: regmap: set MASK_ON_SUSPEND/SKIP_SET_WAKE if no wake_base If a regmap-irq chip has no wake base: * There's no point calling .irq_set_wake, hence IRQCHIP_SKIP_SET_WAKE. * If some IRQs in the chip are enabled for wake and some aren't, we should mask those interrupts that are not wake enabled, so that if they occur during suspend, the system is not awoken. Hence, IRQCHIP_MASK_ON_SUSPEND. Note that IRQCHIP_MASK_ON_SUSPEND is handled by check_wakeup_irqs(), which always iterates over every single interrupt in the system, irrespective of whether an interrupt is a child of a controller whose output interrupt has no wake-enabled inputs and hence is presumably masked itself. Hence this change might cause interrupt unnecessary masking operations and associated register I/O. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index bf1203cbd122..9a6649c82daa 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -287,6 +287,11 @@ 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 40052ca0c243d101cfadd65936f60ef81df10b02 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Aug 2012 13:57:24 -0600 Subject: regmap: irq: initialize all irqs to wake disabled The kerneldoc for irq_set_irq_wake() says: Enable/disable power management wakeup mode, which is disabled by default. regmap_irq_set_wake() clears bits to enable wake for an interrupt, and sets bits to disable wake. Hence, we should set all bits in wake_buf initially, to mirror the expected disabled state. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 9a6649c82daa..6f8f0c1e550f 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -322,6 +322,22 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } } + /* Wake is disabled by default */ + 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); + ret = regmap_update_bits(map, reg, d->wake_buf[i], + d->wake_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", + reg, ret); + goto err_alloc; + } + } + } + if (irq_base) d->domain = irq_domain_add_legacy(map->dev->of_node, chip->num_irqs, irq_base, 0, -- cgit v1.2.3 From 0c00c50b41a9a6ee83eb16fb5c6c22e5115391b8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Jul 2012 15:41:19 +0100 Subject: regmap: irq: Enable devices for runtime PM while handling interrupts Some devices need to have a runtime PM reference while handling interrupts to ensure that the register I/O is available. Support this with a flag in the chip. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 25 +++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 6f8f0c1e550f..bb5e10305197 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "internal.h" @@ -62,6 +63,13 @@ static void regmap_irq_sync_unlock(struct irq_data *data) int i, ret; u32 reg; + if (d->chip->runtime_pm) { + ret = pm_runtime_get_sync(map->dev); + if (ret < 0) + dev_err(map->dev, "IRQ sync failed to resume: %d\n", + ret); + } + /* * If there's been a change in the mask write it back to the * hardware. We rely on the use of the regmap core cache to @@ -77,6 +85,9 @@ static void regmap_irq_sync_unlock(struct irq_data *data) reg); } + if (d->chip->runtime_pm) + pm_runtime_put(map->dev); + /* If we've changed our wakeup count propagate it to the parent */ if (d->wake_count < 0) for (i = d->wake_count; i < 0; i++) @@ -147,6 +158,15 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) bool handled = false; u32 reg; + if (chip->runtime_pm) { + ret = pm_runtime_get_sync(map->dev); + if (ret < 0) { + dev_err(map->dev, "IRQ thread failed to resume: %d\n", + ret); + 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 @@ -162,6 +182,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) 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; } @@ -185,6 +207,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } + if (chip->runtime_pm) + pm_runtime_put(map->dev); + if (handled) return IRQ_HANDLED; else diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 7f7e00df3adf..9c4c9c673f38 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -285,6 +285,7 @@ struct regmap_irq { * @ack_base: Base ack address. If zero then the chip is clear on read. * @wake_base: Base address for wake enables. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. + * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. * @irqs: Descriptors for individual IRQs. Interrupt numbers are @@ -299,6 +300,7 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; + bool runtime_pm; int num_regs; -- cgit v1.2.3 From 7a97637988c355646e730bcf0e6d548619112cdd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Jul 2012 15:41:53 +0100 Subject: mfd: wm8994: Flag the interrupt block as requiring runtime PM be enabled It's only required in a few circumstances but better to be safe. Signed-off-by: Mark Brown --- drivers/mfd/wm8994-irq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index 0aac4aff17a5..a050e56a9bbd 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -135,6 +135,7 @@ static struct regmap_irq_chip wm8994_irq_chip = { .status_base = WM8994_INTERRUPT_STATUS_1, .mask_base = WM8994_INTERRUPT_STATUS_1_MASK, .ack_base = WM8994_INTERRUPT_STATUS_1, + .runtime_pm = true, }; int wm8994_irq_init(struct wm8994 *wm8994) -- cgit v1.2.3 From 36ac914ba2a5f1a14c5e0bf5d33bc20016d50940 Mon Sep 17 00:00:00 2001 From: Xiaofan Tian Date: Thu, 30 Aug 2012 17:03:35 +0800 Subject: regmap: irq: Add mask invert flag for enable register Currently, regmap will write 1 to mask_base to mask an interrupt and write 0 to unmask it. But some chips do not have an interrupt mask register, and only have interrupt enable register. Then we should write 0 to disable interrupt and 1 to enable. So add an mask_invert flag to handle this. If it is not set, behavior is same as previous. If set it to 1, the mask value will be inverted before written to mask_base Signed-off-by: Xiaofan Tian Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 12 ++++++++++-- include/linux/regmap.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index bb5e10305197..094f9988fdf2 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -78,7 +78,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data) for (i = 0; i < d->chip->num_regs; i++) { reg = d->chip->mask_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(d->map, reg, + if (d->chip->mask_invert) + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], ~d->mask_buf[i]); + else + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", @@ -338,7 +342,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->mask_buf[i] = d->mask_buf_def[i]; reg = chip->mask_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(map, reg, + if (chip->mask_invert) + ret = regmap_update_bits(map, reg, + d->mask_buf[i], ~d->mask_buf[i]); + else + ret = regmap_update_bits(map, reg, d->mask_buf[i], d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 9c4c9c673f38..e3bcc3f4dcb8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -300,6 +300,7 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; + unsigned int mask_invert; bool runtime_pm; int num_regs; -- cgit v1.2.3 From 81380739516730124067576c9cc9f2418be5bf36 Mon Sep 17 00:00:00 2001 From: Yunfan Zhang Date: Sat, 8 Sep 2012 03:53:25 -0700 Subject: regmap: no need primary handler for nested irq The primary handler will NOT be called if the interrupt nests into another interrupt thread. Remove it to avoid confusing. Signed-off-by: Yunfan Zhang Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 094f9988fdf2..5b6b1d8e6cc0 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -226,7 +226,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq, struct regmap_irq_chip_data *data = h->host_data; irq_set_chip_data(virq, data); - irq_set_chip_and_handler(virq, &data->irq_chip, handle_edge_irq); + irq_set_chip(virq, &data->irq_chip); irq_set_nested_thread(virq, 1); /* ARM needs us to explicitly flag the IRQ as valid -- cgit v1.2.3