[PATCH 3/3] regulator: qcom_spmi: don't take a sleeping lock in the SAW IPI

From: Dmitry Baryshkov

Date: Mon Oct 19 2026 - 11:35:07 EST


The SAW voltage change runs on CPU 0 from an IPI, but it reaches the SAW
registers through the syscon regmap, which is protected by a spinlock_t.
That is a sleeping lock on PREEMPT_RT, and lockdep complains as soon as
VDD_APCC actually changes voltage:

BUG: Invalid wait context
...
regmap_lock_spinlock+0x18/0x30
regmap_read+0x3c/0x78
spmi_saw_set_vdd+0x50/0x1ac
__flush_smp_call_function_queue+0x1ac/0x334

Map the SAW with a regmap of our own that uses a raw spinlock, and fail
the probe when that cannot be done instead of carrying an error pointer
around as the SAW regmap.

Fixes: 0caecaa87202 ("regulator: qcom_spmi: Add support for SAW")
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
---
drivers/regulator/Kconfig | 1 +
drivers/regulator/qcom_spmi-regulator.c | 35 +++++++++++++++++++++++++--------
2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index be6b72daa76f..83a76b78e58c 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1210,6 +1210,7 @@ config REGULATOR_QCOM_SMD_RPM
config REGULATOR_QCOM_SPMI
tristate "Qualcomm SPMI regulator driver"
depends on SPMI || COMPILE_TEST
+ select REGMAP_MMIO
help
If you say yes to this option, support will be included for the
regulators found in Qualcomm SPMI PMICs.
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
index c1a41ce70b36..a04ddb1eac94 100644
--- a/drivers/regulator/qcom_spmi-regulator.c
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -12,13 +12,13 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/ktime.h>
#include <linux/regulator/driver.h>
#include <linux/regmap.h>
#include <linux/list.h>
-#include <linux/mfd/syscon.h>
#include <linux/io.h>

/* Pin control enable input pins. */
@@ -1335,6 +1335,14 @@ static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)

static struct regmap *saw_regmap;

+static const struct regmap_config saw_regmap_config = {
+ .name = "saw",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .use_raw_spinlock = true,
+};
+
static void spmi_saw_set_vdd(void *data)
{
u32 vctl, data3, avs_ctl, pmic_sts;
@@ -2504,7 +2512,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
const char *name;
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
- struct device_node *syscon, *reg_node;
+ struct device_node *saw, *reg_node;
+ void __iomem *saw_base;
struct property *reg_prop;
int ret, lenp;
struct list_head *vreg_list;
@@ -2523,12 +2532,22 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
if (!reg)
return -ENODEV;

- syscon = of_parse_phandle(node, "qcom,saw-reg", 0);
- if (syscon) {
- saw_regmap = syscon_node_to_regmap(syscon);
- of_node_put(syscon);
- if (IS_ERR(saw_regmap))
- dev_err(dev, "ERROR reading SAW regmap\n");
+ saw = of_parse_phandle(node, "qcom,saw-reg", 0);
+ if (saw) {
+ saw_base = devm_of_iomap(dev, saw, 0, NULL);
+ of_node_put(saw);
+ if (IS_ERR(saw_base))
+ return dev_err_probe(dev, PTR_ERR(saw_base),
+ "failed to map the SAW\n");
+
+ saw_regmap = devm_regmap_init_mmio(dev, saw_base,
+ &saw_regmap_config);
+ if (IS_ERR(saw_regmap)) {
+ ret = PTR_ERR(saw_regmap);
+ saw_regmap = NULL;
+ return dev_err_probe(dev, ret,
+ "failed to set the SAW regmap up\n");
+ }
}

for (; reg->name; reg++) {

--
2.47.3