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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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 = shell_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_is_regulator(const struct device * dev)525  static bool device_is_regulator(const struct device *dev)
526  {
527  	return DEVICE_API_IS(regulator, dev);
528  }
529  
device_name_get(size_t idx,struct shell_static_entry * entry)530  static void device_name_get(size_t idx, struct shell_static_entry *entry)
531  {
532  	const struct device *dev = shell_device_filter(idx, device_is_regulator);
533  
534  	entry->syntax = (dev != NULL) ? dev->name : NULL;
535  	entry->handler = NULL;
536  	entry->help = NULL;
537  	entry->subcmd = NULL;
538  }
539  
540  SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
541  
542  SHELL_STATIC_SUBCMD_SET_CREATE(
543  	sub_regulator_cmds,
544  	SHELL_CMD_ARG(enable, &dsub_device_name,
545  		      "Enable regulator\n"
546  		      "Usage: enable <device>",
547  		      cmd_enable, 2, 0),
548  	SHELL_CMD_ARG(disable, &dsub_device_name,
549  		      "Disable regulator\n"
550  		      "Usage: disable <device>",
551  		      cmd_disable, 2, 0),
552  	SHELL_CMD_ARG(is_enabled, &dsub_device_name,
553  		      "Report whether regulator is enabled or disabled\n"
554  		      "Usage: is_enabled <device>",
555  		      cmd_is_enabled, 2, 0),
556  	SHELL_CMD_ARG(vlist, &dsub_device_name,
557  		      "List all supported voltages\n"
558  		      "Usage: vlist <device>",
559  		      cmd_vlist, 2, 0),
560  	SHELL_CMD_ARG(vset, &dsub_device_name,
561  		      "Set voltage\n"
562  		      "Input requires units, e.g. 200mv, 20.5mv, 10uv, 1v...\n"
563  		      "Usage: vset <device> <minimum> [<maximum>]\n"
564  		      "If maximum is not set, exact voltage will be requested",
565  		      cmd_vset, 3, 1),
566  	SHELL_CMD_ARG(vget, &dsub_device_name,
567  		      "Get voltage\n"
568  		      "Usage: vget <device>",
569  		      cmd_vget, 2, 0),
570  	SHELL_CMD_ARG(clist, &dsub_device_name,
571  		      "List all supported current limits\n"
572  		      "Usage: clist <device>",
573  		      cmd_clist, 2, 0),
574  	SHELL_CMD_ARG(iset, &dsub_device_name,
575  		      "Set current limit\n"
576  		      "Input requires units, e.g. 200ma, 20.5ma, 10ua, 1a...\n"
577  		      "Usage: iset <device> <minimum> [<maximum>]"
578  		      "If maximum is not set, exact current will be requested",
579  		      cmd_iset, 3, 1),
580  	SHELL_CMD_ARG(iget, &dsub_device_name,
581  		      "Get current limit\n"
582  		      "Usage: iget <device>",
583  		      cmd_iget, 2, 0),
584  	SHELL_CMD_ARG(modeset, &dsub_device_name,
585  		      "Set regulator mode\n"
586  		      "Usage: modeset <device> <mode identifier>",
587  		      cmd_modeset, 3, 0),
588  	SHELL_CMD_ARG(modeget, &dsub_device_name,
589  		      "Get regulator mode\n"
590  		      "Usage: modeget <device>",
591  		      cmd_modeget, 2, 0),
592  	SHELL_CMD_ARG(adset, NULL,
593  		      "Set active discharge\n"
594  		      "Usage: adset <device> <enable/disable>",
595  		      cmd_adset, 3, 0),
596  	SHELL_CMD_ARG(adget, NULL,
597  		      "Get active discharge\n"
598  		      "Usage: adget <device>",
599  		      cmd_adget, 2, 0),
600  	SHELL_CMD_ARG(errors, &dsub_device_name,
601  		      "Get errors\n"
602  		      "Usage: errors <device>",
603  		      cmd_errors, 2, 0),
604  	SHELL_CMD_ARG(dvsset, &dsub_device_name,
605  		      "Set regulator dynamic voltage scaling state\n"
606  		      "Usage: dvsset <device> <state identifier>",
607  		      cmd_dvsset, 3, 0),
608  	SHELL_CMD_ARG(shipmode, &dsub_device_name,
609  		      "Enable regulator ship mode\n"
610  		      "Usage: shipmode <device>",
611  		      cmd_shipmode, 2, 0),
612  	SHELL_SUBCMD_SET_END);
613  
614  SHELL_CMD_REGISTER(regulator, &sub_regulator_cmds, "Regulator playground",
615  		   NULL);
616