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