[PATCH 2/2] regulator: mt6363: Fix VEMC voltage selection
Alexandre Mergnat
amergnat at baylibre.com
Mon Nov 2 07:54:18 PST 2026
VEMC encodes its voltage as a range and a trim value. The custom
getter looks up the range with
if (rdesc->linear_range_selectors_bitfield[range] != calsel)
so it stops at the first non-matching range and the reported voltage is
wrong for every range setting. With hardware trap 1, the custom getter
and setter apply MT6363_RG_VEMC_VOSEL_1_MASK, the trap 1 range field,
to the trap 0 trim register and keep the trap 0 range field. Voltage
changes are then not applied and the reported voltage does not reflect
the trap 1 settings.
Apart from the trap handling, both callbacks reimplement the regulator
core pickable regmap helpers, which work for VEMC once the descriptor
holds the range field and trim register of the hardware trap. Select
that field and register at probe and use the helpers instead of
fixing the copies. Keep the setter as a wrapper for the TMA unlock
and relock around the write, which the helper does not do. A trap
value other than 0 or 1, or a failed trap read, now fails the probe
instead of the VEMC voltage get and set.
Fixes: 3c36965df808 ("regulator: Add support for MediaTek MT6363 SPMI PMIC Regulators")
Signed-off-by: Alexandre Mergnat <amergnat at baylibre.com>
---
drivers/regulator/mt6363-regulator.c | 120 ++++++++---------------------
include/linux/regulator/mt6363-regulator.h | 1 +
2 files changed, 33 insertions(+), 88 deletions(-)
diff --git a/drivers/regulator/mt6363-regulator.c b/drivers/regulator/mt6363-regulator.c
index ac79ad6698bf..27d12baa9f2f 100644
--- a/drivers/regulator/mt6363-regulator.c
+++ b/drivers/regulator/mt6363-regulator.c
@@ -4,7 +4,6 @@
// Copyright (c) 2025 Collabora Ltd
// AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
-#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/devm-helpers.h>
#include <linux/err.h>
@@ -484,30 +483,7 @@ static int mt6363_vemc_set_voltage_sel(struct regulator_dev *rdev, unsigned int
{
const __le16 tma_unlock_key = cpu_to_le16(MT6363_TMA_UNLOCK_VALUE);
const __le16 tma_lock_key = cpu_to_le16(0);
- const struct regulator_desc *rdesc = rdev->desc;
- struct regmap *regmap = rdev->regmap;
- unsigned int range, val;
- int i, ret;
- u16 mask;
-
- for (i = 0; i < rdesc->n_linear_ranges; i++) {
- const struct linear_range *r = &rdesc->linear_ranges[i];
- unsigned int voltages_in_range = linear_range_values_in_range(r);
-
- if (sel < voltages_in_range)
- break;
- sel -= voltages_in_range;
- }
-
- if (i == rdesc->n_linear_ranges)
- return -EINVAL;
-
- ret = regmap_read(rdev->regmap, MT6363_TOP_TRAP, &val);
- if (ret)
- return ret;
-
- if (val > 1)
- return -EINVAL;
+ int ret;
/* Unlock TMA for writing */
ret = regmap_bulk_write(rdev->regmap, MT6363_TOP_TMA_KEY_L,
@@ -515,30 +491,8 @@ static int mt6363_vemc_set_voltage_sel(struct regulator_dev *rdev, unsigned int
if (ret)
return ret;
- /* If HW trapping value is 1, use VEMC_VOSEL_1 instead of VEMC_VOSEL_0 */
- if (val == 1) {
- mask = MT6363_RG_VEMC_VOSEL_1_MASK;
- sel = FIELD_PREP(MT6363_RG_VEMC_VOSEL_1_MASK, sel);
- } else {
- mask = rdesc->vsel_mask;
- }
-
- sel <<= ffs(rdesc->vsel_mask) - 1;
- sel += rdesc->linear_ranges[i].min_sel;
-
- range = rdesc->linear_range_selectors_bitfield[i];
- range <<= ffs(rdesc->vsel_range_mask) - 1;
-
- /* Write to the vreg calibration register for voltage finetuning */
- ret = regmap_update_bits(regmap, rdesc->vsel_range_reg,
- rdesc->vsel_range_mask, range);
- if (ret)
- goto lock_tma;
-
- /* Function must return the result of this write operation */
- ret = regmap_update_bits(regmap, rdesc->vsel_reg, mask, sel);
+ ret = regulator_set_voltage_sel_pickable_regmap(rdev, sel);
-lock_tma:
/* Unconditionally re-lock TMA */
regmap_bulk_write(rdev->regmap, MT6363_TOP_TMA_KEY_L,
&tma_lock_key, sizeof(tma_lock_key));
@@ -546,45 +500,6 @@ static int mt6363_vemc_set_voltage_sel(struct regulator_dev *rdev, unsigned int
return ret;
}
-static int mt6363_vemc_get_voltage_sel(struct regulator_dev *rdev)
-{
- const struct regulator_desc *rdesc = rdev->desc;
- unsigned int vosel, trap, calsel;
- int vcal, vsel, range, ret;
-
- ret = regmap_read(rdev->regmap, rdesc->vsel_reg, &vosel);
- if (ret)
- return ret;
-
- ret = regmap_read(rdev->regmap, rdesc->vsel_range_reg, &calsel);
- if (ret)
- return ret;
-
- calsel &= rdesc->vsel_range_mask;
- for (range = 0; range < rdesc->n_linear_ranges; range++)
- if (rdesc->linear_range_selectors_bitfield[range] != calsel)
- break;
-
- if (range == rdesc->n_linear_ranges)
- return -EINVAL;
-
- ret = regmap_read(rdev->regmap, MT6363_TOP_TRAP, &trap);
- if (ret)
- return ret;
-
- /* If HW trapping value is 1, use VEMC_VOSEL_1 instead of VEMC_VOSEL_0 */
- if (trap > 1)
- return -EINVAL;
- else if (trap == 1)
- vsel = FIELD_GET(MT6363_RG_VEMC_VOSEL_1_MASK, vosel);
- else
- vsel = vosel & rdesc->vsel_mask;
-
- vcal = linear_range_values_in_range_array(rdesc->linear_ranges, range);
-
- return vsel + vcal;
-}
-
static int mt6363_va15_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
{
struct regmap *regmap = rdev->regmap;
@@ -697,7 +612,7 @@ static const struct regulator_ops mt6363_ldo_vemc_ops = {
.list_voltage = regulator_list_voltage_pickable_linear_range,
.map_voltage = regulator_map_voltage_pickable_linear_range,
.set_voltage_sel = mt6363_vemc_set_voltage_sel,
- .get_voltage_sel = mt6363_vemc_get_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -794,6 +709,30 @@ static int mt6363_backup_op_setting(struct regmap *map, struct mt6363_regulator_
return 0;
}
+static int mt6363_vemc_select_bank(struct regmap *map, struct regulator_desc *desc)
+{
+ unsigned int trap;
+ int ret;
+
+ ret = regmap_read(map, MT6363_TOP_TRAP, &trap);
+ if (ret)
+ return ret;
+
+ /* The hardware trap value selects which VEMC voltage controls are used */
+ switch (trap) {
+ case 0:
+ desc->vsel_reg = MT6363_RG_VEMC_VOCAL_ADDR;
+ desc->vsel_range_mask = MT6363_RG_VEMC_VOSEL_MASK;
+ return 0;
+ case 1:
+ desc->vsel_reg = MT6363_RG_VEMC_VOCAL_1_ADDR;
+ desc->vsel_range_mask = MT6363_RG_VEMC_VOSEL_1_MASK;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static void mt6363_irq_remove(void *data)
{
int *virq = data;
@@ -878,6 +817,11 @@ static int mt6363_regulator_probe(struct platform_device *pdev)
*/
regmap_read(config.regmap, MT6363_TOP_TRAP, &val);
+ ret = mt6363_vemc_select_bank(config.regmap,
+ &mt6363_regulators[MT6363_ID_VEMC].desc);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to select VEMC bank\n");
+
interrupt_parent = of_irq_find_parent(dev->of_node);
if (!interrupt_parent)
return dev_err_probe(dev, -EINVAL, "Cannot find IRQ parent\n");
diff --git a/include/linux/regulator/mt6363-regulator.h b/include/linux/regulator/mt6363-regulator.h
index 60761f01d3ad..e0d2c01827a7 100644
--- a/include/linux/regulator/mt6363-regulator.h
+++ b/include/linux/regulator/mt6363-regulator.h
@@ -138,6 +138,7 @@
#define MT6363_RG_LDO_VSRAM_APU_VOSEL_ADDR 0x1b17
#define MT6363_RG_LDO_VSRAM_APU_VOSEL_MASK GENMASK(6, 0)
#define MT6363_RG_VEMC_VOCAL_ADDR 0x1b1b
+#define MT6363_RG_VEMC_VOCAL_1_ADDR 0x1b1c
#define MT6363_RG_VEMC_VOCAL_MASK GENMASK(3, 0)
#define MT6363_RG_LDO_VCN15_ADDR 0x1b57
#define MT6363_RG_LDO_VCN15_EN_BIT 0
--
2.43.0
More information about the linux-arm-kernel
mailing list