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)®, + 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
