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