diff --git a/drivers/regulator/CMakeLists.txt b/drivers/regulator/CMakeLists.txt index 94c3a032aff..c7612fa2067 100644 --- a/drivers/regulator/CMakeLists.txt +++ b/drivers/regulator/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library() zephyr_library_sources(regulator_common.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_AXP192 regulator_axp192.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_ADP5360 regulator_adp5360.c) +zephyr_library_sources_ifdef(CONFIG_REGULATOR_DA1469X regulator_da1469x.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_FAKE regulator_fake.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_FIXED regulator_fixed.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_GPIO regulator_gpio.c) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c35ae389669..754ba31783e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -28,6 +28,7 @@ source "subsys/logging/Kconfig.template.log_config" source "drivers/regulator/Kconfig.axp192" source "drivers/regulator/Kconfig.adp5360" +source "drivers/regulator/Kconfig.da1469x" source "drivers/regulator/Kconfig.fake" source "drivers/regulator/Kconfig.fixed" source "drivers/regulator/Kconfig.gpio" diff --git a/drivers/regulator/Kconfig.da1469x b/drivers/regulator/Kconfig.da1469x new file mode 100644 index 00000000000..d8205ed4925 --- /dev/null +++ b/drivers/regulator/Kconfig.da1469x @@ -0,0 +1,16 @@ +# Copyright 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config REGULATOR_DA1469X + bool "DA1469X regulators driver" + default y + depends on DT_HAS_RENESAS_SMARTBOND_REGULATOR_ENABLED + help + Enable support for the Smartbond DA1469x regulators. + +config REGULATOR_DA1469X_INIT_PRIORITY + int "Renesas DA1469x regulators driver init priority" + default KERNEL_INIT_PRIORITY_DEVICE + depends on REGULATOR_DA1469X + help + Init priority for the Renesas DA1469x regulators driver. diff --git a/drivers/regulator/regulator_da1469x.c b/drivers/regulator/regulator_da1469x.c new file mode 100644 index 00000000000..36d8fe1d35f --- /dev/null +++ b/drivers/regulator/regulator_da1469x.c @@ -0,0 +1,415 @@ +/* + * Copyright 2023 Renesas Electronics Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_smartbond_regulator + +#include + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(regulator_da1469x, CONFIG_REGULATOR_LOG_LEVEL); + +#define DCDC_REQUESTED (DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_HV_Msk |\ + DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_LV_Msk) + +#define DA1469X_LDO_3V0_MODE_VBAT BIT(8) +#define DA1469X_LDO_3V0_MODE_VBUS BIT(9) + +static const struct linear_range curren_ranges[] = { + LINEAR_RANGE_INIT(30000, 30000, 0, 31), +}; + +static const struct linear_range vdd_clamp_ranges[] = { + LINEAR_RANGE_INIT(706000, 0, 15, 15), + LINEAR_RANGE_INIT(798000, 0, 14, 14), + LINEAR_RANGE_INIT(828000, 0, 13, 13), + LINEAR_RANGE_INIT(861000, 0, 11, 11), + LINEAR_RANGE_INIT(862000, 0, 12, 12), + LINEAR_RANGE_INIT(889000, 0, 10, 10), + LINEAR_RANGE_INIT(918000, 0, 9, 9), + LINEAR_RANGE_INIT(946000, 0, 3, 3), + LINEAR_RANGE_INIT(952000, 0, 8, 8), + LINEAR_RANGE_INIT(978000, 0, 2, 2), + LINEAR_RANGE_INIT(1005000, 0, 1, 1), + LINEAR_RANGE_INIT(1030000, 0, 7, 7), + LINEAR_RANGE_INIT(1037000, 0, 0, 0), + LINEAR_RANGE_INIT(1058000, 0, 6, 6), + LINEAR_RANGE_INIT(1089000, 0, 5, 5), + LINEAR_RANGE_INIT(1120000, 0, 4, 4), +}; + +static const struct linear_range vdd_ranges[] = { + LINEAR_RANGE_INIT(900000, 100000, 0, 3), +}; + +static const struct linear_range vdd_sleep_ranges[] = { + LINEAR_RANGE_INIT(750000, 50000, 0, 3), +}; + +static const struct linear_range v14_ranges[] = { + LINEAR_RANGE_INIT(1200000, 50000, 0, 7), +}; + +static const struct linear_range v30_ranges[] = { + LINEAR_RANGE_INIT(3000000, 300000, 0, 1), +}; + +static const struct linear_range v18_ranges[] = { + LINEAR_RANGE_INIT(1200000, 600000, 0, 1), +}; + +static const struct linear_range v18p_ranges[] = { + LINEAR_RANGE_INIT(1800000, 0, 0, 0), +}; + +enum da1469x_rail { + VDD_CLAMP, + VDD_SLEEP, + VDD, + V14, + V18, + V18P, + V30, +}; + +struct regulator_da1469x_desc { + const struct linear_range *voltage_ranges; + const struct linear_range *current_ranges; + uint8_t voltage_range_count; + /* Bit from POWER_CTRL_REG that can be used for enabling rail */ + uint32_t enable_mask; + uint32_t voltage_idx_mask; + volatile uint32_t *dcdc_register; +}; + +static const struct regulator_da1469x_desc vdd_desc = { + .voltage_ranges = vdd_ranges, + .current_ranges = curren_ranges, + .voltage_range_count = ARRAY_SIZE(vdd_ranges), + .enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_CORE_ENABLE_Msk, + .voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_VDD_LEVEL_Msk, + .dcdc_register = &DCDC->DCDC_VDD_REG, +}; + +static const struct regulator_da1469x_desc vdd_sleep_desc = { + .voltage_ranges = vdd_sleep_ranges, + .voltage_range_count = ARRAY_SIZE(vdd_sleep_ranges), + .enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_CORE_RET_ENABLE_SLEEP_Msk, + .voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_VDD_SLEEP_LEVEL_Msk, +}; + +static const struct regulator_da1469x_desc vdd_clamp_desc = { + .voltage_ranges = vdd_clamp_ranges, + .voltage_range_count = ARRAY_SIZE(vdd_clamp_ranges), + .enable_mask = 0, + .voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_VDD_CLAMP_LEVEL_Msk, +}; + +static const struct regulator_da1469x_desc v14_desc = { + .voltage_ranges = v14_ranges, + .current_ranges = curren_ranges, + .voltage_range_count = ARRAY_SIZE(v14_ranges), + .enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk, + .voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_V14_LEVEL_Msk, + .dcdc_register = &DCDC->DCDC_V14_REG, +}; + +static const struct regulator_da1469x_desc v18_desc = { + .voltage_ranges = v18_ranges, + .current_ranges = curren_ranges, + .voltage_range_count = ARRAY_SIZE(v18_ranges), + .enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_1V8_ENABLE_Msk | + CRG_TOP_POWER_CTRL_REG_LDO_1V8_RET_ENABLE_SLEEP_Msk, + .voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_V18_LEVEL_Msk, + .dcdc_register = &DCDC->DCDC_V18_REG, +}; + +static const struct regulator_da1469x_desc v18p_desc = { + .voltage_ranges = v18p_ranges, + .current_ranges = curren_ranges, + .voltage_range_count = ARRAY_SIZE(v18p_ranges), + .enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_1V8P_ENABLE_Msk | + CRG_TOP_POWER_CTRL_REG_LDO_1V8P_RET_ENABLE_SLEEP_Msk, + .voltage_idx_mask = 0, + .dcdc_register = &DCDC->DCDC_V18P_REG, +}; + +static const struct regulator_da1469x_desc v30_desc = { + .voltage_ranges = v30_ranges, + .voltage_range_count = ARRAY_SIZE(v30_ranges), + .enable_mask = CRG_TOP_POWER_CTRL_REG_LDO_3V0_RET_ENABLE_SLEEP_Msk | + CRG_TOP_POWER_CTRL_REG_LDO_3V0_MODE_Msk, + .voltage_idx_mask = CRG_TOP_POWER_CTRL_REG_V30_LEVEL_Msk, +}; + +#define DA1469X_LDO_VDD_CLAMP_RET 0 +#define DA1469X_LDO_VDD_SLEEP_RET 0 +#define DA1469X_LDO_VDD_RET CRG_TOP_POWER_CTRL_REG_LDO_CORE_RET_ENABLE_SLEEP_Msk +#define DA1469X_LDO_V14_RET 0 +#define DA1469X_LDO_V18_RET CRG_TOP_POWER_CTRL_REG_LDO_1V8_RET_ENABLE_SLEEP_Msk +#define DA1469X_LDO_V18P_RET CRG_TOP_POWER_CTRL_REG_LDO_1V8P_RET_ENABLE_SLEEP_Msk +#define DA1469X_LDO_V30_RET CRG_TOP_POWER_CTRL_REG_LDO_3V0_RET_ENABLE_SLEEP_Msk + +struct regulator_da1469x_config { + struct regulator_common_config common; + enum da1469x_rail rail; + const struct regulator_da1469x_desc *desc; + uint32_t power_bits; + uint32_t dcdc_bits; +}; + +struct regulator_da1469x_data { + struct regulator_common_data common; +}; + +static int regulator_da1469x_enable(const struct device *dev) +{ + const struct regulator_da1469x_config *config = dev->config; + uint32_t reg_val; + + if (config->desc->enable_mask & config->power_bits) { + reg_val = CRG_TOP->POWER_CTRL_REG & ~(config->desc->enable_mask); + reg_val |= config->power_bits & config->desc->enable_mask; + CRG_TOP->POWER_CTRL_REG |= reg_val; + } + + if (config->desc->dcdc_register) { + reg_val = *config->desc->dcdc_register & + ~(DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_HV_Msk | + DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_LV_Msk); + reg_val |= config->dcdc_bits; + *config->desc->dcdc_register = reg_val; + } + + /* + * Enable DCDC if: + * 1. it was not already enabled, and + * 2. VBAT is above minimal value + * 3. Just turned on rail requested DCDC + */ + if (((DCDC->DCDC_CTRL1_REG & DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk) == 0) && + (CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_COMP_VBAT_HIGH_Msk) && + config->dcdc_bits & DCDC_REQUESTED) { + DCDC->DCDC_CTRL1_REG |= DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk; + } + + return 0; +} + +static int regulator_da1469x_disable(const struct device *dev) +{ + const struct regulator_da1469x_config *config = dev->config; + uint32_t reg_val; + + if (config->desc->enable_mask & config->power_bits) { + CRG_TOP->POWER_CTRL_REG &= ~(config->desc->enable_mask & + config->power_bits); + } + if (config->desc->dcdc_register) { + reg_val = *config->desc->dcdc_register & + ~(DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_HV_Msk | + DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_LV_Msk); + *config->desc->dcdc_register = reg_val; + } + + /* Turn off DCDC if it's no longer requested by any rail */ + if ((DCDC->DCDC_CTRL1_REG & DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk) && + (DCDC->DCDC_VDD_REG & DCDC_REQUESTED) == 0 && + (DCDC->DCDC_V14_REG & DCDC_REQUESTED) == 0 && + (DCDC->DCDC_V18_REG & DCDC_REQUESTED) == 0 && + (DCDC->DCDC_V18P_REG & DCDC_REQUESTED) == 0) { + DCDC->DCDC_CTRL1_REG &= ~DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk; + } + + return 0; +} + +static unsigned int regulator_da1469x_count_voltages(const struct device *dev) +{ + const struct regulator_da1469x_config *config = dev->config; + + return linear_range_group_values_count(config->desc->voltage_ranges, + config->desc->voltage_range_count); +} + +static int regulator_da1469x_list_voltage(const struct device *dev, + unsigned int idx, + int32_t *volt_uv) +{ + const struct regulator_da1469x_config *config = dev->config; + + if (config->desc->voltage_ranges) { + return linear_range_group_get_value(config->desc->voltage_ranges, + config->desc->voltage_range_count, + idx, volt_uv); + } + + return -ENOTSUP; +} + +static int regulator_da1469x_set_voltage(const struct device *dev, int32_t min_uv, + int32_t max_uv) +{ + int ret; + const struct regulator_da1469x_config *config = dev->config; + uint16_t idx; + uint32_t mask; + + ret = linear_range_group_get_win_index(config->desc->voltage_ranges, + config->desc->voltage_range_count, + min_uv, max_uv, &idx); + + if (ret == 0) { + mask = config->desc->voltage_idx_mask; + /* + * Mask is 0 for V18. + * Setting value 1.8V is accepted since range is valid and already checked. + */ + if (mask) { + CRG_TOP->POWER_CTRL_REG = (CRG_TOP->POWER_CTRL_REG & ~mask) | + FIELD_PREP(mask, idx); + } + } + + return ret; +} + +static int regulator_da1469x_get_voltage(const struct device *dev, + int32_t *volt_uv) +{ + const struct regulator_da1469x_config *config = dev->config; + uint16_t idx; + + if (config->desc->voltage_idx_mask) { + idx = FIELD_GET(CRG_TOP->POWER_CTRL_REG, config->desc->voltage_idx_mask); + } else { + idx = 0; + } + + return linear_range_group_get_value(config->desc->voltage_ranges, + config->desc->voltage_range_count, idx, volt_uv); +} + +static int regulator_da1469x_set_current_limit(const struct device *dev, + int32_t min_ua, int32_t max_ua) +{ + const struct regulator_da1469x_config *config = dev->config; + int ret; + uint16_t idx; + uint32_t reg_val; + + if (config->desc->current_ranges == NULL) { + return -ENOTSUP; + } + + ret = linear_range_group_get_win_index(config->desc->current_ranges, + 1, + min_ua, max_ua, &idx); + if (ret) { + return ret; + } + + /* All registers have same bits layout */ + reg_val = *config->desc->dcdc_register & ~(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_HV_Msk | + DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_LV_Msk | + DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MIN_Msk); + reg_val |= FIELD_PREP(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_HV_Msk, idx); + reg_val |= FIELD_PREP(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_LV_Msk, idx); + reg_val |= FIELD_PREP(DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MIN_Msk, idx); + + *config->desc->dcdc_register = reg_val; + + return ret; +} + +static int regulator_da1469x_get_current_limit(const struct device *dev, + int32_t *curr_ua) +{ + const struct regulator_da1469x_config *config = dev->config; + int ret; + uint16_t idx; + + if (config->desc->current_ranges == NULL) { + return -ENOTSUP; + } + idx = FIELD_GET(*config->desc->dcdc_register, + DCDC_DCDC_V14_REG_DCDC_V14_CUR_LIM_MAX_HV_Msk); + ret = linear_range_group_get_value(config->desc->current_ranges, 1, idx, curr_ua); + + return ret; +} + +static const struct regulator_driver_api regulator_da1469x_api = { + .enable = regulator_da1469x_enable, + .disable = regulator_da1469x_disable, + .count_voltages = regulator_da1469x_count_voltages, + .list_voltage = regulator_da1469x_list_voltage, + .set_voltage = regulator_da1469x_set_voltage, + .get_voltage = regulator_da1469x_get_voltage, + .set_current_limit = regulator_da1469x_set_current_limit, + .get_current_limit = regulator_da1469x_get_current_limit, +}; + +static int regulator_da1469x_init(const struct device *dev) +{ + const struct regulator_da1469x_config *config = dev->config; + + regulator_common_data_init(dev); + + if ((config->rail == V30) && + (config->power_bits & CRG_TOP_POWER_CTRL_REG_LDO_3V0_REF_Msk)) { + CRG_TOP->POWER_CTRL_REG |= CRG_TOP_POWER_CTRL_REG_LDO_3V0_REF_Msk; + } + + return regulator_common_init(dev, 0); +} + +#define REGULATOR_DA1469X_DEFINE(node, id, rail_id) \ + static struct regulator_da1469x_data data_##id; \ + \ + static const struct regulator_da1469x_config config_##id = { \ + .common = REGULATOR_DT_COMMON_CONFIG_INIT(node), \ + .desc = &id ## _desc, \ + .power_bits = \ + (DT_PROP(node, renesas_regulator_v30_clamp) * \ + CRG_TOP_POWER_CTRL_REG_CLAMP_3V0_VBAT_ENABLE_Msk) | \ + (DT_PROP(node, renesas_regulator_v30_vbus) * \ + DA1469X_LDO_3V0_MODE_VBAT) | \ + (DT_PROP(node, renesas_regulator_v30_vbat) * \ + DA1469X_LDO_3V0_MODE_VBUS) | \ + (DT_PROP(node, renesas_regulator_sleep_ldo) * \ + (DA1469X_LDO_ ## rail_id ##_RET)) | \ + (DT_PROP(node, renesas_regulator_v30_ref_bandgap) * \ + CRG_TOP_POWER_CTRL_REG_LDO_3V0_REF_Msk), \ + .dcdc_bits = \ + (DT_PROP(node, renesas_regulator_dcdc_vbat_high) * \ + DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_HV_Msk) | \ + (DT_PROP(node, renesas_regulator_dcdc_vbat_low) * \ + DCDC_DCDC_VDD_REG_DCDC_VDD_ENABLE_LV_Msk) \ + }; \ + DEVICE_DT_DEFINE(node, regulator_da1469x_init, NULL, &data_##id, \ + &config_##id, PRE_KERNEL_1, \ + CONFIG_REGULATOR_DA1469X_INIT_PRIORITY, \ + ®ulator_da1469x_api); + +#define REGULATOR_DA1469X_DEFINE_COND(inst, child, source) \ + COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \ + (REGULATOR_DA1469X_DEFINE( \ + DT_INST_CHILD(inst, child), child, source)), \ + ()) + +#define REGULATOR_DA1469X_DEFINE_ALL(inst) \ + REGULATOR_DA1469X_DEFINE_COND(inst, vdd_clamp, VDD_CLAMP) \ + REGULATOR_DA1469X_DEFINE_COND(inst, vdd_sleep, VDD_SLEEP) \ + REGULATOR_DA1469X_DEFINE_COND(inst, vdd, VDD) \ + REGULATOR_DA1469X_DEFINE_COND(inst, v14, V14) \ + REGULATOR_DA1469X_DEFINE_COND(inst, v18, V18) \ + REGULATOR_DA1469X_DEFINE_COND(inst, v18p, V18P) \ + REGULATOR_DA1469X_DEFINE_COND(inst, v30, V30) \ + +DT_INST_FOREACH_STATUS_OKAY(REGULATOR_DA1469X_DEFINE_ALL) diff --git a/dts/arm/renesas/smartbond/da1469x.dtsi b/dts/arm/renesas/smartbond/da1469x.dtsi index 6916b2b804c..72f7e6fa89f 100644 --- a/dts/arm/renesas/smartbond/da1469x.dtsi +++ b/dts/arm/renesas/smartbond/da1469x.dtsi @@ -86,6 +86,52 @@ clock-src = <&rc32k>; status = "okay"; }; + + regulators { + compatible = "renesas,smartbond-regulator"; + vdd: VDD { + regulator-init-microvolt = <900000>; + regulator-boot-on; + renesas,regulator-sleep-ldo; + renesas,regulator-dcdc-vbat-high; + renesas,regulator-dcdc-vbat-low; + }; + vdd_clamp: VDD_CLAMP { + regulator-boot-on; + regulator-always-on; + regulator-init-microvolt = <706000>; + }; + vdd_sleep: VDD_SLEEP { + regulator-boot-on; + regulator-init-microvolt = <750000>; + }; + v14: V14 { + regulator-init-microvolt = <1400000>; + regulator-boot-on; + renesas,regulator-dcdc-vbat-high; + renesas,regulator-dcdc-vbat-low; + }; + v18: V18 { + regulator-init-microvolt = <1800000>; + regulator-boot-on; + renesas,regulator-dcdc-vbat-high; + }; + v18p: V18P { + regulator-init-microvolt = <1800000>; + regulator-boot-on; + renesas,regulator-sleep-ldo; + renesas,regulator-dcdc-vbat-high; + }; + v30: V30 { + regulator-init-microvolt = <3000000>; + regulator-boot-on; + renesas,regulator-sleep-ldo; + renesas,regulator-v30-vbus; + renesas,regulator-v30-vbat; + renesas,regulator-v30-clamp; + renesas,regulator-v30-ref-bandgap; + }; + }; }; soc { diff --git a/dts/bindings/regulator/renesas,da1469x-regulator.yaml b/dts/bindings/regulator/renesas,da1469x-regulator.yaml new file mode 100644 index 00000000000..2537b7e09d7 --- /dev/null +++ b/dts/bindings/regulator/renesas,da1469x-regulator.yaml @@ -0,0 +1,46 @@ +# Copyright (c), 2023 Renesas Electronics Corporation +# SPDX -License-Identifier: Apache-2.0 + +description: | + Renesas Smartbond(tm) LDO and DCDC regulators + +compatible: "renesas,smartbond-regulator" + +child-binding: + include: + - name: regulator.yaml + property-allowlist: + - regulator-always-on + - regulator-boot-on + - regulator-init-microvolt + - regulator-initial-mode + - regulator-max-microamp + properties: + renesas,regulator-v30-ref-bandgap: + type: boolean + description: | + Selects reference source for V30 LDO to Bandgap output. + renesas,regulator-v30-clamp: + type: boolean + description: | + Enables clamp that can supply V30 from VBAT. + renesas,regulator-v30-vbus: + type: boolean + description: | + Allow V30 to be powered from VBUS. + renesas,regulator-v30-vbat: + type: boolean + description: | + Allow V30 to be powered from VBAT. + renesas,regulator-dcdc-vbat-high: + type: boolean + description: | + Enable DCDC in high battery voltage mode. + renesas,regulator-dcdc-vbat-low: + type: boolean + description: | + Enable DCDC in low battery voltage mode. + renesas,regulator-sleep-ldo: + type: boolean + description: | + Enable LDO in sleep mode.