| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * List, select, and deselect mux controllers on the fly. | |
| * | |
| * Copyright (c) 2020 Texas Instruments Inc. | |
| * Author: Pratyush Yadav <p.yadav@ti.com> | |
| */ | |
| #include <command.h> | |
| #include <errno.h> | |
| #include <dm.h> | |
| #include <dm/device_compat.h> | |
| #include <mux.h> | |
| #include <mux-internal.h> | |
| #include <linux/err.h> | |
| #include <dt-bindings/mux/mux.h> | |
| #define COLUMN_SIZE 16 | |
| /* | |
| * Print a member of a column. The total size of the text printed, including | |
| * trailing whitespace, will always be COLUMN_SIZE. | |
| */ | |
| #define PRINT_COLUMN(fmt, args...) do { \ | |
| char buf[COLUMN_SIZE + 1]; \ | |
| snprintf(buf, COLUMN_SIZE + 1, fmt, ##args); \ | |
| printf("%-*s", COLUMN_SIZE, buf); \ | |
| } while (0) | |
| /* | |
| * Find a mux based on its device name in argv[1] and index in the chip in | |
| * argv[2]. | |
| */ | |
| static struct mux_control *cmd_mux_find(char *const argv[]) | |
| { | |
| struct udevice *dev; | |
| struct mux_chip *chip; | |
| int ret; | |
| unsigned long id; | |
| ret = strict_strtoul(argv[2], 10, &id); | |
| if (ret) | |
| return ERR_PTR(ret); | |
| ret = uclass_get_device_by_name(UCLASS_MUX, argv[1], &dev); | |
| if (ret) | |
| return ERR_PTR(ret); | |
| chip = dev_get_uclass_priv(dev); | |
| if (!chip) | |
| return ERR_PTR(-EINVAL); | |
| if (id >= chip->controllers) | |
| return ERR_PTR(-EINVAL); | |
| return &chip->mux[id]; | |
| } | |
| /* | |
| * Print the details of a mux. The columns printed correspond to: "Selected", | |
| * "Current State", "Idle State", and "Num States". | |
| */ | |
| static void print_mux(struct mux_control *mux) | |
| { | |
| PRINT_COLUMN("%s", mux->in_use ? "yes" : "no"); | |
| if (mux->cached_state == MUX_IDLE_AS_IS) | |
| PRINT_COLUMN("%s", "unknown"); | |
| else | |
| PRINT_COLUMN("0x%x", mux->cached_state); | |
| if (mux->idle_state == MUX_IDLE_AS_IS) | |
| PRINT_COLUMN("%s", "as-is"); | |
| else if (mux->idle_state == MUX_IDLE_DISCONNECT) | |
| PRINT_COLUMN("%s", "disconnect"); | |
| else | |
| PRINT_COLUMN("0x%x", mux->idle_state); | |
| PRINT_COLUMN("0x%x", mux->states); | |
| printf("\n"); | |
| } | |
| static int do_mux_list(struct cmd_tbl *cmdtp, int flag, int argc, | |
| char *const argv[]) | |
| { | |
| struct udevice *dev; | |
| struct mux_chip *chip; | |
| int j; | |
| for (uclass_first_device(UCLASS_MUX, &dev); | |
| dev; | |
| uclass_next_device(&dev)) { | |
| chip = dev_get_uclass_priv(dev); | |
| if (!chip) { | |
| dev_err(dev, "can't find mux chip\n"); | |
| continue; | |
| } | |
| printf("%s:\n", dev->name); | |
| printf(" "); | |
| PRINT_COLUMN("ID"); | |
| PRINT_COLUMN("Selected"); | |
| PRINT_COLUMN("Current State"); | |
| PRINT_COLUMN("Idle State"); | |
| PRINT_COLUMN("Num States"); | |
| printf("\n"); | |
| for (j = 0; j < chip->controllers; j++) { | |
| printf(" "); | |
| PRINT_COLUMN("%d", j); | |
| print_mux(&chip->mux[j]); | |
| } | |
| printf("\n"); | |
| } | |
| return 0; | |
| } | |
| static int do_mux_select(struct cmd_tbl *cmdtp, int flag, int argc, | |
| char *const argv[]) | |
| { | |
| struct mux_control *mux; | |
| int ret; | |
| unsigned long state; | |
| if (argc != 4) | |
| return CMD_RET_USAGE; | |
| mux = cmd_mux_find(argv); | |
| if (IS_ERR_OR_NULL(mux)) { | |
| printf("Failed to find the specified mux\n"); | |
| return CMD_RET_FAILURE; | |
| } | |
| ret = strict_strtoul(argv[3], 16, &state); | |
| if (ret) { | |
| printf("Invalid state\n"); | |
| return CMD_RET_FAILURE; | |
| } | |
| ret = mux_control_select(mux, state); | |
| if (ret) { | |
| printf("Failed to select requested state\n"); | |
| return CMD_RET_FAILURE; | |
| } | |
| return CMD_RET_SUCCESS; | |
| } | |
| static int do_mux_deselect(struct cmd_tbl *cmdtp, int flag, int argc, | |
| char *const argv[]) | |
| { | |
| struct mux_control *mux; | |
| int ret; | |
| if (argc != 3) | |
| return CMD_RET_USAGE; | |
| mux = cmd_mux_find(argv); | |
| if (IS_ERR_OR_NULL(mux)) { | |
| printf("Failed to find the specified mux\n"); | |
| return CMD_RET_FAILURE; | |
| } | |
| ret = mux_control_deselect(mux); | |
| if (ret) { | |
| printf("Failed to deselect mux\n"); | |
| return CMD_RET_FAILURE; | |
| } | |
| return CMD_RET_SUCCESS; | |
| } | |
| U_BOOT_LONGHELP(mux, | |
| "list - List all Muxes and their states\n" | |
| "select <chip> <id> <state> - Select the given mux state\n" | |
| "deselect <chip> <id> - Deselect the given mux and reset it to its idle state"); | |
| U_BOOT_CMD_WITH_SUBCMDS(mux, "List, select, and deselect muxes", mux_help_text, | |
| U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mux_list), | |
| U_BOOT_SUBCMD_MKENT(select, 4, 0, do_mux_select), | |
| U_BOOT_SUBCMD_MKENT(deselect, 3, 0, do_mux_deselect)); |