This driver supports basic operations (get/set voltage, enable/disable)
for regulators found in the MediaTek MT6373 PMIC.

Signed-off-by: Arnaud Ferraris <[email protected]>
---
 drivers/power/regulator/Kconfig            |   9 +
 drivers/power/regulator/Makefile           |   1 +
 drivers/power/regulator/mt6373_regulator.c | 797 +++++++++++++++++++++++++++++
 3 files changed, 807 insertions(+)

diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 428689702c8..b529285cfd7 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -581,6 +581,15 @@ config DM_REGULATOR_MT6359
          This driver supports the control of different power rails of device
          through regulator interface.
 
+config DM_REGULATOR_MT6373
+       bool "Enable driver for MediaTek MT6373 PMIC regulators"
+       depends on DM_REGULATOR && PMIC_MTK
+       help
+         Say y here to select this option to enable the power regulator of
+         MediaTek MT6373 PMIC.
+         This driver supports the control of different power rails of device
+         through regulator interface.
+
 config DM_REGULATOR_PMBUS_HELPER
        bool "Shared regulator helpers for PMBus chip drivers"
        depends on DM_REGULATOR && PMBUS && DM_I2C
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index e17937f4c26..ce8b465afc4 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_USB_POWER) += 
axp_usb_power.o
 obj-$(CONFIG_$(PHASE_)DM_REGULATOR_DA9063) += da9063.o
 obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX77663) += max77663_regulator.o
 obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX8907) += max8907_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MT6373) += mt6373_regulator.o
 obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o
 obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PFUZE100) += pfuze100.o
