1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT adi_adp5360_regulator
7
8 #include <errno.h>
9
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/regulator.h>
12 #include <zephyr/dt-bindings/regulator/adp5360.h>
13 #include <zephyr/sys/linear_range.h>
14 #include <zephyr/sys/util.h>
15
16 /* ADP5360 regulator related registers */
17 #define ADP5360_BUCK_CFG 0x29U
18 #define ADP5360_BUCK_OUTPUT 0x2AU
19 #define ADP5360_BUCKBST_CFG 0x2BU
20 #define ADP5360_BUCKBST_OUTPUT 0x2CU
21
22 /* Buck/boost configure register. */
23 #define ADP5360_BUCK_CFG_SS_MSK GENMASK(7, 6)
24 #define ADP5360_BUCK_CFG_SS_POS 6U
25 #define ADP5360_BUCK_CFG_BST_ILIM_MSK GENMASK(5, 3)
26 #define ADP5360_BUCK_CFG_BST_ILIM_POS 3U
27 #define ADP5360_BUCK_CFG_BUCK_ILIM_MSK GENMASK(5, 3)
28 #define ADP5360_BUCK_CFG_BUCK_ILIM_POS 3U
29 #define ADP5360_BUCK_CFG_BUCK_MODE_MSK BIT(3)
30 #define ADP5360_BUCK_CFG_BUCK_MODE_POS 3U
31 #define ADP5360_BUCK_CFG_STP_MSK BIT(2)
32 #define ADP5360_BUCK_CFG_DISCHG_MSK BIT(1)
33 #define ADP5360_BUCK_CFG_EN_MSK BIT(0)
34
35 /* Buck/boost output voltage setting register. */
36 #define ADP5360_BUCK_OUTPUT_VOUT_MSK GENMASK(5, 0)
37 #define ADP5360_BUCK_OUTPUT_VOUT_POS 0U
38 #define ADP5360_BUCK_OUTPUT_DLY_MSK GENMASK(7, 6)
39 #define ADP5360_BUCK_OUTPUT_DLY_POS 6U
40
41 struct regulator_adp5360_desc {
42 uint8_t cfg_reg;
43 uint8_t out_reg;
44 bool has_modes;
45 const struct linear_range *ranges;
46 uint8_t nranges;
47 };
48
49 static const struct linear_range buck_ranges[] = {
50 LINEAR_RANGE_INIT(600000, 50000U, 0x0U, 0x3FU),
51 };
52
53 static const struct regulator_adp5360_desc buck_desc = {
54 .cfg_reg = ADP5360_BUCK_CFG,
55 .out_reg = ADP5360_BUCK_OUTPUT,
56 .has_modes = true,
57 .ranges = buck_ranges,
58 .nranges = ARRAY_SIZE(buck_ranges),
59 };
60
61 static const struct linear_range buckboost_ranges[] = {
62 LINEAR_RANGE_INIT(1800000, 100000U, 0x0U, 0x0BU),
63 LINEAR_RANGE_INIT(2950000, 50000U, 0xCU, 0x3FU),
64 };
65
66 static const struct regulator_adp5360_desc buckboost_desc = {
67 .cfg_reg = ADP5360_BUCKBST_CFG,
68 .out_reg = ADP5360_BUCKBST_OUTPUT,
69 .has_modes = false,
70 .ranges = buckboost_ranges,
71 .nranges = ARRAY_SIZE(buckboost_ranges),
72 };
73
74 struct regulator_adp5360_config {
75 struct regulator_common_config common;
76 struct i2c_dt_spec i2c;
77 const struct regulator_adp5360_desc *desc;
78 int8_t dly_idx;
79 int8_t ss_idx;
80 int8_t ilim_idx;
81 bool stp_en;
82 bool dis_en;
83 };
84
85 struct regulator_adp5360_data {
86 struct regulator_common_data data;
87 };
88
regulator_adp5360_count_voltages(const struct device * dev)89 static unsigned int regulator_adp5360_count_voltages(const struct device *dev)
90 {
91 const struct regulator_adp5360_config *config = dev->config;
92
93 return linear_range_group_values_count(config->desc->ranges, config->desc->nranges);
94 }
95
regulator_adp5360_list_voltage(const struct device * dev,unsigned int idx,int32_t * volt_uv)96 static int regulator_adp5360_list_voltage(const struct device *dev, unsigned int idx,
97 int32_t *volt_uv)
98 {
99 const struct regulator_adp5360_config *config = dev->config;
100
101 return linear_range_group_get_value(config->desc->ranges, config->desc->nranges, idx,
102 volt_uv);
103 }
104
regulator_adp5360_set_voltage(const struct device * dev,int32_t min_uv,int32_t max_uv)105 static int regulator_adp5360_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv)
106 {
107 const struct regulator_adp5360_config *config = dev->config;
108 uint16_t idx;
109 int ret;
110
111 ret = linear_range_group_get_win_index(config->desc->ranges, config->desc->nranges, min_uv,
112 max_uv, &idx);
113 if (ret == -EINVAL) {
114 return ret;
115 }
116
117 return i2c_reg_update_byte_dt(&config->i2c, config->desc->out_reg,
118 ADP5360_BUCK_OUTPUT_VOUT_MSK,
119 (uint8_t)idx << ADP5360_BUCK_OUTPUT_VOUT_POS);
120 }
121
regulator_adp5360_get_voltage(const struct device * dev,int32_t * volt_uv)122 static int regulator_adp5360_get_voltage(const struct device *dev, int32_t *volt_uv)
123 {
124 const struct regulator_adp5360_config *config = dev->config;
125 int ret;
126 uint8_t raw_reg;
127
128 ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->out_reg, &raw_reg);
129 if (ret < 0) {
130 return ret;
131 }
132
133 raw_reg = (raw_reg & ADP5360_BUCK_OUTPUT_VOUT_MSK) >> ADP5360_BUCK_OUTPUT_VOUT_POS;
134
135 return linear_range_group_get_value(config->desc->ranges, config->desc->nranges, raw_reg,
136 volt_uv);
137 }
138
regulator_adp5360_set_mode(const struct device * dev,regulator_mode_t mode)139 static int regulator_adp5360_set_mode(const struct device *dev, regulator_mode_t mode)
140 {
141 const struct regulator_adp5360_config *config = dev->config;
142
143 if (!config->desc->has_modes || (mode > ADP5360_MODE_PWM)) {
144 return -ENOTSUP;
145 }
146
147 return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg,
148 ADP5360_BUCK_CFG_BUCK_MODE_MSK,
149 mode << ADP5360_BUCK_CFG_BUCK_MODE_POS);
150 }
151
regulator_adp5360_get_mode(const struct device * dev,regulator_mode_t * mode)152 static int regulator_adp5360_get_mode(const struct device *dev, regulator_mode_t *mode)
153 {
154 const struct regulator_adp5360_config *config = dev->config;
155 uint8_t val;
156 int ret;
157
158 if (!config->desc->has_modes) {
159 return -ENOTSUP;
160 }
161
162 ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->cfg_reg, &val);
163 if (ret < 0) {
164 return ret;
165 }
166
167 *mode = (val & ADP5360_BUCK_CFG_BUCK_MODE_MSK) >> ADP5360_BUCK_CFG_BUCK_MODE_POS;
168
169 return 0;
170 }
171
regulator_adp5360_enable(const struct device * dev)172 static int regulator_adp5360_enable(const struct device *dev)
173 {
174 const struct regulator_adp5360_config *config = dev->config;
175
176 return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_EN_MSK,
177 1U);
178 }
179
regulator_adp5360_disable(const struct device * dev)180 static int regulator_adp5360_disable(const struct device *dev)
181 {
182 const struct regulator_adp5360_config *config = dev->config;
183
184 return i2c_reg_update_byte_dt(&config->i2c, config->desc->cfg_reg, ADP5360_BUCK_CFG_EN_MSK,
185 0U);
186 }
187
regulator_adp5360_init(const struct device * dev)188 static int regulator_adp5360_init(const struct device *dev)
189 {
190 const struct regulator_adp5360_config *config = dev->config;
191 int ret;
192 uint8_t val, nval, msk;
193
194 regulator_common_data_init(dev);
195
196 if (!i2c_is_ready_dt(&config->i2c)) {
197 return -ENODEV;
198 }
199
200 /* apply optional delay */
201 msk = 0U;
202 nval = 0U;
203
204 ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->out_reg, &val);
205 if (ret < 0) {
206 return ret;
207 }
208
209 if (config->dly_idx >= 0) {
210 msk |= ADP5360_BUCK_OUTPUT_DLY_MSK;
211 nval |= ((uint8_t)config->dly_idx << ADP5360_BUCK_OUTPUT_DLY_POS) &
212 ADP5360_BUCK_OUTPUT_DLY_MSK;
213 }
214
215 if (msk != 0U) {
216 ret = i2c_reg_write_byte_dt(&config->i2c, config->desc->out_reg,
217 (val & ~msk) | nval);
218 if (ret < 0) {
219 return ret;
220 }
221 }
222
223 /* apply optional initial configuration */
224 msk = 0U;
225 nval = 0U;
226
227 ret = i2c_reg_read_byte_dt(&config->i2c, config->desc->cfg_reg, &val);
228 if (ret < 0) {
229 return ret;
230 }
231
232 if (config->ss_idx >= 0) {
233 msk |= ADP5360_BUCK_CFG_SS_MSK;
234 nval |= ((uint8_t)config->ss_idx << ADP5360_BUCK_CFG_SS_POS) &
235 ADP5360_BUCK_CFG_SS_MSK;
236 }
237
238 if (config->ilim_idx >= 0) {
239 if (config->desc->has_modes) {
240 msk |= ADP5360_BUCK_CFG_BUCK_ILIM_MSK;
241 nval |= ((uint8_t)config->ilim_idx << ADP5360_BUCK_CFG_BUCK_ILIM_POS) &
242 ADP5360_BUCK_CFG_BUCK_ILIM_MSK;
243 } else {
244 msk |= ADP5360_BUCK_CFG_BST_ILIM_MSK;
245 nval |= ((uint8_t)config->ilim_idx << ADP5360_BUCK_CFG_BST_ILIM_POS) &
246 ADP5360_BUCK_CFG_BST_ILIM_MSK;
247 }
248 }
249
250 if (config->stp_en) {
251 msk |= ADP5360_BUCK_CFG_STP_MSK;
252 nval |= ADP5360_BUCK_CFG_STP_MSK;
253 }
254
255 if (config->dis_en) {
256 msk |= ADP5360_BUCK_CFG_DISCHG_MSK;
257 nval |= ADP5360_BUCK_CFG_DISCHG_MSK;
258 }
259
260 if (msk != 0U) {
261 ret = i2c_reg_write_byte_dt(&config->i2c, config->desc->cfg_reg,
262 (val & ~msk) | nval);
263 if (ret < 0) {
264 return ret;
265 }
266 }
267
268 return regulator_common_init(dev, (val & ADP5360_BUCK_CFG_EN_MSK) != 0U);
269 }
270
271 static DEVICE_API(regulator, api) = {
272 .enable = regulator_adp5360_enable,
273 .disable = regulator_adp5360_disable,
274 .count_voltages = regulator_adp5360_count_voltages,
275 .list_voltage = regulator_adp5360_list_voltage,
276 .set_voltage = regulator_adp5360_set_voltage,
277 .get_voltage = regulator_adp5360_get_voltage,
278 .set_mode = regulator_adp5360_set_mode,
279 .get_mode = regulator_adp5360_get_mode,
280 };
281
282 #define REGULATOR_ADP5360_DEFINE(node_id, id, name) \
283 static struct regulator_adp5360_data data_##id; \
284 \
285 static const struct regulator_adp5360_config config_##id = { \
286 .common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id), \
287 .i2c = I2C_DT_SPEC_GET(DT_GPARENT(node_id)), \
288 .desc = &name##_desc, \
289 .dly_idx = DT_ENUM_IDX_OR(node_id, adi_switch_delay_us, -1), \
290 .ss_idx = DT_ENUM_IDX_OR(node_id, adi_soft_start_ms, -1), \
291 .ilim_idx = DT_ENUM_IDX_OR(node_id, adi_ilim_milliamp, -1), \
292 .stp_en = DT_PROP(node_id, adi_enable_stop_pulse), \
293 .dis_en = DT_PROP(node_id, adi_enable_output_discharge), \
294 }; \
295 \
296 DEVICE_DT_DEFINE(node_id, regulator_adp5360_init, NULL, &data_##id, &config_##id, \
297 POST_KERNEL, CONFIG_REGULATOR_ADP5360_INIT_PRIORITY, &api);
298
299 #define REGULATOR_ADP5360_DEFINE_COND(inst, child) \
300 COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)), \
301 (REGULATOR_ADP5360_DEFINE(DT_INST_CHILD(inst, child), child##inst, child)), \
302 ())
303
304 #define REGULATOR_ADP5360_DEFINE_ALL(inst) \
305 REGULATOR_ADP5360_DEFINE_COND(inst, buck) \
306 REGULATOR_ADP5360_DEFINE_COND(inst, buckboost)
307
308 DT_INST_FOREACH_STATUS_OKAY(REGULATOR_ADP5360_DEFINE_ALL)
309