1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <stdlib.h>
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/regulator.h>
11 #include <zephyr/drivers/watchdog.h>
12 #include <zephyr/dt-bindings/gpio/nordic-npm6001-gpio.h>
13 #include <zephyr/dt-bindings/regulator/npm6001.h>
14 #include <zephyr/posix/unistd.h>
15 #include <zephyr/shell/shell.h>
16 #include <zephyr/sys/printk.h>
17 
18 #include <getopt.h>
19 
20 struct regulators_map {
21 	const char *name;
22 	const struct device *dev;
23 };
24 
25 static const struct device *const gpio = DEVICE_DT_GET_ONE(nordic_npm6001_gpio);
26 static const struct device *const wdt = DEVICE_DT_GET_ONE(nordic_npm6001_wdt);
27 static const struct regulators_map regulators[] = {
28 	{ "BUCK0", DEVICE_DT_GET_OR_NULL(DT_NODELABEL(npm6001_ek_buck0)) },
29 	{ "BUCK1", DEVICE_DT_GET_OR_NULL(DT_NODELABEL(npm6001_ek_buck1)) },
30 	{ "BUCK2", DEVICE_DT_GET_OR_NULL(DT_NODELABEL(npm6001_ek_buck2)) },
31 	{ "BUCK3", DEVICE_DT_GET_OR_NULL(DT_NODELABEL(npm6001_ek_buck3)) },
32 	{ "LDO0", DEVICE_DT_GET_OR_NULL(DT_NODELABEL(npm6001_ek_ldo0)) },
33 	{ "LDO1", DEVICE_DT_GET_OR_NULL(DT_NODELABEL(npm6001_ek_ldo1)) },
34 };
35 
name2reg(const char * name)36 static const struct device *name2reg(const char *name)
37 {
38 	for (size_t i = 0U; i < ARRAY_SIZE(regulators); i++) {
39 		if (strcmp(name, regulators[i].name) == 0) {
40 			return regulators[i].dev;
41 		}
42 	}
43 
44 	return NULL;
45 }
46 
main(void)47 int main(void)
48 {
49 	if (!device_is_ready(gpio)) {
50 		printk("nPM6001 GPIO device not ready\n");
51 		return 0;
52 	}
53 
54 	if (!device_is_ready(wdt)) {
55 		printk("nPM6001 Watchdog device not ready\n");
56 		return 0;
57 	}
58 
59 	for (size_t i = 0U; i < ARRAY_SIZE(regulators); i++) {
60 		if ((regulators[i].dev != NULL) &&
61 		    !device_is_ready(regulators[i].dev)) {
62 			printk("nPM6001 %s regulator device not ready\n",
63 			       regulators[i].name);
64 			return 0;
65 		}
66 	}
67 	return 0;
68 }
69 
cmd_regulator_list(const struct shell * sh,size_t argc,char ** argv)70 static int cmd_regulator_list(const struct shell *sh, size_t argc, char **argv)
71 {
72 	ARG_UNUSED(argc);
73 	ARG_UNUSED(argv);
74 
75 	for (size_t i = 0U; i < ARRAY_SIZE(regulators); i++) {
76 		if (regulators[i].dev != NULL) {
77 			shell_print(sh, "%s", regulators[i].name);
78 		}
79 	}
80 
81 	return 0;
82 }
83 
cmd_regulator_voltages(const struct shell * sh,size_t argc,char ** argv)84 static int cmd_regulator_voltages(const struct shell *sh, size_t argc, char **argv)
85 {
86 	const struct device *dev;
87 	unsigned int volt_cnt;
88 
89 	ARG_UNUSED(argc);
90 
91 	dev = name2reg(argv[1]);
92 	if (dev == NULL) {
93 		shell_error(sh, "Invalid regulator: %s", argv[1]);
94 		return -ENODEV;
95 	}
96 
97 	volt_cnt = regulator_count_voltages(dev);
98 
99 	for (unsigned int i = 0U; i < volt_cnt; i++) {
100 		int32_t volt_uv;
101 
102 		(void)regulator_list_voltage(dev, i, &volt_uv);
103 		shell_print(sh, "%d mV", volt_uv / 1000);
104 	}
105 
106 	return 0;
107 }
108 
cmd_regulator_enable(const struct shell * sh,size_t argc,char ** argv)109 static int cmd_regulator_enable(const struct shell *sh, size_t argc, char **argv)
110 {
111 	const struct device *dev;
112 	int ret;
113 
114 	ARG_UNUSED(argc);
115 
116 	dev = name2reg(argv[1]);
117 	if (dev == NULL) {
118 		shell_error(sh, "Invalid regulator: %s", argv[1]);
119 		return -ENODEV;
120 	}
121 
122 	ret = regulator_enable(dev);
123 	if (ret < 0) {
124 		shell_error(sh, "Could not enable regulator (%d)", ret);
125 		return ret;
126 	}
127 
128 	return 0;
129 }
130 
cmd_regulator_disable(const struct shell * sh,size_t argc,char ** argv)131 static int cmd_regulator_disable(const struct shell *sh, size_t argc, char **argv)
132 {
133 	const struct device *dev;
134 	int ret;
135 
136 	ARG_UNUSED(argc);
137 
138 	dev = name2reg(argv[1]);
139 	if (dev == NULL) {
140 		shell_error(sh, "Invalid regulator: %s", argv[1]);
141 		return -ENODEV;
142 	}
143 
144 	ret = regulator_disable(dev);
145 	if (ret < 0) {
146 		shell_error(sh, "Could not disable regulator (%d)", ret);
147 		return ret;
148 	}
149 
150 	return 0;
151 }
152 
cmd_regulator_set(const struct shell * sh,size_t argc,char ** argv)153 static int cmd_regulator_set(const struct shell *sh, size_t argc, char **argv)
154 {
155 	const struct device *dev;
156 	int32_t min_uv, max_uv;
157 	int ret;
158 
159 	dev = name2reg(argv[1]);
160 	if (dev == NULL) {
161 		shell_error(sh, "Invalid regulator: %s", argv[1]);
162 		return -ENODEV;
163 	}
164 
165 	min_uv = (int32_t)strtoul(argv[2], NULL, 10) * 1000;
166 	if (argc == 4) {
167 		max_uv = (int32_t)strtoul(argv[3], NULL, 10) * 1000;
168 	} else {
169 		max_uv = min_uv;
170 	}
171 
172 	ret = regulator_set_voltage(dev, min_uv, max_uv);
173 	if (ret < 0) {
174 		shell_error(sh, "Could not set voltage (%d)", ret);
175 		return ret;
176 	}
177 
178 	return 0;
179 }
180 
cmd_regulator_get(const struct shell * sh,size_t argc,char ** argv)181 static int cmd_regulator_get(const struct shell *sh, size_t argc, char **argv)
182 {
183 	const struct device *dev;
184 	int32_t volt_uv;
185 	int ret;
186 
187 	ARG_UNUSED(argc);
188 
189 	dev = name2reg(argv[1]);
190 	if (dev == NULL) {
191 		shell_error(sh, "Invalid regulator: %s", argv[1]);
192 		return -ENODEV;
193 	}
194 
195 	ret = regulator_get_voltage(dev, &volt_uv);
196 	if (ret < 0) {
197 		shell_error(sh, "Could not get voltage (%d)", ret);
198 		return ret;
199 	}
200 
201 	shell_print(sh, "%d mV", volt_uv / 1000);
202 
203 	return 0;
204 }
205 
cmd_regulator_modeset(const struct shell * sh,size_t argc,char ** argv)206 static int cmd_regulator_modeset(const struct shell *sh, size_t argc, char **argv)
207 {
208 	const struct device *dev;
209 	regulator_mode_t mode;
210 	int ret;
211 
212 	ARG_UNUSED(argc);
213 
214 	dev = name2reg(argv[1]);
215 	if (dev == NULL) {
216 		shell_error(sh, "Invalid regulator: %s", argv[1]);
217 		return -ENODEV;
218 	}
219 
220 	if (strcmp(argv[2], "pwm") == 0) {
221 		mode = NPM6001_MODE_PWM;
222 	} else if (strcmp(argv[2], "hys") == 0) {
223 		mode = NPM6001_MODE_HYS;
224 	} else {
225 		shell_error(sh, "Invalid mode: %s", argv[1]);
226 		return -EINVAL;
227 	}
228 
229 	ret = regulator_set_mode(dev, mode);
230 	if (ret < 0) {
231 		shell_error(sh, "Could not set mode (%d)", ret);
232 		return ret;
233 	}
234 
235 	return 0;
236 }
237 
cmd_regulator_modeget(const struct shell * sh,size_t argc,char ** argv)238 static int cmd_regulator_modeget(const struct shell *sh, size_t argc, char **argv)
239 {
240 	const struct device *dev;
241 	regulator_mode_t mode;
242 	int ret;
243 
244 	ARG_UNUSED(argc);
245 
246 	dev = name2reg(argv[1]);
247 	if (dev == NULL) {
248 		shell_error(sh, "Invalid regulator: %s", argv[1]);
249 		return -ENODEV;
250 	}
251 
252 	ret = regulator_get_mode(dev, &mode);
253 	if (ret < 0) {
254 		shell_error(sh, "Could not get mode (%d)", ret);
255 		return ret;
256 	}
257 
258 	if (mode == NPM6001_MODE_PWM) {
259 		shell_print(sh, "PWM");
260 	} else if (mode == NPM6001_MODE_HYS) {
261 		shell_print(sh, "Hysteretic");
262 	} else {
263 		shell_error(sh, "Invalid mode: %u", mode);
264 		return -EINVAL;
265 	}
266 
267 	return 0;
268 }
269 
cmd_regulator_errors(const struct shell * sh,size_t argc,char ** argv)270 static int cmd_regulator_errors(const struct shell *sh, size_t argc, char **argv)
271 {
272 	const struct device *dev;
273 	regulator_error_flags_t errors;
274 	int ret;
275 
276 	ARG_UNUSED(argc);
277 
278 	dev = name2reg(argv[1]);
279 	if (dev == NULL) {
280 		shell_error(sh, "Invalid regulator: %s", argv[1]);
281 		return -ENODEV;
282 	}
283 
284 	ret = regulator_get_error_flags(dev, &errors);
285 	if (ret < 0) {
286 		shell_error(sh, "Could not get error flags (%d)", ret);
287 		return ret;
288 	}
289 
290 	shell_print(sh, "Overcurrent:\t[%s]",
291 		    ((errors & REGULATOR_ERROR_OVER_CURRENT) != 0U) ? "X" : " ");
292 	shell_print(sh, "Overtemp.:\t[%s]",
293 		    ((errors & REGULATOR_ERROR_OVER_TEMP) != 0U) ? "X" : " ");
294 
295 	return 0;
296 }
297 
cmd_gpio_configure(const struct shell * sh,size_t argc,char ** argv)298 static int cmd_gpio_configure(const struct shell *sh, size_t argc, char **argv)
299 {
300 	int ret;
301 	int opt, long_index = 0;
302 	static int high_drive, pull_down, cmos;
303 	gpio_pin_t pin = 0U;
304 	gpio_flags_t flags = 0U;
305 
306 	static const struct option long_options[] = {
307 		{"pin", required_argument, NULL, 'p'},
308 		{"direction", required_argument, NULL, 'd'},
309 		{"high-drive", no_argument, &high_drive, 1},
310 		{"pull-down", no_argument, &pull_down, 1},
311 		{"cmos", no_argument, &cmos, 1},
312 		{NULL, 0, NULL, 0},
313 	};
314 
315 	high_drive = 0;
316 	pull_down = 0;
317 	cmos = 0;
318 
319 	while ((opt = getopt_long(argc, argv, "p:d:", long_options,
320 				  &long_index)) != -1) {
321 		switch (opt) {
322 		case 0:
323 			/* options setting a flag, do nothing */
324 			break;
325 		case 'p':
326 			pin = atoi(optarg);
327 			break;
328 		case 'd':
329 			if (strcmp(optarg, "in") == 0) {
330 				flags |= GPIO_INPUT;
331 			} else if (strcmp(optarg, "out") == 0) {
332 				flags |= GPIO_OUTPUT;
333 			} else if (strcmp(optarg, "outh") == 0) {
334 				flags |= GPIO_OUTPUT_HIGH;
335 			} else if (strcmp(optarg, "outl") == 0) {
336 				flags |= GPIO_OUTPUT_LOW;
337 			}
338 			break;
339 		default:
340 			shell_error(sh, "Invalid option: %c", opt);
341 			return -EINVAL;
342 		}
343 	}
344 
345 	if (pull_down == 1) {
346 		flags |= GPIO_PULL_DOWN;
347 	}
348 
349 	if (high_drive == 1) {
350 		flags |= NPM6001_GPIO_DRIVE_HIGH;
351 	}
352 
353 	if (cmos == 1) {
354 		flags |= NPM6001_GPIO_SENSE_CMOS;
355 	}
356 
357 	ret = gpio_pin_configure(gpio, pin, flags);
358 	if (ret < 0) {
359 		shell_error(sh, "Configuration failed (%d)", ret);
360 		return ret;
361 	}
362 
363 	return 0;
364 }
365 
cmd_gpio_get(const struct shell * sh,size_t argc,char ** argv)366 static int cmd_gpio_get(const struct shell *sh, size_t argc, char **argv)
367 {
368 	gpio_pin_t pin;
369 	int val;
370 
371 	pin = (gpio_pin_t)atoi(argv[1]);
372 
373 	val = gpio_pin_get(gpio, pin);
374 	if (val < 0) {
375 		shell_error(sh, "Could not get pin level (%d)", val);
376 		return val;
377 	}
378 
379 	shell_print(sh, "Level: %d", val);
380 
381 	return 0;
382 }
383 
cmd_gpio_set(const struct shell * sh,size_t argc,char ** argv)384 static int cmd_gpio_set(const struct shell *sh, size_t argc, char **argv)
385 {
386 	gpio_pin_t pin;
387 	int val;
388 	int ret;
389 
390 	pin = (gpio_pin_t)atoi(argv[1]);
391 	val = atoi(argv[2]);
392 
393 	ret = gpio_pin_set(gpio, pin, val);
394 	if (ret < 0) {
395 		shell_error(sh, "Could not set pin level (%d)", ret);
396 		return ret;
397 	}
398 
399 	return 0;
400 }
401 
cmd_gpio_toggle(const struct shell * sh,size_t argc,char ** argv)402 static int cmd_gpio_toggle(const struct shell *sh, size_t argc, char **argv)
403 {
404 	gpio_pin_t pin;
405 	int ret;
406 
407 	pin = (gpio_pin_t)atoi(argv[1]);
408 
409 	ret = gpio_pin_toggle(gpio, pin);
410 	if (ret < 0) {
411 		shell_error(sh, "Could not toggle pin level (%d)", ret);
412 		return ret;
413 	}
414 
415 	return 0;
416 }
417 
cmd_wdt_enable(const struct shell * sh,size_t argc,char ** argv)418 static int cmd_wdt_enable(const struct shell *sh, size_t argc, char **argv)
419 {
420 	int ret;
421 	struct wdt_timeout_cfg cfg = { 0 };
422 
423 	cfg.window.max = (uint32_t)strtoul(argv[1], NULL, 10) * 1000U;
424 
425 	ret = wdt_install_timeout(wdt, &cfg);
426 	if (ret < 0) {
427 		shell_error(sh, "Could not install watchdog timeout (%d)", ret);
428 	}
429 
430 	return 0;
431 }
432 
cmd_wdt_disable(const struct shell * sh,size_t argc,char ** argv)433 static int cmd_wdt_disable(const struct shell *sh, size_t argc, char **argv)
434 {
435 	int ret;
436 
437 	ret = wdt_disable(wdt);
438 	if (ret < 0) {
439 		shell_error(sh, "Could not disable watchdog (%d)", ret);
440 	}
441 
442 	return 0;
443 }
444 
cmd_wdt_kick(const struct shell * sh,size_t argc,char ** argv)445 static int cmd_wdt_kick(const struct shell *sh, size_t argc, char **argv)
446 {
447 	int ret;
448 
449 	ret = wdt_feed(wdt, 0);
450 	if (ret < 0) {
451 		shell_error(sh, "Could not kick watchdog (%d)", ret);
452 	}
453 
454 	return 0;
455 }
456 
457 SHELL_STATIC_SUBCMD_SET_CREATE(sub_npm6001_regulator_cmds,
458 			       SHELL_CMD(list, NULL, "List regulator names",
459 					     cmd_regulator_list),
460 			       SHELL_CMD_ARG(voltages, NULL, "List voltages",
461 					     cmd_regulator_voltages, 2, 0),
462 			       SHELL_CMD_ARG(enable, NULL, "Enable regulator",
463 					     cmd_regulator_enable, 2, 0),
464 			       SHELL_CMD_ARG(disable, NULL, "Disable regulator",
465 					     cmd_regulator_disable, 2, 0),
466 			       SHELL_CMD_ARG(set, NULL, "Set voltage",
467 					     cmd_regulator_set, 3, 1),
468 			       SHELL_CMD_ARG(get, NULL, "Get voltage",
469 					     cmd_regulator_get, 2, 0),
470 			       SHELL_CMD_ARG(modeset, NULL, "Set mode PWM/HYS",
471 					     cmd_regulator_modeset, 3, 0),
472 			       SHELL_CMD_ARG(modeget, NULL, "Get mode PWM/HYS",
473 					     cmd_regulator_modeget, 2, 0),
474 			       SHELL_CMD_ARG(errors, NULL, "Get active errors",
475 					     cmd_regulator_errors, 2, 0),
476 			       SHELL_SUBCMD_SET_END);
477 
478 SHELL_STATIC_SUBCMD_SET_CREATE(sub_npm6001_gpio_cmds,
479 			       SHELL_CMD_ARG(configure, NULL, "Configure GPIO",
480 					     cmd_gpio_configure, 5, 3),
481 			       SHELL_CMD_ARG(get, NULL, "Get GPIO level",
482 					     cmd_gpio_get, 2, 0),
483 			       SHELL_CMD_ARG(set, NULL, "Set GPIO level",
484 					     cmd_gpio_set, 3, 0),
485 			       SHELL_CMD_ARG(toggle, NULL, "Toggle GPIO level",
486 					     cmd_gpio_toggle, 2, 0),
487 			       SHELL_SUBCMD_SET_END);
488 
489 SHELL_STATIC_SUBCMD_SET_CREATE(sub_npm6001_wdt_cmds,
490 			       SHELL_CMD_ARG(enable, NULL, "Enable watchdog",
491 					     cmd_wdt_enable, 2, 0),
492 			       SHELL_CMD(disable, NULL, "Disable watchdog",
493 					 cmd_wdt_disable),
494 			       SHELL_CMD(kick, NULL, "Kick watchdog",
495 					 cmd_wdt_kick),
496 			       SHELL_SUBCMD_SET_END);
497 
498 SHELL_STATIC_SUBCMD_SET_CREATE(sub_npm6001_cmds,
499 			       SHELL_CMD(regulator, &sub_npm6001_regulator_cmds,
500 					 "Regulators", NULL),
501 			       SHELL_CMD(gpio, &sub_npm6001_gpio_cmds, "GPIO",
502 					 NULL),
503 			       SHELL_CMD(wdt, &sub_npm6001_wdt_cmds, "Watchdog",
504 					 NULL),
505 			       SHELL_SUBCMD_SET_END);
506 
507 SHELL_CMD_REGISTER(npm6001, &sub_npm6001_cmds, "nPM6001 commands", NULL);
508