From 2c9a9c3671814c2cb61fc07098d44b772d16e35a Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Wed, 21 Dec 2022 14:06:10 +0100 Subject: [PATCH] drivers: regulator: shell: refactor shell - Refactor the regulator shell so that it exposes all regulator APIs - vset/iset commands allow to specify a single value (equal min/max) or a range - Voltage/current input is now more user friendly, e.g. user can specify units and decimals: 3.3v, 200mv, -4mv, etc. - Reported values are also printed in a more user friendly way, e.g. 1800000 uV will be printed as 1.800 V. - Added new command to list supported voltages Signed-off-by: Gerard Marull-Paretas --- drivers/regulator/regulator_shell.c | 437 ++++++++++++++++++++++------ 1 file changed, 349 insertions(+), 88 deletions(-) diff --git a/drivers/regulator/regulator_shell.c b/drivers/regulator/regulator_shell.c index 717d30ae456..26b85365bde 100644 --- a/drivers/regulator/regulator_shell.c +++ b/drivers/regulator/regulator_shell.c @@ -1,152 +1,413 @@ /* * Copyright 2022 NXP + * Copyright 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ +#include #include -#include +#include + +#include #include -#include #include +#include -LOG_MODULE_REGISTER(regulator_shell, CONFIG_REGULATOR_LOG_LEVEL); - -static int cmd_reg_en(const struct shell *sh, size_t argc, char **argv) +static int strtomicro(char *inp, char units, int32_t *val) { - const struct device *reg_dev; - int ret; + size_t len, start, end; + int32_t mult, decdiv = 1; - reg_dev = device_get_binding(argv[1]); - if (reg_dev == NULL) { - shell_error(sh, "regulator device %s not available", argv[1]); - return -ENODEV; + len = strlen(inp); + if (len < 2) { + return -EINVAL; } - ret = regulator_enable(reg_dev); - if (ret < 0) { - shell_error(sh, "failed to enable regulator, error %d", ret); - return ret; + /* suffix */ + if (tolower(inp[len - 1]) != units) { + return -EINVAL; } - shell_print(sh, "enabled regulator"); + + if ((len > 2) && (inp[len - 2] == 'u')) { + mult = 1; + end = len - 3; + } else if ((len > 2) && (inp[len - 2] == 'm')) { + mult = 1000; + end = len - 3; + } else if (isdigit(inp[len - 2]) > 0) { + mult = 1000000; + end = len - 2; + } else { + return -EINVAL; + } + + /* optional prefix (sign) */ + if (inp[0] == '-') { + mult *= -1; + start = 1; + } else if (inp[0] == '+') { + start = 1; + } else { + start = 0; + } + + /* numeric part */ + *val = 0; + for (size_t i = start; i <= end; i++) { + if (isdigit(inp[i]) > 0) { + *val = *val * 10 / decdiv + + (int32_t)(inp[i] - '0') * mult / decdiv; + } else if (inp[i] == '.') { + decdiv = 10; + } else { + return -EINVAL; + } + } + return 0; } - -static int cmd_reg_dis(const struct shell *sh, size_t argc, char **argv) +static void microtoshell(const struct shell *sh, char unit, int32_t val) { - const struct device *reg_dev; + if (val > 100000) { + shell_print(sh, "%d.%03d %c", val / 1000000, + (val % 1000000) / 1000, unit); + } else if (val > 1000) { + shell_print(sh, "%d.%03d m%c", val / 1000, val % 1000, unit); + } else { + shell_print(sh, "%d u%c", val, unit); + } +} + +static int cmd_enable(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; int ret; - reg_dev = device_get_binding(argv[1]); - if (reg_dev == NULL) { - shell_error(sh, "regulator device %s not available", argv[1]); + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); return -ENODEV; } - ret = regulator_disable(reg_dev); + ret = regulator_enable(dev); if (ret < 0) { - shell_error(sh, "failed to disable regulator, error %d", ret); + shell_error(sh, "Could not enable regulator (%d)", ret); return ret; } - shell_print(sh, "disabled regulator"); + return 0; } -static int cmd_set_vol(const struct shell *sh, size_t argc, char **argv) +static int cmd_disable(const struct shell *sh, size_t argc, char **argv) { - int lvol, uvol, ret; + const struct device *dev; + int ret; + + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); + return -ENODEV; + } + + ret = regulator_disable(dev); + if (ret < 0) { + shell_error(sh, "Could not disable regulator (%d)", ret); + return ret; + } + + return 0; +} + +static int cmd_vlist(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + unsigned int volt_cnt; + int32_t last_volt_uv; + + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); + return -ENODEV; + } + + volt_cnt = regulator_count_voltages(dev); + + for (unsigned int i = 0U; i < volt_cnt; i++) { + int32_t volt_uv; + + (void)regulator_list_voltage(dev, i, &volt_uv); + + /* do not print repeated voltages */ + if ((i > 0U) && (last_volt_uv != volt_uv)) { + microtoshell(sh, 'V', volt_uv); + } + + last_volt_uv = volt_uv; + } + + return 0; +} + +static int cmd_vset(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + int32_t min_uv, max_uv; + int ret; + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); + return -ENODEV; + } + + ret = strtomicro(argv[2], 'v', &min_uv); + if (ret < 0) { + shell_error(sh, "Invalid min. voltage: %s", argv[2]); + return ret; + } + + if (argc == 4) { + ret = strtomicro(argv[3], 'v', &max_uv); + if (ret < 0) { + shell_error(sh, "Invalid max. voltage: %s", argv[3]); + return ret; + } + } else { + max_uv = min_uv; + } + + ret = regulator_set_voltage(dev, min_uv, max_uv); + if (ret < 0) { + shell_error(sh, "Could not set voltage (%d)", ret); + return ret; + } + + return 0; +} + +static int cmd_vget(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; int32_t volt_uv; - const struct device *reg_dev; + int ret; - reg_dev = device_get_binding(argv[1]); - if (reg_dev == NULL) { - shell_error(sh, "regulator device %s not available", argv[1]); + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); return -ENODEV; } - lvol = strtol(argv[2], NULL, 10) * 1000; - uvol = strtol(argv[3], NULL, 10) * 1000; - shell_print(sh, "Setting range to %d-%d uV", lvol, uvol); - ret = regulator_set_voltage(reg_dev, lvol, uvol); + ret = regulator_get_voltage(dev, &volt_uv); if (ret < 0) { - shell_error(sh, "failed to set voltage, error %d", ret); + shell_error(sh, "Could not get voltage (%d)", ret); return ret; } - ret = regulator_get_voltage(reg_dev, &volt_uv); - if (ret < 0) { - shell_error(sh, "failed to read voltage, error %d", ret); - return ret; - } - shell_print(sh, "set voltage to %d uV", volt_uv); + + microtoshell(sh, 'V', volt_uv); + return 0; } -static int cmd_set_ilim(const struct shell *sh, size_t argc, char **argv) +static int cmd_iset(const struct shell *sh, size_t argc, char **argv) { - int lcur, ucur, ret; + const struct device *dev; + int32_t min_ua, max_ua; + int ret; + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); + return -ENODEV; + } + + ret = strtomicro(argv[2], 'a', &min_ua); + if (ret < 0) { + shell_error(sh, "Invalid min. current: %s", argv[2]); + return ret; + } + if (argc == 4) { + ret = strtomicro(argv[3], 'a', &max_ua); + if (ret < 0) { + shell_error(sh, "Invalid max. current: %s", argv[3]); + return ret; + } + } else { + max_ua = min_ua; + } + + ret = regulator_set_current_limit(dev, min_ua, max_ua); + if (ret < 0) { + shell_error(sh, "Could not set current limit (%d)", ret); + return ret; + } + + return 0; +} + +static int cmd_iget(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; int32_t curr_ua; - const struct device *reg_dev; + int ret; - reg_dev = device_get_binding(argv[1]); - if (reg_dev == NULL) { - shell_error(sh, "regulator device %s not available", argv[1]); + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); return -ENODEV; } - lcur = strtol(argv[2], NULL, 10) * 1000; - ucur = strtol(argv[3], NULL, 10) * 1000; - shell_print(sh, "Setting range to %d-%d uA", lcur, ucur); - ret = regulator_set_current_limit(reg_dev, lcur, ucur); + ret = regulator_get_current_limit(dev, &curr_ua); if (ret < 0) { - shell_error(sh, "failed to set current, error %d", ret); + shell_error(sh, "Could not get current limit (%d)", ret); return ret; } - ret = regulator_get_current_limit(reg_dev, &curr_ua); - if (ret < 0) { - shell_error(sh, "failed to read current, error %d", ret); - return ret; - } - shell_print(sh, "set current limit to %d uA", curr_ua); + + microtoshell(sh, 'A', curr_ua); + return 0; } -static int cmd_set_mode(const struct shell *sh, size_t argc, char **argv) +static int cmd_modeset(const struct shell *sh, size_t argc, char **argv) { - int mode, ret; - const struct device *reg_dev; + const struct device *dev; + regulator_mode_t mode; + int ret; - reg_dev = device_get_binding(argv[1]); - if (reg_dev == NULL) { - shell_error(sh, "regulator device %s not available", argv[1]); + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); return -ENODEV; } - mode = strtol(argv[2], NULL, 10); - ret = regulator_set_mode(reg_dev, mode); + mode = (regulator_mode_t)strtoul(argv[2], NULL, 10); + + ret = regulator_set_mode(dev, mode); if (ret < 0) { - shell_error(sh, "failed to set mode, error %d", ret); + shell_error(sh, "Could not set mode (%d)", ret); + return ret; } - return ret; + + return 0; } -SHELL_STATIC_SUBCMD_SET_CREATE(regulator_set, - SHELL_CMD_ARG(enable, NULL, - "Enable regulator\n" - "Usage: enable ", cmd_reg_en, 2, 0), - SHELL_CMD_ARG(disable, NULL, - "Disable regulator\n" - "Usage: disable ", cmd_reg_dis, 2, 0), - SHELL_CMD_ARG(set_vol, NULL, "Set voltage (in mV)\n" - "Usage: set_vol ", - cmd_set_vol, 4, 0), - SHELL_CMD_ARG(set_current, NULL, "Set current limit( in mA)\n" - "Usage: set_current ", - cmd_set_ilim, 4, 0), - SHELL_CMD_ARG(set_mode, NULL, "Set mode of regulator\n" - "Usage: set_mode ", - cmd_set_mode, 3, 0), - SHELL_SUBCMD_SET_END -); +static int cmd_modeget(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + regulator_mode_t mode; + int ret; -SHELL_CMD_REGISTER(regulator, ®ulator_set, "Regulator Management", NULL); + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); + return -ENODEV; + } + + ret = regulator_get_mode(dev, &mode); + if (ret < 0) { + shell_error(sh, "Could not get mode (%d)", ret); + return ret; + } + + shell_print(sh, "Mode: %u", (unsigned int)mode); + + return 0; +} + +static int cmd_errors(const struct shell *sh, size_t argc, char **argv) +{ + const struct device *dev; + regulator_error_flags_t errors; + int ret; + + ARG_UNUSED(argc); + + dev = device_get_binding(argv[1]); + if (dev == NULL) { + shell_error(sh, "Regulator device %s not available", argv[1]); + return -ENODEV; + } + + ret = regulator_get_error_flags(dev, &errors); + if (ret < 0) { + shell_error(sh, "Could not get error flags (%d)", ret); + return ret; + } + + shell_print(sh, "Overvoltage:\t[%s]", + ((errors & REGULATOR_ERROR_OVER_VOLTAGE) != 0U) ? "X" + : " "); + shell_print(sh, "Overcurrent:\t[%s]", + ((errors & REGULATOR_ERROR_OVER_CURRENT) != 0U) ? "X" + : " "); + shell_print(sh, "Overtemp.:\t[%s]", + ((errors & REGULATOR_ERROR_OVER_TEMP) != 0U) ? "X" : " "); + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_regulator_cmds, + SHELL_CMD_ARG(enable, NULL, + "Enable regulator\n" + "Usage: enable ", + cmd_enable, 2, 0), + SHELL_CMD_ARG(disable, NULL, + "Disable regulator\n" + "Usage: disable ", + cmd_disable, 2, 0), + SHELL_CMD_ARG(vlist, NULL, + "List all supported voltages\n" + "Usage: vlist ", + cmd_vlist, 2, 0), + SHELL_CMD_ARG(vset, NULL, + "Set voltage\n" + "Input requires units, e.g. 200mv, 20.5mv, 10uv, 1v...\n" + "Usage: vset []\n" + "If maximum is not set, exact voltage will be requested", + cmd_vset, 3, 1), + SHELL_CMD_ARG(vget, NULL, + "Get voltage\n" + "Usage: vget ", + cmd_vget, 2, 0), + SHELL_CMD_ARG(iset, NULL, + "Set current limit\n" + "Input requires units, e.g. 200ma, 20.5ma, 10ua, 1a...\n" + "Usage: iset []" + "If maximum is not set, exact current will be requested", + cmd_iset, 3, 1), + SHELL_CMD_ARG(iget, NULL, + "Get current limit\n" + "Usage: iget ", + cmd_iget, 2, 0), + SHELL_CMD_ARG(modeset, NULL, + "Set regulator mode\n" + "Usage: modeset ", + cmd_modeset, 3, 0), + SHELL_CMD_ARG(modeget, NULL, + "Get regulator mode\n" + "Usage: modeget ", + cmd_modeget, 2, 0), + SHELL_CMD_ARG(errors, NULL, + "Get errors\n" + "Usage: errors ", + cmd_errors, 2, 0), + SHELL_SUBCMD_SET_END); + +SHELL_CMD_REGISTER(regulator, &sub_regulator_cmds, "Regulator playground", + NULL);