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_iset(const struct shell * sh,size_t argc,char ** argv)224 static int cmd_iset(const struct shell *sh, size_t argc, char **argv)
225 {
226 const struct device *dev;
227 int32_t min_ua, max_ua;
228 int ret;
229
230 dev = device_get_binding(argv[1]);
231 if (dev == NULL) {
232 shell_error(sh, "Regulator device %s not available", argv[1]);
233 return -ENODEV;
234 }
235
236 ret = strtomicro(argv[2], 'a', &min_ua);
237 if (ret < 0) {
238 shell_error(sh, "Invalid min. current: %s", argv[2]);
239 return ret;
240 }
241 if (argc == 4) {
242 ret = strtomicro(argv[3], 'a', &max_ua);
243 if (ret < 0) {
244 shell_error(sh, "Invalid max. current: %s", argv[3]);
245 return ret;
246 }
247 } else {
248 max_ua = min_ua;
249 }
250
251 ret = regulator_set_current_limit(dev, min_ua, max_ua);
252 if (ret < 0) {
253 shell_error(sh, "Could not set current limit (%d)", ret);
254 return ret;
255 }
256
257 return 0;
258 }
259
cmd_iget(const struct shell * sh,size_t argc,char ** argv)260 static int cmd_iget(const struct shell *sh, size_t argc, char **argv)
261 {
262 const struct device *dev;
263 int32_t curr_ua;
264 int ret;
265
266 ARG_UNUSED(argc);
267
268 dev = device_get_binding(argv[1]);
269 if (dev == NULL) {
270 shell_error(sh, "Regulator device %s not available", argv[1]);
271 return -ENODEV;
272 }
273
274 ret = regulator_get_current_limit(dev, &curr_ua);
275 if (ret < 0) {
276 shell_error(sh, "Could not get current limit (%d)", ret);
277 return ret;
278 }
279
280 microtoshell(sh, 'A', curr_ua);
281
282 return 0;
283 }
284
cmd_modeset(const struct shell * sh,size_t argc,char ** argv)285 static int cmd_modeset(const struct shell *sh, size_t argc, char **argv)
286 {
287 const struct device *dev;
288 regulator_mode_t mode;
289 int ret;
290
291 ARG_UNUSED(argc);
292
293 dev = device_get_binding(argv[1]);
294 if (dev == NULL) {
295 shell_error(sh, "Regulator device %s not available", argv[1]);
296 return -ENODEV;
297 }
298
299 mode = (regulator_mode_t)strtoul(argv[2], NULL, 10);
300
301 ret = regulator_set_mode(dev, mode);
302 if (ret < 0) {
303 shell_error(sh, "Could not set mode (%d)", ret);
304 return ret;
305 }
306
307 return 0;
308 }
309
cmd_modeget(const struct shell * sh,size_t argc,char ** argv)310 static int cmd_modeget(const struct shell *sh, size_t argc, char **argv)
311 {
312 const struct device *dev;
313 regulator_mode_t mode;
314 int ret;
315
316 ARG_UNUSED(argc);
317
318 dev = device_get_binding(argv[1]);
319 if (dev == NULL) {
320 shell_error(sh, "Regulator device %s not available", argv[1]);
321 return -ENODEV;
322 }
323
324 ret = regulator_get_mode(dev, &mode);
325 if (ret < 0) {
326 shell_error(sh, "Could not get mode (%d)", ret);
327 return ret;
328 }
329
330 shell_print(sh, "Mode: %u", (unsigned int)mode);
331
332 return 0;
333 }
334
cmd_errors(const struct shell * sh,size_t argc,char ** argv)335 static int cmd_errors(const struct shell *sh, size_t argc, char **argv)
336 {
337 const struct device *dev;
338 regulator_error_flags_t errors;
339 int ret;
340
341 ARG_UNUSED(argc);
342
343 dev = device_get_binding(argv[1]);
344 if (dev == NULL) {
345 shell_error(sh, "Regulator device %s not available", argv[1]);
346 return -ENODEV;
347 }
348
349 ret = regulator_get_error_flags(dev, &errors);
350 if (ret < 0) {
351 shell_error(sh, "Could not get error flags (%d)", ret);
352 return ret;
353 }
354
355 shell_print(sh, "Overvoltage:\t[%s]",
356 ((errors & REGULATOR_ERROR_OVER_VOLTAGE) != 0U) ? "X"
357 : " ");
358 shell_print(sh, "Overcurrent:\t[%s]",
359 ((errors & REGULATOR_ERROR_OVER_CURRENT) != 0U) ? "X"
360 : " ");
361 shell_print(sh, "Overtemp.:\t[%s]",
362 ((errors & REGULATOR_ERROR_OVER_TEMP) != 0U) ? "X" : " ");
363
364 return 0;
365 }
366
cmd_dvsset(const struct shell * sh,size_t argc,char ** argv)367 static int cmd_dvsset(const struct shell *sh, size_t argc, char **argv)
368 {
369 const struct device *dev;
370 int ret = 0;
371 regulator_dvs_state_t state;
372
373 dev = device_get_binding(argv[1]);
374 if (dev == NULL) {
375 shell_error(sh, "Regulator device %s not available", argv[1]);
376 return -ENODEV;
377 }
378
379 state = shell_strtoul(argv[2], 10, &ret);
380 if (ret < 0) {
381 shell_error(sh, "Could not parse state (%d)", ret);
382 return ret;
383 }
384
385 ret = regulator_parent_dvs_state_set(dev, state);
386 if (ret < 0) {
387 shell_error(sh, "Could not set DVS state (%d)", ret);
388 return ret;
389 }
390
391 return 0;
392 }
393
cmd_shipmode(const struct shell * sh,size_t argc,char ** argv)394 static int cmd_shipmode(const struct shell *sh, size_t argc, char **argv)
395 {
396 const struct device *dev;
397 int ret;
398
399 ARG_UNUSED(argc);
400
401 dev = device_get_binding(argv[1]);
402 if (dev == NULL) {
403 shell_error(sh, "Regulator device %s not available", argv[1]);
404 return -ENODEV;
405 }
406
407 ret = regulator_parent_ship_mode(dev);
408 if (ret < 0) {
409 shell_error(sh, "Could not enable ship mode (%d)", ret);
410 return ret;
411 }
412
413 return 0;
414 }
415
device_name_get(size_t idx,struct shell_static_entry * entry)416 static void device_name_get(size_t idx, struct shell_static_entry *entry)
417 {
418 const struct device *dev = shell_device_lookup(idx, NULL);
419
420 entry->syntax = (dev != NULL) ? dev->name : NULL;
421 entry->handler = NULL;
422 entry->help = NULL;
423 entry->subcmd = NULL;
424 }
425
426 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
427
428 SHELL_STATIC_SUBCMD_SET_CREATE(
429 sub_regulator_cmds,
430 SHELL_CMD_ARG(enable, &dsub_device_name,
431 "Enable regulator\n"
432 "Usage: enable <device>",
433 cmd_enable, 2, 0),
434 SHELL_CMD_ARG(disable, &dsub_device_name,
435 "Disable regulator\n"
436 "Usage: disable <device>",
437 cmd_disable, 2, 0),
438 SHELL_CMD_ARG(vlist, &dsub_device_name,
439 "List all supported voltages\n"
440 "Usage: vlist <device>",
441 cmd_vlist, 2, 0),
442 SHELL_CMD_ARG(vset, &dsub_device_name,
443 "Set voltage\n"
444 "Input requires units, e.g. 200mv, 20.5mv, 10uv, 1v...\n"
445 "Usage: vset <device> <minimum> [<maximum>]\n"
446 "If maximum is not set, exact voltage will be requested",
447 cmd_vset, 3, 1),
448 SHELL_CMD_ARG(vget, &dsub_device_name,
449 "Get voltage\n"
450 "Usage: vget <device>",
451 cmd_vget, 2, 0),
452 SHELL_CMD_ARG(iset, &dsub_device_name,
453 "Set current limit\n"
454 "Input requires units, e.g. 200ma, 20.5ma, 10ua, 1a...\n"
455 "Usage: iset <device> <minimum> [<maximum>]"
456 "If maximum is not set, exact current will be requested",
457 cmd_iset, 3, 1),
458 SHELL_CMD_ARG(iget, &dsub_device_name,
459 "Get current limit\n"
460 "Usage: iget <device>",
461 cmd_iget, 2, 0),
462 SHELL_CMD_ARG(modeset, &dsub_device_name,
463 "Set regulator mode\n"
464 "Usage: modeset <device> <mode identifier>",
465 cmd_modeset, 3, 0),
466 SHELL_CMD_ARG(modeget, &dsub_device_name,
467 "Get regulator mode\n"
468 "Usage: modeget <device>",
469 cmd_modeget, 2, 0),
470 SHELL_CMD_ARG(errors, &dsub_device_name,
471 "Get errors\n"
472 "Usage: errors <device>",
473 cmd_errors, 2, 0),
474 SHELL_CMD_ARG(dvsset, &dsub_device_name,
475 "Set regulator dynamic voltage scaling state\n"
476 "Usage: dvsset <device> <state identifier>",
477 cmd_dvsset, 3, 0),
478 SHELL_CMD_ARG(shipmode, &dsub_device_name,
479 "Enable regulator ship mode\n"
480 "Usage: shipmode <device>",
481 cmd_shipmode, 2, 0),
482 SHELL_SUBCMD_SET_END);
483
484 SHELL_CMD_REGISTER(regulator, &sub_regulator_cmds, "Regulator playground",
485 NULL);
486