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, ¤t_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