From 0c16e7d2fbb99fb546efd5303ec863742d99500e Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Thu, 17 Sep 2020 11:54:52 +0200 Subject: [PATCH 01/15] refactor(stm32mp1): re-order drivers init SYSCFG can be initialized later, after console is up, to display the warnings or messages it could issue. PMIC should be initialized earlier, before SYSCFG init. Change-Id: Icc3a1366083a1b1fde7f0e173645449b4c04c49b Signed-off-by: Yann Gautier --- plat/st/stm32mp1/bl2_plat_setup.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index 512afa832..2b878332a 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -130,10 +130,6 @@ void bl2_platform_setup(void) { int ret; - if (dt_pmic_status() > 0) { - initialize_pmic(); - } - ret = stm32mp1_ddr_probe(); if (ret < 0) { ERROR("Invalid DDR init: error %d\n", ret); @@ -247,8 +243,6 @@ void bl2_el3_plat_arch_setup(void) panic(); } - stm32mp1_syscfg_init(); - stm32_save_boot_interface(boot_context->boot_interface_selected, boot_context->boot_interface_instance); @@ -277,6 +271,12 @@ void bl2_el3_plat_arch_setup(void) } skip_console_init: + if (dt_pmic_status() > 0) { + initialize_pmic(); + } + + stm32mp1_syscfg_init(); + if (stm32_iwdg_init() < 0) { panic(); } From 16e56a75deb30fb18c43a94762a830fc94c71a16 Mon Sep 17 00:00:00 2001 From: Nicolas Le Bayon Date: Thu, 19 Sep 2019 11:24:50 +0200 Subject: [PATCH 02/15] refactor(stpmic1): set stpmic1_is_regulator_enabled() as boolean Improve use and readability. Change-Id: Ia99fc38287f36c9dd12bfe51352afa5da68c0e47 Signed-off-by: Nicolas Le Bayon --- drivers/st/pmic/stm32mp_pmic.c | 9 ++++----- drivers/st/pmic/stpmic1.c | 4 ++-- include/drivers/st/stpmic1.h | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index be410a1f4..6fa0d0a59 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -6,10 +6,6 @@ #include -#include - -#include - #include #include #include @@ -17,6 +13,9 @@ #include #include #include +#include + +#include #define STPMIC1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2)) #define STPMIC1_LDO12356_OUTPUT_SHIFT 2 @@ -175,7 +174,7 @@ int dt_pmic_configure_boot_on_regulators(void) return status; } - if (stpmic1_is_regulator_enabled(node_name) == 0U) { + if (!stpmic1_is_regulator_enabled(node_name)) { status = stpmic1_regulator_enable(node_name); if (status != 0) { return status; diff --git a/drivers/st/pmic/stpmic1.c b/drivers/st/pmic/stpmic1.c index 0a35df372..8199b7a01 100644 --- a/drivers/st/pmic/stpmic1.c +++ b/drivers/st/pmic/stpmic1.c @@ -606,7 +606,7 @@ int stpmic1_regulator_disable(const char *name) regul->enable_mask); } -uint8_t stpmic1_is_regulator_enabled(const char *name) +bool stpmic1_is_regulator_enabled(const char *name) { uint8_t val; const struct regul_struct *regul = get_regulator_data(name); @@ -615,7 +615,7 @@ uint8_t stpmic1_is_regulator_enabled(const char *name) panic(); } - return (val & regul->enable_mask); + return (val & regul->enable_mask) == regul->enable_mask; } int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts) diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h index dc096cd1a..eb104e47b 100644 --- a/include/drivers/st/stpmic1.h +++ b/include/drivers/st/stpmic1.h @@ -156,7 +156,7 @@ int stpmic1_register_write(uint8_t register_id, uint8_t value); int stpmic1_register_update(uint8_t register_id, uint8_t value, uint8_t mask); int stpmic1_regulator_enable(const char *name); int stpmic1_regulator_disable(const char *name); -uint8_t stpmic1_is_regulator_enabled(const char *name); +bool stpmic1_is_regulator_enabled(const char *name); int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts); int stpmic1_regulator_voltage_get(const char *name); int stpmic1_regulator_pull_down_set(const char *name); From c77c7d9e309b6c27ada533f887baf6506f587c4a Mon Sep 17 00:00:00 2001 From: Nicolas Le Bayon Date: Fri, 15 Nov 2019 15:56:06 +0100 Subject: [PATCH 03/15] refactor(st-pmic): improve driver usage Store status of dt_pmic_status() as local static variable, this avoids parsing DT several times. In the same way, store nodes in dt_pmic_i2c_config() and in dt_get_pmic_node() as local static variables. Change-Id: I4585e9dfdde2847a369bffcc6f2b39ecc2b74de1 Signed-off-by: Nicolas Le Bayon --- drivers/st/pmic/stm32mp_pmic.c | 66 ++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index 6fa0d0a59..03d719009 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -17,6 +17,7 @@ #include +#define PMIC_NODE_NOT_FOUND 1 #define STPMIC1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2)) #define STPMIC1_LDO12356_OUTPUT_SHIFT 2 #define STPMIC1_LDO3_MODE (uint8_t)(BIT(7)) @@ -33,24 +34,39 @@ static uint32_t pmic_i2c_addr; static int dt_get_pmic_node(void *fdt) { - return fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1"); + static int node = -FDT_ERR_BADOFFSET; + + if (node == -FDT_ERR_BADOFFSET) { + node = fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1"); + } + + return node; } int dt_pmic_status(void) { + static int status = -FDT_ERR_BADVALUE; int node; void *fdt; + if (status != -FDT_ERR_BADVALUE) { + return status; + } + if (fdt_get_address(&fdt) == 0) { return -ENOENT; } node = dt_get_pmic_node(fdt); if (node <= 0) { - return -FDT_ERR_NOTFOUND; + status = -FDT_ERR_NOTFOUND; + + return status; } - return fdt_get_status(node); + status = (int)fdt_get_status(node); + + return status; } static bool dt_pmic_is_secure(void) @@ -64,37 +80,41 @@ static bool dt_pmic_is_secure(void) /* * Get PMIC and its I2C bus configuration from the device tree. - * Return 0 on success, negative on error, 1 if no PMIC node is found. + * Return 0 on success, negative on error, 1 if no PMIC node is defined. */ static int dt_pmic_i2c_config(struct dt_node_info *i2c_info, struct stm32_i2c_init_s *init) { - int pmic_node, i2c_node; + static int i2c_node = -FDT_ERR_NOTFOUND; void *fdt; - const fdt32_t *cuint; if (fdt_get_address(&fdt) == 0) { - return -ENOENT; - } - - pmic_node = dt_get_pmic_node(fdt); - if (pmic_node < 0) { - return 1; - } - - cuint = fdt_getprop(fdt, pmic_node, "reg", NULL); - if (cuint == NULL) { return -FDT_ERR_NOTFOUND; } - pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1; - if (pmic_i2c_addr > UINT16_MAX) { - return -EINVAL; - } + if (i2c_node == -FDT_ERR_NOTFOUND) { + int pmic_node; + const fdt32_t *cuint; - i2c_node = fdt_parent_offset(fdt, pmic_node); - if (i2c_node < 0) { - return -FDT_ERR_NOTFOUND; + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return PMIC_NODE_NOT_FOUND; + } + + cuint = fdt_getprop(fdt, pmic_node, "reg", NULL); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1; + if (pmic_i2c_addr > UINT16_MAX) { + return -FDT_ERR_BADVALUE; + } + + i2c_node = fdt_parent_offset(fdt, pmic_node); + if (i2c_node < 0) { + return -FDT_ERR_NOTFOUND; + } } dt_fill_device_info(i2c_info, i2c_node); From 13fbfe046e71393961d2c70a4f748a15f9c15f77 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Fri, 10 Jan 2020 08:31:13 +0100 Subject: [PATCH 04/15] feat(stpmic1): add USB OTG regulators Add regulators boost, pwr_sw1 and pwr_sw2 regulators related to USB OTG supply BOOST, SW_OTG and SWIN/SWOUT. These regulators are needed since manipulated during the suspend/resume power sequence as per FDT description for stm32mp15x-xxx boards from STMicroelectronics. Change-Id: I6217de707e49882bd5a9100db43e0d354908800d Signed-off-by: Etienne Carriere --- drivers/st/pmic/stpmic1.c | 25 +++++++++++++++++++++++++ include/drivers/st/stpmic1.h | 1 + 2 files changed, 26 insertions(+) diff --git a/drivers/st/pmic/stpmic1.c b/drivers/st/pmic/stpmic1.c index 8199b7a01..714d53232 100644 --- a/drivers/st/pmic/stpmic1.c +++ b/drivers/st/pmic/stpmic1.c @@ -421,6 +421,10 @@ static const uint16_t vref_ddr_voltage_table[] = { 3300, }; +static const uint16_t fixed_5v_voltage_table[] = { + 5000, +}; + /* Table of Regulators in PMIC SoC */ static const struct regul_struct regulators_table[] = { { @@ -541,6 +545,27 @@ static const struct regul_struct regulators_table[] = { .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = VREF_DDR_MASK_RESET, }, + { + .dt_node_name = "boost", + .voltage_table = fixed_5v_voltage_table, + .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), + .control_reg = USB_CONTROL_REG, + .enable_mask = BOOST_ENABLED, + }, + { + .dt_node_name = "pwr_sw1", + .voltage_table = fixed_5v_voltage_table, + .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), + .control_reg = USB_CONTROL_REG, + .enable_mask = USBSW_OTG_SWITCH_ENABLED, + }, + { + .dt_node_name = "pwr_sw2", + .voltage_table = fixed_5v_voltage_table, + .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), + .control_reg = USB_CONTROL_REG, + .enable_mask = SWIN_SWOUT_ENABLED, + }, }; #define MAX_REGUL ARRAY_SIZE(regulators_table) diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h index eb104e47b..a69e5b561 100644 --- a/include/drivers/st/stpmic1.h +++ b/include/drivers/st/stpmic1.h @@ -148,6 +148,7 @@ #define OCP_LIMIT_HIGH BIT(3) #define SWIN_SWOUT_ENABLED BIT(2) #define USBSW_OTG_SWITCH_ENABLED BIT(1) +#define BOOST_ENABLED BIT(0) int stpmic1_powerctrl_on(void); int stpmic1_switch_off(void); From ea552bf5a57b573a6b09e396e3466b3c4af727f0 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Tue, 15 Dec 2020 18:28:34 +0100 Subject: [PATCH 05/15] feat(stpmic1): add new services Add support for ICC, sink mode, bypass mode, active discharge and list voltages. Handle LDO3 sink source mode in a different way to avoid setting voltage while in sink source mode. Change-Id: Ib1b909fd8a153f542917f650e43e24317a570534 Signed-off-by: Pascal Paillet --- drivers/st/pmic/stpmic1.c | 136 +++++++++++++++++++++++++++++++++-- include/drivers/st/stpmic1.h | 28 ++++++++ 2 files changed, 160 insertions(+), 4 deletions(-) diff --git a/drivers/st/pmic/stpmic1.c b/drivers/st/pmic/stpmic1.c index 714d53232..37eb50bbc 100644 --- a/drivers/st/pmic/stpmic1.c +++ b/drivers/st/pmic/stpmic1.c @@ -23,10 +23,17 @@ struct regul_struct { uint8_t pull_down; uint8_t mask_reset_reg; uint8_t mask_reset; + uint8_t icc_reg; + uint8_t icc_mask; }; static struct i2c_handle_s *pmic_i2c_handle; static uint16_t pmic_i2c_addr; +/* + * Special mode corresponds to LDO3 in sink source mode or in bypass mode. + * LDO3 doesn't switch back from special to normal mode. + */ +static bool ldo3_special_mode; /* Voltage tables in mV */ static const uint16_t buck1_voltage_table[] = { @@ -347,8 +354,11 @@ static const uint16_t ldo3_voltage_table[] = { 3300, 3300, 3300, - 500, - 0xFFFF, /* VREFDDR */ +}; + +/* Special mode table is used for sink source OR bypass mode */ +static const uint16_t ldo3_special_mode_table[] = { + 0, }; static const uint16_t ldo5_voltage_table[] = { @@ -438,6 +448,8 @@ static const struct regul_struct regulators_table[] = { .pull_down = BUCK1_PULL_DOWN_SHIFT, .mask_reset_reg = MASK_RESET_BUCK_REG, .mask_reset = BUCK1_MASK_RESET, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = BUCK1_ICC_SHIFT, }, { .dt_node_name = "buck2", @@ -450,6 +462,8 @@ static const struct regul_struct regulators_table[] = { .pull_down = BUCK2_PULL_DOWN_SHIFT, .mask_reset_reg = MASK_RESET_BUCK_REG, .mask_reset = BUCK2_MASK_RESET, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = BUCK2_ICC_SHIFT, }, { .dt_node_name = "buck3", @@ -462,6 +476,8 @@ static const struct regul_struct regulators_table[] = { .pull_down = BUCK3_PULL_DOWN_SHIFT, .mask_reset_reg = MASK_RESET_BUCK_REG, .mask_reset = BUCK3_MASK_RESET, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = BUCK3_ICC_SHIFT, }, { .dt_node_name = "buck4", @@ -474,6 +490,8 @@ static const struct regul_struct regulators_table[] = { .pull_down = BUCK4_PULL_DOWN_SHIFT, .mask_reset_reg = MASK_RESET_BUCK_REG, .mask_reset = BUCK4_MASK_RESET, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = BUCK4_ICC_SHIFT, }, { .dt_node_name = "ldo1", @@ -484,6 +502,8 @@ static const struct regul_struct regulators_table[] = { .low_power_reg = LDO1_PWRCTRL_REG, .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = LDO1_MASK_RESET, + .icc_reg = LDO_ICC_TURNOFF_REG, + .icc_mask = LDO1_ICC_SHIFT, }, { .dt_node_name = "ldo2", @@ -494,6 +514,8 @@ static const struct regul_struct regulators_table[] = { .low_power_reg = LDO2_PWRCTRL_REG, .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = LDO2_MASK_RESET, + .icc_reg = LDO_ICC_TURNOFF_REG, + .icc_mask = LDO2_ICC_SHIFT, }, { .dt_node_name = "ldo3", @@ -504,6 +526,8 @@ static const struct regul_struct regulators_table[] = { .low_power_reg = LDO3_PWRCTRL_REG, .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = LDO3_MASK_RESET, + .icc_reg = LDO_ICC_TURNOFF_REG, + .icc_mask = LDO3_ICC_SHIFT, }, { .dt_node_name = "ldo4", @@ -514,6 +538,8 @@ static const struct regul_struct regulators_table[] = { .low_power_reg = LDO4_PWRCTRL_REG, .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = LDO4_MASK_RESET, + .icc_reg = LDO_ICC_TURNOFF_REG, + .icc_mask = LDO4_ICC_SHIFT, }, { .dt_node_name = "ldo5", @@ -524,6 +550,8 @@ static const struct regul_struct regulators_table[] = { .low_power_reg = LDO5_PWRCTRL_REG, .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = LDO5_MASK_RESET, + .icc_reg = LDO_ICC_TURNOFF_REG, + .icc_mask = LDO5_ICC_SHIFT, }, { .dt_node_name = "ldo6", @@ -534,6 +562,8 @@ static const struct regul_struct regulators_table[] = { .low_power_reg = LDO6_PWRCTRL_REG, .mask_reset_reg = MASK_RESET_LDO_REG, .mask_reset = LDO6_MASK_RESET, + .icc_reg = LDO_ICC_TURNOFF_REG, + .icc_mask = LDO6_ICC_SHIFT, }, { .dt_node_name = "vref_ddr", @@ -551,6 +581,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), .control_reg = USB_CONTROL_REG, .enable_mask = BOOST_ENABLED, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = BOOST_ICC_SHIFT, }, { .dt_node_name = "pwr_sw1", @@ -558,6 +590,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), .control_reg = USB_CONTROL_REG, .enable_mask = USBSW_OTG_SWITCH_ENABLED, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = PWR_SW1_ICC_SHIFT, }, { .dt_node_name = "pwr_sw2", @@ -565,6 +599,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table), .control_reg = USB_CONTROL_REG, .enable_mask = SWIN_SWOUT_ENABLED, + .icc_reg = BUCK_ICC_TURNOFF_REG, + .icc_mask = PWR_SW2_ICC_SHIFT, }, }; @@ -649,11 +685,21 @@ int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts) const struct regul_struct *regul = get_regulator_data(name); uint8_t mask; + if ((strncmp(name, "ldo3", 5) == 0) && ldo3_special_mode) { + /* + * when the LDO3 is in special mode, we do not change voltage, + * because by setting voltage, the LDO would leaves sink-source + * mode. There is obviously no reason to leave sink-source mode + * at runtime. + */ + return 0; + } + /* Voltage can be set for buck or ldo (except ldo4) regulators */ if (strncmp(name, "buck", 4) == 0) { mask = BUCK_VOLTAGE_MASK; } else if ((strncmp(name, "ldo", 3) == 0) && - (strncmp(name, "ldo4", 4) != 0)) { + (strncmp(name, "ldo4", 5) != 0)) { mask = LDO_VOLTAGE_MASK; } else { return 0; @@ -682,12 +728,90 @@ int stpmic1_regulator_mask_reset_set(const char *name) { const struct regul_struct *regul = get_regulator_data(name); + if (regul->mask_reset_reg == 0U) { + return -EPERM; + } + return stpmic1_register_update(regul->mask_reset_reg, BIT(regul->mask_reset), LDO_BUCK_RESET_MASK << regul->mask_reset); } +int stpmic1_regulator_icc_set(const char *name) +{ + const struct regul_struct *regul = get_regulator_data(name); + + if (regul->mask_reset_reg == 0U) { + return -EPERM; + } + + return stpmic1_register_update(regul->icc_reg, + BIT(regul->icc_mask), + BIT(regul->icc_mask)); +} + +int stpmic1_regulator_sink_mode_set(const char *name) +{ + if (strncmp(name, "ldo3", 5) != 0) { + return -EPERM; + } + + ldo3_special_mode = true; + + /* disable bypass mode, enable sink mode */ + return stpmic1_register_update(LDO3_CONTROL_REG, + LDO3_DDR_SEL << LDO_BUCK_VOLTAGE_SHIFT, + LDO3_BYPASS | LDO_VOLTAGE_MASK); +} + +int stpmic1_regulator_bypass_mode_set(const char *name) +{ + if (strncmp(name, "ldo3", 5) != 0) { + return -EPERM; + } + + ldo3_special_mode = true; + + /* enable bypass mode, disable sink mode */ + return stpmic1_register_update(LDO3_CONTROL_REG, + LDO3_BYPASS, + LDO3_BYPASS | LDO_VOLTAGE_MASK); +} + +int stpmic1_active_discharge_mode_set(const char *name) +{ + if (strncmp(name, "pwr_sw1", 8) == 0) { + return stpmic1_register_update(USB_CONTROL_REG, + VBUS_OTG_DISCHARGE, + VBUS_OTG_DISCHARGE); + } + + if (strncmp(name, "pwr_sw2", 8) == 0) { + return stpmic1_register_update(USB_CONTROL_REG, + SW_OUT_DISCHARGE, + SW_OUT_DISCHARGE); + } + + return -EPERM; +} + +int stpmic1_regulator_levels_mv(const char *name, const uint16_t **levels, + size_t *levels_count) +{ + const struct regul_struct *regul = get_regulator_data(name); + + if ((strncmp(name, "ldo3", 5) == 0) && ldo3_special_mode) { + *levels_count = ARRAY_SIZE(ldo3_special_mode_table); + *levels = ldo3_special_mode_table; + } else { + *levels_count = regul->voltage_table_size; + *levels = regul->voltage_table; + } + + return 0; +} + int stpmic1_regulator_voltage_get(const char *name) { const struct regul_struct *regul = get_regulator_data(name); @@ -695,11 +819,15 @@ int stpmic1_regulator_voltage_get(const char *name) uint8_t mask; int status; + if ((strncmp(name, "ldo3", 5) == 0) && ldo3_special_mode) { + return 0; + } + /* Voltage can be set for buck or ldo (except ldo4) regulators */ if (strncmp(name, "buck", 4) == 0) { mask = BUCK_VOLTAGE_MASK; } else if ((strncmp(name, "ldo", 3) == 0) && - (strncmp(name, "ldo4", 4) != 0)) { + (strncmp(name, "ldo4", 5) != 0)) { mask = LDO_VOLTAGE_MASK; } else { return 0; diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h index a69e5b561..2dfc7f830 100644 --- a/include/drivers/st/stpmic1.h +++ b/include/drivers/st/stpmic1.h @@ -103,6 +103,22 @@ #define BUCK4_PULL_DOWN_SHIFT 6 #define VREF_DDR_PULL_DOWN_SHIFT 4 +/* ICC register */ +#define BUCK1_ICC_SHIFT 0 +#define BUCK2_ICC_SHIFT 1 +#define BUCK3_ICC_SHIFT 2 +#define BUCK4_ICC_SHIFT 3 +#define PWR_SW1_ICC_SHIFT 4 +#define PWR_SW2_ICC_SHIFT 5 +#define BOOST_ICC_SHIFT 6 + +#define LDO1_ICC_SHIFT 0 +#define LDO2_ICC_SHIFT 1 +#define LDO3_ICC_SHIFT 2 +#define LDO4_ICC_SHIFT 3 +#define LDO5_ICC_SHIFT 4 +#define LDO6_ICC_SHIFT 5 + /* Buck Mask reset register */ #define BUCK1_MASK_RESET 0 #define BUCK2_MASK_RESET 1 @@ -118,6 +134,10 @@ #define LDO6_MASK_RESET 5 #define VREF_DDR_MASK_RESET 6 +/* LDO3 Special modes */ +#define LDO3_BYPASS BIT(7) +#define LDO3_DDR_SEL 31U + /* Main PMIC Control Register (MAIN_CONTROL_REG) */ #define ICC_EVENT_ENABLED BIT(4) #define PWRCTRL_POLARITY_HIGH BIT(3) @@ -145,6 +165,8 @@ /* USB Control Register */ #define BOOST_OVP_DISABLED BIT(7) #define VBUS_OTG_DETECTION_DISABLED BIT(6) +#define SW_OUT_DISCHARGE BIT(5) +#define VBUS_OTG_DISCHARGE BIT(4) #define OCP_LIMIT_HIGH BIT(3) #define SWIN_SWOUT_ENABLED BIT(2) #define USBSW_OTG_SWITCH_ENABLED BIT(1) @@ -159,9 +181,15 @@ int stpmic1_regulator_enable(const char *name); int stpmic1_regulator_disable(const char *name); bool stpmic1_is_regulator_enabled(const char *name); int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts); +int stpmic1_regulator_levels_mv(const char *name, const uint16_t **levels, + size_t *levels_count); int stpmic1_regulator_voltage_get(const char *name); int stpmic1_regulator_pull_down_set(const char *name); int stpmic1_regulator_mask_reset_set(const char *name); +int stpmic1_regulator_icc_set(const char *name); +int stpmic1_regulator_sink_mode_set(const char *name); +int stpmic1_regulator_bypass_mode_set(const char *name); +int stpmic1_active_discharge_mode_set(const char *name); void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr); int stpmic1_get_version(unsigned long *version); From d5b4a2c4e7fd0bcb9f08584b242e69a2e591fb71 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Tue, 15 Dec 2020 18:26:39 +0100 Subject: [PATCH 06/15] feat(regulator): add a regulator framework Add a regulator framework to: - provide an interface to consumers and drivers, - connect consumers with drivers, - handle most of devicetree-parsing, - handle always-on and boot-on regulators, - handle min/max voltages, Change-Id: I23c939fdef2c71a416c44c9de332f70db0d2aa53 Signed-off-by: Pascal Paillet --- drivers/st/regulator/regulator_core.c | 536 ++++++++++++++++++++++++++ include/drivers/st/regulator.h | 108 ++++++ 2 files changed, 644 insertions(+) create mode 100644 drivers/st/regulator/regulator_core.c create mode 100644 include/drivers/st/regulator.h diff --git a/drivers/st/regulator/regulator_core.c b/drivers/st/regulator/regulator_core.c new file mode 100644 index 000000000..94b3ceff7 --- /dev/null +++ b/drivers/st/regulator/regulator_core.c @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2021, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PROPERTY_LEN 64 + +static struct rdev rdev_array[PLAT_NB_RDEVS]; + +#define for_each_rdev(rdev) \ + for (rdev = rdev_array; rdev < (rdev_array + PLAT_NB_RDEVS); rdev++) + +#define for_each_registered_rdev(rdev) \ + for (rdev = rdev_array; \ + (rdev < (rdev_array + PLAT_NB_RDEVS)) && (rdev->desc != NULL); rdev++) + +static void lock_driver(const struct rdev *rdev) +{ + if (rdev->desc->ops->lock != NULL) { + rdev->desc->ops->lock(rdev->desc); + } +} + +static void unlock_driver(const struct rdev *rdev) +{ + if (rdev->desc->ops->unlock != NULL) { + rdev->desc->ops->unlock(rdev->desc); + } +} + +static struct rdev *regulator_get_by_phandle(int32_t phandle) +{ + struct rdev *rdev; + + for_each_registered_rdev(rdev) { + if (rdev->phandle == phandle) { + return rdev; + } + } + + WARN("%s: phandle %d not found\n", __func__, phandle); + return NULL; +} + +/* + * Get a regulator from its node name + * + * @fdt - pointer to device tree memory + * @node_name - name of the node "ldo1" + * Return pointer to rdev if succeed, NULL else. + */ +struct rdev *regulator_get_by_name(const char *node_name) +{ + struct rdev *rdev; + + assert(node_name != NULL); + VERBOSE("get %s\n", node_name); + + for_each_registered_rdev(rdev) { + if (strcmp(rdev->desc->node_name, node_name) == 0) { + return rdev; + } + } + + WARN("%s: %s not found\n", __func__, node_name); + return NULL; +} + +static int32_t get_supply_phandle(const void *fdt, int node, const char *name) +{ + const fdt32_t *cuint; + int len __unused; + int supply_phandle = -FDT_ERR_NOTFOUND; + char prop_name[MAX_PROPERTY_LEN]; + + len = snprintf(prop_name, MAX_PROPERTY_LEN - 1, "%s-supply", name); + assert((len >= 0) && (len < MAX_PROPERTY_LEN - 1)); + + cuint = fdt_getprop(fdt, node, prop_name, NULL); + if (cuint != NULL) { + supply_phandle = fdt32_to_cpu(*cuint); + VERBOSE("%s: supplied by %d\n", name, supply_phandle); + } + + return supply_phandle; +} + +/* + * Get a regulator from a supply name + * + * @fdt - pointer to device tree memory + * @node - offset of the node that contains the supply description + * @name - name of the supply "vdd" for "vdd-supply' + * Return pointer to rdev if succeed, NULL else. + */ +struct rdev *regulator_get_by_supply_name(const void *fdt, int node, const char *name) +{ + const int p = get_supply_phandle(fdt, node, name); + + if (p < 0) { + return NULL; + } + + return regulator_get_by_phandle(p); +} + +static int __regulator_set_state(struct rdev *rdev, bool state) +{ + if (rdev->desc->ops->set_state == NULL) { + return -ENODEV; + } + + return rdev->desc->ops->set_state(rdev->desc, state); +} + +/* + * Enable regulator + * + * @rdev - pointer to rdev struct + * Return 0 if succeed, non 0 else. + */ +int regulator_enable(struct rdev *rdev) +{ + int ret; + + assert(rdev != NULL); + + ret = __regulator_set_state(rdev, STATE_ENABLE); + + udelay(rdev->enable_ramp_delay); + + return ret; +} + +/* + * Disable regulator + * + * @rdev - pointer to rdev struct + * Return 0 if succeed, non 0 else. + */ +int regulator_disable(struct rdev *rdev) +{ + int ret; + + assert(rdev != NULL); + + ret = __regulator_set_state(rdev, STATE_DISABLE); + + udelay(rdev->enable_ramp_delay); + + return ret; +} + +/* + * Regulator enabled query + * + * @rdev - pointer to rdev struct + * Return 0 if disabled, 1 if enabled, <0 else. + */ +int regulator_is_enabled(const struct rdev *rdev) +{ + int ret; + + assert(rdev != NULL); + + VERBOSE("%s: is en\n", rdev->desc->node_name); + + if (rdev->desc->ops->get_state == NULL) { + return -ENODEV; + } + + lock_driver(rdev); + + ret = rdev->desc->ops->get_state(rdev->desc); + if (ret < 0) { + ERROR("regul %s get state failed: err:%d\n", + rdev->desc->node_name, ret); + } + + unlock_driver(rdev); + + return ret; +} + +/* + * Set regulator voltage + * + * @rdev - pointer to rdev struct + * @mvolt - Target voltage level in millivolt + * Return 0 if succeed, non 0 else. + */ +int regulator_set_voltage(struct rdev *rdev, uint16_t mvolt) +{ + int ret; + + assert(rdev != NULL); + + VERBOSE("%s: set mvolt\n", rdev->desc->node_name); + + if (rdev->desc->ops->set_voltage == NULL) { + return -ENODEV; + } + + if ((mvolt < rdev->min_mv) || (mvolt > rdev->max_mv)) { + return -EPERM; + } + + lock_driver(rdev); + + ret = rdev->desc->ops->set_voltage(rdev->desc, mvolt); + if (ret < 0) { + ERROR("regul %s set volt failed: err:%d\n", + rdev->desc->node_name, ret); + } + + unlock_driver(rdev); + + return ret; +} + +/* + * Set regulator min voltage + * + * @rdev - pointer to rdev struct + * Return 0 if succeed, non 0 else. + */ +int regulator_set_min_voltage(struct rdev *rdev) +{ + return regulator_set_voltage(rdev, rdev->min_mv); +} + +/* + * Get regulator voltage + * + * @rdev - pointer to rdev struct + * Return milli volts if succeed, <0 else. + */ +int regulator_get_voltage(const struct rdev *rdev) +{ + int ret; + + assert(rdev != NULL); + + VERBOSE("%s: get volt\n", rdev->desc->node_name); + + if (rdev->desc->ops->get_voltage == NULL) { + return rdev->min_mv; + } + + lock_driver(rdev); + + ret = rdev->desc->ops->get_voltage(rdev->desc); + if (ret < 0) { + ERROR("regul %s get voltage failed: err:%d\n", + rdev->desc->node_name, ret); + } + + unlock_driver(rdev); + + return ret; +} + +/* + * List regulator voltages + * + * @rdev - pointer to rdev struct + * @levels - out: array of supported millitvolt levels from min to max value + * @count - out: number of possible millivolt values + * Return 0 if succeed, non 0 else. + */ +int regulator_list_voltages(const struct rdev *rdev, const uint16_t **levels, size_t *count) +{ + int ret; + size_t n; + + assert(rdev != NULL); + assert(levels != NULL); + assert(count != NULL); + + VERBOSE("%s: list volt\n", rdev->desc->node_name); + + if (rdev->desc->ops->list_voltages == NULL) { + return -ENODEV; + } + + lock_driver(rdev); + + ret = rdev->desc->ops->list_voltages(rdev->desc, levels, count); + + unlock_driver(rdev); + + if (ret < 0) { + ERROR("regul %s list_voltages failed: err: %d\n", + rdev->desc->node_name, ret); + return ret; + } + + /* + * Reduce the possible values depending on min and max from device-tree + */ + n = *count; + while ((n > 1U) && ((*levels)[n - 1U] > rdev->max_mv)) { + n--; + } + + /* Verify that max val is a valid value */ + if (rdev->max_mv != (*levels)[n - 1]) { + ERROR("regul %s: max value %u is invalid\n", + rdev->desc->node_name, rdev->max_mv); + return -EINVAL; + } + + while ((n > 1U) && ((*levels[0U]) < rdev->min_mv)) { + (*levels)++; + n--; + } + + /* Verify that min is not too high */ + if (n == 0U) { + ERROR("regul %s set min voltage is too high\n", + rdev->desc->node_name); + return -EINVAL; + } + + /* Verify that min val is a valid vlue */ + if (rdev->min_mv != (*levels)[0U]) { + ERROR("regul %s: min value %u is invalid\n", + rdev->desc->node_name, rdev->min_mv); + return -EINVAL; + } + + *count = n; + + VERBOSE("rdev->min_mv=%u rdev->max_mv=%u\n", rdev->min_mv, rdev->max_mv); + + return 0; +} + +/* + * Get regulator voltages range + * + * @rdev - pointer to rdev struct + * @min_mv - out: min possible millivolt value + * @max_mv - out: max possible millivolt value + * Return 0 if succeed, non 0 else. + */ +void regulator_get_range(const struct rdev *rdev, uint16_t *min_mv, uint16_t *max_mv) +{ + assert(rdev != NULL); + + if (min_mv != NULL) { + *min_mv = rdev->min_mv; + } + if (max_mv != NULL) { + *max_mv = rdev->max_mv; + } +} + +/* + * Set regulator flag + * + * @rdev - pointer to rdev struct + * @flag - flag value to set (eg: REGUL_OCP) + * Return 0 if succeed, non 0 else. + */ +int regulator_set_flag(struct rdev *rdev, uint16_t flag) +{ + int ret; + + /* check that only one bit is set on flag */ + if (__builtin_popcount(flag) != 1) { + return -EINVAL; + } + + /* REGUL_ALWAYS_ON and REGUL_BOOT_ON are internal properties of the core */ + if ((flag == REGUL_ALWAYS_ON) || (flag == REGUL_BOOT_ON)) { + rdev->flags |= flag; + return 0; + } + + if (rdev->desc->ops->set_flag == NULL) { + ERROR("%s can not set any flag\n", rdev->desc->node_name); + return -ENODEV; + } + + lock_driver(rdev); + + ret = rdev->desc->ops->set_flag(rdev->desc, flag); + + unlock_driver(rdev); + + if (ret != 0) { + ERROR("%s: could not set flag %d ret=%d\n", + rdev->desc->node_name, flag, ret); + return ret; + } + + rdev->flags |= flag; + + return 0; +} + +/* + * Parse the device-tree for a regulator + * + * Read min/max voltage from dt and check its validity + * Read the properties, and call the driver to set flags + * Read power supply phandle + * Read and store low power mode states + * + * @rdev - pointer to rdev struct + * @node - device-tree node offset of the regulator + * Return 0 if disabled, 1 if enabled, <0 else. + */ +static int parse_dt(struct rdev *rdev, int node) +{ + void *fdt; + const fdt32_t *cuint; + const uint16_t *levels; + size_t size; + int ret; + + VERBOSE("%s: parse dt\n", rdev->desc->node_name); + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + rdev->phandle = fdt_get_phandle(fdt, node); + + cuint = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); + if (cuint != NULL) { + uint16_t min_mv; + + min_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + VERBOSE("%s: min_mv=%d\n", rdev->desc->node_name, (int)min_mv); + if (min_mv <= rdev->max_mv) { + rdev->min_mv = min_mv; + } else { + ERROR("%s: min_mv=%d is too high\n", + rdev->desc->node_name, (int)min_mv); + return -EINVAL; + } + } + + cuint = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); + if (cuint != NULL) { + uint16_t max_mv; + + max_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + VERBOSE("%s: max_mv=%d\n", rdev->desc->node_name, (int)max_mv); + if (max_mv >= rdev->min_mv) { + rdev->max_mv = max_mv; + } else { + ERROR("%s: max_mv=%d is too low\n", + rdev->desc->node_name, (int)max_mv); + return -EINVAL; + } + } + + /* validate that min and max values can be used */ + ret = regulator_list_voltages(rdev, &levels, &size); + if ((ret != 0) && (ret != -ENODEV)) { + return ret; + } + + return 0; +} + +/* + * Register a regulator driver in regulator framework. + * Initialize voltage range from driver description + * + * @desc - pointer to the regulator description + * @node - device-tree node offset of the regulator + * Return 0 if succeed, non 0 else. + */ +int regulator_register(const struct regul_description *desc, int node) +{ + struct rdev *rdev; + + assert(desc != NULL); + + VERBOSE("register %s\n", desc->node_name); + + for_each_rdev(rdev) { + if (rdev->desc == NULL) { + break; + } + } + + if (rdev == rdev_array + PLAT_NB_RDEVS) { + WARN("Not enough place for regulators, PLAT_NB_RDEVS should be increased.\n"); + return -ENOMEM; + } + + rdev->desc = desc; + rdev->enable_ramp_delay = rdev->desc->enable_ramp_delay; + + if (rdev->desc->ops->list_voltages != NULL) { + int ret; + const uint16_t *levels; + size_t count; + + lock_driver(rdev); + + ret = rdev->desc->ops->list_voltages(rdev->desc, &levels, &count); + + unlock_driver(rdev); + + if (ret < 0) { + ERROR("regul %s set state failed: err:%d\n", + rdev->desc->node_name, ret); + return ret; + } + + rdev->min_mv = levels[0]; + rdev->max_mv = levels[count - 1U]; + } else { + rdev->max_mv = UINT16_MAX; + } + + return parse_dt(rdev, node); +} diff --git a/include/drivers/st/regulator.h b/include/drivers/st/regulator.h new file mode 100644 index 000000000..bf583e224 --- /dev/null +++ b/include/drivers/st/regulator.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef REGULATOR_H +#define REGULATOR_H + +#include + +#ifndef PLAT_NB_RDEVS +#error "Missing PLAT_NB_RDEVS" +#endif + +/* + * Consumer interface + */ + +/* regulator-always-on : regulator should never be disabled */ +#define REGUL_ALWAYS_ON BIT(0) +/* + * regulator-boot-on: + * It's expected that this regulator was left on by the bootloader. + * The core shouldn't prevent it from being turned off later. + * The regulator is needed to exit from suspend so it is turned on during suspend entry. + */ +#define REGUL_BOOT_ON BIT(1) +/* regulator-over-current-protection: Enable over current protection. */ +#define REGUL_OCP BIT(2) +/* regulator-active-discharge: enable active discharge. */ +#define REGUL_ACTIVE_DISCHARGE BIT(3) +/* regulator-pull-down: Enable pull down resistor when the regulator is disabled. */ +#define REGUL_PULL_DOWN BIT(4) +/* + * st,mask-reset: set mask reset for the regulator, meaning that the regulator + * setting is maintained during pmic reset. + */ +#define REGUL_MASK_RESET BIT(5) +/* st,regulator-sink-source: set the regulator in sink source mode */ +#define REGUL_SINK_SOURCE BIT(6) +/* st,regulator-bypass: set the regulator in bypass mode */ +#define REGUL_ENABLE_BYPASS BIT(7) + +struct rdev *regulator_get_by_name(const char *node_name); + +struct rdev *regulator_get_by_supply_name(const void *fdt, int node, const char *name); + +int regulator_enable(struct rdev *rdev); +int regulator_disable(struct rdev *rdev); +int regulator_is_enabled(const struct rdev *rdev); + +int regulator_set_voltage(struct rdev *rdev, uint16_t volt); +int regulator_set_min_voltage(struct rdev *rdev); +int regulator_get_voltage(const struct rdev *rdev); + +int regulator_list_voltages(const struct rdev *rdev, const uint16_t **levels, size_t *count); +void regulator_get_range(const struct rdev *rdev, uint16_t *min_mv, uint16_t *max_mv); +int regulator_set_flag(struct rdev *rdev, uint16_t flag); + +/* + * Driver Interface + */ + +/* set_state() arguments */ +#define STATE_DISABLE false +#define STATE_ENABLE true + +struct regul_description { + const char *node_name; + const struct regul_ops *ops; + const void *driver_data; + const char *supply_name; + const uint32_t enable_ramp_delay; +}; + +struct regul_ops { + int (*set_state)(const struct regul_description *desc, bool state); + int (*get_state)(const struct regul_description *desc); + int (*set_voltage)(const struct regul_description *desc, uint16_t mv); + int (*get_voltage)(const struct regul_description *desc); + int (*list_voltages)(const struct regul_description *desc, + const uint16_t **levels, size_t *count); + int (*set_flag)(const struct regul_description *desc, uint16_t flag); + void (*lock)(const struct regul_description *desc); + void (*unlock)(const struct regul_description *desc); +}; + +int regulator_register(const struct regul_description *desc, int node); + +/* + * Internal regulator structure + * The structure is internal to the core, and the content should not be used + * by a consumer nor a driver. + */ +struct rdev { + const struct regul_description *desc; + + int32_t phandle; + + uint16_t min_mv; + uint16_t max_mv; + + uint16_t flags; + + uint32_t enable_ramp_delay; +}; + +#endif /* REGULATOR_H */ From bba9fdee589fb9a7aca5963f53b7ce67c30520b3 Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Wed, 15 Dec 2021 13:16:15 +0100 Subject: [PATCH 07/15] feat(stm32mp1): add regulator framework compilation Add required macro PLAT_NB_RDEVS in platform code, and update platform.mk to compile regulator framework. Change-Id: I9dc7a0a4c4f5a23d9bedda368d407612c9cd21cd Signed-off-by: Pascal Paillet Signed-off-by: Yann Gautier --- plat/st/stm32mp1/platform.mk | 1 + plat/st/stm32mp1/stm32mp1_def.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 3a76d2863..d6fe13105 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -200,6 +200,7 @@ PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ drivers/st/iwdg/stm32_iwdg.c \ drivers/st/pmic/stm32mp_pmic.c \ drivers/st/pmic/stpmic1.c \ + drivers/st/regulator/regulator_core.c \ drivers/st/reset/stm32mp1_reset.c \ plat/st/common/stm32mp_dt.c \ plat/st/stm32mp1/stm32mp1_dbgmcu.c \ diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index f5d4b2f38..ee1644cb0 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -460,6 +460,12 @@ static inline uint32_t tamp_bkpr(uint32_t idx) #define STGEN_BASE U(0x5c008000) #define SYSCFG_BASE U(0x50020000) +/******************************************************************************* + * REGULATORS + ******************************************************************************/ +/* 3 PWR + 1 VREFBUF + 14 PMIC regulators + 1 FIXED */ +#define PLAT_NB_RDEVS U(19) + /******************************************************************************* * Device Tree defines ******************************************************************************/ From ae7792e0583f83adc06eb3b14693539e95110490 Mon Sep 17 00:00:00 2001 From: Nicolas Le Bayon Date: Mon, 18 Nov 2019 13:13:36 +0100 Subject: [PATCH 08/15] refactor(st-pmic): split initialize_pmic() print_pmic_info_and_debug() prints the PMIC version ID and displays regulator information if debug is enabled. It is under DEBUG flag and called after initialize_pmic() in BL2. Change-Id: Ib81a625740b7ec6abb49cfca05e44c69efaa4718 Signed-off-by: Nicolas Le Bayon --- drivers/st/pmic/stm32mp_pmic.c | 19 +++++++++++-------- include/drivers/st/stm32mp_pmic.h | 10 +++++++++- plat/st/stm32mp1/bl2_plat_setup.c | 1 + 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index 03d719009..4c0ecdbb6 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -270,8 +270,6 @@ static void register_pmic_shared_peripherals(void) void initialize_pmic(void) { - unsigned long pmic_version; - if (!initialize_pmic_i2c()) { VERBOSE("No PMIC\n"); return; @@ -279,6 +277,16 @@ void initialize_pmic(void) register_pmic_shared_peripherals(); + if (dt_pmic_configure_boot_on_regulators() < 0) { + panic(); + }; +} + +#if DEBUG +void print_pmic_info_and_debug(void) +{ + unsigned long pmic_version; + if (stpmic1_get_version(&pmic_version) != 0) { ERROR("Failed to access PMIC\n"); panic(); @@ -286,13 +294,8 @@ void initialize_pmic(void) INFO("PMIC version = 0x%02lx\n", pmic_version); stpmic1_dump_regulators(); - -#if defined(IMAGE_BL2) - if (dt_pmic_configure_boot_on_regulators() != 0) { - panic(); - }; -#endif } +#endif int pmic_ddr_power_init(enum ddr_type ddr_type) { diff --git a/include/drivers/st/stm32mp_pmic.h b/include/drivers/st/stm32mp_pmic.h index 984cd6014..4872734c3 100644 --- a/include/drivers/st/stm32mp_pmic.h +++ b/include/drivers/st/stm32mp_pmic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -41,6 +41,14 @@ bool initialize_pmic_i2c(void); */ void initialize_pmic(void); +#if DEBUG +void print_pmic_info_and_debug(void); +#else +static inline void print_pmic_info_and_debug(void) +{ +} +#endif + /* * pmic_ddr_power_init - Initialize regulators required for DDR * diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index 2b878332a..157efecbc 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -273,6 +273,7 @@ void bl2_el3_plat_arch_setup(void) skip_console_init: if (dt_pmic_status() > 0) { initialize_pmic(); + print_pmic_info_and_debug(); } stm32mp1_syscfg_init(); From 85fb175b5ef854bc4607db98a4cfb5f35d822cee Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Mon, 27 Sep 2021 14:31:40 +0200 Subject: [PATCH 09/15] feat(st-pmic): register the PMIC to regulator framework Register the PMIC to the regulator framework. Change-Id: Ic825a8ef08505316db3dbd5944d62ea907f73c4a Signed-off-by: Pascal Paillet Signed-off-by: Yann Gautier --- drivers/st/pmic/stm32mp_pmic.c | 260 ++++++++++++++++++++---------- include/drivers/st/stm32mp_pmic.h | 8 - 2 files changed, 177 insertions(+), 91 deletions(-) diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index 4c0ecdbb6..c25a2427c 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -4,10 +4,12 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include #include +#include #include #include #include @@ -32,6 +34,8 @@ static struct i2c_handle_s i2c_handle; static uint32_t pmic_i2c_addr; +static int register_pmic(void); + static int dt_get_pmic_node(void *fdt) { static int node = -FDT_ERR_BADOFFSET; @@ -125,86 +129,6 @@ static int dt_pmic_i2c_config(struct dt_node_info *i2c_info, return stm32_i2c_get_setup_from_fdt(fdt, i2c_node, init); } -int dt_pmic_configure_boot_on_regulators(void) -{ - int pmic_node, regulators_node, regulator_node; - void *fdt; - - if (fdt_get_address(&fdt) == 0) { - return -ENOENT; - } - - pmic_node = dt_get_pmic_node(fdt); - if (pmic_node < 0) { - return -FDT_ERR_NOTFOUND; - } - - regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); - if (regulators_node < 0) { - return -ENOENT; - } - - fdt_for_each_subnode(regulator_node, fdt, regulators_node) { - const fdt32_t *cuint; - const char *node_name = fdt_get_name(fdt, regulator_node, NULL); - uint16_t voltage; - int status; - -#if defined(IMAGE_BL2) - if ((fdt_getprop(fdt, regulator_node, "regulator-boot-on", - NULL) == NULL) && - (fdt_getprop(fdt, regulator_node, "regulator-always-on", - NULL) == NULL)) { -#else - if (fdt_getprop(fdt, regulator_node, "regulator-boot-on", - NULL) == NULL) { -#endif - continue; - } - - if (fdt_getprop(fdt, regulator_node, "regulator-pull-down", - NULL) != NULL) { - - status = stpmic1_regulator_pull_down_set(node_name); - if (status != 0) { - return status; - } - } - - if (fdt_getprop(fdt, regulator_node, "st,mask-reset", - NULL) != NULL) { - - status = stpmic1_regulator_mask_reset_set(node_name); - if (status != 0) { - return status; - } - } - - cuint = fdt_getprop(fdt, regulator_node, - "regulator-min-microvolt", NULL); - if (cuint == NULL) { - continue; - } - - /* DT uses microvolts, whereas driver awaits millivolts */ - voltage = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); - - status = stpmic1_regulator_voltage_set(node_name, voltage); - if (status != 0) { - return status; - } - - if (!stpmic1_is_regulator_enabled(node_name)) { - status = stpmic1_regulator_enable(node_name); - if (status != 0) { - return status; - } - } - } - - return 0; -} - bool initialize_pmic_i2c(void) { int ret; @@ -277,9 +201,14 @@ void initialize_pmic(void) register_pmic_shared_peripherals(); - if (dt_pmic_configure_boot_on_regulators() < 0) { + if (register_pmic() < 0) { panic(); - }; + } + + if (stpmic1_powerctrl_on() < 0) { + panic(); + } + } #if DEBUG @@ -293,7 +222,6 @@ void print_pmic_info_and_debug(void) } INFO("PMIC version = 0x%02lx\n", pmic_version); - stpmic1_dump_regulators(); } #endif @@ -414,3 +342,169 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) return 0; } + +enum { + STPMIC1_BUCK1 = 0, + STPMIC1_BUCK2, + STPMIC1_BUCK3, + STPMIC1_BUCK4, + STPMIC1_LDO1, + STPMIC1_LDO2, + STPMIC1_LDO3, + STPMIC1_LDO4, + STPMIC1_LDO5, + STPMIC1_LDO6, + STPMIC1_VREF_DDR, + STPMIC1_BOOST, + STPMIC1_VBUS_OTG, + STPMIC1_SW_OUT, +}; + +static int pmic_set_state(const struct regul_description *desc, bool enable) +{ + VERBOSE("%s: set state to %u\n", desc->node_name, enable); + + if (enable == STATE_ENABLE) { + return stpmic1_regulator_enable(desc->node_name); + } else { + return stpmic1_regulator_disable(desc->node_name); + } +} + +static int pmic_get_state(const struct regul_description *desc) +{ + VERBOSE("%s: get state\n", desc->node_name); + + return stpmic1_is_regulator_enabled(desc->node_name); +} + +static int pmic_get_voltage(const struct regul_description *desc) +{ + VERBOSE("%s: get volt\n", desc->node_name); + + return stpmic1_regulator_voltage_get(desc->node_name); +} + +static int pmic_set_voltage(const struct regul_description *desc, uint16_t mv) +{ + VERBOSE("%s: get volt\n", desc->node_name); + + return stpmic1_regulator_voltage_set(desc->node_name, mv); +} + +static int pmic_list_voltages(const struct regul_description *desc, + const uint16_t **levels, size_t *count) +{ + VERBOSE("%s: list volt\n", desc->node_name); + + return stpmic1_regulator_levels_mv(desc->node_name, levels, count); +} + +static int pmic_set_flag(const struct regul_description *desc, uint16_t flag) +{ + VERBOSE("%s: set_flag 0x%x\n", desc->node_name, flag); + + switch (flag) { + case REGUL_OCP: + return stpmic1_regulator_icc_set(desc->node_name); + + case REGUL_ACTIVE_DISCHARGE: + return stpmic1_active_discharge_mode_set(desc->node_name); + + case REGUL_PULL_DOWN: + return stpmic1_regulator_pull_down_set(desc->node_name); + + case REGUL_MASK_RESET: + return stpmic1_regulator_mask_reset_set(desc->node_name); + + case REGUL_SINK_SOURCE: + return stpmic1_regulator_sink_mode_set(desc->node_name); + + case REGUL_ENABLE_BYPASS: + return stpmic1_regulator_bypass_mode_set(desc->node_name); + + default: + return -EINVAL; + } +} + +struct regul_ops pmic_ops = { + .set_state = pmic_set_state, + .get_state = pmic_get_state, + .set_voltage = pmic_set_voltage, + .get_voltage = pmic_get_voltage, + .list_voltages = pmic_list_voltages, + .set_flag = pmic_set_flag, +}; + +#define DEFINE_REGU(name) { \ + .node_name = name, \ + .ops = &pmic_ops, \ + .driver_data = NULL, \ + .enable_ramp_delay = 1000, \ +} + +static const struct regul_description pmic_regs[] = { + [STPMIC1_BUCK1] = DEFINE_REGU("buck1"), + [STPMIC1_BUCK2] = DEFINE_REGU("buck2"), + [STPMIC1_BUCK3] = DEFINE_REGU("buck3"), + [STPMIC1_BUCK4] = DEFINE_REGU("buck4"), + [STPMIC1_LDO1] = DEFINE_REGU("ldo1"), + [STPMIC1_LDO2] = DEFINE_REGU("ldo2"), + [STPMIC1_LDO3] = DEFINE_REGU("ldo3"), + [STPMIC1_LDO4] = DEFINE_REGU("ldo4"), + [STPMIC1_LDO5] = DEFINE_REGU("ldo5"), + [STPMIC1_LDO6] = DEFINE_REGU("ldo6"), + [STPMIC1_VREF_DDR] = DEFINE_REGU("vref_ddr"), + [STPMIC1_BOOST] = DEFINE_REGU("boost"), + [STPMIC1_VBUS_OTG] = DEFINE_REGU("pwr_sw1"), + [STPMIC1_SW_OUT] = DEFINE_REGU("pwr_sw2"), +}; + +#define NB_REG ARRAY_SIZE(pmic_regs) + +static int register_pmic(void) +{ + void *fdt; + int pmic_node, regulators_node, subnode; + + VERBOSE("Register pmic\n"); + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + pmic_node = dt_get_pmic_node(fdt); + if (pmic_node < 0) { + return pmic_node; + } + + regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators"); + if (regulators_node < 0) { + return -ENOENT; + } + + fdt_for_each_subnode(subnode, fdt, regulators_node) { + const char *reg_name = fdt_get_name(fdt, subnode, NULL); + const struct regul_description *desc; + unsigned int i; + int ret; + + for (i = 0; i < NB_REG; i++) { + desc = &pmic_regs[i]; + if (strcmp(desc->node_name, reg_name) == 0) { + break; + } + } + assert(i < NB_REG); + + ret = regulator_register(desc, subnode); + if (ret != 0) { + WARN("%s:%d failed to register %s\n", __func__, + __LINE__, reg_name); + return ret; + } + } + + return 0; +} diff --git a/include/drivers/st/stm32mp_pmic.h b/include/drivers/st/stm32mp_pmic.h index 4872734c3..4dfb038d6 100644 --- a/include/drivers/st/stm32mp_pmic.h +++ b/include/drivers/st/stm32mp_pmic.h @@ -19,14 +19,6 @@ */ int dt_pmic_status(void); -/* - * dt_pmic_configure_boot_on_regulators - Configure boot-on and always-on - * regulators from device tree configuration - * - * Returns 0 on success, and negative values on errors - */ -int dt_pmic_configure_boot_on_regulators(void); - /* * initialize_pmic_i2c - Initialize I2C for the PMIC control * From 0ba71ac90149e91db5a3ebb8f82a0ab320b6067a Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Tue, 15 Dec 2020 19:05:09 +0100 Subject: [PATCH 10/15] refactor(st-pmic): use regulator framework for DDR init Use regulator framework for DDR initialization. Change-Id: I9dffe499ca12cdc35904de7daf2dda821b267a31 Signed-off-by: Pascal Paillet Signed-off-by: Yann Gautier --- drivers/st/pmic/stm32mp_pmic.c | 115 +++++++++++++-------------------- 1 file changed, 45 insertions(+), 70 deletions(-) diff --git a/drivers/st/pmic/stm32mp_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index c25a2427c..6a30dce4a 100644 --- a/drivers/st/pmic/stm32mp_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -20,16 +20,6 @@ #include #define PMIC_NODE_NOT_FOUND 1 -#define STPMIC1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2)) -#define STPMIC1_LDO12356_OUTPUT_SHIFT 2 -#define STPMIC1_LDO3_MODE (uint8_t)(BIT(7)) -#define STPMIC1_LDO3_DDR_SEL 31U -#define STPMIC1_LDO3_1800000 (9U << STPMIC1_LDO12356_OUTPUT_SHIFT) - -#define STPMIC1_BUCK_OUTPUT_SHIFT 2 -#define STPMIC1_BUCK3_1V8 (39U << STPMIC1_BUCK_OUTPUT_SHIFT) - -#define STPMIC1_DEFAULT_START_UP_DELAY_MS 1 static struct i2c_handle_s i2c_handle; static uint32_t pmic_i2c_addr; @@ -227,53 +217,51 @@ void print_pmic_info_and_debug(void) int pmic_ddr_power_init(enum ddr_type ddr_type) { - bool buck3_at_1v8 = false; - uint8_t read_val; int status; + uint16_t buck3_min_mv; + struct rdev *buck2, *buck3, *ldo3, *vref; + + buck2 = regulator_get_by_name("buck2"); + if (buck2 == NULL) { + return -ENOENT; + } + + ldo3 = regulator_get_by_name("ldo3"); + if (ldo3 == NULL) { + return -ENOENT; + } + + vref = regulator_get_by_name("vref_ddr"); + if (vref == NULL) { + return -ENOENT; + } switch (ddr_type) { case STM32MP_DDR3: - /* Set LDO3 to sync mode */ - status = stpmic1_register_read(LDO3_CONTROL_REG, &read_val); + status = regulator_set_flag(ldo3, REGUL_SINK_SOURCE); if (status != 0) { return status; } - read_val &= ~STPMIC1_LDO3_MODE; - read_val &= ~STPMIC1_LDO12356_OUTPUT_MASK; - read_val |= STPMIC1_LDO3_DDR_SEL << - STPMIC1_LDO12356_OUTPUT_SHIFT; - - status = stpmic1_register_write(LDO3_CONTROL_REG, read_val); + status = regulator_set_min_voltage(buck2); if (status != 0) { return status; } - status = stpmic1_regulator_voltage_set("buck2", 1350); + status = regulator_enable(buck2); if (status != 0) { return status; } - status = stpmic1_regulator_enable("buck2"); + status = regulator_enable(vref); if (status != 0) { return status; } - mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - - status = stpmic1_regulator_enable("vref_ddr"); + status = regulator_enable(ldo3); if (status != 0) { return status; } - - mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - - status = stpmic1_regulator_enable("ldo3"); - if (status != 0) { - return status; - } - - mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); break; case STM32MP_LPDDR2: @@ -283,57 +271,44 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) * Set LDO3 to bypass mode if BUCK3 = 1.8V * Set LDO3 to normal mode if BUCK3 != 1.8V */ - status = stpmic1_register_read(BUCK3_CONTROL_REG, &read_val); + buck3 = regulator_get_by_name("buck3"); + if (buck3 == NULL) { + return -ENOENT; + } + + regulator_get_range(buck3, &buck3_min_mv, NULL); + + if (buck3_min_mv != 1800) { + status = regulator_set_min_voltage(ldo3); + if (status != 0) { + return status; + } + } else { + status = regulator_set_flag(ldo3, REGUL_ENABLE_BYPASS); + if (status != 0) { + return status; + } + } + + status = regulator_set_min_voltage(buck2); if (status != 0) { return status; } - if ((read_val & STPMIC1_BUCK3_1V8) == STPMIC1_BUCK3_1V8) { - buck3_at_1v8 = true; - } - - status = stpmic1_register_read(LDO3_CONTROL_REG, &read_val); + status = regulator_enable(ldo3); if (status != 0) { return status; } - read_val &= ~STPMIC1_LDO3_MODE; - read_val &= ~STPMIC1_LDO12356_OUTPUT_MASK; - read_val |= STPMIC1_LDO3_1800000; - if (buck3_at_1v8) { - read_val |= STPMIC1_LDO3_MODE; - } - - status = stpmic1_register_write(LDO3_CONTROL_REG, read_val); + status = regulator_enable(buck2); if (status != 0) { return status; } - status = stpmic1_regulator_voltage_set("buck2", 1200); + status = regulator_enable(vref); if (status != 0) { return status; } - - status = stpmic1_regulator_enable("ldo3"); - if (status != 0) { - return status; - } - - mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - - status = stpmic1_regulator_enable("buck2"); - if (status != 0) { - return status; - } - - mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - - status = stpmic1_regulator_enable("vref_ddr"); - if (status != 0) { - return status; - } - - mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); break; default: From 67d95409baaeafed238ce617b7c16a4bf27b5fe2 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Thu, 7 Jan 2021 18:05:46 +0100 Subject: [PATCH 11/15] refactor(stm32mp1-fdts): update regulator description Update regulator description to match with pmic driver updates. vref_ddr does not support over-current protection. vtt_ddr is set to sink source mode. Change-Id: I725f35b091ca8c230994c2b5f81693ebc97bf4aa Signed-off-by: Pascal Paillet Signed-off-by: Yann Gautier --- fdts/stm32mp157c-ed1.dts | 5 +++-- fdts/stm32mp15xx-dkx.dtsi | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fdts/stm32mp157c-ed1.dts b/fdts/stm32mp157c-ed1.dts index 11e0a6111..5c9818fab 100644 --- a/fdts/stm32mp157c-ed1.dts +++ b/fdts/stm32mp157c-ed1.dts @@ -135,14 +135,15 @@ vtt_ddr: ldo3 { regulator-name = "vtt_ddr"; - regulator-min-microvolt = <500000>; - regulator-max-microvolt = <750000>; regulator-always-on; regulator-over-current-protection; + st,regulator-sink-source; }; vdd_usb: ldo4 { regulator-name = "vdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; }; vdd_sd: ldo5 { diff --git a/fdts/stm32mp15xx-dkx.dtsi b/fdts/stm32mp15xx-dkx.dtsi index 9cc5368d8..975d749d4 100644 --- a/fdts/stm32mp15xx-dkx.dtsi +++ b/fdts/stm32mp15xx-dkx.dtsi @@ -131,10 +131,9 @@ vtt_ddr: ldo3 { regulator-name = "vtt_ddr"; - regulator-min-microvolt = <500000>; - regulator-max-microvolt = <750000>; regulator-always-on; regulator-over-current-protection; + st,regulator-sink-source; }; vdd_usb: ldo4 { @@ -160,7 +159,6 @@ vref_ddr: vref_ddr { regulator-name = "vref_ddr"; regulator-always-on; - regulator-over-current-protection; }; bst_out: boost { From c39c658e75d98d10eefcc6a58055cc8937cfce4f Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Fri, 17 Sep 2021 16:08:12 +0200 Subject: [PATCH 12/15] refactor(st): update CPU and VDD voltage get Use regulator framework to get CPU and VDD power supplies. Change-Id: Ice745fb21ff10e71ef811e747165499c2e19253e Signed-off-by: Pascal Paillet Signed-off-by: Yann Gautier --- plat/st/common/include/stm32mp_dt.h | 2 + plat/st/common/stm32mp_dt.c | 60 ++++++++++++++++------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/plat/st/common/include/stm32mp_dt.h b/plat/st/common/include/stm32mp_dt.h index f7201c0d9..a87f94185 100644 --- a/plat/st/common/include/stm32mp_dt.h +++ b/plat/st/common/include/stm32mp_dt.h @@ -37,6 +37,8 @@ int dt_get_stdout_uart_info(struct dt_node_info *info); int dt_match_instance_by_compatible(const char *compatible, uintptr_t address); uint32_t dt_get_ddr_size(void); uint32_t dt_get_pwr_vdd_voltage(void); +struct rdev *dt_get_vdd_regulator(void); +struct rdev *dt_get_cpu_regulator(void); const char *dt_get_board_model(void); int fdt_get_gpio_bank_pin_count(unsigned int bank); diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c index 4dc990896..863a90fb6 100644 --- a/plat/st/common/stm32mp_dt.c +++ b/plat/st/common/stm32mp_dt.c @@ -7,16 +7,15 @@ #include #include -#include - -#include - #include #include +#include #include #include #include +#include +#include #include static void *fdt; @@ -262,37 +261,46 @@ uint32_t dt_get_ddr_size(void) ******************************************************************************/ uint32_t dt_get_pwr_vdd_voltage(void) { - int node, pwr_regulators_node; - const fdt32_t *cuint; + struct rdev *regul = dt_get_vdd_regulator(); + uint16_t min; + + if (regul == NULL) { + return 0; + } + + regulator_get_range(regul, &min, NULL); + + return (uint32_t)min * 1000U; +} + +/******************************************************************************* + * This function retrieves VDD supply regulator from DT. + * Returns an rdev taken from supply node, NULL otherwise. + ******************************************************************************/ +struct rdev *dt_get_vdd_regulator(void) +{ + int node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); - node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT); if (node < 0) { - INFO("%s: Cannot read PWR node in DT\n", __func__); - return 0; + return NULL; } - pwr_regulators_node = fdt_subnode_offset(fdt, node, "pwr-regulators"); - if (pwr_regulators_node < 0) { - INFO("%s: Cannot read pwr-regulators node in DT\n", __func__); - return 0; - } + return regulator_get_by_supply_name(fdt, node, "vdd"); +} - cuint = fdt_getprop(fdt, pwr_regulators_node, "vdd-supply", NULL); - if (cuint == NULL) { - return 0; - } +/******************************************************************************* + * This function retrieves CPU supply regulator from DT. + * Returns an rdev taken from supply node, NULL otherwise. + ******************************************************************************/ +struct rdev *dt_get_cpu_regulator(void) +{ + int node = fdt_path_offset(fdt, "/cpus/cpu@0"); - node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); if (node < 0) { - return 0; + return NULL; } - cuint = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); - if (cuint == NULL) { - return 0; - } - - return fdt32_to_cpu(*cuint); + return regulator_get_by_supply_name(fdt, node, "cpu"); } /******************************************************************************* From 5d6a2646f7759a5a2b3daed0d8aef4588c552ba4 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Wed, 20 Jan 2021 17:09:10 +0100 Subject: [PATCH 13/15] feat(st-drivers): introduce fixed regulator driver Fixed regulator is mainly used when no pmic is available Change-Id: Ib6a998684bcb055ba95a093bee563372d9051474 Signed-off-by: Pascal Paillet --- drivers/st/regulator/regulator_fixed.c | 87 ++++++++++++++++++++++++++ include/drivers/st/regulator_fixed.h | 12 ++++ 2 files changed, 99 insertions(+) create mode 100644 drivers/st/regulator/regulator_fixed.c create mode 100644 include/drivers/st/regulator_fixed.h diff --git a/drivers/st/regulator/regulator_fixed.c b/drivers/st/regulator/regulator_fixed.c new file mode 100644 index 000000000..f1c224e04 --- /dev/null +++ b/drivers/st/regulator/regulator_fixed.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef PLAT_NB_FIXED_REGS +#error "Missing PLAT_NB_FIXED_REGS" +#endif + +#define FIXED_NAME_LEN 32 + +struct fixed_data { + char name[FIXED_NAME_LEN]; + uint16_t volt; + struct regul_description desc; +}; + +static struct fixed_data data[PLAT_NB_FIXED_REGS]; + +static int fixed_set_state(const struct regul_description *desc, bool state) +{ + return 0; +} + +static int fixed_get_state(const struct regul_description *desc) +{ + return 1; +} + +static struct regul_ops fixed_ops = { + .set_state = fixed_set_state, + .get_state = fixed_get_state, +}; + +int fixed_regulator_register(void) +{ + uint32_t count = 0; + void *fdt; + int node; + + VERBOSE("fixed reg init!\n"); + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_compatible_node(fdt, node, "regulator-fixed") { + int len __unused; + int ret; + struct fixed_data *d = &data[count]; + const char *reg_name; + + reg_name = fdt_get_name(fdt, node, NULL); + + VERBOSE("register fixed reg %s!\n", reg_name); + + len = snprintf(d->name, FIXED_NAME_LEN - 1, "%s", reg_name); + assert((len > 0) && (len < (FIXED_NAME_LEN - 1))); + + d->desc.node_name = d->name; + d->desc.driver_data = d; + d->desc.ops = &fixed_ops; + + ret = regulator_register(&d->desc, node); + if (ret != 0) { + WARN("%s:%d failed to register %s\n", __func__, + __LINE__, reg_name); + return ret; + } + + count++; + assert(count <= PLAT_NB_FIXED_REGS); + + } + + return 0; +} diff --git a/include/drivers/st/regulator_fixed.h b/include/drivers/st/regulator_fixed.h new file mode 100644 index 000000000..b981262e9 --- /dev/null +++ b/include/drivers/st/regulator_fixed.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2021, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef REGULATOR_FIXED_H +#define REGULATOR_FIXED_H + +int fixed_regulator_register(void); + +#endif /* REGULATOR_FIXED_H */ From 967a8e63c33822680e3a4631430dcd9a4a64becd Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 29 Jan 2021 14:48:49 +0100 Subject: [PATCH 14/15] feat(stm32mp1): register fixed regulator Register fixed regulator in BL2. Change-Id: I24292f549b2cd24fb717fbb68eb95af7aa68e3b9 Signed-off-by: Pascal Paillet Signed-off-by: Yann Gautier --- plat/st/stm32mp1/bl2_plat_setup.c | 5 +++++ plat/st/stm32mp1/platform.mk | 1 + plat/st/stm32mp1/stm32mp1_def.h | 2 ++ 3 files changed, 8 insertions(+) diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index 157efecbc..bac77bbe4 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -271,6 +272,10 @@ void bl2_el3_plat_arch_setup(void) } skip_console_init: + if (fixed_regulator_register() != 0) { + panic(); + } + if (dt_pmic_status() > 0) { initialize_pmic(); print_pmic_info_and_debug(); diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index d6fe13105..fec4cb1fe 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -201,6 +201,7 @@ PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ drivers/st/pmic/stm32mp_pmic.c \ drivers/st/pmic/stpmic1.c \ drivers/st/regulator/regulator_core.c \ + drivers/st/regulator/regulator_fixed.c \ drivers/st/reset/stm32mp1_reset.c \ plat/st/common/stm32mp_dt.c \ plat/st/stm32mp1/stm32mp1_dbgmcu.c \ diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index ee1644cb0..b43245f64 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -465,6 +465,8 @@ static inline uint32_t tamp_bkpr(uint32_t idx) ******************************************************************************/ /* 3 PWR + 1 VREFBUF + 14 PMIC regulators + 1 FIXED */ #define PLAT_NB_RDEVS U(19) +/* 1 FIXED */ +#define PLAT_NB_FIXED_REGS U(1) /******************************************************************************* * Device Tree defines From 258bef913aa76ead1b10c257d1695d9c0ef1c79d Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Fri, 10 May 2019 16:01:34 +0200 Subject: [PATCH 15/15] feat(st-sdmmc2): manage cards power cycle To correctly initialize the MMC devices, a power cycle is required. For this we need to: - disable vmmc-supply regulator - make the power cycle required for SDMMC2 peripheral - enable regulators Change-Id: I2be6d9082d1cc4c864a82cf2c31ff8522e2d31a2 Signed-off-by: Yann Gautier --- drivers/st/mmc/stm32_sdmmc2.c | 42 ++++++++++++++++++++++++++----- include/drivers/st/stm32_sdmmc2.h | 4 ++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/drivers/st/mmc/stm32_sdmmc2.c b/drivers/st/mmc/stm32_sdmmc2.c index d3adeab13..2102782ad 100644 --- a/drivers/st/mmc/stm32_sdmmc2.c +++ b/drivers/st/mmc/stm32_sdmmc2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,10 +8,6 @@ #include #include -#include - -#include - #include #include #include @@ -22,8 +18,11 @@ #include #include #include +#include #include +#include + /* Registers offsets */ #define SDMMC_POWER 0x00U #define SDMMC_CLKCR 0x04U @@ -50,6 +49,7 @@ /* SDMMC power control register */ #define SDMMC_POWER_PWRCTRL GENMASK(1, 0) +#define SDMMC_POWER_PWRCTRL_PWR_CYCLE BIT(1) #define SDMMC_POWER_DIRPOL BIT(4) /* SDMMC clock control register */ @@ -117,6 +117,13 @@ #define TIMEOUT_US_10_MS 10000U #define TIMEOUT_US_1_S 1000000U +/* Power cycle delays in ms */ +#define VCC_POWER_OFF_DELAY 2 +#define VCC_POWER_ON_DELAY 2 +#define POWER_CYCLE_DELAY 2 +#define POWER_OFF_DELAY 2 +#define POWER_ON_DELAY 1 + #define DT_SDMMC2_COMPAT "st,stm32-sdmmc2" static void stm32_sdmmc2_init(void); @@ -154,6 +161,25 @@ static void stm32_sdmmc2_init(void) freq = MIN(sdmmc2_params.max_freq, freq); } + if (sdmmc2_params.vmmc_regu != NULL) { + regulator_disable(sdmmc2_params.vmmc_regu); + } + + mdelay(VCC_POWER_OFF_DELAY); + + mmio_write_32(base + SDMMC_POWER, + SDMMC_POWER_PWRCTRL_PWR_CYCLE | sdmmc2_params.dirpol); + mdelay(POWER_CYCLE_DELAY); + + if (sdmmc2_params.vmmc_regu != NULL) { + regulator_enable(sdmmc2_params.vmmc_regu); + } + + mdelay(VCC_POWER_ON_DELAY); + + mmio_write_32(base + SDMMC_POWER, sdmmc2_params.dirpol); + mdelay(POWER_OFF_DELAY); + clock_div = div_round_up(sdmmc2_params.clk_rate, freq * 2U); mmio_write_32(base + SDMMC_CLKCR, SDMMC_CLKCR_HWFC_EN | clock_div | @@ -163,7 +189,7 @@ static void stm32_sdmmc2_init(void) mmio_write_32(base + SDMMC_POWER, SDMMC_POWER_PWRCTRL | sdmmc2_params.dirpol); - mdelay(1); + mdelay(POWER_ON_DELAY); } static int stm32_sdmmc2_stop_transfer(void) @@ -689,6 +715,8 @@ static int stm32_sdmmc2_dt_get_config(void) sdmmc2_params.max_freq = fdt32_to_cpu(*cuint); } + sdmmc2_params.vmmc_regu = regulator_get_by_supply_name(fdt, sdmmc_node, "vmmc"); + return 0; } @@ -709,6 +737,8 @@ int stm32_sdmmc2_mmc_init(struct stm32_sdmmc2_params *params) memcpy(&sdmmc2_params, params, sizeof(struct stm32_sdmmc2_params)); + sdmmc2_params.vmmc_regu = NULL; + if (stm32_sdmmc2_dt_get_config() != 0) { ERROR("%s: DT error\n", __func__); return -ENOMEM; diff --git a/include/drivers/st/stm32_sdmmc2.h b/include/drivers/st/stm32_sdmmc2.h index 4853208c2..c83f62509 100644 --- a/include/drivers/st/stm32_sdmmc2.h +++ b/include/drivers/st/stm32_sdmmc2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2021, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,7 @@ #include #include +#include struct stm32_sdmmc2_params { uintptr_t reg_base; @@ -24,6 +25,7 @@ struct stm32_sdmmc2_params { unsigned int reset_id; unsigned int max_freq; bool use_dma; + struct rdev *vmmc_regu; }; unsigned long long stm32_sdmmc2_mmc_get_device_size(void);