/* * Copyright (c) 2023 Nordic Semiconductor ASA * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT adi_adp5360_regulator #include #include #include #include #include #include /* ADP5360 regulator related registers */ #define ADP5360_BUCK_CFG 0x29U #define ADP5360_BUCK_OUTPUT 0x2AU #define ADP5360_BUCKBST_CFG 0x2BU #define ADP5360_BUCKBST_OUTPUT 0x2CU /* Buck/boost configure register. */ #define ADP5360_BUCK_CFG_SS_MSK GENMASK(7, 6) #define ADP5360_BUCK_CFG_SS_POS 6U #define ADP5360_BUCK_CFG_BST_ILIM_MSK GENMASK(5, 3) #define ADP5360_BUCK_CFG_BST_ILIM_POS 3U #define ADP5360_BUCK_CFG_BUCK_ILIM_MSK GENMASK(5, 3) #define ADP5360_BUCK_CFG_BUCK_ILIM_POS 3U #define ADP5360_BUCK_CFG_BUCK_MODE_MSK BIT(3) #define ADP5360_BUCK_CFG_BUCK_MODE_POS 3U #define ADP5360_BUCK_CFG_STP_MSK BIT(2) #define ADP5360_BUCK_CFG_DISCHG_MSK BIT(1) #define ADP5360_BUCK_CFG_EN_MSK BIT(0) /* Buck/boost output voltage setting register. */ #define ADP5360_BUCK_OUTPUT_VOUT_MSK GENMASK(5, 0) #define ADP5360_BUCK_OUTPUT_VOUT_POS 0U #define ADP5360_BUCK_OUTPUT_DLY_MSK GENMASK(7, 6) #define ADP5360_BUCK_OUTPUT_DLY_POS 6U struct regulator_adp5360_desc { uint8_t cfg_reg; uint8_t out_reg; bool has_modes; const struct linear_range *ranges; uint8_t nranges; }; static const struct linear_range buck_ranges[] = { LINEAR_RANGE_INIT(600000, 50000U, 0x0U, 0x3FU), }; static const struct regulator_adp5360_desc buck_desc = { .cfg_reg = ADP5360_BUCK_CFG, .out_reg = ADP5360_BUCK_OUTPUT, .has_modes = true, .ranges = buck_ranges, .nranges = ARRAY_SIZE(buck_ranges), }; static const struct linear_range buckboost_ranges[] = { LINEAR_RANGE_INIT(1800000, 100000U, 0x0U, 0x0BU), LINEAR_RANGE_INIT(2950000, 50000U, 0xCU, 0x3FU), }; static const struct regulator_adp5360_desc buckboost_desc = { .cfg_reg = ADP5360_BUCKBST_CFG, .out_reg = ADP5360_BUCKBST_OUTPUT, .has_modes = false, .ranges = buckboost_ranges, .nranges = ARRAY_SIZE(buckboost_ranges), }; struct regulator_adp5360_config { struct regulator_common_config common; struct i2c_dt_spec i2c; const struct regulator_adp5360_desc *desc; int8_t dly_idx; int8_t ss_idx; int8_t ilim_idx; bool stp_en; bool dis_en; }; struct regulator_adp5360_data { struct regulator_common_data data; }; static unsigned int regulator_adp5360_count_voltages(const struct device *dev) { const struct regulator_adp5360_config *config = dev->config; return linear_range_group_values_count(config->desc->ranges, config->desc->nranges); } static int regulator_adp5360_list_voltage(const struct device *dev, unsigned int idx, int32_t *volt_uv) { const struct regulator_adp5360_config *config = dev->config; return linear_range_group_get_value(config->desc->ranges, config->desc->nranges, idx, volt_uv); } static int regulator_adp5360_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) { const struct regulator_adp5360_config *config = dev->config; uint16_t idx; int ret; ret = linear_range_group_get_win_index(config->desc->ranges, config->desc->nranges, min_uv, max_uv, &idx); if (ret == -EINVAL) { return ret; } return i2c_reg_update_byte_dt(&config->i2c, config->desc->out_reg, ADP5360_BUCK_OUTPUT_VOUT_MSK, (uint8_t)idx << ADP5360_BUCK_OUTPUT_VOUT_POS); } static int regulator_adp5360_get_voltage(const struct device *dev, int32_t *volt_uv) { const struct regulator_adp5360_config *config = dev->config; int ret; uint8_t raw_reg; ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->out_reg, &raw_reg); if (ret < 0) { return ret; } raw_reg = (raw_reg & ADP5360_BUCK_OUTPUT_VOUT_MSK) >> ADP5360_BUCK_OUTPUT_VOUT_POS; return linear_range_group_get_value(config->desc->ranges, config->desc->nranges, raw_reg, volt_uv); } static int regulator_adp5360_set_mode(const struct device *dev, regulator_mode_t mode) { const struct regulator_adp5360_config *config = dev->config; if (!config->desc->has_modes || (mode > ADP5360_MODE_PWM)) { return -ENOTSUP; } return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_BUCK_MODE_MSK, mode << ADP5360_BUCK_CFG_BUCK_MODE_POS); } static int regulator_adp5360_get_mode(const struct device *dev, regulator_mode_t *mode) { const struct regulator_adp5360_config *config = dev->config; uint8_t val; int ret; if (!config->desc->has_modes) { return -ENOTSUP; } ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->cfg_reg, &val); if (ret < 0) { return ret; } *mode = (val & ADP5360_BUCK_CFG_BUCK_MODE_MSK) >> ADP5360_BUCK_CFG_BUCK_MODE_POS; return 0; } static int regulator_adp5360_enable(const struct device *dev) { const struct regulator_adp5360_config *config = dev->config; return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_EN_MSK, 1U); } static int regulator_adp5360_disable(const struct device *dev) { const struct regulator_adp5360_config *config = dev->config; return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_EN_MSK, 0U); } static int regulator_adp5360_init(const struct device *dev) { const struct regulator_adp5360_config *config = dev->config; int ret; uint8_t val, nval, msk; regulator_common_data_init(dev); if (!i2c_is_ready_dt(&config->i2c)) { return -ENODEV; } /* apply optional delay */ msk = 0U; nval = 0U; ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->out_reg, &val); if (ret < 0) { return ret; } if (config->dly_idx >= 0) { msk |= ADP5360_BUCK_OUTPUT_DLY_MSK; nval |= ((uint8_t)config->dly_idx << ADP5360_BUCK_OUTPUT_DLY_POS) & ADP5360_BUCK_OUTPUT_DLY_MSK; } if (msk != 0U) { ret = i2c_reg_write_byte_dt(&config->i2c, config->desc->out_reg, (val & ~msk) | nval); if (ret < 0) { return ret; } } /* apply optional initial configuration */ msk = 0U; nval = 0U; ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->cfg_reg, &val); if (ret < 0) { return ret; } if (config->ss_idx >= 0) { msk |= ADP5360_BUCK_CFG_SS_MSK; nval |= ((uint8_t)config->ss_idx << ADP5360_BUCK_CFG_SS_POS) & ADP5360_BUCK_CFG_SS_MSK; } if (config->ilim_idx >= 0) { if (config->desc->has_modes) { msk |= ADP5360_BUCK_CFG_BUCK_ILIM_MSK; nval |= ((uint8_t)config->ilim_idx << ADP5360_BUCK_CFG_BUCK_ILIM_POS) & ADP5360_BUCK_CFG_BUCK_ILIM_MSK; } else { msk |= ADP5360_BUCK_CFG_BST_ILIM_MSK; nval |= ((uint8_t)config->ilim_idx << ADP5360_BUCK_CFG_BST_ILIM_POS) & ADP5360_BUCK_CFG_BST_ILIM_MSK; } } if (config->stp_en) { msk |= ADP5360_BUCK_CFG_STP_MSK; nval |= ADP5360_BUCK_CFG_STP_MSK; } if (config->dis_en) { msk |= ADP5360_BUCK_CFG_DISCHG_MSK; nval |= ADP5360_BUCK_CFG_DISCHG_MSK; } if (msk != 0U) { ret = i2c_reg_write_byte_dt(&config->i2c, config->desc->cfg_reg, (val & ~msk) | nval); if (ret < 0) { return ret; } } return regulator_common_init(dev, (val & ADP5360_BUCK_CFG_EN_MSK) != 0U); } static const struct regulator_driver_api api = { .enable = regulator_adp5360_enable, .disable = regulator_adp5360_disable, .count_voltages = regulator_adp5360_count_voltages, .list_voltage = regulator_adp5360_list_voltage, .set_voltage = regulator_adp5360_set_voltage, .get_voltage = regulator_adp5360_get_voltage, .set_mode = regulator_adp5360_set_mode, .get_mode = regulator_adp5360_get_mode, }; #define REGULATOR_ADP5360_DEFINE(node_id, id, name) \ static struct regulator_adp5360_data data_##id; \ \ static const struct regulator_adp5360_config config_##id = { \ .common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id), \ .i2c = I2C_DT_SPEC_GET(DT_GPARENT(node_id)), \ .desc = &name##_desc, \ .dly_idx = DT_ENUM_IDX_OR(node_id, adi_switch_delay_us, -1), \ .ss_idx = DT_ENUM_IDX_OR(node_id, adi_soft_start_ms, -1), \ .ilim_idx = DT_ENUM_IDX_OR(node_id, adi_ilim_milliamp, -1), \ .stp_en = DT_PROP(node_id, adi_enable_stop_pulse), \ .dis_en = DT_PROP(node_id, adi_enable_output_discharge), \ }; \ \ DEVICE_DT_DEFINE(node_id, regulator_adp5360_init, NULL, &data_##id, &config_##id, \ POST_KERNEL, CONFIG_REGULATOR_ADP5360_INIT_PRIORITY, &api); #define REGULATOR_ADP5360_DEFINE_COND(inst, child) \ COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \ (REGULATOR_ADP5360_DEFINE(DT_INST_CHILD(inst, child), child##inst, child)), \ ()) #define REGULATOR_ADP5360_DEFINE_ALL(inst) \ REGULATOR_ADP5360_DEFINE_COND(inst, buck) \ REGULATOR_ADP5360_DEFINE_COND(inst, buckboost) DT_INST_FOREACH_STATUS_OKAY(REGULATOR_ADP5360_DEFINE_ALL)