1 /*
2  * Copyright (c) 2021 NXP
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nxp_pca9420
7 
8 #include <errno.h>
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/regulator.h>
13 #include <zephyr/drivers/regulator/pca9420.h>
14 #include <zephyr/sys/linear_range.h>
15 #include <zephyr/sys/util.h>
16 
17 /** Register memory map. See datasheet for more details. */
18 /** General purpose registers */
19 /** @brief Top level system ctrl 0 */
20 #define PCA9420_TOP_CNTL0     0x09U
21 /** @brief Top level system ctrl 3 */
22 #define PCA9420_TOP_CNTL3     0x0CU
23 
24 /** Regulator status indication registers */
25 /** @brief Mode configuration for mode 0_0 */
26 #define PCA9420_MODECFG_0_0          0x22U
27 /** @brief Mode configuration for mode 0_1 */
28 #define PCA9420_MODECFG_0_1          0x23U
29 /** @brief Mode configuration for mode 0_2 */
30 #define PCA9420_MODECFG_0_2          0x24U
31 /** @brief Mode configuration for mode 0_3 */
32 #define PCA9420_MODECFG_0_3          0x25U
33 
34 /** @brief VIN input current limit selection */
35 #define PCA9420_TOP_CNTL0_VIN_ILIM_SEL_POS 5U
36 #define PCA9420_TOP_CNTL0_VIN_ILIM_SEL_MASK 0xE0U
37 #define PCA9420_TOP_CNTL0_VIN_ILIM_SEL_DISABLED 0x7U
38 
39 /** @brief I2C Mode control mask */
40 #define PCA9420_TOP_CNTL3_MODE_I2C_POS 3U
41 #define PCA9420_TOP_CNTL3_MODE_I2C_MASK 0x18U
42 
43 /*
44  * @brief Mode control selection mask. When this bit is set, the external
45  * PMIC pins MODESEL0 and MODESEL1 can be used to select the active mode
46  */
47 #define PCA9420_MODECFG_0_X_EN_MODE_SEL_BY_PIN 0x40U
48 
49 /*
50  * @brief Mode configuration upon falling edge applied to ON pin. If set,
51  * the device will switch to mode 0 when a valid falling edge is applied.
52  * to the ON pin
53  */
54 /** @brief Mode output voltage mask */
55 #define PCA9420_MODECFG_0_SW1_OUT_MASK       0x3FU
56 #define PCA9420_MODECFG_0_SW1_OUT_POS        0U
57 /** @brief SW2_OUT offset and voltage level mask */
58 #define PCA9420_MODECFG_1_SW2_OUT_MASK       0x3FU
59 #define PCA9420_MODECFG_1_SW2_OUT_POS        0U
60 /** @brief LDO1_OUT voltage level mask */
61 #define PCA9420_MODECFG_2_LDO1_OUT_MASK      0xF0U
62 #define PCA9420_MODECFG_2_LDO1_OUT_POS       4U
63 /** @brief SW1 Enable */
64 #define PCA9420_MODECFG_2_SW1_EN_MASK	     0x08U
65 #define PCA9420_MODECFG_2_SW1_EN_VAL	     0x08U
66 /** @brief SW2 Enable */
67 #define PCA9420_MODECFG_2_SW2_EN_MASK	     0x04U
68 #define PCA9420_MODECFG_2_SW2_EN_VAL	     0x04U
69 /** @brief LDO1 Enable */
70 #define PCA9420_MODECFG_2_LDO1_EN_MASK	     0x02U
71 #define PCA9420_MODECFG_2_LDO1_EN_VAL	     0x02U
72 /** @brief LDO2 Enable */
73 #define PCA9420_MODECFG_2_LDO2_EN_MASK	     0x01U
74 #define PCA9420_MODECFG_2_LDO2_EN_VAL	     0x01U
75 /** @brief LDO2_OUT offset and voltage level mask */
76 #define PCA9420_MODECFG_3_LDO2_OUT_MASK      0x3FU
77 #define PCA9420_MODECFG_3_LDO2_OUT_POS       0U
78 
79 /** VIN ILIM resolution, uA/LSB */
80 #define PCA9420_VIN_ILIM_UA_LSB 170000
81 /** VIN ILIM minimum value, uA */
82 #define PCA9420_VIN_ILIM_MIN_UA 85000
83 
84 /** Number of modes */
85 #define PCA9420_NUM_MODES 4U
86 /** Offset applied to MODECFG* registers for a given mode */
87 #define PCA9420_MODECFG_OFFSET(mode) ((mode) * 4U)
88 
89 struct regulator_pca9420_desc {
90 	uint8_t enable_reg;
91 	uint8_t enable_mask;
92 	uint8_t enable_val;
93 	uint8_t vsel_reg;
94 	uint8_t vsel_mask;
95 	uint8_t vsel_pos;
96 	int32_t max_ua;
97 	uint8_t num_ranges;
98 	const struct linear_range *ranges;
99 };
100 
101 struct regulator_pca9420_common_config {
102 	struct i2c_dt_spec i2c;
103 	int32_t vin_ilim_ua;
104 	bool enable_modesel_pins;
105 };
106 
107 struct regulator_pca9420_common_data {
108 	regulator_dvs_state_t dvs_state;
109 };
110 
111 struct regulator_pca9420_config {
112 	struct regulator_common_config common;
113 	bool enable_inverted;
114 	int32_t modes_uv[4];
115 	const struct regulator_pca9420_desc *desc;
116 	const struct device *parent;
117 };
118 
119 struct regulator_pca9420_data {
120 	struct regulator_common_data data;
121 };
122 
123 static const struct linear_range buck1_ranges[] = {
124 	LINEAR_RANGE_INIT(500000, 25000U, 0x0U, 0x28U),
125 	LINEAR_RANGE_INIT(1500000, 0U, 0x29U, 0x3E),
126 	LINEAR_RANGE_INIT(1800000, 0U, 0x3FU, 0x3FU),
127 };
128 
129 static const struct linear_range buck2_ranges[] = {
130 	LINEAR_RANGE_INIT(1500000, 25000U, 0x0U, 0x18U),
131 	LINEAR_RANGE_INIT(2100000, 0U, 0x19U, 0x1F),
132 	LINEAR_RANGE_INIT(2700000, 25000U, 0x20U, 0x38U),
133 	LINEAR_RANGE_INIT(3300000, 0U, 0x39U, 0x3F),
134 };
135 
136 static const struct linear_range ldo1_ranges[] = {
137 	LINEAR_RANGE_INIT(1700000, 25000U, 0x0U, 0x9U),
138 	LINEAR_RANGE_INIT(1900000, 0U, 0x9U, 0xFU),
139 };
140 
141 static const struct linear_range ldo2_ranges[] = {
142 	LINEAR_RANGE_INIT(1500000, 25000U, 0x0U, 0x18U),
143 	LINEAR_RANGE_INIT(2100000, 0U, 0x19U, 0x1FU),
144 	LINEAR_RANGE_INIT(2700000, 25000U, 0x20U, 0x38U),
145 	LINEAR_RANGE_INIT(3300000, 0U, 0x39U, 0x3FU),
146 };
147 
148 static const struct regulator_pca9420_desc buck1_desc = {
149 	.enable_reg = PCA9420_MODECFG_0_2,
150 	.enable_mask = PCA9420_MODECFG_2_SW1_EN_MASK,
151 	.enable_val = PCA9420_MODECFG_2_SW1_EN_VAL,
152 	.vsel_mask = PCA9420_MODECFG_0_SW1_OUT_MASK,
153 	.vsel_pos = PCA9420_MODECFG_0_SW1_OUT_POS,
154 	.vsel_reg = PCA9420_MODECFG_0_0,
155 	.max_ua = 250000,
156 	.ranges = buck1_ranges,
157 	.num_ranges = ARRAY_SIZE(buck1_ranges),
158 };
159 
160 static const struct regulator_pca9420_desc buck2_desc = {
161 	.enable_reg = PCA9420_MODECFG_0_2,
162 	.enable_mask = PCA9420_MODECFG_2_SW2_EN_MASK,
163 	.enable_val = PCA9420_MODECFG_2_SW2_EN_VAL,
164 	.vsel_mask = PCA9420_MODECFG_1_SW2_OUT_MASK,
165 	.vsel_pos = PCA9420_MODECFG_1_SW2_OUT_POS,
166 	.vsel_reg = PCA9420_MODECFG_0_1,
167 	.max_ua = 500000,
168 	.ranges = buck2_ranges,
169 	.num_ranges = ARRAY_SIZE(buck2_ranges),
170 };
171 
172 static const struct regulator_pca9420_desc ldo1_desc = {
173 	.enable_reg = PCA9420_MODECFG_0_2,
174 	.enable_mask = PCA9420_MODECFG_2_LDO1_EN_MASK,
175 	.enable_val = PCA9420_MODECFG_2_LDO1_EN_VAL,
176 	.vsel_mask = PCA9420_MODECFG_2_LDO1_OUT_MASK,
177 	.vsel_pos = PCA9420_MODECFG_2_LDO1_OUT_POS,
178 	.vsel_reg = PCA9420_MODECFG_0_2,
179 	.max_ua = 1000,
180 	.ranges = ldo1_ranges,
181 	.num_ranges = ARRAY_SIZE(ldo1_ranges),
182 };
183 
184 static const struct regulator_pca9420_desc ldo2_desc = {
185 	.enable_reg = PCA9420_MODECFG_0_2,
186 	.enable_mask = PCA9420_MODECFG_2_LDO2_EN_MASK,
187 	.enable_val = PCA9420_MODECFG_2_LDO2_EN_VAL,
188 	.vsel_reg = PCA9420_MODECFG_0_3,
189 	.vsel_mask = PCA9420_MODECFG_3_LDO2_OUT_MASK,
190 	.vsel_pos = PCA9420_MODECFG_3_LDO2_OUT_POS,
191 	.max_ua = 250000,
192 	.ranges = ldo2_ranges,
193 	.num_ranges = ARRAY_SIZE(ldo2_ranges),
194 };
195 
regulator_pca9420_count_voltages(const struct device * dev)196 static unsigned int regulator_pca9420_count_voltages(const struct device *dev)
197 {
198 	const struct regulator_pca9420_config *config = dev->config;
199 
200 	return linear_range_group_values_count(config->desc->ranges,
201 					       config->desc->num_ranges);
202 }
203 
regulator_pca9420_list_voltage(const struct device * dev,unsigned int idx,int32_t * volt_uv)204 static int regulator_pca9420_list_voltage(const struct device *dev,
205 					  unsigned int idx, int32_t *volt_uv)
206 {
207 	const struct regulator_pca9420_config *config = dev->config;
208 
209 	return linear_range_group_get_value(config->desc->ranges,
210 					    config->desc->num_ranges, idx,
211 					    volt_uv);
212 }
213 
regulator_pca9420_set_voltage(const struct device * dev,int32_t min_uv,int32_t max_uv)214 static int regulator_pca9420_set_voltage(const struct device *dev,
215 					 int32_t min_uv, int32_t max_uv)
216 {
217 	const struct regulator_pca9420_config *config = dev->config;
218 	const struct regulator_pca9420_common_config *cconfig = config->parent->config;
219 	struct regulator_pca9420_common_data *cdata = config->parent->data;
220 	uint16_t idx;
221 	int ret;
222 
223 	ret = linear_range_group_get_win_index(config->desc->ranges,
224 					       config->desc->num_ranges, min_uv,
225 					       max_uv, &idx);
226 	if (ret == -EINVAL) {
227 		return ret;
228 	}
229 
230 	idx <<= config->desc->vsel_pos;
231 
232 	return i2c_reg_update_byte_dt(&cconfig->i2c, config->desc->vsel_reg +
233 				      PCA9420_MODECFG_OFFSET(cdata->dvs_state),
234 				      config->desc->vsel_mask, (uint8_t)idx);
235 }
236 
regulator_pca9420_get_voltage(const struct device * dev,int32_t * volt_uv)237 static int regulator_pca9420_get_voltage(const struct device *dev,
238 					 int32_t *volt_uv)
239 {
240 	const struct regulator_pca9420_config *config = dev->config;
241 	const struct regulator_pca9420_common_config *cconfig = config->parent->config;
242 	struct regulator_pca9420_common_data *cdata = config->parent->data;
243 	int ret;
244 	uint8_t raw_reg;
245 
246 	ret = i2c_reg_read_byte_dt(&cconfig->i2c, config->desc->vsel_reg +
247 				   PCA9420_MODECFG_OFFSET(cdata->dvs_state),
248 				   &raw_reg);
249 	if (ret < 0) {
250 		return ret;
251 	}
252 
253 	raw_reg = (raw_reg & config->desc->vsel_mask) >> config->desc->vsel_pos;
254 
255 	return linear_range_group_get_value(config->desc->ranges,
256 					    config->desc->num_ranges, raw_reg,
257 					    volt_uv);
258 }
259 
regulator_pca9420_get_current_limit(const struct device * dev,int32_t * curr_ua)260 static int regulator_pca9420_get_current_limit(const struct device *dev,
261 					       int32_t *curr_ua)
262 {
263 	const struct regulator_pca9420_config *config = dev->config;
264 	const struct regulator_pca9420_common_config *cconfig = config->parent->config;
265 
266 	if (cconfig->vin_ilim_ua == 0U) {
267 		*curr_ua = config->desc->max_ua;
268 	} else {
269 		*curr_ua = MIN(config->desc->max_ua, cconfig->vin_ilim_ua);
270 	}
271 
272 	return 0;
273 }
274 
regulator_pca9420_enable(const struct device * dev)275 static int regulator_pca9420_enable(const struct device *dev)
276 {
277 	const struct regulator_pca9420_config *config = dev->config;
278 	const struct regulator_pca9420_common_config *cconfig = config->parent->config;
279 	struct regulator_pca9420_common_data *cdata = config->parent->data;
280 	uint8_t en_val;
281 
282 	en_val = config->enable_inverted ? 0 : config->desc->enable_val;
283 	return i2c_reg_update_byte_dt(&cconfig->i2c, config->desc->enable_reg
284 				      + PCA9420_MODECFG_OFFSET(cdata->dvs_state),
285 				      config->desc->enable_mask, en_val);
286 }
287 
regulator_pca9420_disable(const struct device * dev)288 static int regulator_pca9420_disable(const struct device *dev)
289 {
290 	const struct regulator_pca9420_config *config = dev->config;
291 	const struct regulator_pca9420_common_config *cconfig = config->parent->config;
292 	struct regulator_pca9420_common_data *cdata = config->parent->data;
293 	uint8_t dis_val;
294 
295 	dis_val = config->enable_inverted ? config->desc->enable_val : 0;
296 	return i2c_reg_update_byte_dt(&cconfig->i2c, config->desc->enable_reg
297 				      + PCA9420_MODECFG_OFFSET(cdata->dvs_state),
298 				      config->desc->enable_mask, dis_val);
299 }
300 
301 static const struct regulator_driver_api api = {
302 	.enable = regulator_pca9420_enable,
303 	.disable = regulator_pca9420_disable,
304 	.count_voltages = regulator_pca9420_count_voltages,
305 	.list_voltage = regulator_pca9420_list_voltage,
306 	.set_voltage = regulator_pca9420_set_voltage,
307 	.get_voltage = regulator_pca9420_get_voltage,
308 	.get_current_limit = regulator_pca9420_get_current_limit,
309 };
310 
regulator_pca9420_init(const struct device * dev)311 static int regulator_pca9420_init(const struct device *dev)
312 {
313 	const struct regulator_pca9420_config *config = dev->config;
314 	const struct regulator_pca9420_common_config *cconfig = config->parent->config;
315 
316 	regulator_common_data_init(dev);
317 
318 	if (!device_is_ready(config->parent)) {
319 		return -ENODEV;
320 	}
321 
322 	/* configure mode voltages */
323 	for (uint8_t i = 0U; i < ARRAY_SIZE(config->modes_uv); i++) {
324 		int ret;
325 
326 		if (config->modes_uv[i] == 0) {
327 			/* disable mode if voltage is 0 */
328 			ret = i2c_reg_update_byte_dt(
329 				&cconfig->i2c,
330 				config->desc->enable_reg + PCA9420_MODECFG_OFFSET(i),
331 				config->desc->enable_mask, 0U);
332 			if (ret < 0) {
333 				return ret;
334 			}
335 		} else if (config->modes_uv[i] > 0) {
336 			uint16_t idx;
337 
338 			/* program mode voltage */
339 			ret = linear_range_group_get_win_index(
340 				config->desc->ranges, config->desc->num_ranges,
341 				config->modes_uv[i], config->modes_uv[i], &idx);
342 			if (ret == -EINVAL) {
343 				return ret;
344 			}
345 
346 			idx <<= config->desc->vsel_pos;
347 
348 			ret = i2c_reg_update_byte_dt(
349 				&cconfig->i2c,
350 				config->desc->vsel_reg + PCA9420_MODECFG_OFFSET(i),
351 				config->desc->vsel_mask, (uint8_t)idx);
352 			if (ret < 0) {
353 				return ret;
354 			}
355 		}
356 	}
357 
358 	return regulator_common_init(dev, false);
359 }
360 
regulator_pca9420_dvs_state_set(const struct device * dev,regulator_dvs_state_t state)361 int regulator_pca9420_dvs_state_set(const struct device *dev,
362 				    regulator_dvs_state_t state)
363 {
364 	const struct regulator_pca9420_common_config *config = dev->config;
365 	struct regulator_pca9420_common_data *data = dev->data;
366 	int ret;
367 
368 	if (state >= PCA9420_NUM_MODES) {
369 		return -ENOTSUP;
370 	}
371 
372 	if (config->enable_modesel_pins) {
373 		/*
374 		 * The user cannot set DVS state via this API,
375 		 * but they may want to query/set voltages for another mode.
376 		 * Return -EPERM to indicate change failed, but change the
377 		 * dvs_state variable so the user can access the alternative
378 		 * dvs mode settings.
379 		 */
380 		data->dvs_state = state;
381 		return -EPERM;
382 	}
383 
384 	ret = i2c_reg_update_byte_dt(&config->i2c, PCA9420_TOP_CNTL3,
385 				      PCA9420_TOP_CNTL3_MODE_I2C_MASK,
386 				      state << PCA9420_TOP_CNTL3_MODE_I2C_POS);
387 	if (ret < 0) {
388 		return ret;
389 	}
390 	/* Record new DVS state */
391 	data->dvs_state = state;
392 	return 0;
393 }
394 
395 static const struct regulator_parent_driver_api parent_api = {
396 	.dvs_state_set = regulator_pca9420_dvs_state_set,
397 };
398 
regulator_pca9420_common_init(const struct device * dev)399 static int regulator_pca9420_common_init(const struct device *dev)
400 {
401 	const struct regulator_pca9420_common_config *config = dev->config;
402 	uint8_t reg_val = PCA9420_TOP_CNTL0_VIN_ILIM_SEL_DISABLED;
403 	int ret;
404 
405 	if (!device_is_ready(config->i2c.bus)) {
406 		return -ENODEV;
407 	}
408 
409 	if (config->enable_modesel_pins) {
410 		/* enable MODESEL0/1 pins for each mode */
411 		for (uint8_t i = 0U; i < PCA9420_NUM_MODES; i++) {
412 			ret = i2c_reg_update_byte_dt(
413 				&config->i2c,
414 				PCA9420_MODECFG_0_0 +
415 				PCA9420_MODECFG_OFFSET(i),
416 				PCA9420_MODECFG_0_X_EN_MODE_SEL_BY_PIN,
417 				PCA9420_MODECFG_0_X_EN_MODE_SEL_BY_PIN);
418 			if (ret < 0) {
419 				return ret;
420 			}
421 		}
422 	}
423 
424 	/* configure VIN current limit */
425 	if (config->vin_ilim_ua != 0U) {
426 		reg_val = (config->vin_ilim_ua - PCA9420_VIN_ILIM_MIN_UA) /
427 			  PCA9420_VIN_ILIM_UA_LSB;
428 	}
429 
430 	return i2c_reg_update_byte_dt(
431 		&config->i2c, PCA9420_TOP_CNTL0,
432 		PCA9420_TOP_CNTL0_VIN_ILIM_SEL_MASK,
433 		reg_val << PCA9420_TOP_CNTL0_VIN_ILIM_SEL_POS);
434 }
435 
436 #define REGULATOR_PCA9420_DEFINE(node_id, id, name, _parent)                   \
437 	static struct regulator_pca9420_data data_##id;                        \
438                                                                                \
439 	static const struct regulator_pca9420_config config_##id = {           \
440 		.common = REGULATOR_DT_COMMON_CONFIG_INIT(node_id),            \
441 		.enable_inverted = DT_PROP(node_id, enable_inverted),          \
442 		.modes_uv = {                                                  \
443 			DT_PROP_OR(node_id, nxp_mode0_microvolt, -1),          \
444 			DT_PROP_OR(node_id, nxp_mode1_microvolt, -1),          \
445 			DT_PROP_OR(node_id, nxp_mode2_microvolt, -1),          \
446 			DT_PROP_OR(node_id, nxp_mode3_microvolt, -1),          \
447 		},                                                             \
448 		.desc = &name ## _desc,                                        \
449 		.parent = _parent,                                             \
450 	};                                                                     \
451                                                                                \
452 	DEVICE_DT_DEFINE(node_id, regulator_pca9420_init, NULL, &data_##id,    \
453 			 &config_##id, POST_KERNEL,                            \
454 			 CONFIG_REGULATOR_PCA9420_INIT_PRIORITY, &api);
455 
456 #define REGULATOR_PCA9420_DEFINE_COND(inst, child, parent)                     \
457 	COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, child)),                \
458 		    (REGULATOR_PCA9420_DEFINE(DT_INST_CHILD(inst, child),      \
459 					      child ## inst, child, parent)),  \
460 		    ())
461 
462 #define REGULATOR_PCA9420_DEFINE_ALL(inst)                                     \
463 	static const struct regulator_pca9420_common_config config_##inst = {  \
464 		.i2c = I2C_DT_SPEC_INST_GET(inst),                             \
465 		.vin_ilim_ua = DT_INST_PROP(inst, nxp_vin_ilim_microamp),      \
466 		.enable_modesel_pins =                                         \
467 			DT_INST_PROP(inst, nxp_enable_modesel_pins),           \
468 	};                                                                     \
469                                                                                \
470 	static struct regulator_pca9420_common_data data_##inst;               \
471                                                                                \
472 	DEVICE_DT_INST_DEFINE(inst, regulator_pca9420_common_init, NULL,       \
473 			      &data_##inst,                                    \
474 			      &config_##inst, POST_KERNEL,                     \
475 			      CONFIG_REGULATOR_PCA9420_COMMON_INIT_PRIORITY,   \
476 			      &parent_api);                                    \
477                                                                                \
478 	REGULATOR_PCA9420_DEFINE_COND(inst, buck1, DEVICE_DT_INST_GET(inst))   \
479 	REGULATOR_PCA9420_DEFINE_COND(inst, buck2, DEVICE_DT_INST_GET(inst))   \
480 	REGULATOR_PCA9420_DEFINE_COND(inst, ldo1, DEVICE_DT_INST_GET(inst))    \
481 	REGULATOR_PCA9420_DEFINE_COND(inst, ldo2, DEVICE_DT_INST_GET(inst))
482 
483 DT_INST_FOREACH_STATUS_OKAY(REGULATOR_PCA9420_DEFINE_ALL)
484