Merge branch '2020-10-28-mux-driver-framework'
- Add a framework for mux drivers
This commit is contained in:
		
						commit
						4f1d3079b9
					
				
							
								
								
									
										2
									
								
								Kconfig
								
								
								
								
							
							
						
						
									
										2
									
								
								Kconfig
								
								
								
								
							| 
						 | 
					@ -196,7 +196,7 @@ config SYS_MALLOC_F_LEN
 | 
				
			||||||
	hex "Size of malloc() pool before relocation"
 | 
						hex "Size of malloc() pool before relocation"
 | 
				
			||||||
	depends on SYS_MALLOC_F
 | 
						depends on SYS_MALLOC_F
 | 
				
			||||||
	default 0x1000 if AM33XX
 | 
						default 0x1000 if AM33XX
 | 
				
			||||||
	default 0x2800 if SANDBOX
 | 
						default 0x4000 if SANDBOX
 | 
				
			||||||
	default 0x2000 if (ARCH_IMX8 || ARCH_IMX8M || ARCH_MX7 || \
 | 
						default 0x2000 if (ARCH_IMX8 || ARCH_IMX8M || ARCH_MX7 || \
 | 
				
			||||||
			   ARCH_MX7ULP || ARCH_MX6 || ARCH_MX5 || \
 | 
								   ARCH_MX7ULP || ARCH_MX6 || ARCH_MX5 || \
 | 
				
			||||||
			   ARCH_LS1012A || ARCH_LS1021A || ARCH_LS1043A || \
 | 
								   ARCH_LS1012A || ARCH_LS1021A || ARCH_LS1043A || \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
#include <dt-bindings/gpio/gpio.h>
 | 
					#include <dt-bindings/gpio/gpio.h>
 | 
				
			||||||
#include <dt-bindings/gpio/sandbox-gpio.h>
 | 
					#include <dt-bindings/gpio/sandbox-gpio.h>
 | 
				
			||||||
#include <dt-bindings/pinctrl/sandbox-pinmux.h>
 | 
					#include <dt-bindings/pinctrl/sandbox-pinmux.h>
 | 
				
			||||||
 | 
					#include <dt-bindings/mux/mux.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/ {
 | 
					/ {
 | 
				
			||||||
	model = "sandbox";
 | 
						model = "sandbox";
 | 
				
			||||||
| 
						 | 
					@ -133,6 +134,12 @@
 | 
				
			||||||
		interrupts-extended = <&irq 3 0>;
 | 
							interrupts-extended = <&irq 3 0>;
 | 
				
			||||||
		acpi,name = "GHIJ";
 | 
							acpi,name = "GHIJ";
 | 
				
			||||||
		phandle-value = <&gpio_c 10>, <0xFFFFFFFF 20>, <&gpio_a 30>;
 | 
							phandle-value = <&gpio_c 10>, <0xFFFFFFFF 20>, <&gpio_a 30>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mux-controls = <&muxcontroller0 0>, <&muxcontroller0 1>,
 | 
				
			||||||
 | 
								       <&muxcontroller0 2>, <&muxcontroller0 3>,
 | 
				
			||||||
 | 
								       <&muxcontroller1>;
 | 
				
			||||||
 | 
							mux-control-names = "mux0", "mux1", "mux2", "mux3", "mux4";
 | 
				
			||||||
 | 
							mux-syscon = <&syscon3>;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	junk {
 | 
						junk {
 | 
				
			||||||
| 
						 | 
					@ -170,6 +177,9 @@
 | 
				
			||||||
		compatible = "denx,u-boot-fdt-test";
 | 
							compatible = "denx,u-boot-fdt-test";
 | 
				
			||||||
		ping-expect = <3>;
 | 
							ping-expect = <3>;
 | 
				
			||||||
		ping-add = <3>;
 | 
							ping-add = <3>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mux-controls = <&muxcontroller0 0>;
 | 
				
			||||||
 | 
							mux-control-names = "mux0";
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	phy_provider0: gen_phy@0 {
 | 
						phy_provider0: gen_phy@0 {
 | 
				
			||||||
| 
						 | 
					@ -884,6 +894,29 @@
 | 
				
			||||||
			0x58 8>;
 | 
								0x58 8>;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						syscon3: syscon@3 {
 | 
				
			||||||
 | 
							compatible = "simple-mfd", "syscon";
 | 
				
			||||||
 | 
							reg = <0x000100 0x10>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							muxcontroller0: a-mux-controller {
 | 
				
			||||||
 | 
								compatible = "mmio-mux";
 | 
				
			||||||
 | 
								#mux-control-cells = <1>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								mux-reg-masks = <0x0 0x30>, /* 0: reg 0x0, bits 5:4 */
 | 
				
			||||||
 | 
										<0xc 0x1E>, /* 1: reg 0xc, bits 4:1 */
 | 
				
			||||||
 | 
										<0x4 0xFF>; /* 2: reg 0x4, bits 7:0 */
 | 
				
			||||||
 | 
								idle-states = <MUX_IDLE_AS_IS>, <0x02>, <0x73>;
 | 
				
			||||||
 | 
								u-boot,mux-autoprobe;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						muxcontroller1: emul-mux-controller {
 | 
				
			||||||
 | 
							compatible = "mux-emul";
 | 
				
			||||||
 | 
							#mux-control-cells = <0>;
 | 
				
			||||||
 | 
							u-boot,mux-autoprobe;
 | 
				
			||||||
 | 
							idle-state = <0xabcd>;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	timer@0 {
 | 
						timer@0 {
 | 
				
			||||||
		compatible = "sandbox,timer";
 | 
							compatible = "sandbox,timer";
 | 
				
			||||||
		clock-frequency = <1000000>;
 | 
							clock-frequency = <1000000>;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1081,6 +1081,12 @@ config CMD_MTD
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  MTD commands support.
 | 
						  MTD commands support.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config CMD_MUX
 | 
				
			||||||
 | 
						bool "mux"
 | 
				
			||||||
 | 
						depends on MULTIPLEXER
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						 List, select, and deselect mux controllers on the fly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config CMD_NAND
 | 
					config CMD_NAND
 | 
				
			||||||
	bool "nand"
 | 
						bool "nand"
 | 
				
			||||||
	default y if NAND_SUNXI
 | 
						default y if NAND_SUNXI
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,6 +105,7 @@ obj-$(CONFIG_CMD_CLONE) += clone.o
 | 
				
			||||||
ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),)
 | 
					ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),)
 | 
				
			||||||
obj-y += legacy-mtd-utils.o
 | 
					obj-y += legacy-mtd-utils.o
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					obj-$(CONFIG_CMD_MUX) += mux.o
 | 
				
			||||||
obj-$(CONFIG_CMD_NAND) += nand.o
 | 
					obj-$(CONFIG_CMD_NAND) += nand.o
 | 
				
			||||||
obj-$(CONFIG_CMD_NET) += net.o
 | 
					obj-$(CONFIG_CMD_NET) += net.o
 | 
				
			||||||
obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
 | 
					obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,184 @@
 | 
				
			||||||
 | 
					// 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 <common.h>
 | 
				
			||||||
 | 
					#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(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char mux_help_text[] =
 | 
				
			||||||
 | 
						"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));
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,7 @@
 | 
				
			||||||
#include <miiphy.h>
 | 
					#include <miiphy.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#include <mmc.h>
 | 
					#include <mmc.h>
 | 
				
			||||||
 | 
					#include <mux.h>
 | 
				
			||||||
#include <nand.h>
 | 
					#include <nand.h>
 | 
				
			||||||
#include <of_live.h>
 | 
					#include <of_live.h>
 | 
				
			||||||
#include <onenand_uboot.h>
 | 
					#include <onenand_uboot.h>
 | 
				
			||||||
| 
						 | 
					@ -341,6 +342,17 @@ static int initr_dm_devices(void)
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (IS_ENABLED(CONFIG_MULTIPLEXER)) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Initialize the multiplexer controls to their default state.
 | 
				
			||||||
 | 
							 * This must be done early as other drivers may unknowingly
 | 
				
			||||||
 | 
							 * rely on it.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							ret = dm_mux_init();
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,7 @@ CONFIG_CMD_GPT_RENAME=y
 | 
				
			||||||
CONFIG_CMD_IDE=y
 | 
					CONFIG_CMD_IDE=y
 | 
				
			||||||
CONFIG_CMD_I2C=y
 | 
					CONFIG_CMD_I2C=y
 | 
				
			||||||
CONFIG_CMD_LSBLK=y
 | 
					CONFIG_CMD_LSBLK=y
 | 
				
			||||||
 | 
					CONFIG_CMD_MUX=y
 | 
				
			||||||
CONFIG_CMD_OSD=y
 | 
					CONFIG_CMD_OSD=y
 | 
				
			||||||
CONFIG_CMD_PCI=y
 | 
					CONFIG_CMD_PCI=y
 | 
				
			||||||
CONFIG_CMD_READ=y
 | 
					CONFIG_CMD_READ=y
 | 
				
			||||||
| 
						 | 
					@ -179,6 +180,8 @@ CONFIG_SPI_FLASH_SPANSION=y
 | 
				
			||||||
CONFIG_SPI_FLASH_STMICRO=y
 | 
					CONFIG_SPI_FLASH_STMICRO=y
 | 
				
			||||||
CONFIG_SPI_FLASH_SST=y
 | 
					CONFIG_SPI_FLASH_SST=y
 | 
				
			||||||
CONFIG_SPI_FLASH_WINBOND=y
 | 
					CONFIG_SPI_FLASH_WINBOND=y
 | 
				
			||||||
 | 
					CONFIG_MULTIPLEXER=y
 | 
				
			||||||
 | 
					CONFIG_MUX_MMIO=y
 | 
				
			||||||
CONFIG_DM_ETH=y
 | 
					CONFIG_DM_ETH=y
 | 
				
			||||||
CONFIG_NVME=y
 | 
					CONFIG_NVME=y
 | 
				
			||||||
CONFIG_PCI=y
 | 
					CONFIG_PCI=y
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,6 +64,8 @@ source "drivers/mmc/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source "drivers/mtd/Kconfig"
 | 
					source "drivers/mtd/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source "drivers/mux/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source "drivers/net/Kconfig"
 | 
					source "drivers/net/Kconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source "drivers/nvme/Kconfig"
 | 
					source "drivers/nvme/Kconfig"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ obj-$(CONFIG_$(SPL_TPL_)INPUT) += input/
 | 
				
			||||||
obj-$(CONFIG_$(SPL_TPL_)LED) += led/
 | 
					obj-$(CONFIG_$(SPL_TPL_)LED) += led/
 | 
				
			||||||
obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/
 | 
					obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/
 | 
				
			||||||
obj-y += mtd/
 | 
					obj-y += mtd/
 | 
				
			||||||
 | 
					obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux/
 | 
				
			||||||
obj-$(CONFIG_$(SPL_TPL_)PCH_SUPPORT) += pch/
 | 
					obj-$(CONFIG_$(SPL_TPL_)PCH_SUPPORT) += pch/
 | 
				
			||||||
obj-$(CONFIG_$(SPL_TPL_)PCI) += pci/
 | 
					obj-$(CONFIG_$(SPL_TPL_)PCI) += pci/
 | 
				
			||||||
obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/
 | 
					obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					menu "Multiplexer drivers"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config MULTIPLEXER
 | 
				
			||||||
 | 
						bool "Multiplexer Support"
 | 
				
			||||||
 | 
						depends on DM
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						 The mux framework is a minimalistic subsystem that handles multiplexer
 | 
				
			||||||
 | 
						 controllers. It provides the same API as Linux and mux drivers should
 | 
				
			||||||
 | 
						 be portable with a minimum effort.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if MULTIPLEXER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config MUX_MMIO
 | 
				
			||||||
 | 
						bool "MMIO register bitfield-controlled Multiplexer"
 | 
				
			||||||
 | 
						depends on MULTIPLEXER && SYSCON
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  MMIO register bitfield-controlled Multiplexer controller.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  The driver builds multiplexer controllers for bitfields in a syscon
 | 
				
			||||||
 | 
						  register. For N bit wide bitfields, there will be 2^N possible
 | 
				
			||||||
 | 
						  multiplexer states.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					endmenu
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-2.0+
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# (C) Copyright 2019
 | 
				
			||||||
 | 
					# Jean-Jacques Hiblot <jjhiblot@ti.com>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					obj-$(CONFIG_$(SPL_)MULTIPLEXER) += mux-uclass.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_$(SPL_)MUX_MMIO) += mmio.o
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,142 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * MMIO register bitfield-controlled multiplexer driver
 | 
				
			||||||
 | 
					 * Based on the linux mmio multiplexer driver
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 Texas Instrument, Jean-jacques Hiblot <jjhiblot@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <common.h>
 | 
				
			||||||
 | 
					#include <dm.h>
 | 
				
			||||||
 | 
					#include <mux-internal.h>
 | 
				
			||||||
 | 
					#include <regmap.h>
 | 
				
			||||||
 | 
					#include <syscon.h>
 | 
				
			||||||
 | 
					#include <dm/device.h>
 | 
				
			||||||
 | 
					#include <dm/device_compat.h>
 | 
				
			||||||
 | 
					#include <dm/read.h>
 | 
				
			||||||
 | 
					#include <dm/devres.h>
 | 
				
			||||||
 | 
					#include <dt-bindings/mux/mux.h>
 | 
				
			||||||
 | 
					#include <linux/bitops.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_mmio_set(struct mux_control *mux, int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct regmap_field **fields = dev_get_priv(mux->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return regmap_field_write(fields[mux_control_get_index(mux)], state);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct mux_control_ops mux_mmio_ops = {
 | 
				
			||||||
 | 
						.set = mux_mmio_set,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct udevice_id mmio_mux_of_match[] = {
 | 
				
			||||||
 | 
						{ .compatible = "mmio-mux" },
 | 
				
			||||||
 | 
						{ /* sentinel */ },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mmio_mux_probe(struct udevice *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct regmap_field **fields;
 | 
				
			||||||
 | 
						struct mux_chip *mux_chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
						struct regmap *regmap;
 | 
				
			||||||
 | 
						u32 *mux_reg_masks;
 | 
				
			||||||
 | 
						u32 *idle_states;
 | 
				
			||||||
 | 
						int num_fields;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						regmap = syscon_node_to_regmap(dev_ofnode(dev->parent));
 | 
				
			||||||
 | 
						if (IS_ERR(regmap)) {
 | 
				
			||||||
 | 
							ret = PTR_ERR(regmap);
 | 
				
			||||||
 | 
							dev_err(dev, "failed to get regmap: %d\n", ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_fields = dev_read_size(dev, "mux-reg-masks");
 | 
				
			||||||
 | 
						if (num_fields < 0)
 | 
				
			||||||
 | 
							return log_msg_ret("mux-reg-masks missing", -EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_fields /= sizeof(u32);
 | 
				
			||||||
 | 
						if (num_fields == 0 || num_fields % 2)
 | 
				
			||||||
 | 
							ret = -EINVAL;
 | 
				
			||||||
 | 
						num_fields = num_fields / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = mux_alloc_controllers(dev, num_fields);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return log_msg_ret("mux_alloc_controllers", ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fields = devm_kmalloc(dev, num_fields * sizeof(*fields), __GFP_ZERO);
 | 
				
			||||||
 | 
						if (!fields)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						dev->priv = fields;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux_reg_masks = devm_kmalloc(dev, num_fields * 2 * sizeof(u32),
 | 
				
			||||||
 | 
									     __GFP_ZERO);
 | 
				
			||||||
 | 
						if (!mux_reg_masks)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = dev_read_u32_array(dev, "mux-reg-masks", mux_reg_masks,
 | 
				
			||||||
 | 
									 num_fields * 2);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return log_msg_ret("mux-reg-masks read", ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						idle_states = devm_kmalloc(dev, num_fields * sizeof(u32), __GFP_ZERO);
 | 
				
			||||||
 | 
						if (!idle_states)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = dev_read_u32_array(dev, "idle-states", idle_states, num_fields);
 | 
				
			||||||
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							log_err("idle-states");
 | 
				
			||||||
 | 
							devm_kfree(dev, idle_states);
 | 
				
			||||||
 | 
							idle_states = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < num_fields; i++) {
 | 
				
			||||||
 | 
							struct mux_control *mux = &mux_chip->mux[i];
 | 
				
			||||||
 | 
							struct reg_field field;
 | 
				
			||||||
 | 
							u32 reg, mask;
 | 
				
			||||||
 | 
							int bits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							reg = mux_reg_masks[2 * i];
 | 
				
			||||||
 | 
							mask = mux_reg_masks[2 * i + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							field.reg = reg;
 | 
				
			||||||
 | 
							field.msb = fls(mask) - 1;
 | 
				
			||||||
 | 
							field.lsb = ffs(mask) - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mask != GENMASK(field.msb, field.lsb))
 | 
				
			||||||
 | 
								return log_msg_ret("invalid mask", -EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fields[i] = devm_regmap_field_alloc(dev, regmap, field);
 | 
				
			||||||
 | 
							if (IS_ERR(fields[i])) {
 | 
				
			||||||
 | 
								ret = PTR_ERR(fields[i]);
 | 
				
			||||||
 | 
								return log_msg_ret("regmap_field_alloc", ret);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bits = 1 + field.msb - field.lsb;
 | 
				
			||||||
 | 
							mux->states = 1 << bits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!idle_states)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (idle_states[i] != MUX_IDLE_AS_IS &&
 | 
				
			||||||
 | 
							    idle_states[i] >= mux->states)
 | 
				
			||||||
 | 
								return log_msg_ret("idle-states range", -EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mux->idle_state = idle_states[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						devm_kfree(dev, mux_reg_masks);
 | 
				
			||||||
 | 
						if (idle_states)
 | 
				
			||||||
 | 
							devm_kfree(dev, idle_states);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					U_BOOT_DRIVER(mmio_mux) = {
 | 
				
			||||||
 | 
						.name = "mmio-mux",
 | 
				
			||||||
 | 
						.id = UCLASS_MUX,
 | 
				
			||||||
 | 
						.of_match = mmio_mux_of_match,
 | 
				
			||||||
 | 
						.probe = mmio_mux_probe,
 | 
				
			||||||
 | 
						.ops = &mux_mmio_ops,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,335 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Multiplexer subsystem
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Based on the linux multiplexer framework
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017 Axentia Technologies AB
 | 
				
			||||||
 | 
					 * Author: Peter Rosin <peda@axentia.se>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
 | 
				
			||||||
 | 
					 * Jean-Jacques Hiblot <jjhiblot@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <common.h>
 | 
				
			||||||
 | 
					#include <dm.h>
 | 
				
			||||||
 | 
					#include <mux-internal.h>
 | 
				
			||||||
 | 
					#include <dm/device-internal.h>
 | 
				
			||||||
 | 
					#include <dm/device_compat.h>
 | 
				
			||||||
 | 
					#include <dm/devres.h>
 | 
				
			||||||
 | 
					#include <dt-bindings/mux/mux.h>
 | 
				
			||||||
 | 
					#include <linux/bug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * The idle-as-is "state" is not an actual state that may be selected, it
 | 
				
			||||||
 | 
					 * only implies that the state should not be changed. So, use that state
 | 
				
			||||||
 | 
					 * as indication that the cached state of the multiplexer is unknown.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define MUX_CACHE_UNKNOWN MUX_IDLE_AS_IS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_ops() - Get the mux_control ops.
 | 
				
			||||||
 | 
					 * @dev: The client device.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: A pointer to the 'mux_control_ops' of the device.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline const struct mux_control_ops *mux_dev_ops(struct udevice *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (const struct mux_control_ops *)dev->driver->ops;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_set() - Set the state of the given mux controller.
 | 
				
			||||||
 | 
					 * @mux: A multiplexer control
 | 
				
			||||||
 | 
					 * @state: The new requested state.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 if OK, or a negative error code.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mux_control_set(struct mux_control *mux, int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = mux_dev_ops(mux->dev)->set(mux, state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned int mux_control_states(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mux->states;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * __mux_control_select() - Select the given multiplexer state.
 | 
				
			||||||
 | 
					 * @mux: The mux-control to request a change of state from.
 | 
				
			||||||
 | 
					 * @state: The new requested state.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Try to set the mux to the requested state. If not, try to revert if
 | 
				
			||||||
 | 
					 * appropriate.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int __mux_control_select(struct mux_control *mux, int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (WARN_ON(state < 0 || state >= mux->states))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mux->cached_state == state)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = mux_control_set(mux, state);
 | 
				
			||||||
 | 
						if (ret >= 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The mux update failed, try to revert if appropriate... */
 | 
				
			||||||
 | 
						if (mux->idle_state != MUX_IDLE_AS_IS)
 | 
				
			||||||
 | 
							mux_control_set(mux, mux->idle_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mux_control_select(struct mux_control *mux, unsigned int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mux->in_use)
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = __mux_control_select(mux, state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux->in_use = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mux_control_deselect(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mux->idle_state != MUX_IDLE_AS_IS &&
 | 
				
			||||||
 | 
						    mux->idle_state != mux->cached_state)
 | 
				
			||||||
 | 
							ret = mux_control_set(mux, mux->idle_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux->in_use = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_of_xlate_default(struct mux_chip *mux_chip,
 | 
				
			||||||
 | 
									struct ofnode_phandle_args *args,
 | 
				
			||||||
 | 
									struct mux_control **muxp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						int id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log_debug("%s(muxp=%p)\n", __func__, muxp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (args->args_count > 1) {
 | 
				
			||||||
 | 
							debug("Invaild args_count: %d\n", args->args_count);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (args->args_count)
 | 
				
			||||||
 | 
							id = args->args[0];
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							id = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (id >= mux_chip->controllers) {
 | 
				
			||||||
 | 
							pr_err("bad mux controller %u specified in %s\n",
 | 
				
			||||||
 | 
								id, ofnode_get_name(args->node));
 | 
				
			||||||
 | 
							return -ERANGE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux = &mux_chip->mux[id];
 | 
				
			||||||
 | 
						mux->id = id;
 | 
				
			||||||
 | 
						*muxp = mux;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_get_by_indexed_prop() - Get a mux control by integer index
 | 
				
			||||||
 | 
					 * @dev: The client device.
 | 
				
			||||||
 | 
					 * @prop_name: Name of the device tree property.
 | 
				
			||||||
 | 
					 * @index: The index of the mux to get
 | 
				
			||||||
 | 
					 * @mux: A pointer to the 'mux_control' struct to initialize.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 of OK, -errno otherwise.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int mux_get_by_indexed_prop(struct udevice *dev, const char *prop_name,
 | 
				
			||||||
 | 
									   int index, struct mux_control **mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						struct ofnode_phandle_args args;
 | 
				
			||||||
 | 
						struct udevice *dev_mux;
 | 
				
			||||||
 | 
						const struct mux_control_ops *ops;
 | 
				
			||||||
 | 
						struct mux_chip *mux_chip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log_debug("%s(dev=%p, index=%d, mux=%p)\n", __func__, dev, index, mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = dev_read_phandle_with_args(dev, prop_name, "#mux-control-cells",
 | 
				
			||||||
 | 
										 0, index, &args);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
 | 
				
			||||||
 | 
							      __func__, ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = uclass_get_device_by_ofnode(UCLASS_MUX, args.node, &dev_mux);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							debug("%s: uclass_get_device_by_ofnode failed: err=%d\n",
 | 
				
			||||||
 | 
							      __func__, ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux_chip = dev_get_uclass_priv(dev_mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ops = mux_dev_ops(dev_mux);
 | 
				
			||||||
 | 
						if (ops->of_xlate)
 | 
				
			||||||
 | 
							ret = ops->of_xlate(mux_chip, &args, mux);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ret = mux_of_xlate_default(mux_chip, &args, mux);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							debug("of_xlate() failed: %d\n", ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						(*mux)->dev = dev_mux;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mux_get_by_index(struct udevice *dev, int index, struct mux_control **mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mux_get_by_indexed_prop(dev, "mux-controls", index, mux);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mux_control_get(struct udevice *dev, const char *name,
 | 
				
			||||||
 | 
							    struct mux_control **mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						debug("%s(dev=%p, name=%s, mux=%p)\n", __func__, dev, name, mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						index = dev_read_stringlist_search(dev, "mux-control-names", name);
 | 
				
			||||||
 | 
						if (index < 0) {
 | 
				
			||||||
 | 
							debug("fdt_stringlist_search() failed: %d\n", index);
 | 
				
			||||||
 | 
							return index;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mux_get_by_index(dev, index, mux);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mux_control_put(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mux_control_deselect(mux);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * devm_mux_control_release() - Release the given managed mux.
 | 
				
			||||||
 | 
					 * @dev: The client device.
 | 
				
			||||||
 | 
					 * @res: Pointer to the mux to be released.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function is called by devres to release the mux. It reverses the
 | 
				
			||||||
 | 
					 * effects of mux_control_get().
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void devm_mux_control_release(struct udevice *dev, void *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mux_control_put(*(struct mux_control **)res);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mux_control *devm_mux_control_get(struct udevice *dev, const char *id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						struct mux_control **mux;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux = devres_alloc(devm_mux_control_release,
 | 
				
			||||||
 | 
								   sizeof(struct mux_control *), __GFP_ZERO);
 | 
				
			||||||
 | 
						if (unlikely(!mux))
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = mux_control_get(dev, id, mux);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							return ERR_PTR(rc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						devres_add(dev, mux);
 | 
				
			||||||
 | 
						return *mux;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mux_alloc_controllers(struct udevice *dev, unsigned int controllers)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						struct mux_chip *mux_chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux_chip->mux = devm_kmalloc(dev,
 | 
				
			||||||
 | 
									     sizeof(struct mux_control) * controllers,
 | 
				
			||||||
 | 
									     __GFP_ZERO);
 | 
				
			||||||
 | 
						if (!mux_chip->mux)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux_chip->controllers = controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < mux_chip->controllers; ++i) {
 | 
				
			||||||
 | 
							struct mux_control *mux = &mux_chip->mux[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mux->dev = dev;
 | 
				
			||||||
 | 
							mux->cached_state = MUX_CACHE_UNKNOWN;
 | 
				
			||||||
 | 
							mux->idle_state = MUX_IDLE_AS_IS;
 | 
				
			||||||
 | 
							mux->in_use = false;
 | 
				
			||||||
 | 
							mux->id = i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_uclass_post_probe(struct udevice *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, ret;
 | 
				
			||||||
 | 
						struct mux_chip *mux_chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set all mux controllers to their idle state. */
 | 
				
			||||||
 | 
						for (i = 0; i < mux_chip->controllers; ++i) {
 | 
				
			||||||
 | 
							struct mux_control *mux = &mux_chip->mux[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mux->idle_state == mux->cached_state)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = mux_control_set(mux, mux->idle_state);
 | 
				
			||||||
 | 
							if (ret < 0) {
 | 
				
			||||||
 | 
								dev_err(dev, "unable to set idle state\n");
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dm_mux_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct uclass *uc;
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = uclass_get(UCLASS_MUX, &uc);
 | 
				
			||||||
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							log_debug("unable to get MUX uclass\n");
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						uclass_foreach_dev(dev, uc) {
 | 
				
			||||||
 | 
							if (dev_read_bool(dev, "u-boot,mux-autoprobe")) {
 | 
				
			||||||
 | 
								ret = device_probe(dev);
 | 
				
			||||||
 | 
								if (ret)
 | 
				
			||||||
 | 
									log_debug("unable to probe device %s\n",
 | 
				
			||||||
 | 
										  dev->name);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UCLASS_DRIVER(mux) = {
 | 
				
			||||||
 | 
						.id		= UCLASS_MUX,
 | 
				
			||||||
 | 
						.name		= "mux",
 | 
				
			||||||
 | 
						.post_probe	= mux_uclass_post_probe,
 | 
				
			||||||
 | 
						.per_device_auto_alloc_size = sizeof(struct mux_chip),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -71,6 +71,7 @@ enum uclass_id {
 | 
				
			||||||
	UCLASS_MMC,		/* SD / MMC card or chip */
 | 
						UCLASS_MMC,		/* SD / MMC card or chip */
 | 
				
			||||||
	UCLASS_MOD_EXP,		/* RSA Mod Exp device */
 | 
						UCLASS_MOD_EXP,		/* RSA Mod Exp device */
 | 
				
			||||||
	UCLASS_MTD,		/* Memory Technology Device (MTD) device */
 | 
						UCLASS_MTD,		/* Memory Technology Device (MTD) device */
 | 
				
			||||||
 | 
						UCLASS_MUX,		/* Multiplexer device */
 | 
				
			||||||
	UCLASS_NOP,		/* No-op devices */
 | 
						UCLASS_NOP,		/* No-op devices */
 | 
				
			||||||
	UCLASS_NORTHBRIDGE,	/* Intel Northbridge / SDRAM controller */
 | 
						UCLASS_NORTHBRIDGE,	/* Intel Northbridge / SDRAM controller */
 | 
				
			||||||
	UCLASS_NVME,		/* NVM Express device */
 | 
						UCLASS_NVME,		/* NVM Express device */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This header provides constants for most Multiplexer bindings.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Most Multiplexer bindings specify an idle state. In most cases, the
 | 
				
			||||||
 | 
					 * the multiplexer can be left as is when idle, and in some cases it can
 | 
				
			||||||
 | 
					 * disconnect the input/output and leave the multiplexer in a high
 | 
				
			||||||
 | 
					 * impedance state.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _DT_BINDINGS_MUX_MUX_H
 | 
				
			||||||
 | 
					#define _DT_BINDINGS_MUX_MUX_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MUX_IDLE_AS_IS      (-1)
 | 
				
			||||||
 | 
					#define MUX_IDLE_DISCONNECT (-2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,109 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0+ */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Based on the linux multiplexer framework
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017 Axentia Technologies AB
 | 
				
			||||||
 | 
					 * Author: Peter Rosin <peda@axentia.se>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
 | 
				
			||||||
 | 
					 * Jean-Jacques Hiblot <jjhiblot@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _MUX_INTERNAL_H
 | 
				
			||||||
 | 
					#define _MUX_INTERNAL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* See mux.h for background documentation. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ofnode_phandle_args;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mux_chip -	Represents a chip holding mux controllers.
 | 
				
			||||||
 | 
					 * @controllers:	Number of mux controllers handled by the chip.
 | 
				
			||||||
 | 
					 * @mux:		Array of mux controllers that are handled.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This a per-device uclass-private data.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mux_chip {
 | 
				
			||||||
 | 
						unsigned int controllers;
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mux_control_ops -	Mux controller operations for a mux chip.
 | 
				
			||||||
 | 
					 * @set:			Set the state of the given mux controller.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mux_control_ops {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * set - Apply a state to a multiplexer control
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @mux:	A multiplexer control
 | 
				
			||||||
 | 
						 * @return 0 if OK, or a negative error code.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						int (*set)(struct mux_control *mux, int state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * of_xlate - Translate a client's device-tree (OF) multiplexer
 | 
				
			||||||
 | 
						 * specifier.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * If this function pointer is set to NULL, the multiplexer core will
 | 
				
			||||||
 | 
						 * use a default implementation, which assumes #mux-control-cells = <1>
 | 
				
			||||||
 | 
						 * and that the DT cell contains a simple integer channel ID.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @dev_mux:	The multiplexer device. A single device may handle
 | 
				
			||||||
 | 
						 *              several multiplexer controls.
 | 
				
			||||||
 | 
						 * @args:	The multiplexer specifier values from device tree.
 | 
				
			||||||
 | 
						 * @muxp:	(out) A multiplexer control
 | 
				
			||||||
 | 
						 * @return 0 if OK, or a negative error code.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						int (*of_xlate)(struct mux_chip *dev_mux,
 | 
				
			||||||
 | 
								struct ofnode_phandle_args *args,
 | 
				
			||||||
 | 
								struct mux_control **muxp);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct mux_control -	Represents a mux controller.
 | 
				
			||||||
 | 
					 * @in_use:		Whether the mux controller is in use or not.
 | 
				
			||||||
 | 
					 * @dev:		The client device.
 | 
				
			||||||
 | 
					 * @cached_state:	The current mux controller state, or -1 if none.
 | 
				
			||||||
 | 
					 * @states:		The number of mux controller states.
 | 
				
			||||||
 | 
					 * @idle_state:		The mux controller state to use when inactive, or one
 | 
				
			||||||
 | 
					 *			of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT.
 | 
				
			||||||
 | 
					 * @id:			The index of the mux controller within the mux chip
 | 
				
			||||||
 | 
					 *			it is a part of.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Mux drivers may only change @states and @idle_state, and may only do so
 | 
				
			||||||
 | 
					 * between allocation and registration of the mux controller. Specifically,
 | 
				
			||||||
 | 
					 * @cached_state is internal to the mux core and should never be written by
 | 
				
			||||||
 | 
					 * mux drivers.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mux_control {
 | 
				
			||||||
 | 
						bool	in_use;
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						int cached_state;
 | 
				
			||||||
 | 
						unsigned int states;
 | 
				
			||||||
 | 
						int idle_state;
 | 
				
			||||||
 | 
						int id;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_get_index() - Get the index of the given mux controller
 | 
				
			||||||
 | 
					 * @mux:		The mux-control to get the index for.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: The index of the mux controller within the mux chip the mux
 | 
				
			||||||
 | 
					 * controller is a part of.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline unsigned int mux_control_get_index(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mux->id;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_alloc_controllers() - Allocate the given number of mux controllers.
 | 
				
			||||||
 | 
					 * @dev:		The client device.
 | 
				
			||||||
 | 
					 * controllers:		Number of controllers to allocate.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 of OK, -errno otherwise.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int mux_alloc_controllers(struct udevice *dev, unsigned int controllers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,159 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0+ */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Based on the linux multiplexer framework
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * At its core, a multiplexer (or mux), also known as a data selector, is a
 | 
				
			||||||
 | 
					 * device that selects between several analog or digital input signals and
 | 
				
			||||||
 | 
					 * forwards it to a single output line. This notion can be extended to work
 | 
				
			||||||
 | 
					 * with buses, like a I2C bus multiplexer for example.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017 Axentia Technologies AB
 | 
				
			||||||
 | 
					 * Author: Peter Rosin <peda@axentia.se>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
 | 
				
			||||||
 | 
					 * Jean-Jacques Hiblot <jjhiblot@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _MUX_H_
 | 
				
			||||||
 | 
					#define _MUX_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/errno.h>
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct udevice;
 | 
				
			||||||
 | 
					struct mux_control;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if CONFIG_IS_ENABLED(MULTIPLEXER)
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_states() - Query the number of multiplexer states.
 | 
				
			||||||
 | 
					 * @mux: The mux-control to query.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: The number of multiplexer states.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					unsigned int mux_control_states(struct mux_control *mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_select() - Select the given multiplexer state.
 | 
				
			||||||
 | 
					 * @mux: The mux-control to request a change of state from.
 | 
				
			||||||
 | 
					 * @state: The new requested state.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * On successfully selecting the mux-control state, it will be locked until
 | 
				
			||||||
 | 
					 * there is a call to mux_control_deselect(). If the mux-control is already
 | 
				
			||||||
 | 
					 * selected when mux_control_select() is called, the function will indicate
 | 
				
			||||||
 | 
					 * -EBUSY
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Therefore, make sure to call mux_control_deselect() when the operation is
 | 
				
			||||||
 | 
					 * complete and the mux-control is free for others to use, but do not call
 | 
				
			||||||
 | 
					 * mux_control_deselect() if mux_control_select() fails.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 when the mux-control state has the requested state or a negative
 | 
				
			||||||
 | 
					 * errno on error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int __must_check mux_control_select(struct mux_control *mux,
 | 
				
			||||||
 | 
									    unsigned int state);
 | 
				
			||||||
 | 
					#define mux_control_try_select(mux) mux_control_select(mux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_deselect() - Deselect the previously selected multiplexer state.
 | 
				
			||||||
 | 
					 * @mux: The mux-control to deselect.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * It is required that a single call is made to mux_control_deselect() for
 | 
				
			||||||
 | 
					 * each and every successful call made to either of mux_control_select() or
 | 
				
			||||||
 | 
					 * mux_control_try_select().
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 on success and a negative errno on error. An error can only
 | 
				
			||||||
 | 
					 * occur if the mux has an idle state. Note that even if an error occurs, the
 | 
				
			||||||
 | 
					 * mux-control is unlocked and is thus free for the next access.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int mux_control_deselect(struct mux_control *mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_get_by_index() = Get a mux by integer index.
 | 
				
			||||||
 | 
					 * @dev: The client device.
 | 
				
			||||||
 | 
					 * @index: The index of the mux to get.
 | 
				
			||||||
 | 
					 * @mux: A pointer to the 'mux_control' struct to initialize.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This looks up and initializes a mux. The index is relative to the client
 | 
				
			||||||
 | 
					 * device.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 if OK, or a negative error code.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int mux_get_by_index(struct udevice *dev, int index, struct mux_control **mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_get() - Get the mux-control for a device.
 | 
				
			||||||
 | 
					 * @dev: The device that needs a mux-control.
 | 
				
			||||||
 | 
					 * @mux_name: The name identifying the mux-control.
 | 
				
			||||||
 | 
					 * @mux: A pointer to the mux-control pointer.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 of OK, or a negative error code.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int mux_control_get(struct udevice *dev, const char *name,
 | 
				
			||||||
 | 
							    struct mux_control **mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mux_control_put() - Put away the mux-control for good.
 | 
				
			||||||
 | 
					 * @mux: The mux-control to put away.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * mux_control_put() reverses the effects of mux_control_get().
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void mux_control_put(struct mux_control *mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * devm_mux_control_get() - Get the mux-control for a device, with resource
 | 
				
			||||||
 | 
					 *			    management.
 | 
				
			||||||
 | 
					 * @dev: The device that needs a mux-control.
 | 
				
			||||||
 | 
					 * @mux_name: The name identifying the mux-control.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct mux_control *devm_mux_control_get(struct udevice *dev,
 | 
				
			||||||
 | 
										 const char *mux_name);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * dm_mux_init() - Initialize the multiplexer controls to their default state.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 if OK, -errno otherwise.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int dm_mux_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					unsigned int mux_control_states(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -ENOSYS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int __must_check mux_control_select(struct mux_control *mux,
 | 
				
			||||||
 | 
									    unsigned int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -ENOSYS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define mux_control_try_select(mux) mux_control_select(mux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mux_control_deselect(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -ENOSYS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mux_control *mux_control_get(struct udevice *dev, const char *mux_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mux_control_put(struct mux_control *mux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mux_control *devm_mux_control_get(struct udevice *dev,
 | 
				
			||||||
 | 
										 const char *mux_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dm_mux_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -ENOSYS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,7 @@ obj-y += irq.o
 | 
				
			||||||
obj-$(CONFIG_LED) += led.o
 | 
					obj-$(CONFIG_LED) += led.o
 | 
				
			||||||
obj-$(CONFIG_DM_MAILBOX) += mailbox.o
 | 
					obj-$(CONFIG_DM_MAILBOX) += mailbox.o
 | 
				
			||||||
obj-$(CONFIG_DM_MMC) += mmc.o
 | 
					obj-$(CONFIG_DM_MMC) += mmc.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_CMD_MUX) += mux-cmd.o
 | 
				
			||||||
obj-y += fdtdec.o
 | 
					obj-y += fdtdec.o
 | 
				
			||||||
obj-y += ofnode.o
 | 
					obj-y += ofnode.o
 | 
				
			||||||
obj-y += ofread.o
 | 
					obj-y += ofread.o
 | 
				
			||||||
| 
						 | 
					@ -57,6 +58,8 @@ obj-$(CONFIG_DM_SPI_FLASH) += sf.o
 | 
				
			||||||
obj-$(CONFIG_SMEM) += smem.o
 | 
					obj-$(CONFIG_SMEM) += smem.o
 | 
				
			||||||
obj-$(CONFIG_DM_SPI) += spi.o
 | 
					obj-$(CONFIG_DM_SPI) += spi.o
 | 
				
			||||||
obj-y += syscon.o
 | 
					obj-y += syscon.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_MUX_MMIO) += mux-mmio.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_MULTIPLEXER) += mux-emul.o
 | 
				
			||||||
obj-$(CONFIG_DM_USB) += usb.o
 | 
					obj-$(CONFIG_DM_USB) += usb.o
 | 
				
			||||||
obj-$(CONFIG_DM_PMIC) += pmic.o
 | 
					obj-$(CONFIG_DM_PMIC) += pmic.o
 | 
				
			||||||
obj-$(CONFIG_DM_REGULATOR) += regulator.o
 | 
					obj-$(CONFIG_DM_REGULATOR) += regulator.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,177 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020 Texas Instruments Inc.
 | 
				
			||||||
 | 
					 * Pratyush Yadav <p.yadav@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <common.h>
 | 
				
			||||||
 | 
					#include <dm.h>
 | 
				
			||||||
 | 
					#include <mux.h>
 | 
				
			||||||
 | 
					#include <mux-internal.h>
 | 
				
			||||||
 | 
					#include <dt-bindings/mux/mux.h>
 | 
				
			||||||
 | 
					#include <asm/test.h>
 | 
				
			||||||
 | 
					#include <dm/test.h>
 | 
				
			||||||
 | 
					#include <test/ut.h>
 | 
				
			||||||
 | 
					#include <console.h>
 | 
				
			||||||
 | 
					#include <rand.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BUF_SIZE		256
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Test 'mux list' */
 | 
				
			||||||
 | 
					static int dm_test_cmd_mux_list(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char str[BUF_SIZE], *tok;
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						struct mux_chip *chip;
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						unsigned long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sandbox_set_enable_memio(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
						ut_assertnonnull(chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						run_command("mux list", 0);
 | 
				
			||||||
 | 
						ut_assert_nextline("a-mux-controller:");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Check the table header to make sure we are not out of sync with the
 | 
				
			||||||
 | 
						 * code in the command. If we are, catch it early.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						console_record_readline(str, BUF_SIZE);
 | 
				
			||||||
 | 
						tok = strtok(str, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("ID", tok);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("Selected", tok);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("Current", tok);
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("State", tok);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("Idle", tok);
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("State", tok);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("Num", tok);
 | 
				
			||||||
 | 
						tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
						ut_asserteq_str("States", tok);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < chip->controllers; i++) {
 | 
				
			||||||
 | 
							mux = &chip->mux[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							console_record_readline(str, BUF_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Check if the ID printed matches with the ID of the chip we
 | 
				
			||||||
 | 
							 * have.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							tok = strtok(str, " ");
 | 
				
			||||||
 | 
							ut_assertok(strict_strtoul(tok, 10, &val));
 | 
				
			||||||
 | 
							ut_asserteq(i, val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Check if mux selection state matches. */
 | 
				
			||||||
 | 
							tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
							if (mux->in_use) {
 | 
				
			||||||
 | 
								ut_asserteq_str("yes", tok);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ut_asserteq_str("no", tok);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Check if the current state matches. */
 | 
				
			||||||
 | 
							tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
							if (mux->cached_state == MUX_IDLE_AS_IS) {
 | 
				
			||||||
 | 
								ut_asserteq_str("unknown", tok);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ut_assertok(strict_strtoul(tok, 16, &val));
 | 
				
			||||||
 | 
								ut_asserteq(mux->cached_state, val);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Check if the idle state matches */
 | 
				
			||||||
 | 
							tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
							if (mux->idle_state == MUX_IDLE_AS_IS) {
 | 
				
			||||||
 | 
								ut_asserteq_str("as-is", tok);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ut_assertok(strict_strtoul(tok, 16, &val));
 | 
				
			||||||
 | 
								ut_asserteq(mux->idle_state, val);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Check if the number of states matches */
 | 
				
			||||||
 | 
							tok = strtok(NULL, " ");
 | 
				
			||||||
 | 
							ut_assertok(strict_strtoul(tok, 16, &val));
 | 
				
			||||||
 | 
							ut_asserteq(mux->states, val);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_cmd_mux_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dm_test_cmd_mux_select(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						struct mux_chip *chip;
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						char cmd[BUF_SIZE];
 | 
				
			||||||
 | 
						unsigned int i, state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sandbox_set_enable_memio(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
						ut_assertnonnull(chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srand(get_ticks() + rand());
 | 
				
			||||||
 | 
						for (i = 0; i < chip->controllers; i++) {
 | 
				
			||||||
 | 
							mux = &chip->mux[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							state = rand() % mux->states;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							snprintf(cmd, BUF_SIZE, "mux select a-mux-controller %x %x", i,
 | 
				
			||||||
 | 
								 state);
 | 
				
			||||||
 | 
							run_command(cmd, 0);
 | 
				
			||||||
 | 
							ut_asserteq(!!mux->in_use, true);
 | 
				
			||||||
 | 
							ut_asserteq(state, mux->cached_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ut_assertok(mux_control_deselect(mux));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_cmd_mux_select, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dm_test_cmd_mux_deselect(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						struct mux_chip *chip;
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						char cmd[BUF_SIZE];
 | 
				
			||||||
 | 
						unsigned int i, state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sandbox_set_enable_memio(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
						ut_assertnonnull(chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srand(get_ticks() + rand());
 | 
				
			||||||
 | 
						for (i = 0; i < chip->controllers; i++) {
 | 
				
			||||||
 | 
							mux = &chip->mux[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							state = rand() % mux->states;
 | 
				
			||||||
 | 
							ut_assertok(mux_control_select(mux, state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							snprintf(cmd, BUF_SIZE, "mux deselect a-mux-controller %d", i);
 | 
				
			||||||
 | 
							run_command(cmd, 0);
 | 
				
			||||||
 | 
							ut_asserteq(!!mux->in_use, false);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_cmd_mux_deselect, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,105 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0+
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
 | 
				
			||||||
 | 
					 * Pratyush Yadav <p.yadav@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <common.h>
 | 
				
			||||||
 | 
					#include <dm.h>
 | 
				
			||||||
 | 
					#include <mux.h>
 | 
				
			||||||
 | 
					#include <mux-internal.h>
 | 
				
			||||||
 | 
					#include <dm/test.h>
 | 
				
			||||||
 | 
					#include <test/ut.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mux_emul_priv {
 | 
				
			||||||
 | 
						u32 state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_emul_set(struct mux_control *mux, int state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mux_emul_priv *priv = dev_get_priv(mux->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						priv->state = state;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mux_emul_probe(struct udevice *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mux_chip *mux_chip = dev_get_uclass_priv(dev);
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						u32 idle_state;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = mux_alloc_controllers(dev, 1);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux = &mux_chip->mux[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = dev_read_u32(dev, "idle-state", &idle_state);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mux->idle_state = idle_state;
 | 
				
			||||||
 | 
						mux->states = 0x100000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct mux_control_ops mux_emul_ops = {
 | 
				
			||||||
 | 
						.set = mux_emul_set,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct udevice_id mux_emul_of_match[] = {
 | 
				
			||||||
 | 
						{ .compatible = "mux-emul" },
 | 
				
			||||||
 | 
						{ /* sentinel */ },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					U_BOOT_DRIVER(emul_mux) = {
 | 
				
			||||||
 | 
						.name = "mux-emul",
 | 
				
			||||||
 | 
						.id = UCLASS_MUX,
 | 
				
			||||||
 | 
						.of_match = mux_emul_of_match,
 | 
				
			||||||
 | 
						.ops = &mux_emul_ops,
 | 
				
			||||||
 | 
						.probe = mux_emul_probe,
 | 
				
			||||||
 | 
						.priv_auto_alloc_size = sizeof(struct mux_emul_priv),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dm_test_mux_emul_default_state(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						struct mux_emul_priv *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_get(dev, "mux4", &mux));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						priv = dev_get_priv(mux->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_asserteq(0xabcd, priv->state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_mux_emul_default_state, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dm_test_mux_emul_select_deselect(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct udevice *dev;
 | 
				
			||||||
 | 
						struct mux_control *mux;
 | 
				
			||||||
 | 
						struct mux_emul_priv *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_get(dev, "mux4", &mux));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						priv = dev_get_priv(mux->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(mux, 0x1234));
 | 
				
			||||||
 | 
						ut_asserteq(priv->state, 0x1234);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(mux));
 | 
				
			||||||
 | 
						ut_asserteq(priv->state, 0xabcd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_mux_emul_select_deselect, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,138 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0+
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
 | 
				
			||||||
 | 
					 * Jean-Jacques Hiblot <jjhiblot@ti.com>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <common.h>
 | 
				
			||||||
 | 
					#include <dm.h>
 | 
				
			||||||
 | 
					#include <mux.h>
 | 
				
			||||||
 | 
					#include <regmap.h>
 | 
				
			||||||
 | 
					#include <syscon.h>
 | 
				
			||||||
 | 
					#include <asm/test.h>
 | 
				
			||||||
 | 
					#include <dm/test.h>
 | 
				
			||||||
 | 
					#include <dm/device-internal.h>
 | 
				
			||||||
 | 
					#include <test/ut.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dm_test_mux_mmio_select(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct udevice *dev, *dev_b;
 | 
				
			||||||
 | 
						struct regmap *map;
 | 
				
			||||||
 | 
						struct mux_control *ctl0_a, *ctl0_b;
 | 
				
			||||||
 | 
						struct mux_control *ctl1;
 | 
				
			||||||
 | 
						struct mux_control *ctl_err;
 | 
				
			||||||
 | 
						u32 val;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sandbox_set_enable_memio(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test",
 | 
				
			||||||
 | 
										      &dev_b));
 | 
				
			||||||
 | 
						map = syscon_regmap_lookup_by_phandle(dev, "mux-syscon");
 | 
				
			||||||
 | 
						ut_assertok_ptr(map);
 | 
				
			||||||
 | 
						ut_assert(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(mux_control_get(dev, "mux0", &ctl0_a));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_get(dev, "mux1", &ctl1));
 | 
				
			||||||
 | 
						ut_asserteq(-ERANGE, mux_control_get(dev, "mux3", &ctl_err));
 | 
				
			||||||
 | 
						ut_asserteq(-ENODATA, mux_control_get(dev, "dummy", &ctl_err));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_get(dev_b, "mux0", &ctl0_b));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < mux_control_states(ctl0_a); i++) {
 | 
				
			||||||
 | 
							/* Select a new state and verify the value in the regmap. */
 | 
				
			||||||
 | 
							ut_assertok(mux_control_select(ctl0_a, i));
 | 
				
			||||||
 | 
							ut_assertok(regmap_read(map, 0, &val));
 | 
				
			||||||
 | 
							ut_asserteq(i, (val & 0x30) >> 4);
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Deselect the mux and verify that the value in the regmap
 | 
				
			||||||
 | 
							 * reflects the idle state (fixed to MUX_IDLE_AS_IS).
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							ut_assertok(mux_control_deselect(ctl0_a));
 | 
				
			||||||
 | 
							ut_assertok(regmap_read(map, 0, &val));
 | 
				
			||||||
 | 
							ut_asserteq(i, (val & 0x30) >> 4);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < mux_control_states(ctl1); i++) {
 | 
				
			||||||
 | 
							/* Select a new state and verify the value in the regmap. */
 | 
				
			||||||
 | 
							ut_assertok(mux_control_select(ctl1, i));
 | 
				
			||||||
 | 
							ut_assertok(regmap_read(map, 0xc, &val));
 | 
				
			||||||
 | 
							ut_asserteq(i, (val & 0x1E) >> 1);
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Deselect the mux and verify that the value in the regmap
 | 
				
			||||||
 | 
							 * reflects the idle state (fixed to 2).
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							ut_assertok(mux_control_deselect(ctl1));
 | 
				
			||||||
 | 
							ut_assertok(regmap_read(map, 0xc, &val));
 | 
				
			||||||
 | 
							ut_asserteq(2, (val & 0x1E) >> 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Try unbalanced selection/deselection. */
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 1));
 | 
				
			||||||
 | 
						ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(ctl0_a));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Try concurrent selection. */
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assert(mux_control_select(ctl0_b, 0));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(ctl0_a));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_b, 0));
 | 
				
			||||||
 | 
						ut_assert(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(ctl0_b));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(ctl0_a));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_mux_mmio_select, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Test that managed API for mux work correctly */
 | 
				
			||||||
 | 
					static int dm_test_devm_mux_mmio(struct unit_test_state *uts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct udevice *dev, *dev_b;
 | 
				
			||||||
 | 
						struct mux_control *ctl0_a, *ctl0_b;
 | 
				
			||||||
 | 
						struct mux_control *ctl1;
 | 
				
			||||||
 | 
						struct mux_control *ctl_err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sandbox_set_enable_memio(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
 | 
				
			||||||
 | 
										      &dev));
 | 
				
			||||||
 | 
						ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test",
 | 
				
			||||||
 | 
										      &dev_b));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctl0_a = devm_mux_control_get(dev, "mux0");
 | 
				
			||||||
 | 
						ut_assertok_ptr(ctl0_a);
 | 
				
			||||||
 | 
						ut_assert(ctl0_a);
 | 
				
			||||||
 | 
						ctl1 = devm_mux_control_get(dev, "mux1");
 | 
				
			||||||
 | 
						ut_assertok_ptr(ctl1);
 | 
				
			||||||
 | 
						ut_assert(ctl1);
 | 
				
			||||||
 | 
						ctl_err = devm_mux_control_get(dev, "mux3");
 | 
				
			||||||
 | 
						ut_asserteq(-ERANGE, PTR_ERR(ctl_err));
 | 
				
			||||||
 | 
						ctl_err = devm_mux_control_get(dev, "dummy");
 | 
				
			||||||
 | 
						ut_asserteq(-ENODATA, PTR_ERR(ctl_err));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctl0_b = devm_mux_control_get(dev_b, "mux0");
 | 
				
			||||||
 | 
						ut_assertok_ptr(ctl0_b);
 | 
				
			||||||
 | 
						ut_assert(ctl0_b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Try concurrent selection. */
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assert(mux_control_select(ctl0_b, 0));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(ctl0_a));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_b, 0));
 | 
				
			||||||
 | 
						ut_assert(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assertok(mux_control_deselect(ctl0_b));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Remove one device and check that the mux is released. */
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_a, 0));
 | 
				
			||||||
 | 
						ut_assert(mux_control_select(ctl0_b, 0));
 | 
				
			||||||
 | 
						device_remove(dev, DM_REMOVE_NORMAL);
 | 
				
			||||||
 | 
						ut_assertok(mux_control_select(ctl0_b, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device_remove(dev_b, DM_REMOVE_NORMAL);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DM_TEST(dm_test_devm_mux_mmio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | 
				
			||||||
		Loading…
	
		Reference in New Issue