[PATCH v2] regulator: mt6363: Serialize BUCK_TOP key access

From: Alexandre Mergnat

Date: Fri Nov 13 2026 - 02:08:14 EST


The bits that select the FAST mode of the bucks only accept writes
while the BUCK_TOP key is unlocked. mt6363_regulator_set_mode() unlocks
the key, updates the bit and locks the key again, but the regulator
core only locks the buck being changed. When two bucks change mode at
the same time, one of them can lock the key between the unlock and
the mode write of the other. The PMIC then ignores that write, and
regulator_set_mode() returns 0 while the buck stays in its previous
mode. mt6363_va15_set_voltage_sel() unlocks and locks the same key,
so it can also lock it during a buck mode change.

Hold a mutex from each unlock of the key to its relock, in the FAST bit
writes and in the VA15 voltage setter.

Fixes: 3c36965df808 ("regulator: Add support for MediaTek MT6363 SPMI PMIC Regulators")
Signed-off-by: Alexandre Mergnat <amergnat@xxxxxxxxxxxx>
---
The race was reproduced on hardware.

The new mutex is static because probe already stores per-device state
in the static regulator table, so the driver only supports one PMIC.
A follow-up series could make that table constant and move this state,
with the mutex, into per-device data.
---
Changes in v2:
- Drop the gotos from mt6363_va15_set_voltage_sel() instead of mixing
them with guard() (Mark Brown)
- Include linux/cleanup.h for guard()
- Link to v1: https://patch.msgid.link/20261109-rgp-91-mt6363-key-lock-v1-1-ea3b03a76997@xxxxxxxxxxxx
---
drivers/regulator/mt6363-regulator.c | 53 +++++++++++++++++++++---------------
1 file changed, 31 insertions(+), 22 deletions(-)

diff --git a/drivers/regulator/mt6363-regulator.c b/drivers/regulator/mt6363-regulator.c
index e6c4bd851fef..b4f5f33779b5 100644
--- a/drivers/regulator/mt6363-regulator.c
+++ b/drivers/regulator/mt6363-regulator.c
@@ -4,6 +4,7 @@
// Copyright (c) 2025 Collabora Ltd
// AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>

+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/devm-helpers.h>
#include <linux/err.h>
@@ -12,6 +13,7 @@
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
@@ -114,6 +116,8 @@ struct mt6363_regulator_info {
int virq;
};

+static DEFINE_MUTEX(mt6363_buck_top_lock);
+
#define MT6363_BUCK(match, vreg, min, max, step, en_reg, lp_reg, \
mset_reg, ocp_intn) \
[MT6363_ID_##vreg] = { \
@@ -388,6 +392,23 @@ static int mt6363_buck_unlock(struct regmap *map, bool unlock)
return regmap_bulk_write(map, MT6363_BUCK_TOP_KEY_PROT_LO, &buf, sizeof(buf));
}

+static int mt6363_buck_top_update_bits(struct regmap *map, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ int ret;
+
+ guard(mutex)(&mt6363_buck_top_lock);
+
+ ret = mt6363_buck_unlock(map, true);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(map, reg, mask, val);
+
+ mt6363_buck_unlock(map, false);
+ return ret;
+}
+
static int mt6363_regulator_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
@@ -400,13 +421,9 @@ static int mt6363_regulator_set_mode(struct regulator_dev *rdev,

switch (mode) {
case REGULATOR_MODE_FAST:
- ret = mt6363_buck_unlock(regmap, true);
- if (ret)
- break;
-
- ret = regmap_set_bits(regmap, info->modeset_reg, info->modeset_mask);
-
- mt6363_buck_unlock(regmap, false);
+ ret = mt6363_buck_top_update_bits(regmap, info->modeset_reg,
+ info->modeset_mask,
+ info->modeset_mask);
break;
case REGULATOR_MODE_NORMAL:
cur_mode = mt6363_regulator_get_mode(rdev);
@@ -416,13 +433,8 @@ static int mt6363_regulator_set_mode(struct regulator_dev *rdev,
}

if (cur_mode == REGULATOR_MODE_FAST) {
- ret = mt6363_buck_unlock(regmap, true);
- if (ret)
- break;
-
- ret = regmap_clear_bits(regmap, info->modeset_reg, info->modeset_mask);
-
- mt6363_buck_unlock(regmap, false);
+ ret = mt6363_buck_top_update_bits(regmap, info->modeset_reg,
+ info->modeset_mask, 0);
break;
} else if (cur_mode == REGULATOR_MODE_IDLE) {
ret = regmap_clear_bits(regmap, info->lp_mode_reg, info->lp_mode_mask);
@@ -508,20 +520,17 @@ static int mt6363_va15_set_voltage_sel(struct regulator_dev *rdev, unsigned int
struct regmap *regmap = rdev->regmap;
int ret;

+ guard(mutex)(&mt6363_buck_top_lock);
+
ret = mt6363_buck_unlock(regmap, true);
if (ret)
return ret;

ret = regulator_set_voltage_sel_pickable_regmap(rdev, sel);
- if (ret)
- goto va15_unlock;
-
- ret = regmap_update_bits(regmap, MT6363_RG_BUCK_EFUSE_RSV1,
- MT6363_RG_BUCK_EFUSE_RSV1_MASK, sel);
- if (ret)
- goto va15_unlock;
+ if (!ret)
+ ret = regmap_update_bits(regmap, MT6363_RG_BUCK_EFUSE_RSV1,
+ MT6363_RG_BUCK_EFUSE_RSV1_MASK, sel);

-va15_unlock:
mt6363_buck_unlock(rdev->regmap, false);
return ret;
}

---
base-commit: 7904338c38426d72c5f7cd9402cb9a20232a7af5
change-id: 20261105-rgp-91-mt6363-key-lock-67b5992de5a8

Best regards,
--
Alexandre Mergnat <amergnat@xxxxxxxxxxxx>