diff --git a/drivers/st/pmic/stm32mp_pmic2.c b/drivers/st/pmic/stm32mp_pmic2.c new file mode 100644 index 000000000..c19d36a4a --- /dev/null +++ b/drivers/st/pmic/stm32mp_pmic2.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PMIC_NODE_NOT_FOUND 1 + +struct regul_handle_s { + const uint32_t id; + uint16_t bypass_mv; +}; + +static struct pmic_handle_s pmic2_handle; +static struct i2c_handle_s i2c_handle; + +/* This driver is monoinstance */ +static struct pmic_handle_s *pmic2; + +static int dt_get_pmic_node(void *fdt) +{ + static int node = -FDT_ERR_BADOFFSET; + + if (node == -FDT_ERR_BADOFFSET) { + node = fdt_node_offset_by_compatible(fdt, -1, "st,stpmic2"); + } + + 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) { + status = -FDT_ERR_NOTFOUND; + + return status; + } + + status = DT_SECURE; + + return status; +} + +/* + * Get PMIC and its I2C bus configuration from the device tree. + * Return 0 on success, negative on error, 1 if no PMIC node is defined. + */ +static int dt_pmic2_i2c_config(struct dt_node_info *i2c_info, + struct stm32_i2c_init_s *init, + uint32_t *i2c_addr) +{ + static int i2c_node = -FDT_ERR_NOTFOUND; + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + if (i2c_node == -FDT_ERR_NOTFOUND) { + int pmic_node; + const fdt32_t *cuint; + + 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; + } + + *i2c_addr = fdt32_to_cpu(*cuint) << 1; + if (*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); + if (i2c_info->base == 0U) { + return -FDT_ERR_NOTFOUND; + } + + i2c_info->status = DT_SECURE; + + return stm32_i2c_get_setup_from_fdt(fdt, i2c_node, init); +} + +bool initialize_pmic_i2c(void) +{ + int ret; + struct dt_node_info i2c_info; + struct i2c_handle_s *i2c = &i2c_handle; + uint32_t i2c_addr = 0U; + struct stm32_i2c_init_s i2c_init; + + ret = dt_pmic2_i2c_config(&i2c_info, &i2c_init, &i2c_addr); + if (ret < 0) { + ERROR("I2C configuration failed %d\n", ret); + panic(); + } + + if (ret != 0) { + return false; + } + + /* Initialize PMIC I2C */ + i2c->i2c_base_addr = i2c_info.base; + i2c->dt_status = i2c_info.status; + i2c->clock = i2c_info.clock; + i2c->i2c_state = I2C_STATE_RESET; + i2c_init.own_address1 = i2c_addr; + i2c_init.addressing_mode = I2C_ADDRESSINGMODE_7BIT; + i2c_init.dual_address_mode = I2C_DUALADDRESS_DISABLE; + i2c_init.own_address2 = 0; + i2c_init.own_address2_masks = I2C_OAR2_OA2NOMASK; + i2c_init.general_call_mode = I2C_GENERALCALL_DISABLE; + i2c_init.no_stretch_mode = I2C_NOSTRETCH_DISABLE; + i2c_init.analog_filter = 1; + i2c_init.digital_filter_coef = 0; + + ret = stm32_i2c_init(i2c, &i2c_init); + if (ret != 0) { + ERROR("Cannot initialize I2C %x (%d)\n", + i2c->i2c_base_addr, ret); + panic(); + } + + if (!stm32_i2c_is_device_ready(i2c, i2c_addr, 1, + I2C_TIMEOUT_BUSY_MS)) { + ERROR("I2C device not ready\n"); + panic(); + } + + pmic2 = &pmic2_handle; + pmic2->i2c_handle = &i2c_handle; + pmic2->i2c_addr = i2c_addr; + + return true; +} + +static int pmic2_set_state(const struct regul_description *desc, bool enable) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + VERBOSE("%s: set state to %d\n", desc->node_name, enable); + + return stpmic2_regulator_set_state(pmic2, regul->id, enable); +} + +static int pmic2_get_state(const struct regul_description *desc) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + bool enabled; + + VERBOSE("%s: get state\n", desc->node_name); + + if (stpmic2_regulator_get_state(pmic2, regul->id, &enabled) < 0) { + panic(); + } + + return enabled; +} + +static int pmic2_get_voltage(const struct regul_description *desc) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + uint16_t mv; + + VERBOSE("%s: get volt\n", desc->node_name); + + if (regul->bypass_mv != 0U) { + int ret; + + /* If the regul is in bypass mode, return bypass value */ + ret = stpmic2_regulator_get_prop(pmic2, regul->id, STPMIC2_BYPASS); + if (ret < 0) { + return ret; + } + + if (ret == 1) { + return regul->bypass_mv; + } + }; + + if (stpmic2_regulator_get_voltage(pmic2, regul->id, &mv) < 0) { + panic(); + } + + return mv; +} + +static int pmic2_set_voltage(const struct regul_description *desc, uint16_t mv) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + VERBOSE("%s: set volt\n", desc->node_name); + + if (regul->bypass_mv != 0U) { + int ret; + + /* If the regul is in bypass mode, authorize bypass mV */ + ret = stpmic2_regulator_get_prop(pmic2, regul->id, STPMIC2_BYPASS); + if (ret < 0) { + return ret; + } + + if ((ret == 1) && (mv != regul->bypass_mv)) { + return -EPERM; + } + }; + + return stpmic2_regulator_set_voltage(pmic2, regul->id, mv); +} + +static int pmic2_list_voltages(const struct regul_description *desc, + const uint16_t **levels, size_t *count) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + VERBOSE("%s: list volt\n", desc->node_name); + + if (regul->bypass_mv != 0U) { + int ret; + + ret = stpmic2_regulator_get_prop(pmic2, regul->id, STPMIC2_BYPASS); + if (ret < 0) { + return ret; + } + + /* bypass is enabled, return a list with only bypass mV */ + if (ret == 1) { + if (count != NULL) { + *count = 1U; + } + if (levels != NULL) { + *levels = ®ul->bypass_mv; + } + return 0; + } + }; + + return stpmic2_regulator_levels_mv(pmic2, regul->id, levels, count); +} + +static int pmic2_set_flag(const struct regul_description *desc, uint16_t flag) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + uint32_t id = regul->id; + int ret = -EPERM; + + VERBOSE("%s: set_flag 0x%x\n", desc->node_name, flag); + + switch (flag) { + case REGUL_PULL_DOWN: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_PULL_DOWN, 1U); + break; + case REGUL_OCP: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_OCP, 1U); + break; + case REGUL_SINK_SOURCE: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_SINK_SOURCE, 1U); + break; + case REGUL_ENABLE_BYPASS: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_BYPASS, 1U); + break; + case REGUL_MASK_RESET: + ret = stpmic2_regulator_set_prop(pmic2, id, STPMIC2_MASK_RESET, 1U); + break; + default: + ERROR("Invalid flag %u", flag); + panic(); + } + + if (ret != 0) { + return -EPERM; + } + + return 0; +} + +int stpmic2_set_prop(const struct regul_description *desc, uint16_t prop, uint32_t value) +{ + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + int ret; + + VERBOSE("%s: set_prop 0x%x val=%u\n", desc->node_name, prop, value); + + ret = stpmic2_regulator_set_prop(pmic2, regul->id, prop, value); + if (ret != 0) + return -EPERM; + + return 0; +} + +static struct regul_ops pmic2_ops = { + .set_state = pmic2_set_state, + .get_state = pmic2_get_state, + .set_voltage = pmic2_set_voltage, + .get_voltage = pmic2_get_voltage, + .list_voltages = pmic2_list_voltages, + .set_flag = pmic2_set_flag, +}; + +#define DEFINE_PMIC_REGUL_HANDLE(rid) \ +[(rid)] = { \ + .id = (rid), \ +} + +static struct regul_handle_s pmic2_regul_handles[STPMIC2_NB_REG] = { + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK1), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK2), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK3), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK4), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK5), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK6), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_BUCK7), + + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO1), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO2), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO3), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO4), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO5), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO6), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO7), + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_LDO8), + + DEFINE_PMIC_REGUL_HANDLE(STPMIC2_REFDDR), +}; + +#define DEFINE_REGUL(rid, name) \ +[rid] = { \ + .node_name = name, \ + .ops = &pmic2_ops, \ + .driver_data = &pmic2_regul_handles[rid], \ +} + +static const struct regul_description pmic2_descs[STPMIC2_NB_REG] = { + DEFINE_REGUL(STPMIC2_BUCK1, "buck1"), + DEFINE_REGUL(STPMIC2_BUCK2, "buck2"), + DEFINE_REGUL(STPMIC2_BUCK3, "buck3"), + DEFINE_REGUL(STPMIC2_BUCK4, "buck4"), + DEFINE_REGUL(STPMIC2_BUCK5, "buck5"), + DEFINE_REGUL(STPMIC2_BUCK6, "buck6"), + DEFINE_REGUL(STPMIC2_BUCK7, "buck7"), + + DEFINE_REGUL(STPMIC2_LDO1, "ldo1"), + DEFINE_REGUL(STPMIC2_LDO2, "ldo2"), + DEFINE_REGUL(STPMIC2_LDO3, "ldo3"), + DEFINE_REGUL(STPMIC2_LDO4, "ldo4"), + DEFINE_REGUL(STPMIC2_LDO5, "ldo5"), + DEFINE_REGUL(STPMIC2_LDO6, "ldo6"), + DEFINE_REGUL(STPMIC2_LDO7, "ldo7"), + DEFINE_REGUL(STPMIC2_LDO8, "ldo8"), + + DEFINE_REGUL(STPMIC2_REFDDR, "refddr"), +}; + +static int register_pmic2(void) +{ + void *fdt; + int pmic_node, regulators_node, subnode; + + VERBOSE("Register pmic2\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; + const fdt32_t *cuint; + + for (i = 0; i < STPMIC2_NB_REG; i++) { + desc = &pmic2_descs[i]; + if (strcmp(desc->node_name, reg_name) == 0) { + break; + } + } + assert(i < STPMIC2_NB_REG); + + ret = regulator_register(desc, subnode); + if (ret != 0) { + WARN("%s:%d failed to register %s\n", __func__, + __LINE__, reg_name); + return ret; + } + + cuint = fdt_getprop(fdt, subnode, "st,regulator-bypass-microvolt", NULL); + if (cuint != NULL) { + struct regul_handle_s *regul = (struct regul_handle_s *)desc->driver_data; + + regul->bypass_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); + VERBOSE("%s: bypass voltage=%umV\n", desc->node_name, + regul->bypass_mv); + } + + if (fdt_getprop(fdt, subnode, "st,mask-reset", NULL) != NULL) { + VERBOSE("%s: set mask-reset\n", desc->node_name); + ret = pmic2_set_flag(desc, REGUL_MASK_RESET); + if (ret != 0) { + ERROR("set mask-reset failed\n"); + return ret; + } + } + + if (fdt_getprop(fdt, subnode, "st,regulator-sink-source", NULL) != NULL) { + VERBOSE("%s: set regulator-sink-source\n", desc->node_name); + ret = pmic2_set_flag(desc, REGUL_SINK_SOURCE); + if (ret != 0) { + ERROR("set regulator-sink-source failed\n"); + return ret; + } + } + } + + return 0; +} + +void initialize_pmic(void) +{ + int ret; + uint8_t val; + + ret = initialize_pmic_i2c(); + if (!ret) { + VERBOSE("No PMIC2\n"); + return; + } + + if (stpmic2_get_version(pmic2, &val) != 0) { + ERROR("Failed to access PMIC\n"); + panic(); + } + INFO("PMIC2 version = 0x%02x\n", val); + + if (stpmic2_get_product_id(pmic2, &val) != 0) { + ERROR("Failed to access PMIC\n"); + panic(); + } + INFO("PMIC2 product ID = 0x%02x\n", val); + + ret = register_pmic2(); + if (ret < 0) { + ERROR("Register pmic2 failed\n"); + panic(); + } + +#if EVENT_LOG_LEVEL == LOG_LEVEL_VERBOSE + stpmic2_dump_regulators(pmic2); +#endif +} diff --git a/drivers/st/pmic/stpmic2.c b/drivers/st/pmic/stpmic2.c new file mode 100644 index 000000000..05a80ecf1 --- /dev/null +++ b/drivers/st/pmic/stpmic2.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include + +#define RET_SUCCESS 0 +#define RET_ERROR_NOT_SUPPORTED -1 +#define RET_ERROR_GENERIC -2 +#define RET_ERROR_BAD_PARAMETERS -3 + +#define I2C_TIMEOUT_MS 25 + +#define VOLTAGE_INDEX_INVALID ((size_t)~0U) + +struct regul_struct { + const char *name; + const uint16_t *volt_table; + uint8_t volt_table_size; + uint8_t volt_cr; + uint8_t volt_shift; + uint8_t en_cr; + uint8_t alt_en_cr; + uint8_t msrt_reg; + uint8_t msrt_mask; + uint8_t pd_reg; + uint8_t pd_val; + uint8_t ocp_reg; + uint8_t ocp_mask; +}; + +/* Voltage tables in mV */ +static const uint16_t buck1236_volt_table[] = { + 500U, 510U, 520U, 530U, 540U, 550U, 560U, 570U, 580U, 590U, + 600U, 610U, 620U, 630U, 640U, 650U, 660U, 670U, 680U, 690U, + 700U, 710U, 720U, 730U, 740U, 750U, 760U, 770U, 780U, 790U, + 800U, 810U, 820U, 830U, 840U, 850U, 860U, 870U, 880U, 890U, + 900U, 910U, 920U, 930U, 940U, 950U, 960U, 970U, 980U, 990U, + 1000U, 1010U, 1020U, 1030U, 1040U, 1050U, 1060U, 1070U, 1080U, 1090U, + 1100U, 1110U, 1120U, 1130U, 1140U, 1150U, 1160U, 1170U, 1180U, 1190U, + 1200U, 1210U, 1220U, 1230U, 1240U, 1250U, 1260U, 1270U, 1280U, 1290U, + 1300U, 1310U, 1320U, 1330U, 1340U, 1350U, 1360U, 1370U, 1380U, 1390U, + 1400U, 1410U, 1420U, 1430U, 1440U, 1450U, 1460U, 1470U, 1480U, 1490U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U +}; + +static const uint16_t buck457_volt_table[] = { + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, 1500U, + 1500U, 1600U, 1700U, 1800U, 1900U, 2000U, 2100U, 2200U, 2300U, 2400U, + 2500U, 2600U, 2700U, 2800U, 2900U, 3000U, 3100U, 3200U, 3300U, 3400U, + 3500U, 3600U, 3700U, 3800U, 3900U, 4000U, 4100U, 4200U +}; + +static const uint16_t ldo235678_volt_table[] = { + 900U, 1000U, 1100U, 1200U, 1300U, 1400U, 1500U, 1600U, 1700U, 1800U, + 1900U, 2000U, 2100U, 2200U, 2300U, 2400U, 2500U, 2600U, 2700U, 2800U, + 2900U, 3000U, 3100U, 3200U, 3300U, 3400U, 3500U, 3600U, 3700U, 3800U, + 3900U, 4000U +}; + +static const uint16_t ldo1_volt_table[] = { + 1800U, +}; + +static const uint16_t ldo4_volt_table[] = { + 3300U, +}; + +static const uint16_t refddr_volt_table[] = { + 0, +}; + +#define DEFINE_BUCK(regu_name, ID, pd, table) { \ + .name = regu_name, \ + .volt_table = table, \ + .volt_table_size = ARRAY_SIZE(table), \ + .en_cr = ID ## _MAIN_CR2, \ + .volt_cr = ID ## _MAIN_CR1, \ + .alt_en_cr = ID ## _ALT_CR2, \ + .msrt_reg = BUCKS_MRST_CR, \ + .msrt_mask = ID ## _MRST, \ + .pd_reg = pd, \ + .pd_val = ID ## _PD_FAST, \ + .ocp_reg = FS_OCP_CR1, \ + .ocp_mask = FS_OCP_ ## ID, \ +} + +#define DEFINE_LDOx(regu_name, ID, table) { \ + .name = regu_name, \ + .volt_table = table, \ + .volt_table_size = ARRAY_SIZE(table), \ + .volt_shift = LDO_VOLT_SHIFT, \ + .en_cr = ID ## _MAIN_CR, \ + .volt_cr = ID ## _MAIN_CR, \ + .alt_en_cr = ID ## _ALT_CR, \ + .msrt_reg = LDOS_MRST_CR, \ + .msrt_mask = ID ## _MRST, \ + .pd_reg = LDOS_PD_CR1, \ + .pd_val = ID ## _PD, \ + .ocp_reg = FS_OCP_CR2, \ + .ocp_mask = FS_OCP_ ## ID, \ +} + +#define DEFINE_REFDDR(regu_name, ID, table) { \ + .name = regu_name, \ + .volt_table = table, \ + .volt_table_size = ARRAY_SIZE(table), \ + .en_cr = ID ## _MAIN_CR, \ + .volt_cr = ID ## _MAIN_CR, \ + .alt_en_cr = ID ## _ALT_CR, \ + .msrt_reg = BUCKS_MRST_CR, \ + .msrt_mask = ID ## _MRST, \ + .pd_reg = LDOS_PD_CR2, \ + .pd_val = ID ## _PD, \ + .ocp_reg = FS_OCP_CR1, \ + .ocp_mask = FS_OCP_ ## ID, \ +} + +/* Table of Regulators in PMIC SoC */ +static const struct regul_struct regul_table[STPMIC2_NB_REG] = { + [STPMIC2_BUCK1] = DEFINE_BUCK("buck1", BUCK1, BUCKS_PD_CR1, + buck1236_volt_table), + [STPMIC2_BUCK2] = DEFINE_BUCK("buck2", BUCK2, BUCKS_PD_CR1, + buck1236_volt_table), + [STPMIC2_BUCK3] = DEFINE_BUCK("buck3", BUCK3, BUCKS_PD_CR1, + buck1236_volt_table), + [STPMIC2_BUCK4] = DEFINE_BUCK("buck4", BUCK4, BUCKS_PD_CR1, + buck457_volt_table), + [STPMIC2_BUCK5] = DEFINE_BUCK("buck5", BUCK5, BUCKS_PD_CR2, + buck457_volt_table), + [STPMIC2_BUCK6] = DEFINE_BUCK("buck6", BUCK6, BUCKS_PD_CR2, + buck1236_volt_table), + [STPMIC2_BUCK7] = DEFINE_BUCK("buck7", BUCK7, BUCKS_PD_CR2, + buck457_volt_table), + + [STPMIC2_REFDDR] = DEFINE_REFDDR("refddr", REFDDR, refddr_volt_table), + + [STPMIC2_LDO1] = DEFINE_LDOx("ldo1", LDO1, ldo1_volt_table), + [STPMIC2_LDO2] = DEFINE_LDOx("ldo2", LDO2, ldo235678_volt_table), + [STPMIC2_LDO3] = DEFINE_LDOx("ldo3", LDO3, ldo235678_volt_table), + [STPMIC2_LDO4] = DEFINE_LDOx("ldo4", LDO4, ldo4_volt_table), + [STPMIC2_LDO5] = DEFINE_LDOx("ldo5", LDO5, ldo235678_volt_table), + [STPMIC2_LDO6] = DEFINE_LDOx("ldo6", LDO6, ldo235678_volt_table), + [STPMIC2_LDO7] = DEFINE_LDOx("ldo7", LDO7, ldo235678_volt_table), + [STPMIC2_LDO8] = DEFINE_LDOx("ldo8", LDO8, ldo235678_volt_table), + +}; + +int stpmic2_register_read(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t *value) +{ + int ret = stm32_i2c_mem_read(pmic->i2c_handle, + pmic->i2c_addr, + (uint16_t)register_id, + I2C_MEMADD_SIZE_8BIT, value, + 1, I2C_TIMEOUT_MS); + if (ret != 0) { + ERROR("Failed to read reg:0x%x\n", register_id); + } + + return ret; +} + +int stpmic2_register_write(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value) +{ + uint8_t val = value; + int ret = stm32_i2c_mem_write(pmic->i2c_handle, + pmic->i2c_addr, + (uint16_t)register_id, + I2C_MEMADD_SIZE_8BIT, &val, + 1, I2C_TIMEOUT_MS); + if (ret != 0) { + ERROR("Failed to write reg:0x%x\n", register_id); + } + + return ret; +} + +int stpmic2_register_update(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value, uint8_t mask) +{ + int status; + uint8_t val = 0U; + + status = stpmic2_register_read(pmic, register_id, &val); + if (status != 0) { + return status; + } + + val = (val & ((uint8_t)~mask)) | (value & mask); + + VERBOSE("REG:0x%x v=0x%x mask=0x%x -> 0x%x\n", + register_id, value, mask, val); + + return stpmic2_register_write(pmic, register_id, val); +} + +int stpmic2_regulator_set_state(struct pmic_handle_s *pmic, + uint8_t id, bool enable) +{ + const struct regul_struct *regul = ®ul_table[id]; + + if (enable) { + return stpmic2_register_update(pmic, regul->en_cr, 1U, 1U); + } else { + return stpmic2_register_update(pmic, regul->en_cr, 0, 1U); + } +} + +int stpmic2_regulator_get_state(struct pmic_handle_s *pmic, + uint8_t id, bool *enabled) +{ + const struct regul_struct *regul = ®ul_table[id]; + uint8_t val; + + if (stpmic2_register_read(pmic, regul->en_cr, &val) != 0) { + return RET_ERROR_GENERIC; + } + + *enabled = (val & 1U) == 1U; + + return RET_SUCCESS; +} + +int stpmic2_regulator_levels_mv(struct pmic_handle_s *pmic, + uint8_t id, const uint16_t **levels, + size_t *levels_count) +{ + const struct regul_struct *regul = ®ul_table[id]; + + if (regul == NULL) { + return RET_ERROR_BAD_PARAMETERS; + } + + if (levels_count != NULL) { + *levels_count = regul->volt_table_size; + } + if (levels != NULL) { + *levels = regul->volt_table; + } + + return RET_SUCCESS; +} + +int stpmic2_regulator_get_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t *val) +{ + const struct regul_struct *regul = ®ul_table[id]; + uint8_t value = 0U; + uint8_t mask; + + if (regul->volt_table_size == 0U) { + return RET_ERROR_GENERIC; + } + + mask = regul->volt_table_size - 1U; + if (mask != 0U) { + if (stpmic2_register_read(pmic, regul->volt_cr, &value) != 0) { + return RET_ERROR_GENERIC; + } + + value = (value >> regul->volt_shift) & mask; + } + + if (value > regul->volt_table_size) { + return RET_ERROR_GENERIC; + } + + *val = regul->volt_table[value]; + + return RET_SUCCESS; +} + +static size_t voltage_to_index(const struct regul_struct *regul, + uint16_t millivolts) +{ + unsigned int i; + + assert(regul->volt_table); + for (i = 0U; i < regul->volt_table_size; i++) { + if (regul->volt_table[i] == millivolts) { + return i; + } + } + + return VOLTAGE_INDEX_INVALID; +} + +int stpmic2_regulator_set_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t millivolts) +{ + const struct regul_struct *regul = ®ul_table[id]; + size_t index; + uint8_t mask; + + if (!regul->volt_table_size) { + return RET_SUCCESS; + } + + mask = regul->volt_table_size - 1U; + + index = voltage_to_index(regul, millivolts); + if (index == VOLTAGE_INDEX_INVALID) { + return RET_ERROR_GENERIC; + } + + return stpmic2_register_update(pmic, regul->volt_cr, + index << regul->volt_shift, + mask << regul->volt_shift); +} + +/* update both normal and alternate register */ +static int stpmic2_update_en_crs(struct pmic_handle_s *pmic, uint8_t id, + uint8_t value, uint8_t mask) +{ + const struct regul_struct *regul = ®ul_table[id]; + + if (stpmic2_register_update(pmic, regul->en_cr, value, mask) != 0) { + return RET_ERROR_GENERIC; + } + + if (stpmic2_register_update(pmic, regul->alt_en_cr, value, mask) != 0) { + return RET_ERROR_GENERIC; + } + + return RET_SUCCESS; +} + +int stpmic2_regulator_get_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop) +{ + const struct regul_struct *regul = ®ul_table[id]; + uint8_t val; + + VERBOSE("%s: get prop 0x%x\n", regul->name, prop); + + switch (prop) { + case STPMIC2_BYPASS: + if ((id <= STPMIC2_BUCK7) || (id == STPMIC2_LDO1) || + (id == STPMIC2_LDO4) || (id == STPMIC2_REFDDR)) { + return 0; + } + + if (stpmic2_register_read(pmic, regul->en_cr, &val) != 0) { + return -EIO; + } + + if ((val & LDO_BYPASS) != 0) { + return 1; + } + + break; + default: + ERROR("Invalid prop %u\n", prop); + panic(); + } + + return 0; +} + +int stpmic2_regulator_set_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop, uint32_t arg) +{ + const struct regul_struct *regul = ®ul_table[id]; + + VERBOSE("%s: set prop 0x%x arg=%u\n", regul->name, prop, arg); + + switch (prop) { + case STPMIC2_PULL_DOWN: + return stpmic2_register_update(pmic, regul->pd_reg, + regul->pd_val, + regul->pd_val); + case STPMIC2_MASK_RESET: + if (!regul->msrt_mask) { + return RET_ERROR_NOT_SUPPORTED; + } + /* enable mask reset */ + return stpmic2_register_update(pmic, regul->msrt_reg, + regul->msrt_mask, + regul->msrt_mask); + case STPMIC2_BYPASS: + if ((id <= STPMIC2_BUCK7) || (id == STPMIC2_LDO1) || + (id == STPMIC2_LDO4) || (id == STPMIC2_REFDDR)) { + return RET_ERROR_NOT_SUPPORTED; + } + + /* clear sink source mode */ + if ((id == STPMIC2_LDO3) && (arg != 0U)) { + if (stpmic2_update_en_crs(pmic, id, 0, LDO3_SNK_SRC) != 0) { + return RET_ERROR_GENERIC; + } + } + + /* enable bypass mode */ + return stpmic2_update_en_crs(pmic, id, + (arg != 0U) ? LDO_BYPASS : 0, + LDO_BYPASS); + case STPMIC2_SINK_SOURCE: + if (id != STPMIC2_LDO3) { + return RET_ERROR_NOT_SUPPORTED; + } + + /* clear bypass mode */ + if (stpmic2_update_en_crs(pmic, id, 0, LDO_BYPASS) != 0) { + return RET_ERROR_GENERIC; + } + + return stpmic2_update_en_crs(pmic, id, LDO3_SNK_SRC, + LDO3_SNK_SRC); + case STPMIC2_OCP: + return stpmic2_register_update(pmic, regul->ocp_reg, + regul->ocp_mask, + regul->ocp_mask); + default: + ERROR("Invalid prop %u\n", prop); + panic(); + } + + return -EPERM; +} + +#if EVENT_LOG_LEVEL == LOG_LEVEL_VERBOSE +void stpmic2_dump_regulators(struct pmic_handle_s *pmic) +{ + size_t i; + char const *name; + + for (i = 0U; i < ARRAY_SIZE(regul_table); i++) { + uint16_t val; + bool state; + + if (!regul_table[i].volt_cr) { + continue; + } + + stpmic2_regulator_get_voltage(pmic, i, &val); + stpmic2_regulator_get_state(pmic, i, &state); + + name = regul_table[i].name; + + VERBOSE("PMIC regul %s: %s, %dmV\n", + name, state ? "EN" : "DIS", val); + } +} +#endif + +int stpmic2_get_version(struct pmic_handle_s *pmic, uint8_t *val) +{ + return stpmic2_register_read(pmic, VERSION_SR, val); +} + +int stpmic2_get_product_id(struct pmic_handle_s *pmic, uint8_t *val) +{ + return stpmic2_register_read(pmic, PRODUCT_ID, val); +} diff --git a/fdts/stm32mp25-pinctrl.dtsi b/fdts/stm32mp25-pinctrl.dtsi index fb1280811..a22c8239b 100644 --- a/fdts/stm32mp25-pinctrl.dtsi +++ b/fdts/stm32mp25-pinctrl.dtsi @@ -6,6 +6,17 @@ #include &pinctrl { + /omit-if-no-ref/ + i2c7_pins_a: i2c7-0 { + pins1 { + pinmux = , /* I2C7_SCL */ + ; /* I2C7_SDA */ + bias-disable; + drive-open-drain; + slew-rate = <0>; + }; + }; + /omit-if-no-ref/ sdmmc1_b4_pins_a: sdmmc1-b4-0 { pins1 { diff --git a/fdts/stm32mp251.dtsi b/fdts/stm32mp251.dtsi index 6f39b5a17..9e898134c 100644 --- a/fdts/stm32mp251.dtsi +++ b/fdts/stm32mp251.dtsi @@ -98,6 +98,134 @@ status = "disabled"; }; + usart3: serial@400f0000 { + compatible = "st,stm32h7-uart"; + reg = <0x400f0000 0x400>; + clocks = <&rcc CK_KER_USART3>; + resets = <&rcc USART3_R>; + status = "disabled"; + }; + + uart4: serial@40100000 { + compatible = "st,stm32h7-uart"; + reg = <0x40100000 0x400>; + clocks = <&rcc CK_KER_UART4>; + resets = <&rcc UART4_R>; + status = "disabled"; + }; + + uart5: serial@40110000 { + compatible = "st,stm32h7-uart"; + reg = <0x40110000 0x400>; + clocks = <&rcc CK_KER_UART5>; + resets = <&rcc UART5_R>; + status = "disabled"; + }; + + i2c1: i2c@40120000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40120000 0x400>; + clocks = <&rcc CK_KER_I2C1>; + resets = <&rcc I2C1_R>; + status = "disabled"; + }; + + i2c2: i2c@40130000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40130000 0x400>; + clocks = <&rcc CK_KER_I2C2>; + resets = <&rcc I2C2_R>; + status = "disabled"; + }; + + i2c3: i2c@40140000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40140000 0x400>; + clocks = <&rcc CK_KER_I2C3>; + resets = <&rcc I2C3_R>; + status = "disabled"; + }; + + i2c4: i2c@40150000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40150000 0x400>; + clocks = <&rcc CK_KER_I2C4>; + resets = <&rcc I2C4_R>; + status = "disabled"; + }; + + i2c5: i2c@40160000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40160000 0x400>; + clocks = <&rcc CK_KER_I2C5>; + resets = <&rcc I2C5_R>; + status = "disabled"; + }; + + i2c6: i2c@40170000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40170000 0x400>; + clocks = <&rcc CK_KER_I2C6>; + resets = <&rcc I2C6_R>; + status = "disabled"; + }; + + i2c7: i2c@40180000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x40180000 0x400>; + clocks = <&rcc CK_KER_I2C7>; + resets = <&rcc I2C7_R>; + status = "disabled"; + }; + + usart6: serial@40220000 { + compatible = "st,stm32h7-uart"; + reg = <0x40220000 0x400>; + clocks = <&rcc CK_KER_USART6>; + resets = <&rcc USART6_R>; + status = "disabled"; + }; + + uart9: serial@402c0000 { + compatible = "st,stm32h7-uart"; + reg = <0x402c0000 0x400>; + clocks = <&rcc CK_KER_UART9>; + resets = <&rcc UART9_R>; + status = "disabled"; + }; + + usart1: serial@40330000 { + compatible = "st,stm32h7-uart"; + reg = <0x40330000 0x400>; + clocks = <&rcc CK_KER_USART1>; + resets = <&rcc USART1_R>; + status = "disabled"; + }; + + uart7: serial@40370000 { + compatible = "st,stm32h7-uart"; + reg = <0x40370000 0x400>; + clocks = <&rcc CK_KER_UART7>; + resets = <&rcc UART7_R>; + status = "disabled"; + }; + + uart8: serial@40380000 { + compatible = "st,stm32h7-uart"; + reg = <0x40380000 0x400>; + clocks = <&rcc CK_KER_UART8>; + resets = <&rcc UART8_R>; + status = "disabled"; + }; + + i2c8: i2c@46040000 { + compatible = "st,stm32mp25-i2c"; + reg = <0x46040000 0x400>; + clocks = <&rcc CK_KER_I2C8>; + resets = <&rcc I2C8_R>; + status = "disabled"; + }; + sdmmc1: mmc@48220000 { compatible = "st,stm32mp25-sdmmc2", "arm,pl18x", "arm,primecell"; arm,primecell-periphid = <0x00353180>; diff --git a/fdts/stm32mp257f-ev1.dts b/fdts/stm32mp257f-ev1.dts index 6df1b30db..d2b5e55bb 100644 --- a/fdts/stm32mp257f-ev1.dts +++ b/fdts/stm32mp257f-ev1.dts @@ -37,6 +37,131 @@ }; }; +&i2c7 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c7_pins_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + clock-frequency = <400000>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + pmic2: stpmic@33 { + compatible = "st,stpmic2"; + reg = <0x33>; + status = "okay"; + + regulators { + compatible = "st,stpmic2-regulators"; + + vddcpu: buck1 { + regulator-name = "vddcpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <910000>; + regulator-always-on; + }; + vddcore: buck2 { + regulator-name = "vddcore"; + regulator-min-microvolt = <820000>; + regulator-max-microvolt = <820000>; + regulator-always-on; + }; + vddgpu: buck3 { + regulator-name = "vddgpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + }; + vddio_pmic: buck4 { + regulator-name = "vddio_pmic"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + v1v8: buck5 { + regulator-name = "v1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + vdd_ddr: buck6 { + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + v3v3: buck7 { + regulator-name = "v3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vdda1v8_aon: ldo1 { + regulator-name = "vdda1v8_aon"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + vdd_emmc: ldo2 { + regulator-name = "vdd_emmc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vtt_ddr: ldo3 { + regulator-name = "vtt_ddr"; + st,regulator-sink-source; + }; + vdd3v3_usb: ldo4 { + regulator-name = "vdd3v3_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vpp_ddr: ldo5 { + regulator-name = "vpp_ddr"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-enable-ramp-delay = <1000>; + }; + vdd_sdcard: ldo7 { + regulator-name = "vdd_sdcard"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vddio_sdcard: ldo8 { + regulator-name = "vddio_sdcard"; + st,regulator-bypass-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + vref_ddr: refddr { + regulator-name = "vref_ddr"; + }; + }; + }; +}; + +&pwr { + vddio1: vddio1 { + vddio1-supply = <&vddio_sdcard>; + }; + vddio2: vddio2 { + vddio2-supply = <&v1v8>; + }; + vddio3: vddio3 { + vddio3-supply = <&vddio_pmic>; + }; + vddio4: vddio4 { + vddio4-supply = <&vddio_pmic>; + }; + vddio: vddio { + vdd-supply = <&vddio_pmic>; + }; +}; + &sdmmc1 { pinctrl-names = "default"; pinctrl-0 = <&sdmmc1_b4_pins_a>; diff --git a/include/drivers/st/stm32mp_pmic2.h b/include/drivers/st/stm32mp_pmic2.h new file mode 100644 index 000000000..51eba38ca --- /dev/null +++ b/include/drivers/st/stm32mp_pmic2.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP_PMIC2_H +#define STM32MP_PMIC2_H + +#include +#include + +#include + +/* + * dt_pmic_status - Check PMIC status from device tree + * + * Returns the status of the PMIC (secure, non-secure), or a negative value on + * error + */ +int dt_pmic_status(void); + +/* + * initialize_pmic_i2c - Initialize I2C for the PMIC control + * + * Returns true if PMIC is available, false if not found, panics on errors + */ +bool initialize_pmic_i2c(void); + +/* + * initialize_pmic - Main PMIC initialization function, called at platform init + * + * Panics on errors + */ +void initialize_pmic(void); + +/* + * stpmic2_set_prop - Set PMIC2 proprietary property + * + * Returns non zero on errors + */ +int stpmic2_set_prop(const struct regul_description *desc, uint16_t prop, uint32_t value); + +/* + * pmic_switch_off - switch off the platform with PMIC + * + * Panics on errors + */ +void pmic_switch_off(void); + +#endif /* STM32MP_PMIC2_H */ diff --git a/include/drivers/st/stpmic2.h b/include/drivers/st/stpmic2.h new file mode 100644 index 000000000..58ba64aef --- /dev/null +++ b/include/drivers/st/stpmic2.h @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2024, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STPMIC2_H +#define STPMIC2_H + +#include +#include + +enum { + STPMIC2_BUCK1 = 0, + STPMIC2_BUCK2, + STPMIC2_BUCK3, + STPMIC2_BUCK4, + STPMIC2_BUCK5, + STPMIC2_BUCK6, + STPMIC2_BUCK7, + STPMIC2_REFDDR, + STPMIC2_LDO1, + STPMIC2_LDO2, + STPMIC2_LDO3, + STPMIC2_LDO4, + STPMIC2_LDO5, + STPMIC2_LDO6, + STPMIC2_LDO7, + STPMIC2_LDO8, + STPMIC2_NB_REG +}; + +/* Status Registers */ +#define PRODUCT_ID 0x00 +#define VERSION_SR 0x01 +#define TURN_ON_SR 0x02 +#define TURN_OFF_SR 0x03 +#define RESTART_SR 0x04 +#define OCP_SR1 0x05 +#define OCP_SR2 0x06 +#define EN_SR1 0x07 +#define EN_SR2 0x08 +#define FS_CNT_SR1 0x09 +#define FS_CNT_SR2 0x0A +#define FS_CNT_SR3 0x0B +#define MODE_SR 0x0C +/* Control Registers */ +#define MAIN_CR 0x10 +#define VINLOW_CR 0x11 +#define PKEY_LKP_CR 0x12 +#define WDG_CR 0x13 +#define WDG_TMR_CR 0x14 +#define WDG_TMR_SR 0x15 +#define FS_OCP_CR1 0x16 +#define FS_OCP_CR2 0x17 +#define PADS_PULL_CR 0x18 +#define BUCKS_PD_CR1 0x19 +#define BUCKS_PD_CR2 0x1A +#define LDOS_PD_CR1 0x1B +#define LDOS_PD_CR2 0x1C +#define BUCKS_MRST_CR 0x1D +#define LDOS_MRST_CR 0x1E +/* Buck CR */ +#define BUCK1_MAIN_CR1 0x20 +#define BUCK1_MAIN_CR2 0x21 +#define BUCK1_ALT_CR1 0x22 +#define BUCK1_ALT_CR2 0x23 +#define BUCK1_PWRCTRL_CR 0x24 +#define BUCK2_MAIN_CR1 0x25 +#define BUCK2_MAIN_CR2 0x26 +#define BUCK2_ALT_CR1 0x27 +#define BUCK2_ALT_CR2 0x28 +#define BUCK2_PWRCTRL_CR 0x29 +#define BUCK3_MAIN_CR1 0x2A +#define BUCK3_MAIN_CR2 0x2B +#define BUCK3_ALT_CR1 0x2C +#define BUCK3_ALT_CR2 0x2D +#define BUCK3_PWRCTRL_CR 0x2E +#define BUCK4_MAIN_CR1 0x2F +#define BUCK4_MAIN_CR2 0x30 +#define BUCK4_ALT_CR1 0x31 +#define BUCK4_ALT_CR2 0x32 +#define BUCK4_PWRCTRL_CR 0x33 +#define BUCK5_MAIN_CR1 0x34 +#define BUCK5_MAIN_CR2 0x35 +#define BUCK5_ALT_CR1 0x36 +#define BUCK5_ALT_CR2 0x37 +#define BUCK5_PWRCTRL_CR 0x38 +#define BUCK6_MAIN_CR1 0x39 +#define BUCK6_MAIN_CR2 0x3A +#define BUCK6_ALT_CR1 0x3B +#define BUCK6_ALT_CR2 0x3C +#define BUCK6_PWRCTRL_CR 0x3D +#define BUCK7_MAIN_CR1 0x3E +#define BUCK7_MAIN_CR2 0x3F +#define BUCK7_ALT_CR1 0x40 +#define BUCK7_ALT_CR2 0x41 +#define BUCK7_PWRCTRL_CR 0x42 +/* LDO CR */ +#define LDO1_MAIN_CR 0x4C +#define LDO1_ALT_CR 0x4D +#define LDO1_PWRCTRL_CR 0x4E +#define LDO2_MAIN_CR 0x4F +#define LDO2_ALT_CR 0x50 +#define LDO2_PWRCTRL_CR 0x51 +#define LDO3_MAIN_CR 0x52 +#define LDO3_ALT_CR 0x53 +#define LDO3_PWRCTRL_CR 0x54 +#define LDO4_MAIN_CR 0x55 +#define LDO4_ALT_CR 0x56 +#define LDO4_PWRCTRL_CR 0x57 +#define LDO5_MAIN_CR 0x58 +#define LDO5_ALT_CR 0x59 +#define LDO5_PWRCTRL_CR 0x5A +#define LDO6_MAIN_CR 0x5B +#define LDO6_ALT_CR 0x5C +#define LDO6_PWRCTRL_CR 0x5D +#define LDO7_MAIN_CR 0x5E +#define LDO7_ALT_CR 0x5F +#define LDO7_PWRCTRL_CR 0x60 +#define LDO8_MAIN_CR 0x61 +#define LDO8_ALT_CR 0x62 +#define LDO8_PWRCTRL_CR 0x63 +#define REFDDR_MAIN_CR 0x64 +#define REFDDR_ALT_CR 0x65 +#define REFDDR_PWRCTRL_CR 0x66 +/* INTERRUPT CR */ +#define INT_PENDING_R1 0x70 +#define INT_PENDING_R2 0x71 +#define INT_PENDING_R3 0x72 +#define INT_PENDING_R4 0x73 +#define INT_CLEAR_R1 0x74 +#define INT_CLEAR_R2 0x75 +#define INT_CLEAR_R3 0x76 +#define INT_CLEAR_R4 0x77 +#define INT_MASK_R1 0x78 +#define INT_MASK_R2 0x79 +#define INT_MASK_R3 0x7A +#define INT_MASK_R4 0x7B +#define INT_SRC_R1 0x7C +#define INT_SRC_R2 0x7D +#define INT_SRC_R3 0x7E +#define INT_SRC_R4 0x7F +#define INT_DBG_LATCH_R1 0x80 +#define INT_DBG_LATCH_R2 0x81 +#define INT_DBG_LATCH_R3 0x82 +#define INT_DBG_LATCH_R4 0x83 + +/* BUCKS_MRST_CR bits definition */ +#define BUCK1_MRST BIT(0) +#define BUCK2_MRST BIT(1) +#define BUCK3_MRST BIT(2) +#define BUCK4_MRST BIT(3) +#define BUCK5_MRST BIT(4) +#define BUCK6_MRST BIT(5) +#define BUCK7_MRST BIT(6) +#define REFDDR_MRST BIT(7) + +/* LDOS_MRST_CR bits definition */ +#define LDO1_MRST BIT(0) +#define LDO2_MRST BIT(1) +#define LDO3_MRST BIT(2) +#define LDO4_MRST BIT(3) +#define LDO5_MRST BIT(4) +#define LDO6_MRST BIT(5) +#define LDO7_MRST BIT(6) +#define LDO8_MRST BIT(7) + +/* LDOx_MAIN_CR */ +#define LDO_VOLT_SHIFT 1 +#define LDO_BYPASS BIT(6) +#define LDO1_INPUT_SRC BIT(7) +#define LDO3_SNK_SRC BIT(7) +#define LDO4_INPUT_SRC_SHIFT 6 +#define LDO4_INPUT_SRC_MASK GENMASK_32(7, 6) + +/* PWRCTRL register bit definition */ +#define PWRCTRL_EN BIT(0) +#define PWRCTRL_RS BIT(1) +#define PWRCTRL_SEL_SHIFT 2 +#define PWRCTRL_SEL_MASK GENMASK_32(3, 2) + +/* BUCKx_MAIN_CR2 */ +#define PREG_MODE_SHIFT 1 +#define PREG_MODE_MASK GENMASK_32(2, 1) + +/* BUCKS_PD_CR1 */ +#define BUCK1_PD_MASK GENMASK_32(1, 0) +#define BUCK2_PD_MASK GENMASK_32(3, 2) +#define BUCK3_PD_MASK GENMASK_32(5, 4) +#define BUCK4_PD_MASK GENMASK_32(7, 6) + +#define BUCK1_PD_FAST BIT(1) +#define BUCK2_PD_FAST BIT(3) +#define BUCK3_PD_FAST BIT(5) +#define BUCK4_PD_FAST BIT(7) + +/* BUCKS_PD_CR2 */ +#define BUCK5_PD_MASK GENMASK_32(1, 0) +#define BUCK6_PD_MASK GENMASK_32(3, 2) +#define BUCK7_PD_MASK GENMASK_32(5, 4) + +#define BUCK5_PD_FAST BIT(1) +#define BUCK6_PD_FAST BIT(3) +#define BUCK7_PD_FAST BIT(5) + +/* LDOS_PD_CR1 */ +#define LDO1_PD BIT(0) +#define LDO2_PD BIT(1) +#define LDO3_PD BIT(2) +#define LDO4_PD BIT(3) +#define LDO5_PD BIT(4) +#define LDO6_PD BIT(5) +#define LDO7_PD BIT(6) +#define LDO8_PD BIT(7) + +/* LDOS_PD_CR2 */ +#define REFDDR_PD BIT(0) + +/* FS_OCP_CR1 */ +#define FS_OCP_BUCK1 BIT(0) +#define FS_OCP_BUCK2 BIT(1) +#define FS_OCP_BUCK3 BIT(2) +#define FS_OCP_BUCK4 BIT(3) +#define FS_OCP_BUCK5 BIT(4) +#define FS_OCP_BUCK6 BIT(5) +#define FS_OCP_BUCK7 BIT(6) +#define FS_OCP_REFDDR BIT(7) + +/* FS_OCP_CR2 */ +#define FS_OCP_LDO1 BIT(0) +#define FS_OCP_LDO2 BIT(1) +#define FS_OCP_LDO3 BIT(2) +#define FS_OCP_LDO4 BIT(3) +#define FS_OCP_LDO5 BIT(4) +#define FS_OCP_LDO6 BIT(5) +#define FS_OCP_LDO7 BIT(6) +#define FS_OCP_LDO8 BIT(7) + +/* IRQ definitions */ +#define IT_PONKEY_F 0 +#define IT_PONKEY_R 1 +#define IT_BUCK1_OCP 16 +#define IT_BUCK2_OCP 17 +#define IT_BUCK3_OCP 18 +#define IT_BUCK4_OCP 19 +#define IT_BUCK5_OCP 20 +#define IT_BUCK6_OCP 21 +#define IT_BUCK7_OCP 22 +#define IT_REFDDR_OCP 23 +#define IT_LDO1_OCP 24 +#define IT_LDO2_OCP 25 +#define IT_LDO3_OCP 26 +#define IT_LDO4_OCP 27 +#define IT_LDO5_OCP 28 +#define IT_LDO6_OCP 29 +#define IT_LDO7_OCP 30 +#define IT_LDO8_OCP 31 + +enum stpmic2_prop_id { + STPMIC2_MASK_RESET = 0, + STPMIC2_PULL_DOWN, + STPMIC2_BYPASS, /* arg: 1=set 0=reset */ + STPMIC2_SINK_SOURCE, + STPMIC2_OCP, +}; + +struct pmic_handle_s { + struct i2c_handle_s *i2c_handle; + uint32_t i2c_addr; + unsigned int pmic_status; +}; + +int stpmic2_register_read(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t *value); +int stpmic2_register_write(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value); +int stpmic2_register_update(struct pmic_handle_s *pmic, + uint8_t register_id, uint8_t value, uint8_t mask); + +int stpmic2_regulator_set_state(struct pmic_handle_s *pmic, + uint8_t id, bool enable); +int stpmic2_regulator_get_state(struct pmic_handle_s *pmic, + uint8_t id, bool *enabled); + +int stpmic2_regulator_levels_mv(struct pmic_handle_s *pmic, + uint8_t id, const uint16_t **levels, + size_t *levels_count); +int stpmic2_regulator_get_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t *val); +int stpmic2_regulator_set_voltage(struct pmic_handle_s *pmic, + uint8_t id, uint16_t millivolts); + +#if EVENT_LOG_LEVEL == LOG_LEVEL_VERBOSE +void stpmic2_dump_regulators(struct pmic_handle_s *pmic); +#endif + +int stpmic2_get_version(struct pmic_handle_s *pmic, uint8_t *val); +int stpmic2_get_product_id(struct pmic_handle_s *pmic, uint8_t *val); + +int stpmic2_regulator_get_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop); + +int stpmic2_regulator_set_prop(struct pmic_handle_s *pmic, uint8_t id, + enum stpmic2_prop_id prop, uint32_t arg); + +#endif /*STPMIC2_H*/ diff --git a/plat/st/stm32mp2/bl2_plat_setup.c b/plat/st/stm32mp2/bl2_plat_setup.c index 50d19ab2f..eb6c6f8d2 100644 --- a/plat/st/stm32mp2/bl2_plat_setup.c +++ b/plat/st/stm32mp2/bl2_plat_setup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -230,6 +231,10 @@ skip_console_init: panic(); } + if (dt_pmic_status() > 0) { + initialize_pmic(); + } + fconf_populate("TB_FW", STM32MP_DTB_BASE); stm32mp_io_setup(); diff --git a/plat/st/stm32mp2/platform.mk b/plat/st/stm32mp2/platform.mk index 32d6235e9..a22fe5cf2 100644 --- a/plat/st/stm32mp2/platform.mk +++ b/plat/st/stm32mp2/platform.mk @@ -110,6 +110,11 @@ PLAT_BL_COMMON_SOURCES += lib/cpus/${ARCH}/cortex_a35.S PLAT_BL_COMMON_SOURCES += drivers/st/uart/${ARCH}/stm32_console.S PLAT_BL_COMMON_SOURCES += plat/st/stm32mp2/${ARCH}/stm32mp2_helper.S +PLAT_BL_COMMON_SOURCES += drivers/st/pmic/stm32mp_pmic2.c \ + drivers/st/pmic/stpmic2.c \ + +PLAT_BL_COMMON_SOURCES += drivers/st/i2c/stm32_i2c.c + PLAT_BL_COMMON_SOURCES += plat/st/stm32mp2/stm32mp2_private.c PLAT_BL_COMMON_SOURCES += drivers/st/bsec/bsec3.c \