/* * Copyright (c) 2022 Nordic Semiconductor ASA * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nordic_npm6001_regulator #include #include #include #include #include #include #include /* nPM6001 voltage sources */ enum npm6001_sources { NPM6001_SOURCE_BUCK0, NPM6001_SOURCE_BUCK1, NPM6001_SOURCE_BUCK2, NPM6001_SOURCE_BUCK3, NPM6001_SOURCE_LDO0, NPM6001_SOURCE_LDO1, }; /* nPM6001 regulator related registers */ #define NPM6001_TASKS_START_BUCK3 0x02U #define NPM6001_TASKS_START_LDO0 0x03U #define NPM6001_TASKS_START_LDO1 0x04U #define NPM6001_TASKS_STOP_BUCK3 0x08U #define NPM6001_TASKS_STOP_LDO0 0x09U #define NPM6001_TASKS_STOP_LDO1 0x0AU #define NPM6001_TASKS_UPDATE_VOUTPWM 0x0EU #define NPM6001_EVENTS_THWARN 0x1EU #define NPM6001_EVENTS_BUCK0OC 0x1FU #define NPM6001_EVENTS_BUCK1OC 0x20U #define NPM6001_EVENTS_BUCK2OC 0x21U #define NPM6001_EVENTS_BUCK3OC 0x22U #define NPM6001_BUCK0VOUTULP 0x3AU #define NPM6001_BUCK1VOUTULP 0x3CU #define NPM6001_BUCK2VOUTULP 0x40U #define NPM6001_BUCK3VOUT 0x45U #define NPM6001_LDO0VOUT 0x46U #define NPM6001_BUCK0CONFPWMMODE 0x4AU #define NPM6001_BUCK1CONFPWMMODE 0x4BU #define NPM6001_BUCK2CONFPWMMODE 0x4CU #define NPM6001_BUCK3CONFPWMMODE 0x4DU #define NPM6001_BUCKMODEPADCONF 0x4EU #define NPM6001_PADDRIVESTRENGTH 0x53U #define NPM6001_OVERRIDEPWRUPBUCK 0xABU /* nPM6001 LDO0VOUT values */ #define NPM6001_LDO0VOUT_SET1V8 0x06U #define NPM6001_LDO0VOUT_SET2V1 0x0BU #define NPM6001_LDO0VOUT_SET2V41 0x10U #define NPM6001_LDO0VOUT_SET2V7 0x15U #define NPM6001_LDO0VOUT_SET3V0 0x1AU #define NPM6001_LDO0VOUT_SET3V3 0x1EU /* nPM6001 BUCKXCONFPWMMODE fields */ #define NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_MSK 0x8U #define NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_POS 3 #define NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM BIT(NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_POS) /* nPM6001 OVERRIDEPWRUPBUCK fields */ #define NPM6001_OVERRIDEPWRUPBUCK_BUCK1DISABLE_MSK 0x22U #define NPM6001_OVERRIDEPWRUPBUCK_BUCK2DISABLE_MSK 0x44U #define NPM6001_OVERRIDEPWRUPBUCK_BUCK1DISABLE BIT(1) #define NPM6001_OVERRIDEPWRUPBUCK_BUCK2DISABLE BIT(2) struct regulator_npm6001_config { struct regulator_common_config common; struct i2c_dt_spec i2c; uint8_t source; }; struct regulator_npm6001_data { struct regulator_common_data data; }; struct regulator_npm6001_vmap { uint8_t reg_val; int32_t volt_uv; }; static const struct linear_range buck0_range = LINEAR_RANGE_INIT(1800000, 100000U, 0x0U, 0xFU); static const struct linear_range buck1_range = LINEAR_RANGE_INIT(700000, 50000U, 0x0U, 0xEU); static const struct linear_range buck2_range = LINEAR_RANGE_INIT(1200000, 50000U, 0xAU, 0xEU); static const struct linear_range buck3_range = LINEAR_RANGE_INIT(500000, 25000U, 0x0U, 0x70U); static const struct regulator_npm6001_vmap ldo0_voltages[] = { {NPM6001_LDO0VOUT_SET1V8, 1800000}, {NPM6001_LDO0VOUT_SET2V1, 2100000}, {NPM6001_LDO0VOUT_SET2V41, 2410000}, {NPM6001_LDO0VOUT_SET2V7, 2700000}, {NPM6001_LDO0VOUT_SET3V0, 3000000}, {NPM6001_LDO0VOUT_SET3V3, 3300000}, }; static int regulator_npm6001_ldo0_list_voltage(const struct device *dev, unsigned int idx, int32_t *volt_uv) { if (idx >= ARRAY_SIZE(ldo0_voltages)) { return -EINVAL; } *volt_uv = ldo0_voltages[idx].volt_uv; return 0; } static int regulator_npm6001_buck012_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv, const struct linear_range *range, uint8_t vout_reg, uint8_t conf_reg) { const struct regulator_npm6001_config *config = dev->config; uint8_t conf, buf[3]; uint16_t idx; int ret; ret = linear_range_get_win_index(range, min_uv, max_uv, &idx); if (ret == -EINVAL) { return ret; } /* force PWM mode when updating voltage */ ret = i2c_reg_read_byte_dt(&config->i2c, conf_reg, &conf); if (ret < 0) { return ret; } if ((conf & NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM) == 0U) { ret = i2c_reg_write_byte_dt(&config->i2c, conf_reg, conf | NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM); if (ret < 0) { return ret; } } /* write voltage in both ULP/PWM registers */ buf[0] = vout_reg; buf[1] = (uint8_t)idx; buf[2] = (uint8_t)idx; ret = i2c_write_dt(&config->i2c, buf, sizeof(buf)); if (ret < 0) { return ret; } ret = i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_UPDATE_VOUTPWM, 1U); if (ret < 0) { return ret; } /* restore HYS mode if enabled before */ if ((conf & NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM) == 0U) { ret = i2c_reg_write_byte_dt(&config->i2c, conf_reg, conf); if (ret < 0) { return ret; } } return 0; } static int regulator_npm6001_buck3_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) { const struct regulator_npm6001_config *config = dev->config; uint16_t idx; uint8_t conf; int ret; ret = linear_range_get_win_index(&buck3_range, min_uv, max_uv, &idx); if (ret == -EINVAL) { return ret; } /* force PWM mode when updating voltage */ ret = i2c_reg_read_byte_dt(&config->i2c, NPM6001_BUCK3CONFPWMMODE, &conf); if (ret < 0) { return ret; } if ((conf & NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM) == 0U) { ret = i2c_reg_write_byte_dt(&config->i2c, NPM6001_BUCK3CONFPWMMODE, conf | NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM); if (ret < 0) { return ret; } } ret = i2c_reg_write_byte_dt(&config->i2c, NPM6001_BUCK3VOUT, (uint8_t)idx); if (ret < 0) { return ret; } /* restore HYS mode if enabled before */ if ((conf & NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM) == 0U) { ret = i2c_reg_write_byte_dt(&config->i2c, NPM6001_BUCK3CONFPWMMODE, conf); if (ret < 0) { return ret; } } return 0; } static int regulator_npm6001_ldo0_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) { const struct regulator_npm6001_config *config = dev->config; uint8_t val = 0U; size_t i; for (i = 0U; i < ARRAY_SIZE(ldo0_voltages); i++) { if ((min_uv <= ldo0_voltages[i].volt_uv) && (max_uv >= ldo0_voltages[i].volt_uv)) { val = ldo0_voltages[i].reg_val; break; } } if (i == ARRAY_SIZE(ldo0_voltages)) { return -EINVAL; } return i2c_reg_write_byte_dt(&config->i2c, NPM6001_LDO0VOUT, val); } static int regulator_npm6001_buck0123_get_voltage(const struct device *dev, const struct linear_range *range, uint8_t vout_reg, int32_t *volt_uv) { const struct regulator_npm6001_config *config = dev->config; uint8_t idx; int ret; ret = i2c_reg_read_byte_dt(&config->i2c, vout_reg, &idx); if (ret < 0) { return ret; } return linear_range_get_value(range, idx, volt_uv); } static int regulator_npm6001_ldo0_get_voltage(const struct device *dev, int32_t *volt_uv) { const struct regulator_npm6001_config *config = dev->config; uint8_t val; int ret; ret = i2c_reg_read_byte_dt(&config->i2c, NPM6001_LDO0VOUT, &val); if (ret < 0) { return ret; } for (size_t i = 0U; i < ARRAY_SIZE(ldo0_voltages); i++) { if (val == ldo0_voltages[i].reg_val) { *volt_uv = ldo0_voltages[i].volt_uv; return 0; } } __ASSERT(NULL, "Unexpected voltage"); return -EINVAL; } static unsigned int regulator_npm6001_count_voltages(const struct device *dev) { const struct regulator_npm6001_config *config = dev->config; switch (config->source) { case NPM6001_SOURCE_BUCK0: return linear_range_values_count(&buck0_range); case NPM6001_SOURCE_BUCK1: return linear_range_values_count(&buck1_range); case NPM6001_SOURCE_BUCK2: return linear_range_values_count(&buck2_range); case NPM6001_SOURCE_BUCK3: return linear_range_values_count(&buck3_range); case NPM6001_SOURCE_LDO0: return 6U; case NPM6001_SOURCE_LDO1: return 1U; default: __ASSERT(NULL, "Unexpected source"); } return 0; } static int regulator_npm6001_list_voltage(const struct device *dev, unsigned int idx, int32_t *volt_uv) { const struct regulator_npm6001_config *config = dev->config; switch (config->source) { case NPM6001_SOURCE_BUCK0: return linear_range_get_value(&buck0_range, idx, volt_uv); case NPM6001_SOURCE_BUCK1: return linear_range_get_value(&buck1_range, idx, volt_uv); case NPM6001_SOURCE_BUCK2: return linear_range_get_value(&buck2_range, idx + buck2_range.min_idx, volt_uv); case NPM6001_SOURCE_BUCK3: return linear_range_get_value(&buck3_range, idx, volt_uv); case NPM6001_SOURCE_LDO0: return regulator_npm6001_ldo0_list_voltage(dev, idx, volt_uv); case NPM6001_SOURCE_LDO1: *volt_uv = 1800000; break; default: __ASSERT(NULL, "Unexpected source"); } return 0; } static int regulator_npm6001_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) { const struct regulator_npm6001_config *config = dev->config; switch (config->source) { case NPM6001_SOURCE_BUCK0: return regulator_npm6001_buck012_set_voltage(dev, min_uv, max_uv, &buck0_range, NPM6001_BUCK0VOUTULP, NPM6001_BUCK0CONFPWMMODE); case NPM6001_SOURCE_BUCK1: return regulator_npm6001_buck012_set_voltage(dev, min_uv, max_uv, &buck1_range, NPM6001_BUCK1VOUTULP, NPM6001_BUCK1CONFPWMMODE); case NPM6001_SOURCE_BUCK2: return regulator_npm6001_buck012_set_voltage(dev, min_uv, max_uv, &buck2_range, NPM6001_BUCK2VOUTULP, NPM6001_BUCK2CONFPWMMODE); case NPM6001_SOURCE_BUCK3: return regulator_npm6001_buck3_set_voltage(dev, min_uv, max_uv); case NPM6001_SOURCE_LDO0: return regulator_npm6001_ldo0_set_voltage(dev, min_uv, max_uv); case NPM6001_SOURCE_LDO1: if ((min_uv != 1800000) && (max_uv != 1800000)) { return -EINVAL; } break; default: __ASSERT(NULL, "Unexpected source"); } return 0; } static int regulator_npm6001_get_voltage(const struct device *dev, int32_t *volt_uv) { const struct regulator_npm6001_config *config = dev->config; switch (config->source) { case NPM6001_SOURCE_BUCK0: return regulator_npm6001_buck0123_get_voltage(dev, &buck0_range, NPM6001_BUCK0VOUTULP, volt_uv); case NPM6001_SOURCE_BUCK1: return regulator_npm6001_buck0123_get_voltage(dev, &buck1_range, NPM6001_BUCK1VOUTULP, volt_uv); case NPM6001_SOURCE_BUCK2: return regulator_npm6001_buck0123_get_voltage(dev, &buck2_range, NPM6001_BUCK2VOUTULP, volt_uv); case NPM6001_SOURCE_BUCK3: return regulator_npm6001_buck0123_get_voltage(dev, &buck3_range, NPM6001_BUCK3VOUT, volt_uv); case NPM6001_SOURCE_LDO0: return regulator_npm6001_ldo0_get_voltage(dev, volt_uv); case NPM6001_SOURCE_LDO1: *volt_uv = 1800000U; break; default: __ASSERT(NULL, "Unexpected source"); } return 0; } static int regulator_npm6001_set_mode(const struct device *dev, regulator_mode_t mode) { const struct regulator_npm6001_config *config = dev->config; uint8_t conf_reg; if (mode > NPM6001_MODE_PWM) { return -ENOTSUP; } switch (config->source) { case NPM6001_SOURCE_BUCK0: conf_reg = NPM6001_BUCK0CONFPWMMODE; break; case NPM6001_SOURCE_BUCK1: conf_reg = NPM6001_BUCK1CONFPWMMODE; break; case NPM6001_SOURCE_BUCK2: conf_reg = NPM6001_BUCK2CONFPWMMODE; break; case NPM6001_SOURCE_BUCK3: conf_reg = NPM6001_BUCK3CONFPWMMODE; break; default: return -ENOTSUP; } return i2c_reg_update_byte_dt(&config->i2c, conf_reg, NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_MSK, mode << NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_POS); } static int regulator_npm6001_get_mode(const struct device *dev, regulator_mode_t *mode) { const struct regulator_npm6001_config *config = dev->config; uint8_t conf_reg, conf; int ret; switch (config->source) { case NPM6001_SOURCE_BUCK0: conf_reg = NPM6001_BUCK0CONFPWMMODE; break; case NPM6001_SOURCE_BUCK1: conf_reg = NPM6001_BUCK1CONFPWMMODE; break; case NPM6001_SOURCE_BUCK2: conf_reg = NPM6001_BUCK2CONFPWMMODE; break; case NPM6001_SOURCE_BUCK3: conf_reg = NPM6001_BUCK3CONFPWMMODE; break; default: return -ENOTSUP; } ret = i2c_reg_read_byte_dt(&config->i2c, conf_reg, &conf); if (ret < 0) { return ret; } *mode = (conf & NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_MSK) >> NPM6001_BUCKXCONFPWMMODE_SETFORCEPWM_POS; return 0; } static int regulator_npm6001_enable(const struct device *dev) { const struct regulator_npm6001_config *config = dev->config; switch (config->source) { case NPM6001_SOURCE_BUCK1: return i2c_reg_update_byte_dt(&config->i2c, NPM6001_OVERRIDEPWRUPBUCK, NPM6001_OVERRIDEPWRUPBUCK_BUCK1DISABLE_MSK, 0U); case NPM6001_SOURCE_BUCK2: return i2c_reg_update_byte_dt(&config->i2c, NPM6001_OVERRIDEPWRUPBUCK, NPM6001_OVERRIDEPWRUPBUCK_BUCK2DISABLE_MSK, 0U); case NPM6001_SOURCE_BUCK3: return i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_START_BUCK3, 1U); case NPM6001_SOURCE_LDO0: return i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_START_LDO0, 1U); case NPM6001_SOURCE_LDO1: return i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_START_LDO1, 1U); default: return 0; } } static int regulator_npm6001_disable(const struct device *dev) { const struct regulator_npm6001_config *config = dev->config; switch (config->source) { case NPM6001_SOURCE_BUCK1: return i2c_reg_update_byte_dt(&config->i2c, NPM6001_OVERRIDEPWRUPBUCK, NPM6001_OVERRIDEPWRUPBUCK_BUCK1DISABLE_MSK, NPM6001_OVERRIDEPWRUPBUCK_BUCK1DISABLE); case NPM6001_SOURCE_BUCK2: return i2c_reg_update_byte_dt(&config->i2c, NPM6001_OVERRIDEPWRUPBUCK, NPM6001_OVERRIDEPWRUPBUCK_BUCK2DISABLE_MSK, NPM6001_OVERRIDEPWRUPBUCK_BUCK2DISABLE); case NPM6001_SOURCE_BUCK3: return i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_STOP_BUCK3, 1U); case NPM6001_SOURCE_LDO0: return i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_STOP_LDO0, 1U); case NPM6001_SOURCE_LDO1: return i2c_reg_write_byte_dt(&config->i2c, NPM6001_TASKS_STOP_LDO1, 1U); default: return 0; } } static int regulator_npm6001_get_error_flags(const struct device *dev, regulator_error_flags_t *flags) { const struct regulator_npm6001_config *config = dev->config; uint8_t oc_reg, val; int ret; *flags = 0U; /* read thermal warning */ ret = i2c_reg_read_byte_dt(&config->i2c, NPM6001_EVENTS_THWARN, &val); if (ret < 0) { return ret; } if (val != 0U) { /* clear thermal warning */ ret = i2c_reg_write_byte_dt(&config->i2c, NPM6001_EVENTS_THWARN, 0U); if (ret < 0) { return ret; } *flags |= REGULATOR_ERROR_OVER_TEMP; } /* read overcurrent event */ switch (config->source) { case NPM6001_SOURCE_BUCK0: oc_reg = NPM6001_EVENTS_BUCK0OC; break; case NPM6001_SOURCE_BUCK1: oc_reg = NPM6001_EVENTS_BUCK1OC; break; case NPM6001_SOURCE_BUCK2: oc_reg = NPM6001_EVENTS_BUCK2OC; break; case NPM6001_SOURCE_BUCK3: oc_reg = NPM6001_EVENTS_BUCK3OC; break; default: return 0; } ret = i2c_reg_read_byte_dt(&config->i2c, oc_reg, &val); if (ret < 0) { return ret; } if (val != 0U) { /* clear overcurrent event */ ret = i2c_reg_write_byte_dt(&config->i2c, oc_reg, 0U); if (ret < 0) { return ret; } *flags |= REGULATOR_ERROR_OVER_CURRENT; } return 0; } static int regulator_npm6001_init(const struct device *dev) { const struct regulator_npm6001_config *config = dev->config; bool is_enabled; regulator_common_data_init(dev); if (!i2c_is_ready_dt(&config->i2c)) { return -ENODEV; } /* BUCK1/2 are ON by default */ is_enabled = (config->source == NPM6001_SOURCE_BUCK1) || (config->source == NPM6001_SOURCE_BUCK2); return regulator_common_init(dev, is_enabled); } static DEVICE_API(regulator, api) = { .enable = regulator_npm6001_enable, .disable = regulator_npm6001_disable, .count_voltages = regulator_npm6001_count_voltages, .list_voltage = regulator_npm6001_list_voltage, .set_voltage = regulator_npm6001_set_voltage, .get_voltage = regulator_npm6001_get_voltage, .set_mode = regulator_npm6001_set_mode, .get_mode = regulator_npm6001_get_mode, .get_error_flags = regulator_npm6001_get_error_flags, }; #define REGULATOR_NPM6001_DEFINE(node_id, id, _source) \ static struct regulator_npm6001_data data_##id; \ \ static const struct regulator_npm6001_config config_##id = { \ .common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id), \ .i2c = I2C_DT_SPEC_GET(DT_GPARENT(node_id)), \ .source = _source, \ }; \ \ DEVICE_DT_DEFINE(node_id, regulator_npm6001_init, NULL, &data_##id, &config_##id, \ POST_KERNEL, CONFIG_REGULATOR_NPM6001_INIT_PRIORITY, &api); #define REGULATOR_NPM6001_DEFINE_COND(inst, child, source) \ COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \ (REGULATOR_NPM6001_DEFINE(DT_INST_CHILD(inst, child), child##inst, source)), \ ()) #define REGULATOR_NPM6001_DEFINE_ALL(inst) \ REGULATOR_NPM6001_DEFINE_COND(inst, buck0, NPM6001_SOURCE_BUCK0) \ REGULATOR_NPM6001_DEFINE_COND(inst, buck1, NPM6001_SOURCE_BUCK1) \ REGULATOR_NPM6001_DEFINE_COND(inst, buck2, NPM6001_SOURCE_BUCK2) \ REGULATOR_NPM6001_DEFINE_COND(inst, buck3, NPM6001_SOURCE_BUCK3) \ REGULATOR_NPM6001_DEFINE_COND(inst, ldo0, NPM6001_SOURCE_LDO0) \ REGULATOR_NPM6001_DEFINE_COND(inst, ldo1, NPM6001_SOURCE_LDO1) DT_INST_FOREACH_STATUS_OKAY(REGULATOR_NPM6001_DEFINE_ALL)