From 67021f25d95292d285dd213c58401642b98eaf24 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 25 Aug 2021 23:50:40 +0300 Subject: [PATCH] regmap: teach regmap to use raw spinlocks if requested in the config Some drivers might access regmap in a context where a raw spinlock is held. An example is drivers/irqchip/irq-ls-extirq.c, which calls regmap_update_bits() from struct irq_chip :: irq_set_type, which is a method called by __irq_set_trigger() under the desc->lock raw spin lock. Since desc->lock is a raw spin lock and the regmap internal lock for mmio is a plain spinlock (which can become sleepable on RT), this is an invalid locking scheme and we get a splat stating that this is a "[ BUG: Invalid wait context ]". It seems reasonable for regmap to have an option use a raw spinlock too, so add that in the config such that drivers can request it. Suggested-by: Mark Brown Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20210825205041.927788-2-vladimir.oltean@nxp.com Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++++ drivers/base/regmap/regmap.c | 35 +++++++++++++++++++++++++++++----- include/linux/regmap.h | 2 ++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 0097696c31de..b1905916f7af 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -53,6 +53,10 @@ struct regmap { spinlock_t spinlock; unsigned long spinlock_flags; }; + struct { + raw_spinlock_t raw_spinlock; + unsigned long raw_spinlock_flags; + }; }; regmap_lock lock; regmap_unlock unlock; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 297e95be25b3..d8510708ec54 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -523,6 +523,23 @@ __releases(&map->spinlock) spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } +static void regmap_lock_raw_spinlock(void *__map) +__acquires(&map->raw_spinlock) +{ + struct regmap *map = __map; + unsigned long flags; + + raw_spin_lock_irqsave(&map->raw_spinlock, flags); + map->raw_spinlock_flags = flags; +} + +static void regmap_unlock_raw_spinlock(void *__map) +__releases(&map->raw_spinlock) +{ + struct regmap *map = __map; + raw_spin_unlock_irqrestore(&map->raw_spinlock, map->raw_spinlock_flags); +} + static void dev_get_regmap_release(struct device *dev, void *res) { /* @@ -760,11 +777,19 @@ struct regmap *__regmap_init(struct device *dev, } else { if ((bus && bus->fast_io) || config->fast_io) { - spin_lock_init(&map->spinlock); - map->lock = regmap_lock_spinlock; - map->unlock = regmap_unlock_spinlock; - lockdep_set_class_and_name(&map->spinlock, - lock_key, lock_name); + if (config->use_raw_spinlock) { + raw_spin_lock_init(&map->raw_spinlock); + map->lock = regmap_lock_raw_spinlock; + map->unlock = regmap_unlock_raw_spinlock; + lockdep_set_class_and_name(&map->raw_spinlock, + lock_key, lock_name); + } else { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + lockdep_set_class_and_name(&map->spinlock, + lock_key, lock_name); + } } else { mutex_init(&map->mutex); map->lock = regmap_lock_mutex; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f87a11a5cc4a..ed57b7c5b468 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -343,6 +343,7 @@ typedef void (*regmap_unlock)(void *); * @ranges: Array of configuration entries for virtual address ranges. * @num_ranges: Number of range configuration entries. * @use_hwlock: Indicate if a hardware spinlock should be used. + * @use_raw_spinlock: Indicate if a raw spinlock should be used. * @hwlock_id: Specify the hardware spinlock id. * @hwlock_mode: The hardware spinlock mode, should be HWLOCK_IRQSTATE, * HWLOCK_IRQ or 0. @@ -402,6 +403,7 @@ struct regmap_config { unsigned int num_ranges; bool use_hwlock; + bool use_raw_spinlock; unsigned int hwlock_id; unsigned int hwlock_mode;