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