Merge changes from topic "st_regulator" into integration

* changes:
  feat(st-sdmmc2): manage cards power cycle
  feat(stm32mp1): register fixed regulator
  feat(st-drivers): introduce fixed regulator driver
  refactor(st): update CPU and VDD voltage get
  refactor(stm32mp1-fdts): update regulator description
  refactor(st-pmic): use regulator framework for DDR init
  feat(st-pmic): register the PMIC to regulator framework
  refactor(st-pmic): split initialize_pmic()
  feat(stm32mp1): add regulator framework compilation
  feat(regulator): add a regulator framework
  feat(stpmic1): add new services
  feat(stpmic1): add USB OTG regulators
  refactor(st-pmic): improve driver usage
  refactor(stpmic1): set stpmic1_is_regulator_enabled() as boolean
  refactor(stm32mp1): re-order drivers init
This commit is contained in:
Madhukar Pappireddy 2021-12-24 00:13:50 +01:00 committed by TrustedFirmware Code Review
commit 93b153b5bf
17 changed files with 1317 additions and 244 deletions

View File

@ -8,10 +8,6 @@
#include <errno.h>
#include <string.h>
#include <libfdt.h>
#include <platform_def.h>
#include <arch.h>
#include <arch_helpers.h>
#include <common/debug.h>
@ -23,8 +19,11 @@
#include <drivers/st/stm32mp_reset.h>
#include <lib/mmio.h>
#include <lib/utils.h>
#include <libfdt.h>
#include <plat/common/platform.h>
#include <platform_def.h>
/* Registers offsets */
#define SDMMC_POWER 0x00U
#define SDMMC_CLKCR 0x04U
@ -51,6 +50,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 */
@ -118,6 +118,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);
@ -155,6 +162,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 |
@ -164,7 +190,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)
@ -690,6 +716,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;
}
@ -710,6 +738,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;

View File

@ -4,54 +4,63 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <libfdt.h>
#include <platform_def.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <drivers/st/regulator.h>
#include <drivers/st/stm32_i2c.h>
#include <drivers/st/stm32mp_pmic.h>
#include <drivers/st/stpmic1.h>
#include <lib/mmio.h>
#include <lib/utils_def.h>
#include <libfdt.h>
#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)
#include <platform_def.h>
#define STPMIC1_BUCK_OUTPUT_SHIFT 2
#define STPMIC1_BUCK3_1V8 (39U << STPMIC1_BUCK_OUTPUT_SHIFT)
#define STPMIC1_DEFAULT_START_UP_DELAY_MS 1
#define PMIC_NODE_NOT_FOUND 1
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)
{
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)
@ -65,37 +74,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);
@ -106,86 +119,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) == 0U) {
status = stpmic1_regulator_enable(node_name);
if (status != 0) {
return status;
}
}
}
return 0;
}
bool initialize_pmic_i2c(void)
{
int ret;
@ -251,8 +184,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;
@ -260,70 +191,77 @@ void initialize_pmic(void)
register_pmic_shared_peripherals();
if (register_pmic() < 0) {
panic();
}
if (stpmic1_powerctrl_on() < 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();
}
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)
{
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:
@ -333,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:
@ -392,3 +317,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;
}

View File

@ -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[] = {
@ -421,6 +431,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[] = {
{
@ -434,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",
@ -446,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",
@ -458,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",
@ -470,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",
@ -480,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",
@ -490,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",
@ -500,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",
@ -510,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",
@ -520,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",
@ -530,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",
@ -541,6 +575,33 @@ 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,
.icc_reg = BUCK_ICC_TURNOFF_REG,
.icc_mask = BOOST_ICC_SHIFT,
},
{
.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,
.icc_reg = BUCK_ICC_TURNOFF_REG,
.icc_mask = PWR_SW1_ICC_SHIFT,
},
{
.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,
.icc_reg = BUCK_ICC_TURNOFF_REG,
.icc_mask = PWR_SW2_ICC_SHIFT,
},
};
#define MAX_REGUL ARRAY_SIZE(regulators_table)
@ -606,7 +667,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 +676,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)
@ -624,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<N> or ldo<N> (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;
@ -657,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);
@ -670,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<N> or ldo<N> (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;

View File

@ -0,0 +1,536 @@
/*
* Copyright (c) 2021, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <drivers/st/regulator.h>
#include <libfdt.h>
#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);
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2021, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <drivers/st/regulator.h>
#include <drivers/st/regulator_fixed.h>
#include <libfdt.h>
#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;
}

View File

@ -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 {

View File

@ -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 {

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2021, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef REGULATOR_H
#define REGULATOR_H
#include <platform_def.h>
#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 */

View File

@ -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 */

View File

@ -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 <stdbool.h>
#include <drivers/mmc.h>
#include <drivers/st/regulator.h>
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);

View File

@ -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
*/
@ -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
*
@ -41,6 +33,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
*

View File

@ -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,9 +165,12 @@
/* 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)
#define BOOST_ENABLED BIT(0)
int stpmic1_powerctrl_on(void);
int stpmic1_switch_off(void);
@ -156,11 +179,17 @@ 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_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);

View File

@ -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);

View File

@ -7,16 +7,15 @@
#include <assert.h>
#include <errno.h>
#include <libfdt.h>
#include <platform_def.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <drivers/st/regulator.h>
#include <drivers/st/stm32_gpio.h>
#include <drivers/st/stm32mp1_ddr.h>
#include <drivers/st/stm32mp1_ram.h>
#include <libfdt.h>
#include <platform_def.h>
#include <stm32mp_dt.h>
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");
}
/*******************************************************************************

View File

@ -15,6 +15,7 @@
#include <drivers/generic_delay_timer.h>
#include <drivers/mmc.h>
#include <drivers/st/bsec.h>
#include <drivers/st/regulator_fixed.h>
#include <drivers/st/stm32_iwdg.h>
#include <drivers/st/stm32_uart.h>
#include <drivers/st/stm32mp1_clk.h>
@ -130,10 +131,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 +244,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 +272,17 @@ 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();
}
stm32mp1_syscfg_init();
if (stm32_iwdg_init() < 0) {
panic();
}

View File

@ -201,6 +201,8 @@ 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/regulator/regulator_fixed.c \
drivers/st/reset/stm32mp1_reset.c \
plat/st/common/stm32mp_dt.c \
plat/st/stm32mp1/stm32mp1_dbgmcu.c \

View File

@ -460,6 +460,14 @@ 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)
/* 1 FIXED */
#define PLAT_NB_FIXED_REGS U(1)
/*******************************************************************************
* Device Tree defines
******************************************************************************/