1 /*
2  * Copyright 2022 NXP
3  * Copyright 2022 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <zephyr/device.h>
13 #include <zephyr/shell/shell.h>
14 #include <zephyr/drivers/regulator.h>
15 #include <zephyr/toolchain.h>
16 
strtomicro(char * inp,char units,int32_t * val)17 static int strtomicro(char *inp, char units, int32_t *val)
18 {
19 	size_t len, start, end;
20 	int32_t mult, decdiv = 1;
21 
22 	len = strlen(inp);
23 	if (len < 2) {
24 		return -EINVAL;
25 	}
26 
27 	/* suffix */
28 	if (tolower(inp[len - 1]) != units) {
29 		return -EINVAL;
30 	}
31 
32 	if ((len > 2) && (inp[len - 2] == 'u')) {
33 		mult = 1;
34 		end = len - 3;
35 	} else if ((len > 2) && (inp[len - 2] == 'm')) {
36 		mult = 1000;
37 		end = len - 3;
38 	} else if (isdigit((unsigned char)inp[len - 2]) > 0) {
39 		mult = 1000000;
40 		end = len - 2;
41 	} else {
42 		return -EINVAL;
43 	}
44 
45 	/* optional prefix (sign) */
46 	if (inp[0] == '-') {
47 		mult *= -1;
48 		start = 1;
49 	} else if (inp[0] == '+') {
50 		start = 1;
51 	} else {
52 		start = 0;
53 	}
54 
55 	/* numeric part */
56 	*val = 0;
57 	for (size_t i = start; (i <= end) && (decdiv <= mult); i++) {
58 		if (isdigit((unsigned char)inp[i]) > 0) {
59 			*val = *val * 10 / decdiv +
60 			       (int32_t)(inp[i] - '0') * mult / decdiv;
61 			if (decdiv > 1) {
62 				mult /= 10;
63 			}
64 		} else if (inp[i] == '.') {
65 			decdiv = 10;
66 		} else {
67 			return -EINVAL;
68 		}
69 	}
70 
71 	return 0;
72 }
73 
microtoshell(const struct shell * sh,char unit,int32_t val)74 static void microtoshell(const struct shell *sh, char unit, int32_t val)
75 {
76 	if (val > 100000) {
77 		shell_print(sh, "%d.%03d %c", val / 1000000,
78 			    (val % 1000000) / 1000, unit);
79 	} else if (val > 1000) {
80 		shell_print(sh, "%d.%03d m%c", val / 1000, val % 1000, unit);
81 	} else {
82 		shell_print(sh, "%d u%c", val, unit);
83 	}
84 }
85 
cmd_enable(const struct shell * sh,size_t argc,char ** argv)86 static int cmd_enable(const struct shell *sh, size_t argc, char **argv)
87 {
88 	const struct device *dev;
89 	int ret;
90 
91 	ARG_UNUSED(argc);
92 
93 	dev = device_get_binding(argv[1]);
94 	if (dev == NULL) {
95 		shell_error(sh, "Regulator device %s not available", argv[1]);
96 		return -ENODEV;
97 	}
98 
99 	ret = regulator_enable(dev);
100 	if (ret < 0) {
101 		shell_error(sh, "Could not enable regulator (%d)", ret);
102 		return ret;
103 	}
104 
105 	return 0;
106 }
107 
cmd_disable(const struct shell * sh,size_t argc,char ** argv)108 static int cmd_disable(const struct shell *sh, size_t argc, char **argv)
109 {
110 	const struct device *dev;
111 	int ret;
112 
113 	ARG_UNUSED(argc);
114 
115 	dev = device_get_binding(argv[1]);
116 	if (dev == NULL) {
117 		shell_error(sh, "Regulator device %s not available", argv[1]);
118 		return -ENODEV;
119 	}
120 
121 	ret = regulator_disable(dev);
122 	if (ret < 0) {
123 		shell_error(sh, "Could not disable regulator (%d)", ret);
124 		return ret;
125 	}
126 
127 	return 0;
128 }
129 
cmd_vlist(const struct shell * sh,size_t argc,char ** argv)130 static int cmd_vlist(const struct shell *sh, size_t argc, char **argv)
131 {
132 	const struct device *dev;
133 	unsigned int volt_cnt;
134 	int32_t last_volt_uv = 0;
135 
136 	ARG_UNUSED(argc);
137 
138 	dev = device_get_binding(argv[1]);
139 	if (dev == NULL) {
140 		shell_error(sh, "Regulator device %s not available", argv[1]);
141 		return -ENODEV;
142 	}
143 
144 	volt_cnt = regulator_count_voltages(dev);
145 
146 	for (unsigned int i = 0U; i < volt_cnt; i++) {
147 		int32_t volt_uv;
148 
149 		(void)regulator_list_voltage(dev, i, &volt_uv);
150 
151 		/* do not print repeated voltages */
152 		if ((i == 0U) || (last_volt_uv != volt_uv)) {
153 			microtoshell(sh, 'V', volt_uv);
154 		}
155 
156 		last_volt_uv = volt_uv;
157 	}
158 
159 	return 0;
160 }
161 
cmd_vset(const struct shell * sh,size_t argc,char ** argv)162 static int cmd_vset(const struct shell *sh, size_t argc, char **argv)
163 {
164 	const struct device *dev;
165 	int32_t min_uv, max_uv;
166 	int ret;
167 
168 	dev = device_get_binding(argv[1]);
169 	if (dev == NULL) {
170 		shell_error(sh, "Regulator device %s not available", argv[1]);
171 		return -ENODEV;
172 	}
173 
174 	ret = strtomicro(argv[2], 'v', &min_uv);
175 	if (ret < 0) {
176 		shell_error(sh, "Invalid min. voltage: %s", argv[2]);
177 		return ret;
178 	}
179 
180 	if (argc == 4) {
181 		ret = strtomicro(argv[3], 'v', &max_uv);
182 		if (ret < 0) {
183 			shell_error(sh, "Invalid max. voltage: %s", argv[3]);
184 			return ret;
185 		}
186 	} else {
187 		max_uv = min_uv;
188 	}
189 
190 	ret = regulator_set_voltage(dev, min_uv, max_uv);
191 	if (ret < 0) {
192 		shell_error(sh, "Could not set voltage (%d)", ret);
193 		return ret;
194 	}
195 
196 	return 0;
197 }
198 
cmd_vget(const struct shell * sh,size_t argc,char ** argv)199 static int cmd_vget(const struct shell *sh, size_t argc, char **argv)
200 {
201 	const struct device *dev;
202 	int32_t volt_uv;
203 	int ret;
204 
205 	ARG_UNUSED(argc);
206 
207 	dev = device_get_binding(argv[1]);
208 	if (dev == NULL) {
209 		shell_error(sh, "Regulator device %s not available", argv[1]);
210 		return -ENODEV;
211 	}
212 
213 	ret = regulator_get_voltage(dev, &volt_uv);
214 	if (ret < 0) {
215 		shell_error(sh, "Could not get voltage (%d)", ret);
216 		return ret;
217 	}
218 
219 	microtoshell(sh, 'V', volt_uv);
220 
221 	return 0;
222 }
223 
cmd_clist(const struct shell * sh,size_t argc,char ** argv)224 static int cmd_clist(const struct shell *sh, size_t argc, char **argv)
225 {
226 	const struct device *dev;
227 	unsigned int current_cnt;
228 	int32_t last_current_ua;
229 
230 	ARG_UNUSED(argc);
231 
232 	dev = device_get_binding(argv[1]);
233 	if (dev == NULL) {
234 		shell_error(sh, "Regulator device %s not available", argv[1]);
235 		return -ENODEV;
236 	}
237 
238 	current_cnt = regulator_count_current_limits(dev);
239 
240 	for (unsigned int i = 0U; i < current_cnt; i++) {
241 		int32_t current_ua;
242 
243 		(void)regulator_list_current_limit(dev, i, &current_ua);
244 
245 		/* do not print repeated current limits */
246 		if ((i == 0U) || (last_current_ua != current_ua)) {
247 			microtoshell(sh, 'A', current_ua);
248 		}
249 
250 		last_current_ua = current_ua;
251 	}
252 
253 	return 0;
254 }
255 
cmd_iset(const struct shell * sh,size_t argc,char ** argv)256 static int cmd_iset(const struct shell *sh, size_t argc, char **argv)
257 {
258 	const struct device *dev;
259 	int32_t min_ua, max_ua;
260 	int ret;
261 
262 	dev = device_get_binding(argv[1]);
263 	if (dev == NULL) {
264 		shell_error(sh, "Regulator device %s not available", argv[1]);
265 		return -ENODEV;
266 	}
267 
268 	ret = strtomicro(argv[2], 'a', &min_ua);
269 	if (ret < 0) {
270 		shell_error(sh, "Invalid min. current: %s", argv[2]);
271 		return ret;
272 	}
273 	if (argc == 4) {
274 		ret = strtomicro(argv[3], 'a', &max_ua);
275 		if (ret < 0) {
276 			shell_error(sh, "Invalid max. current: %s", argv[3]);
277 			return ret;
278 		}
279 	} else {
280 		max_ua = min_ua;
281 	}
282 
283 	ret = regulator_set_current_limit(dev, min_ua, max_ua);
284 	if (ret < 0) {
285 		shell_error(sh, "Could not set current limit (%d)", ret);
286 		return ret;
287 	}
288 
289 	return 0;
290 }
291 
cmd_iget(const struct shell * sh,size_t argc,char ** argv)292 static int cmd_iget(const struct shell *sh, size_t argc, char **argv)
293 {
294 	const struct device *dev;
295 	int32_t curr_ua;
296 	int ret;
297 
298 	ARG_UNUSED(argc);
299 
300 	dev = device_get_binding(argv[1]);
301 	if (dev == NULL) {
302 		shell_error(sh, "Regulator device %s not available", argv[1]);
303 		return -ENODEV;
304 	}
305 
306 	ret = regulator_get_current_limit(dev, &curr_ua);
307 	if (ret < 0) {
308 		shell_error(sh, "Could not get current limit (%d)", ret);
309 		return ret;
310 	}
311 
312 	microtoshell(sh, 'A', curr_ua);
313 
314 	return 0;
315 }
316 
cmd_modeset(const struct shell * sh,size_t argc,char ** argv)317 static int cmd_modeset(const struct shell *sh, size_t argc, char **argv)
318 {
319 	const struct device *dev;
320 	regulator_mode_t mode;
321 	int ret;
322 
323 	ARG_UNUSED(argc);
324 
325 	dev = device_get_binding(argv[1]);
326 	if (dev == NULL) {
327 		shell_error(sh, "Regulator device %s not available", argv[1]);
328 		return -ENODEV;
329 	}
330 
331 	mode = (regulator_mode_t)strtoul(argv[2], NULL, 10);
332 
333 	ret = regulator_set_mode(dev, mode);
334 	if (ret < 0) {
335 		shell_error(sh, "Could not set mode (%d)", ret);
336 		return ret;
337 	}
338 
339 	return 0;
340 }
341 
cmd_modeget(const struct shell * sh,size_t argc,char ** argv)342 static int cmd_modeget(const struct shell *sh, size_t argc, char **argv)
343 {
344 	const struct device *dev;
345 	regulator_mode_t mode;
346 	int ret;
347 
348 	ARG_UNUSED(argc);
349 
350 	dev = device_get_binding(argv[1]);
351 	if (dev == NULL) {
352 		shell_error(sh, "Regulator device %s not available", argv[1]);
353 		return -ENODEV;
354 	}
355 
356 	ret = regulator_get_mode(dev, &mode);
357 	if (ret < 0) {
358 		shell_error(sh, "Could not get mode (%d)", ret);
359 		return ret;
360 	}
361 
362 	shell_print(sh, "Mode: %u", (unsigned int)mode);
363 
364 	return 0;
365 }
366 
cmd_adset(const struct shell * sh,size_t argc,char ** argv)367 static int cmd_adset(const struct shell *sh, size_t argc, char **argv)
368 {
369 	const struct device *dev;
370 	bool ad;
371 	int ret;
372 
373 	ARG_UNUSED(argc);
374 
375 	dev = device_get_binding(argv[1]);
376 	if (dev == NULL) {
377 		shell_error(sh, "Regulator device %s not available", argv[1]);
378 		return -ENODEV;
379 	}
380 
381 	if (strcmp(argv[2], "enable")) {
382 		ad = true;
383 	} else if (strcmp(argv[2], "disable")) {
384 		ad = false;
385 	} else {
386 		shell_error(sh, "Invalid parameter");
387 		return -EINVAL;
388 	}
389 
390 	ret = regulator_set_active_discharge(dev, ad);
391 	if (ret < 0) {
392 		shell_error(sh, "Could not set mode (%d)", ret);
393 		return ret;
394 	}
395 
396 	return 0;
397 }
398 
cmd_adget(const struct shell * sh,size_t argc,char ** argv)399 static int cmd_adget(const struct shell *sh, size_t argc, char **argv)
400 {
401 	const struct device *dev;
402 	bool ad;
403 	int ret;
404 
405 	ARG_UNUSED(argc);
406 
407 	dev = device_get_binding(argv[1]);
408 	if (dev == NULL) {
409 		shell_error(sh, "Regulator device %s not available", argv[1]);
410 		return -ENODEV;
411 	}
412 
413 	ret = regulator_get_active_discharge(dev, &ad);
414 	if (ret < 0) {
415 		shell_error(sh, "Could not get active discharge (%d)", ret);
416 		return ret;
417 	}
418 
419 	shell_print(sh, "Active Discharge: %s", ad ? "enabled" : "disabled");
420 
421 	return 0;
422 }
423 
cmd_errors(const struct shell * sh,size_t argc,char ** argv)424 static int cmd_errors(const struct shell *sh, size_t argc, char **argv)
425 {
426 	const struct device *dev;
427 	regulator_error_flags_t errors;
428 	int ret;
429 
430 	ARG_UNUSED(argc);
431 
432 	dev = device_get_binding(argv[1]);
433 	if (dev == NULL) {
434 		shell_error(sh, "Regulator device %s not available", argv[1]);
435 		return -ENODEV;
436 	}
437 
438 	ret = regulator_get_error_flags(dev, &errors);
439 	if (ret < 0) {
440 		shell_error(sh, "Could not get error flags (%d)", ret);
441 		return ret;
442 	}
443 
444 	shell_print(sh, "Overvoltage:\t[%s]",
445 		    ((errors & REGULATOR_ERROR_OVER_VOLTAGE) != 0U) ? "X"
446 								    : " ");
447 	shell_print(sh, "Overcurrent:\t[%s]",
448 		    ((errors & REGULATOR_ERROR_OVER_CURRENT) != 0U) ? "X"
449 								    : " ");
450 	shell_print(sh, "Overtemp.:\t[%s]",
451 		    ((errors & REGULATOR_ERROR_OVER_TEMP) != 0U) ? "X" : " ");
452 
453 	return 0;
454 }
455 
cmd_dvsset(const struct shell * sh,size_t argc,char ** argv)456 static int cmd_dvsset(const struct shell *sh, size_t argc, char **argv)
457 {
458 	const struct device *dev;
459 	int ret = 0;
460 	regulator_dvs_state_t state;
461 
462 	dev = device_get_binding(argv[1]);
463 	if (dev == NULL) {
464 		shell_error(sh, "Regulator device %s not available", argv[1]);
465 		return -ENODEV;
466 	}
467 
468 	state = shell_strtoul(argv[2], 10, &ret);
469 	if (ret < 0) {
470 		shell_error(sh, "Could not parse state (%d)", ret);
471 		return ret;
472 	}
473 
474 	ret = regulator_parent_dvs_state_set(dev, state);
475 	if (ret < 0) {
476 		shell_error(sh, "Could not set DVS state (%d)", ret);
477 		return ret;
478 	}
479 
480 	return 0;
481 }
482 
cmd_shipmode(const struct shell * sh,size_t argc,char ** argv)483 static int cmd_shipmode(const struct shell *sh, size_t argc, char **argv)
484 {
485 	const struct device *dev;
486 	int ret;
487 
488 	ARG_UNUSED(argc);
489 
490 	dev = device_get_binding(argv[1]);
491 	if (dev == NULL) {
492 		shell_error(sh, "Regulator device %s not available", argv[1]);
493 		return -ENODEV;
494 	}
495 
496 	ret = regulator_parent_ship_mode(dev);
497 	if (ret < 0) {
498 		shell_error(sh, "Could not enable ship mode (%d)", ret);
499 		return ret;
500 	}
501 
502 	return 0;
503 }
504 
device_name_get(size_t idx,struct shell_static_entry * entry)505 static void device_name_get(size_t idx, struct shell_static_entry *entry)
506 {
507 	const struct device *dev = shell_device_lookup(idx, NULL);
508 
509 	entry->syntax = (dev != NULL) ? dev->name : NULL;
510 	entry->handler = NULL;
511 	entry->help = NULL;
512 	entry->subcmd = NULL;
513 }
514 
515 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
516 
517 SHELL_STATIC_SUBCMD_SET_CREATE(
518 	sub_regulator_cmds,
519 	SHELL_CMD_ARG(enable, &dsub_device_name,
520 		      "Enable regulator\n"
521 		      "Usage: enable <device>",
522 		      cmd_enable, 2, 0),
523 	SHELL_CMD_ARG(disable, &dsub_device_name,
524 		      "Disable regulator\n"
525 		      "Usage: disable <device>",
526 		      cmd_disable, 2, 0),
527 	SHELL_CMD_ARG(vlist, &dsub_device_name,
528 		      "List all supported voltages\n"
529 		      "Usage: vlist <device>",
530 		      cmd_vlist, 2, 0),
531 	SHELL_CMD_ARG(vset, &dsub_device_name,
532 		      "Set voltage\n"
533 		      "Input requires units, e.g. 200mv, 20.5mv, 10uv, 1v...\n"
534 		      "Usage: vset <device> <minimum> [<maximum>]\n"
535 		      "If maximum is not set, exact voltage will be requested",
536 		      cmd_vset, 3, 1),
537 	SHELL_CMD_ARG(vget, &dsub_device_name,
538 		      "Get voltage\n"
539 		      "Usage: vget <device>",
540 		      cmd_vget, 2, 0),
541 	SHELL_CMD_ARG(clist, &dsub_device_name,
542 		      "List all supported current limits\n"
543 		      "Usage: clist <device>",
544 		      cmd_clist, 2, 0),
545 	SHELL_CMD_ARG(iset, &dsub_device_name,
546 		      "Set current limit\n"
547 		      "Input requires units, e.g. 200ma, 20.5ma, 10ua, 1a...\n"
548 		      "Usage: iset <device> <minimum> [<maximum>]"
549 		      "If maximum is not set, exact current will be requested",
550 		      cmd_iset, 3, 1),
551 	SHELL_CMD_ARG(iget, &dsub_device_name,
552 		      "Get current limit\n"
553 		      "Usage: iget <device>",
554 		      cmd_iget, 2, 0),
555 	SHELL_CMD_ARG(modeset, &dsub_device_name,
556 		      "Set regulator mode\n"
557 		      "Usage: modeset <device> <mode identifier>",
558 		      cmd_modeset, 3, 0),
559 	SHELL_CMD_ARG(modeget, &dsub_device_name,
560 		      "Get regulator mode\n"
561 		      "Usage: modeget <device>",
562 		      cmd_modeget, 2, 0),
563 	SHELL_CMD_ARG(adset, NULL,
564 		      "Set active discharge\n"
565 		      "Usage: adset <device> <enable/disable>",
566 		      cmd_adset, 3, 0),
567 	SHELL_CMD_ARG(adget, NULL,
568 		      "Get active discharge\n"
569 		      "Usage: adset <device>",
570 		      cmd_adget, 2, 0),
571 	SHELL_CMD_ARG(errors, &dsub_device_name,
572 		      "Get errors\n"
573 		      "Usage: errors <device>",
574 		      cmd_errors, 2, 0),
575 	SHELL_CMD_ARG(dvsset, &dsub_device_name,
576 		      "Set regulator dynamic voltage scaling state\n"
577 		      "Usage: dvsset <device> <state identifier>",
578 		      cmd_dvsset, 3, 0),
579 	SHELL_CMD_ARG(shipmode, &dsub_device_name,
580 		      "Enable regulator ship mode\n"
581 		      "Usage: shipmode <device>",
582 		      cmd_shipmode, 2, 0),
583 	SHELL_SUBCMD_SET_END);
584 
585 SHELL_CMD_REGISTER(regulator, &sub_regulator_cmds, "Regulator playground",
586 		   NULL);
587