diff --git a/drivers/power/regulator/mt6373_regulator.c 
b/drivers/power/regulator/mt6373_regulator.c
new file mode 100644
index 00000000000..7927a620fb6
--- /dev/null
+++ b/drivers/power/regulator/mt6373_regulator.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 MediaTek Inc.
+ * Copyright (c) 2025-2026 Collabora Ltd
+ * Authors: AngeloGioacchino Del Regno 
<[email protected]>
+ *          Arnaud Ferraris <[email protected]>
+ */
+
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+#include <dm/devres.h>
+#include <dm/lists.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+/**
+ * struct linear_range - table of selector - value pairs
+ *
+ * Define a lookup-table for range of values. Intended to help when looking
+ * for a register value matching certaing physical measure (like voltage).
+ * Usable when increment of one in register always results a constant increment
+ * of the physical measure (like voltage).
+ *
+ * @min:  Lowest value in range
+ * @min_sel: Lowest selector for range
+ * @max_sel: Highest selector for range
+ * @step: Value step size
+ */
+struct linear_range {
+       unsigned int min;
+       unsigned int min_sel;
+       unsigned int max_sel;
+       unsigned int step;
+};
+
+/* Initialize struct linear_range for regulators */
+#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV)  \
+{                                                                      \
+       .min            = _min_uV,                                      \
+       .min_sel        = _min_sel,                                     \
+       .max_sel        = _max_sel,                                     \
+       .step           = _step_uV,                                     \
+}
+
+/* MT6373 registers */
+#define MT6373_TOP_CFG_ELR5                    0x117
+#define MT6373_TOP_CFG_ELR5_SHUTDOWN           BIT(0)
+
+#define MT6373_PMIC_RG_BUCK0_EN_ADDR           0x210
+#define MT6373_PMIC_RG_BUCK_VBUCK0_EN_BIT      0
+#define MT6373_PMIC_RG_BUCK_VBUCK1_EN_BIT      1
+#define MT6373_PMIC_RG_BUCK_VBUCK2_EN_BIT      2
+#define MT6373_PMIC_RG_BUCK_VBUCK3_EN_BIT      3
+#define MT6373_PMIC_RG_BUCK_VBUCK4_EN_BIT      4
+#define MT6373_PMIC_RG_BUCK_VBUCK5_EN_BIT      5
+#define MT6373_PMIC_RG_BUCK_VBUCK6_EN_BIT      6
+#define MT6373_PMIC_RG_BUCK_VBUCK7_EN_BIT      7
+
+#define MT6373_PMIC_RG_BUCK1_EN_ADDR           0x213
+#define MT6373_PMIC_RG_BUCK_VBUCK8_EN_BIT      0
+#define MT6373_PMIC_RG_BUCK_VBUCK9_EN_BIT      1
+
+#define MT6373_PMIC_RG_BUCK0_LP_ADDR           0x216
+#define MT6373_PMIC_RG_BUCK_VBUCK0_LP_BIT      0
+#define MT6373_PMIC_RG_BUCK_VBUCK1_LP_BIT      1
+#define MT6373_PMIC_RG_BUCK_VBUCK2_LP_BIT      2
+#define MT6373_PMIC_RG_BUCK_VBUCK3_LP_BIT      3
+#define MT6373_PMIC_RG_BUCK_VBUCK4_LP_BIT      4
+#define MT6373_PMIC_RG_BUCK_VBUCK5_LP_BIT      5
+#define MT6373_PMIC_RG_BUCK_VBUCK6_LP_BIT      6
+#define MT6373_PMIC_RG_BUCK_VBUCK7_LP_BIT      7
+
+#define MT6373_PMIC_RG_BUCK1_LP_ADDR           0x219
+#define MT6373_PMIC_RG_BUCK_VBUCK8_LP_BIT      0
+#define MT6373_PMIC_RG_BUCK_VBUCK9_LP_BIT      1
+
+#define MT6373_PMIC_RG_BUCK_VBUCK0_VOSEL_ADDR  0x21c
+#define MT6373_PMIC_RG_BUCK_VBUCK1_VOSEL_ADDR  0x21d
+#define MT6373_PMIC_RG_BUCK_VBUCK2_VOSEL_ADDR  0x21e
+#define MT6373_PMIC_RG_BUCK_VBUCK3_VOSEL_ADDR  0x21f
+#define MT6373_PMIC_RG_BUCK_VBUCK4_VOSEL_ADDR  0x220
+#define MT6373_PMIC_RG_BUCK_VBUCK5_VOSEL_ADDR  0x221
+#define MT6373_PMIC_RG_BUCK_VBUCK6_VOSEL_ADDR  0x222
+#define MT6373_PMIC_RG_BUCK_VBUCK7_VOSEL_ADDR  0x223
+#define MT6373_PMIC_RG_BUCK_VBUCK8_VOSEL_ADDR  0x224
+#define MT6373_PMIC_RG_BUCK_VBUCK9_VOSEL_ADDR  0x225
+#define MT6373_PMIC_RG_BUCK_VOSEL_MASK         GENMASK(7, 0)
+
+#define MT6373_PLG_CFG_ELR1                    0x37b
+#define MT6373_ELR_VARIANT_MASK                        GENMASK(3, 2)
+#define MT6373_ELR_VARIANT_MT6373CW            1
+#define MT6373_RG_RSV_SWREG_H                  0x9d9
+#define MT6373_RG_RSV_SWREG_VBUCK4_HW_CTRL     BIT(0)
+
+#define MT6373_BUCK_TOP_KEY_PROT_LO            0x13fa
+
+#define MT6373_PMIC_RG_BUCK1_FCCM_ADDR         0x196d
+#define MT6373_PMIC_RG_VBUCK8_FCCM_BIT         6
+#define MT6373_PMIC_RG_VBUCK9_FCCM_BIT         7
+
+#define MT6373_PMIC_RG_BUCK0_FCCM_ADDR         0x1a02
+#define MT6373_PMIC_RG_VBUCK0_FCCM_BIT         0
+#define MT6373_PMIC_RG_VBUCK1_FCCM_BIT         1
+#define MT6373_PMIC_RG_VBUCK2_FCCM_BIT         2
+#define MT6373_PMIC_RG_VBUCK3_FCCM_BIT         3
+
+#define MT6373_PMIC_RG_BUCK0_1_FCCM_ADDR       0x1a82
+#define MT6373_PMIC_RG_VBUCK4_FCCM_BIT         0
+#define MT6373_PMIC_RG_VBUCK5_FCCM_BIT         1
+#define MT6373_PMIC_RG_VBUCK6_FCCM_BIT         2
+#define MT6373_PMIC_RG_VBUCK7_FCCM_BIT         3
+
+#define MT6373_PMIC_RG_VSRAM_DIGRF_AIF_VOSEL_ADDR 0x1b09
+#define MT6373_PMIC_RG_VSRAM_DIGRF_AIF_VOSEL_MASK GENMASK(6, 0)
+
+#define MT6373_PMIC_RG_LDO_VAUD18_ADDR         0x1b57
+#define MT6373_PMIC_RG_LDO_VUSB_ADDR           0x1b65
+#define MT6373_PMIC_RG_LDO_VAUX18_ADDR         0x1b73
+#define MT6373_PMIC_RG_LDO_VRF13_AIF_ADDR      0x1b81
+#define MT6373_PMIC_RG_LDO_VRF18_AIF_ADDR      0x1b8f
+#define MT6373_PMIC_RG_LDO_VRFIO18_AIF_ADDR    0x1b9d
+#define MT6373_PMIC_RG_LDO_VCN33_1_ADDR                0x1bd7
+#define MT6373_PMIC_RG_LDO_VCN33_2_ADDR                0x1be5
+#define MT6373_PMIC_RG_LDO_VCN33_3_ADDR                0x1bf3
+#define MT6373_PMIC_RG_LDO_VCN18IO_ADDR                0x1c01
+#define MT6373_PMIC_RG_LDO_VRF09_AIF_ADDR      0x1c0f
+#define MT6373_PMIC_RG_LDO_VRF12_AIF_ADDR      0x1c1d
+#define MT6373_PMIC_RG_LDO_VANT18_ADDR         0x1c57
+#define MT6373_PMIC_RG_LDO_VEFUSE_ADDR         0x1c73
+#define MT6373_PMIC_RG_LDO_VMCH_ADDR           0x1c81
+#define MT6373_PMIC_RG_LDO_VMCH_EINT_ADDR      0x1c8f
+#define MT6373_PMIC_RG_LDO_VMCH_EINT_HIGH_ADDR 
MT6373_PMIC_RG_LDO_VMCH_EINT_ADDR
+#define MT6373_PMIC_RG_LDO_VMCH_EINT_LOW_ADDR  
MT6373_PMIC_RG_LDO_VMCH_EINT_ADDR
+#define MT6373_PMIC_RG_LDO_VMCH_EINT_POL_BIT   BIT(2)
+#define MT6373_PMIC_RG_LDO_VMC_ADDR            0x1c90
+#define MT6373_PMIC_RG_LDO_VIBR_ADDR           0x1c9e
+#define MT6373_PMIC_RG_LDO_VIO28_ADDR          0x1cd7
+#define MT6373_PMIC_RG_LDO_VFP_ADDR            0x1ce5
+#define MT6373_PMIC_RG_LDO_VTP_ADDR            0x1cf3
+#define MT6373_PMIC_RG_LDO_VSIM1_ADDR          0x1d01
+#define MT6373_PMIC_RG_LDO_VSIM2_ADDR          0x1d10
+#define MT6373_PMIC_RG_LDO_VSIM2_LP_ADDR       0x1d10
+#define MT6373_PMIC_RG_LDO_VSRAM_DIGRF_AIF_ADDR        0x1d57
+#define MT6373_PMIC_RG_VAUX18_VOCAL_ADDR       0x1dd8
+#define MT6373_PMIC_RG_VAUX18_VOSEL_ADDR       0x1dd9
+#define MT6373_PMIC_RG_VUSB_VOCAL_ADDR         0x1ddc
+#define MT6373_PMIC_RG_VUSB_VOSEL_ADDR         0x1ddd
+#define MT6373_PMIC_RG_VCN33_1_VOCAL_ADDR      0x1de0
+#define MT6373_PMIC_RG_VCN33_1_VOSEL_ADDR      0x1de1
+#define MT6373_PMIC_RG_VCN33_2_VOCAL_ADDR      0x1de4
+#define MT6373_PMIC_RG_VCN33_2_VOSEL_ADDR      0x1de5
+#define MT6373_PMIC_RG_VCN33_3_VOCAL_ADDR      0x1de8
+#define MT6373_PMIC_RG_VCN33_3_VOSEL_ADDR      0x1de9
+#define MT6373_PMIC_RG_VMCH_VOCAL_ADDR         0x1dec
+#define MT6373_PMIC_RG_VMCH_VOSEL_ADDR         0x1ded
+#define MT6373_PMIC_RG_VMCH_EINT_HIGH_VOSEL_ADDR MT6373_PMIC_RG_VMCH_VOSEL_ADDR
+#define MT6373_PMIC_RG_VMCH_EINT_LOW_VOSEL_ADDR        
MT6373_PMIC_RG_VMCH_VOSEL_ADDR
+#define MT6373_PMIC_RG_VEFUSE_VOCAL_ADDR       0x1df0
+#define MT6373_PMIC_RG_VEFUSE_VOSEL_ADDR       0x1df1
+#define MT6373_PMIC_RG_VMC_VOCAL_ADDR          0x1df4
+#define MT6373_PMIC_RG_VMCH_EINT_HIGH_VOCAL_ADDR MT6373_PMIC_RG_VMC_VOCAL_ADDR
+#define MT6373_PMIC_RG_VMCH_EINT_LOW_VOCAL_ADDR        
MT6373_PMIC_RG_VMC_VOCAL_ADDR
+#define MT6373_PMIC_RG_VMC_VOSEL_ADDR          0x1df5
+#define MT6373_PMIC_RG_VIBR_VOCAL_ADDR         0x1df8
+#define MT6373_PMIC_RG_VIBR_VOSEL_ADDR         0x1df9
+#define MT6373_PMIC_RG_VIO28_VOCAL_ADDR                0x1dfc
+#define MT6373_PMIC_RG_VIO28_VOSEL_ADDR                0x1dfd
+#define MT6373_PMIC_RG_VFP_VOCAL_ADDR          0x1e00
+#define MT6373_PMIC_RG_VFP_VOSEL_ADDR          0x1e01
+#define MT6373_PMIC_RG_VTP_VOCAL_ADDR          0x1e04
+#define MT6373_PMIC_RG_VTP_VOSEL_ADDR          0x1e05
+#define MT6373_PMIC_RG_VSIM1_VOCAL_ADDR                0x1e08
+#define MT6373_PMIC_RG_VSIM1_VOSEL_ADDR                0x1e09
+#define MT6373_PMIC_RG_VSIM2_VOCAL_ADDR                0x1e0c
+#define MT6373_PMIC_RG_VSIM2_VOSEL_ADDR                0x1e0d
+#define MT6373_PMIC_RG_VAUD18_VOCAL_ADDR       0x1e58
+#define MT6373_PMIC_RG_VAUD18_VOSEL_ADDR       0x1e59
+#define MT6373_PMIC_RG_VRF18_AIF_VOCAL_ADDR    0x1e5c
+#define MT6373_PMIC_RG_VRF18_AIF_VOSEL_ADDR    0x1e5d
+#define MT6373_PMIC_RG_VCN18IO_VOCAL_ADDR      0x1e60
+#define MT6373_PMIC_RG_VCN18IO_VOSEL_ADDR      0x1e61
+#define MT6373_PMIC_RG_VRFIO18_AIF_VOCAL_ADDR  0x1e64
+#define MT6373_PMIC_RG_VRFIO18_AIF_VOSEL_ADDR  0x1e65
+#define MT6373_PMIC_RG_VANT18_VOCAL_ADDR       0x1e68
+#define MT6373_PMIC_RG_VANT18_VOSEL_ADDR       0x1e69
+#define MT6373_PMIC_RG_VRF13_AIF_VOCAL_ADDR    0x1ed8
+#define MT6373_PMIC_RG_VRF13_AIF_VOSEL_ADDR    0x1ed9
+#define MT6373_PMIC_RG_VRF12_AIF_VOCAL_ADDR    0x1edc
+#define MT6373_PMIC_RG_VRF12_AIF_VOSEL_ADDR    0x1edd
+#define MT6373_PMIC_RG_VRF09_AIF_VOCAL_ADDR    0x1f58
+#define MT6373_PMIC_RG_VRF09_AIF_VOSEL_ADDR    0x1f59
+#define MT6373_PMIC_RG_LDO_VT_VOCALSEL_MASK    GENMASK(7, 0)
+
+#define EN_SET_OFFSET                  0x1
+#define EN_CLR_OFFSET                  0x2
+
+enum {
+       MT6373_ID_VBUCK0,
+       MT6373_ID_VBUCK1,
+       MT6373_ID_VBUCK2,
+       MT6373_ID_VBUCK3,
+       MT6373_ID_VBUCK4,
+       MT6373_ID_VBUCK5,
+       MT6373_ID_VBUCK6,
+       MT6373_ID_VBUCK7,
+       MT6373_ID_VBUCK8,
+       MT6373_ID_VBUCK9,
+       MT6373_ID_VANT18,
+       MT6373_ID_VAUD18,
+       MT6373_ID_VAUX18,
+       MT6373_ID_VCN18IO,
+       MT6373_ID_VCN33_1,
+       MT6373_ID_VCN33_2,
+       MT6373_ID_VCN33_3,
+       MT6373_ID_VEFUSE,
+       MT6373_ID_VFP,
+       MT6373_ID_VIBR,
+       MT6373_ID_VIO28,
+       MT6373_ID_VMC,
+       MT6373_ID_VMCH,
+       MT6373_ID_VMCH_EINT_HIGH,
+       MT6373_ID_VMCH_EINT_LOW,
+       MT6373_ID_VRF09_AIF,
+       MT6373_ID_VRF12_AIF,
+       MT6373_ID_VRF13_AIF,
+       MT6373_ID_VRF18_AIF,
+       MT6373_ID_VRFIO18_AIF,
+       MT6373_ID_VSRAM_DIGRF_AIF,
+       MT6373_ID_VTP,
+       MT6373_ID_VUSB,
+};
+
+/**
+ * struct mt6373_regulator_info - MT6373 regulators information
+ */
+struct mt6373_regulator_info {
+       int id;
+       char *name;
+       char *supply_name;
+       u16 enable_reg;
+       u8  enable_mask;
+       u16 vsel_reg;
+       u8  vsel_mask;
+       u16 vsel_range_reg;
+       u8  vsel_range_mask;
+       const struct linear_range *linear_ranges;
+       int n_linear_ranges;
+};
+
+struct mt6373_regulator_data {
+       u16 base;
+       struct udevice *pmic;
+};
+
+struct mt6373_regulator {
+       struct mt6373_regulator_data *data;
+       const struct mt6373_regulator_info *info;
+};
+
+#define MT6373_BUCK(match, vreg, en_reg)                       \
+[MT6373_ID_##vreg] = {                                         \
+       .id = MT6373_ID_##vreg,                                 \
+       .name = match,                                          \
+       .supply_name = "vsys-"match,                            \
+       .enable_reg = en_reg,                                   \
+       .enable_mask = BIT(MT6373_PMIC_RG_BUCK_##vreg##_EN_BIT),\
+       .vsel_reg = MT6373_PMIC_RG_BUCK_##vreg##_VOSEL_ADDR,    \
+       .vsel_mask = MT6373_PMIC_RG_BUCK_VOSEL_MASK,            \
+       .linear_ranges = buck_volt_ranges,                      \
+       .n_linear_ranges = ARRAY_SIZE(buck_volt_ranges),        \
+}
+
+#define MT6373_LDO_L(match, vreg, in_sup)                      \
+[MT6373_ID_##vreg] = {                                         \
+       .id = MT6373_ID_##vreg,                                 \
+       .name = match,                                          \
+       .supply_name = in_sup,                                  \
+       .enable_reg = MT6373_PMIC_RG_LDO_##vreg##_ADDR,         \
+       .enable_mask = BIT(0),                                  \
+       .vsel_reg = MT6373_PMIC_RG_##vreg##_VOSEL_ADDR,         \
+       .vsel_mask = MT6373_PMIC_RG_##vreg##_VOSEL_MASK,        \
+       .linear_ranges = ldo_linear_volt_ranges,                \
+       .n_linear_ranges = ARRAY_SIZE(ldo_linear_volt_ranges),  \
+}
+
+#define MT6373_LDO_VT(match, vreg, in_sup, vrnum)              \
+[MT6373_ID_##vreg] = {                                         \
+       .id = MT6373_ID_##vreg,                                 \
+       .name = match,                                          \
+       .supply_name = in_sup,                                  \
+       .enable_reg = MT6373_PMIC_RG_LDO_##vreg##_ADDR,         \
+       .enable_mask = BIT(0),                                  \
+       .vsel_reg = MT6373_PMIC_RG_##vreg##_VOCAL_ADDR,         \
+       .vsel_mask = MT6373_PMIC_RG_LDO_VT_VOCALSEL_MASK,       \
+       .vsel_range_reg = MT6373_PMIC_RG_##vreg##_VOSEL_ADDR,   \
+       .vsel_range_mask = MT6373_PMIC_RG_LDO_VT_VOCALSEL_MASK, \
+       .linear_ranges = ldo_volt_ranges##vrnum,                \
+       .n_linear_ranges = ARRAY_SIZE(ldo_volt_ranges##vrnum),  \
+}
+
+static const struct linear_range buck_volt_ranges[] = {
+       REGULATOR_LINEAR_RANGE(0, 0, 191, 6250),
+};
+
+static const struct linear_range ldo_volt_ranges1[] = {
+       REGULATOR_LINEAR_RANGE(1200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1300000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1500000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3300000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3400000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3500000, 0, 10, 10000)
+};
+
+static const struct linear_range ldo_volt_ranges2[] = {
+       REGULATOR_LINEAR_RANGE(1800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2300000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2400000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2500000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2600000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3300000, 0, 10, 10000)
+};
+
+static const struct linear_range ldo_volt_ranges3[] = {
+       REGULATOR_LINEAR_RANGE(600000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1300000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1400000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1500000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1600000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2100000, 0, 10, 10000)
+};
+
+static const struct linear_range ldo_volt_ranges4[] = {
+       REGULATOR_LINEAR_RANGE(1200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1300000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1500000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2500000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2600000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2700000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2800000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(2900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3300000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3400000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(3500000, 0, 10, 10000)
+};
+
+static const struct linear_range ldo_volt_ranges5[] = {
+       REGULATOR_LINEAR_RANGE(900000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1000000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1100000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1200000, 0, 10, 10000),
+       REGULATOR_LINEAR_RANGE(1300000, 0, 10, 10000),
+};
+
+static const struct linear_range ldo_linear_volt_ranges[] = {
+       REGULATOR_LINEAR_RANGE(400000, 0, 127, 6250),
+};
+
+/* The array is indexed by id(MT6373_ID_XXX) */
+static struct mt6373_regulator_info mt6373_regulators_info[] = {
+       MT6373_BUCK("vbuck0", VBUCK0, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck1", VBUCK1, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck2", VBUCK2, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck3", VBUCK3, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck4", VBUCK4, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck5", VBUCK5, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck6", VBUCK6, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck7", VBUCK7, MT6373_PMIC_RG_BUCK0_EN_ADDR),
+       MT6373_BUCK("vbuck8", VBUCK8, MT6373_PMIC_RG_BUCK1_EN_ADDR),
+       MT6373_BUCK("vbuck9", VBUCK9, MT6373_PMIC_RG_BUCK1_EN_ADDR),
+       MT6373_LDO_VT("vant18", VANT18, "vs1-ldo1", 3),
+       MT6373_LDO_VT("vaud18", VAUD18, "vs1-ldo1", 3),
+       MT6373_LDO_VT("vaux18", VAUX18, "vsys-ldo2", 2),
+       MT6373_LDO_VT("vcn18io", VCN18IO, "vs1-ldo1", 3),
+       MT6373_LDO_VT("vcn33-1", VCN33_1, "vsys-ldo1", 4),
+       MT6373_LDO_VT("vcn33-2", VCN33_2, "vsys-ldo1", 4),
+       MT6373_LDO_VT("vcn33-3", VCN33_3, "vsys-ldo2", 4),
+       MT6373_LDO_VT("vefuse", VEFUSE, "vsys-ldo2", 1),
+       MT6373_LDO_VT("vfp", VFP, "vsys-ldo2", 1),
+       MT6373_LDO_VT("vibr", VIBR, "vsys-ldo2", 1),
+       MT6373_LDO_VT("vio28", VIO28, "vsys-ldo2", 1),
+       MT6373_LDO_VT("vmc", VMC, "vsys-ldo1", 1),
+       MT6373_LDO_VT("vmch", VMCH, "vsys-ldo3", 4),
+       MT6373_LDO_VT("vmch-eint-high", VMCH_EINT_HIGH, "vsys-ldo3", 4),
+       MT6373_LDO_VT("vmch-eint-low", VMCH_EINT_LOW, "vsys-ldo3", 4),
+       MT6373_LDO_VT("vrf09-aif", VRF09_AIF, "vs3-ldo1", 3),
+       MT6373_LDO_VT("vrf12-aif", VRF12_AIF, "vs2-ldo1", 5),
+       MT6373_LDO_VT("vrf13-aif", VRF13_AIF, "vs2-ldo1", 3),
+       MT6373_LDO_VT("vrf18-aif", VRF18_AIF, "vs1-ldo1", 3),
+       MT6373_LDO_VT("vrfio18-aif", VRFIO18_AIF, "vs1-ldo1", 3),
+       MT6373_LDO_L("vsram-digrf-aif", VSRAM_DIGRF_AIF, "vs3-ldo1"),
+       MT6373_LDO_VT("vtp", VTP, "vsys-ldo2", 1),
+       MT6373_LDO_VT("vusb", VUSB, "vsys-ldo2", 1),
+};
+
+static u8 mt6373_read(struct mt6373_regulator *reg, u16 addr, u8 *value)
+{
+       return pmic_read(reg->data->pmic, reg->data->base + addr, value, 1);
+}
+
+static int mt6373_write(struct mt6373_regulator *reg, u16 addr, u8 value)
+{
+       return pmic_write(reg->data->pmic, reg->data->base + addr, &value, 1);
+}
+
+static int mt6373_clear_bits(struct mt6373_regulator *reg, u16 addr, u8 mask)
+{
+       int ret;
+       u8 val;
+
+       ret = mt6373_read(reg, addr, &val);
+       if (ret)
+               return ret;
+
+       return mt6373_write(reg, addr, val & ~mask);
+}
+
+static int mt6373_set_bits(struct mt6373_regulator *reg, u16 addr, u8 mask)
+{
+       int ret;
+       u8 val;
+
+       ret = mt6373_read(reg, addr, &val);
+       if (ret)
+               return ret;
+
+       return mt6373_write(reg, addr, val | mask);
+}
+
+static int mt6373_regulator_get_value(struct udevice *dev)
+{
+       struct mt6373_regulator *plat = dev_get_plat(dev);
+       const struct linear_range *range;
+       int ret, nranges = plat->info->n_linear_ranges;
+       u8 val;
+
+       if (nranges > 1) {
+               ret = mt6373_read(plat, plat->info->vsel_range_reg, &val);
+               if (ret < 0)
+                       return ret;
+
+               if (val >= nranges) {
+                       dev_err(dev,
+                               "Read value (%u) out of available ranges 
(%d)\n",
+                               val, nranges);
+                       return -EINVAL;
+               }
+
+               range = &plat->info->linear_ranges[val];
+       } else {
+               range = plat->info->linear_ranges;
+       }
+
+       ret = mt6373_read(plat, plat->info->vsel_reg, &val);
+       if (ret < 0)
+               return ret;
+
+       val &= plat->info->vsel_mask;
+
+       return (range->min + range->step * val);
+}
+
+static int mt6373_regulator_set_value(struct udevice *dev, int uV)
+{
+       struct mt6373_regulator *plat = dev_get_plat(dev);
+       const struct linear_range *range = NULL;
+       int i, max, ret, nranges = plat->info->n_linear_ranges;
+       unsigned int vsel;
+
+       if (nranges > 1) {
+               for (i = 0; i < nranges; i++) {
+                       if (uV < plat->info->linear_ranges[i].min) {
+                               if (i) {
+                                       i--;
+                                       range = &plat->info->linear_ranges[i];
+                                       break;
+                               }
+
+                               dev_err(dev,
+                                       "Requested voltage (%d) is lower than"
+                                       " minimum regulator voltage (%d)\n",
+                                       uV, plat->info->linear_ranges[0].min);
+                               return -EINVAL;
+                       }
+               }
+
+               if (!range) {
+                       i = nranges - 1;
+                       max = plat->info->linear_ranges[i].min
+                               + (plat->info->linear_ranges[i].max_sel
+                                       * plat->info->linear_ranges[i].step);
+
+                       if (uV > max)
+                               dev_err(dev,
+                                       "Requested voltage (%d) is higher than"
+                                       " maximum regulator voltage (%d)\n",
+                                       uV, max);
+                       else
+                               range = &plat->info->linear_ranges[i];
+               }
+
+               if (!range) {
+                       dev_err(dev,
+                               "Unable to determine proper range for value 
(%d)\n",
+                               uV);
+                       return -EINVAL;
+               }
+
+               ret = mt6373_write(plat, plat->info->vsel_range_reg,
+                                  (u8)i & plat->info->vsel_range_mask);
+               if (ret)
+                       return ret;
+       } else {
+               range = plat->info->linear_ranges;
+       }
+
+       vsel = (uV - range->min) / range->step;
+       return mt6373_write(plat, plat->info->vsel_reg,
+                           (u8)vsel & plat->info->vsel_mask);
+}
+
+static int mt6373_vmch_eint_enable(struct mt6373_regulator *reg)
+{
+       int ret;
+
+       if (reg->info->id == MT6373_ID_VMCH_EINT_HIGH) {
+               ret = mt6373_set_bits(reg, MT6373_PMIC_RG_LDO_VMCH_EINT_ADDR,
+                                     MT6373_PMIC_RG_LDO_VMCH_EINT_POL_BIT);
+       } else {
+               ret = mt6373_clear_bits(reg, MT6373_PMIC_RG_LDO_VMCH_EINT_ADDR,
+                                       MT6373_PMIC_RG_LDO_VMCH_EINT_POL_BIT);
+       }
+
+       if (ret)
+               return ret;
+
+       ret = mt6373_set_bits(reg, MT6373_PMIC_RG_LDO_VMCH_ADDR,
+                             reg->info->enable_mask);
+       if (ret)
+               return ret;
+
+       return mt6373_set_bits(reg, reg->info->enable_reg, 
reg->info->enable_mask);
+}
+
+static int mt6373_vmch_eint_disable(struct mt6373_regulator *reg)
+{
+       int ret;
+
+       ret = mt6373_clear_bits(reg, MT6373_PMIC_RG_LDO_VMCH_ADDR,
+                               reg->info->enable_mask);
+       if (ret)
+               return ret;
+
+       /* Wait for VMCH discharging */
+       udelay(1500);
+
+       return mt6373_clear_bits(reg, reg->info->enable_reg,
+                                reg->info->enable_mask);
+}
+
+static int mt6373_regulator_get_enable(struct udevice *dev)
+{
+       struct mt6373_regulator *plat = dev_get_plat(dev);
+       int ret;
+       u8 val;
+
+       ret = mt6373_read(plat, plat->info->enable_reg, &val);
+       if (ret < 0) {
+               dev_err(dev, "Unable to read enable status\n");
+               return ret;
+       }
+
+       return !!(val & plat->info->enable_mask);
+}
+
+static int mt6373_regulator_buck_set_enable(struct udevice *dev, bool enable)
+{
+       struct mt6373_regulator *plat = dev_get_plat(dev);
+       u16 addr = plat->info->enable_reg;
+
+       if (enable)
+               addr += EN_SET_OFFSET;
+       else
+               addr += EN_CLR_OFFSET;
+
+       return mt6373_write(plat, addr, plat->info->enable_mask);
+}
+
+static int mt6373_regulator_ldo_set_enable(struct udevice *dev, bool enable)
+{
+       struct mt6373_regulator *plat = dev_get_plat(dev);
+
+       if (plat->info->id == MT6373_ID_VMCH_EINT_LOW ||
+           plat->info->id == MT6373_ID_VMCH_EINT_HIGH) {
+               return enable ? mt6373_vmch_eint_enable(plat)
+                             : mt6373_vmch_eint_disable(plat);
+       }
+
+       if (enable)
+               return mt6373_set_bits(plat, plat->info->enable_reg,
+                                      plat->info->enable_mask);
+
+       return mt6373_clear_bits(plat, plat->info->enable_reg,
+                                plat->info->enable_mask);
+}
+
+static struct dm_regulator_ops mt6373_regulator_buck_ops = {
+       .get_enable = mt6373_regulator_get_enable,
+       .set_enable = mt6373_regulator_buck_set_enable,
+       .get_value = mt6373_regulator_get_value,
+       .set_value = mt6373_regulator_set_value,
+};
+
+static struct dm_regulator_ops mt6373_regulator_ldo_ops = {
+       .get_enable = mt6373_regulator_get_enable,
+       .set_enable = mt6373_regulator_ldo_set_enable,
+       .get_value = mt6373_regulator_get_value,
+       .set_value = mt6373_regulator_set_value,
+};
+
+static int mt6373_regulator_bind(struct udevice *dev)
+{
+       struct mt6373_regulator *reg = dev_get_plat(dev);
+       struct mt6373_regulator *drv_data;
+
+       drv_data = (struct mt6373_regulator *)dev_get_driver_data(dev);
+       if (!drv_data) {
+               dev_err(dev, "Unable to get driver data\n");
+               return -EINVAL;
+       }
+
+       reg->data = drv_data->data;
+       reg->info = drv_data->info;
+
+       return 0;
+}
+
+U_BOOT_DRIVER(mt6373_regulator_buck) = {
+       .name = "mt6373_regulator_buck",
+       .id = UCLASS_REGULATOR,
+       .bind = mt6373_regulator_bind,
+       .plat_auto = sizeof(struct mt6373_regulator),
+       .ops = &mt6373_regulator_buck_ops,
+};
+
+U_BOOT_DRIVER(mt6373_regulator_ldo) = {
+       .name = "mt6373_regulator_ldo",
+       .id = UCLASS_REGULATOR,
+       .bind = mt6373_regulator_bind,
+       .plat_auto = sizeof(struct mt6373_regulator),
+       .ops = &mt6373_regulator_ldo_ops,
+};
+
+/* This driver intentionally only supports a subset of the available 
regulators.
+ * This function checks to see if a given regulator node in DT matches a 
regulator
+ * defined in the driver.
+ */
+static const struct mt6373_regulator_info *mt6373_get_regulator_info(ofnode 
node)
+{
+       int i, max = ARRAY_SIZE(mt6373_regulators_info);
+
+       for (i = 0; i < max; i++) {
+               if (!strcmp(mt6373_regulators_info[i].name, 
ofnode_get_name(node)))
+                       return &mt6373_regulators_info[i];
+       }
+
+       return NULL;
+}
+
+static int mt6373_regulators_probe(struct udevice *dev)
+{
+       int ret;
+       u8 val;
+
+       ret = pmic_read(dev->parent, 0xb, (u8 *)&val, 1);
+       if (ret < 0)
+               ret = pmic_read(dev->parent, 0xb, (u8 *)&val, 1);
+       if (ret < 0) {
+               dev_err(dev, "Unable to read device ID\n");
+               return ret;
+       }
+
+       if (val != 0x73) {
+               dev_err(dev, "Device ID doesn't match: 0x%02x != 0x73\n", val);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int mt6373_regulators_bind(struct udevice *dev)
+{
+       struct mt6373_regulator_data *data = dev_get_plat(dev);
+       struct driver *drv;
+       char *name;
+       ofnode child, node = dev_ofnode(dev);
+       int ret;
+       size_t namelen;
+       u32 addr;
+
+       ret = ofnode_read_u32_index(node, "reg", 0, &addr);
+       if (ret < 0)
+               return -EINVAL;
+
+       data->base = (u16)addr;
+       data->pmic = dev->parent;
+
+       ofnode_for_each_subnode(child, node) {
+               struct mt6373_regulator reg;
+
+               reg.data = data;
+               reg.info = mt6373_get_regulator_info(child);
+               if (!reg.info)
+                       continue;
+
+               namelen = strlen("reg-mt6373-") + strlen(reg.info->name) + 1;
+               name = devm_kzalloc(dev, namelen, GFP_KERNEL);
+               ret = snprintf(name, namelen, "reg-mt6373-%s", reg.info->name);
+               if (ret < 0 || ret >= namelen) {
+                       dev_err(dev, "Failed to create MT6373 regulator 
name\n");
+                       return -ENOMEM;
+               }
+
+               if (reg.info->id <= MT6373_ID_VBUCK9)
+                       drv = DM_DRIVER_GET(mt6373_regulator_buck);
+               else
+                       drv = DM_DRIVER_GET(mt6373_regulator_ldo);
+
+               ret = device_bind_with_driver_data(dev, drv, name, (ulong)&reg,
+                                                  child, NULL);
+               if (ret < 0) {
+                       dev_err(dev, "Failed to bind MT6373 regulator %s: %d\n",
+                               name, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct udevice_id mt6373_regulators_ids[] = {
+       { .compatible = "mediatek,mt6373-regulator" },
+       { /* sentinel */ }
+};
+
+/*
+ * Driver for a 'bank' of regulators. This creates devices for each
+ * individual regulator
+ */
+U_BOOT_DRIVER(mt6373_regulators) = {
+       .name = "mt6373_regulators",
+       .id = UCLASS_MISC,
+       .bind = mt6373_regulators_bind,
+       .probe = mt6373_regulators_probe,
+       .of_match = mt6373_regulators_ids,
+       .ops = &mt6373_regulator_ldo_ops,
+       .plat_auto = sizeof(struct mt6373_regulator_data),
+};

-- 
2.53.0

Reply via email to