Unverified Commit 67021f25 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by Mark Brown
Browse files

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: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Link: https://lore.kernel.org/r/20210825205041.927788-2-vladimir.oltean@nxp.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 6efb943b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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;
+30 −5
Original line number Diff line number Diff line
@@ -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) {
			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;
+2 −0
Original line number Diff line number Diff line
@@ -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;