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