[PATCH v2] pinctrl: zynqmp: Add SPL support

Michal Simek michal.simek at amd.com
Wed Feb 11 09:39:15 CET 2026



On 1/29/26 19:40, Sean Anderson wrote:
> Although the pinctrl pm requests are implemented in the PMU firmware,
> PM_QUERY_DATA is actually implemented in ATF. In SPL (or when running in
> EL3), ATF is not yet running, so we need to implement this API
> ourselves. Do the bare minimum, allowing SPL to enumerate functions, but
> don't bother with groups. Groups take up a lot of space, and can be
> emulated with pins. For example, a node like
> 
> 	display-port {
> 		mux {
> 			groups = "dpaux0_1";
> 			function = "dpaux0";
> 		};
> 	};
> 
> can be replaced by
> 
> 	display-port {
> 		mux {
> 			pins = "MIO34", "MIO35", "MIO36", "MIO37";
> 			function = "dpaux0";
> 		};
> 	};
> 
> While this isn't backwards-compatible with existing devicetrees, it's
> more than enough for SPL where we may only need to mux one or two pins.
> 
> Add SPL_PINCTRL_ZYNQMP to ensure there's no SPL size growth when pinctrl
> is enabled in U-Boot but isn't necessary for SPL. The only config this
> would affect is Kria, but SPL_PINCTRL_GENERIC is disabled so
> SPL_PINCTRL_ZYNQMP is not selected.
> 
> Signed-off-by: Sean Anderson <sean.anderson at linux.dev>
> ---
> 
> Changes in v2:
> - Move some defines into zynqmp_firmware.h so they can be reused
> - Calculate a few more constants instead of using literals
> - Add SPL_PINCTRL_ZYNQMP to avoid size growth in SPL
> 
>   drivers/firmware/firmware-zynqmp.c | 101 +++++++++++++++++++++++++++++
>   drivers/pinctrl/Kconfig            |   8 +++
>   drivers/pinctrl/Makefile           |   2 +-
>   drivers/pinctrl/pinctrl-zynqmp.c   |   8 +--
>   include/zynqmp_firmware.h          |   5 ++
>   5 files changed, 117 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
> index f8a9945c1da..fb583580ebe 100644
> --- a/drivers/firmware/firmware-zynqmp.c
> +++ b/drivers/firmware/firmware-zynqmp.c
> @@ -427,6 +427,104 @@ U_BOOT_DRIVER(zynqmp_power) = {
>   };
>   #endif
>   
> +static const char *const pinctrl_functions[] = {
> +	"can0",
> +	"can1",
> +	"ethernet0",
> +	"ethernet1",
> +	"ethernet2",
> +	"ethernet3",
> +	"gemtsu0",
> +	"gpio0",
> +	"i2c0",
> +	"i2c1",
> +	"mdio0",
> +	"mdio1",
> +	"mdio2",
> +	"mdio3",
> +	"qspi0",
> +	"qspi_fbclk",
> +	"qspi_ss",
> +	"spi0",
> +	"spi1",
> +	"spi0_ss",
> +	"spi1_ss",
> +	"sdio0",
> +	"sdio0_pc",
> +	"sdio0_cd",
> +	"sdio0_wp",
> +	"sdio1",
> +	"sdio1_pc",
> +	"sdio1_cd",
> +	"sdio1_wp",
> +	"nand0",
> +	"nand0_ce",
> +	"nand0_rb",
> +	"nand0_dqs",
> +	"ttc0_clk",
> +	"ttc0_wav",
> +	"ttc1_clk",
> +	"ttc1_wav",
> +	"ttc2_clk",
> +	"ttc2_wav",
> +	"ttc3_clk",
> +	"ttc3_wav",
> +	"uart0",
> +	"uart1",
> +	"usb0",
> +	"usb1",
> +	"swdt0_clk",
> +	"swdt0_rst",
> +	"swdt1_clk",
> +	"swdt1_rst",
> +	"pmu0",
> +	"pcie0",
> +	"csu0",
> +	"dpaux0",
> +	"pjtag0",
> +	"trace0",
> +	"trace0_clk",
> +	"testscan0",
> +};
> +
> +/*
> + * PM_QUERY_DATA is implemented by ATF and not the PMU firmware, so we have to
> + * emulate it in SPL. Just implement functions/pins since the groups take up a
> + * lot of rodata and are mostly superfluous.
> + */
> +static int zynqmp_pm_query_data(enum pm_query_id qid, u32 arg1, u32 arg2,
> +				u32 *ret_payload)
> +{
> +	switch (qid) {
> +	case PM_QID_PINCTRL_GET_NUM_PINS:
> +		ret_payload[1] = 78; /* NUM_PINS */
> +		ret_payload[0] = 0;
> +		return 0;
> +	case PM_QID_PINCTRL_GET_NUM_FUNCTIONS:
> +		ret_payload[1] = ARRAY_SIZE(pinctrl_functions);
> +		ret_payload[0] = 0;
> +		return 0;
> +	case PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS:
> +		ret_payload[1] = 0;
> +		ret_payload[0] = 0;
> +		return 0;
> +	case PM_QID_PINCTRL_GET_FUNCTION_NAME:
> +		assert(arg1 < ARRAY_SIZE(pinctrl_functions));
> +		memset(ret_payload, 0, MAX_FUNC_NAME_LEN);
> +		strcpy((char *)ret_payload, pinctrl_functions[arg1]);
> +		return 0;
> +	case PM_QID_PINCTRL_GET_FUNCTION_GROUPS:
> +	case PM_QID_PINCTRL_GET_PIN_GROUPS:
> +		memset(ret_payload + 1, 0xff,
> +		       sizeof(s16) * NUM_GROUPS_PER_RESP);
> +		ret_payload[0] = 0;
> +		return 0;
> +	default:
> +		ret_payload[0] = 1;
> +		return 1;
> +	}
> +}
> +
>   smc_call_handler_t __data smc_call_handler;
>   
>   static int smc_call_legacy(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
> @@ -493,6 +591,9 @@ int __maybe_unused xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
>   	      __func__, current_el(), api_id, arg0, arg1, arg2, arg3, arg4, arg5);
>   
>   	if (IS_ENABLED(CONFIG_XPL_BUILD) || current_el() == 3) {
> +		if (CONFIG_IS_ENABLED(PINCTRL_ZYNQMP) &&
> +		    api_id == PM_QUERY_DATA)
> +			return zynqmp_pm_query_data(arg0, arg1, arg2, ret_payload);
>   #if defined(CONFIG_ZYNQMP_IPI)
>   		/*
>   		 * Use fixed payload and arg size as the EL2 call. The firmware
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 48119694031..06f3dce99b3 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -385,6 +385,14 @@ config PINCTRL_ZYNQMP
>   	  Generic Pinctrl framework and is compatible with the Linux driver,
>   	  i.e. it uses the same device tree configuration.
>   
> +config SPL_PINCTRL_ZYNQMP
> +	bool "Xilinx ZynqMP pin control driver in SPL"
> +	depends on SPL_DM && SPL_PINCTRL_GENERIC && ARCH_ZYNQMP
> +	default PINCTRL_ZYNQMP
> +	help
> +	  Support pin multiplexing control in SPL on Xilinx ZynqMP. Only "pins"
> +	  can be muxed; "groups" are not supported.
> +
>   endif
>   
>   source "drivers/pinctrl/broadcom/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 33ff7b95ef2..4fb6cef3113 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -36,5 +36,5 @@ obj-$(CONFIG_$(PHASE_)PINCTRL_SX150X) += pinctrl-sx150x.o
>   obj-$(CONFIG_$(PHASE_)PINCTRL_STMFX)	+= pinctrl-stmfx.o
>   obj-$(CONFIG_PINCTRL_TH1520)	+= pinctrl-th1520.o
>   obj-y				+= broadcom/
> -obj-$(CONFIG_PINCTRL_ZYNQMP)	+= pinctrl-zynqmp.o
> +obj-$(CONFIG_$(PHASE_)PINCTRL_ZYNQMP)	+= pinctrl-zynqmp.o
>   obj-$(CONFIG_PINCTRL_STARFIVE)	+= starfive/
> diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
> index 7c11ac4c8b8..0b936684f8a 100644
> --- a/drivers/pinctrl/pinctrl-zynqmp.c
> +++ b/drivers/pinctrl/pinctrl-zynqmp.c
> @@ -19,15 +19,11 @@
>   #include <linux/compat.h>
>   #include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
>   
> -#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN	12
> -#define PINCTRL_GET_PIN_GROUPS_RESP_LEN		12
> -#define NUM_GROUPS_PER_RESP			6
> -#define NA_GROUP				-1
> -#define RESERVED_GROUP				-2
> +#define PINCTRL_GET_FUNC_GROUPS_RESP_LEN	(sizeof(s16) * NUM_GROUPS_PER_RESP)
> +#define PINCTRL_GET_PIN_GROUPS_RESP_LEN		(sizeof(s16) * NUM_GROUPS_PER_RESP)
>   #define MAX_GROUP_PIN				50
>   #define MAX_PIN_GROUPS				50
>   #define MAX_GROUP_NAME_LEN			32
> -#define MAX_FUNC_NAME_LEN			16
>   
>   #define DRIVE_STRENGTH_2MA	2
>   #define DRIVE_STRENGTH_4MA	4
> diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h
> index 05df49f292a..f5e72625e53 100644
> --- a/include/zynqmp_firmware.h
> +++ b/include/zynqmp_firmware.h
> @@ -185,6 +185,11 @@ enum pm_query_id {
>   	PM_QID_CLOCK_GET_MAX_DIVISOR = 13,
>   };
>   
> +#define NUM_GROUPS_PER_RESP			6
> +#define NA_GROUP				-1
> +#define RESERVED_GROUP				-2
> +#define MAX_FUNC_NAME_LEN			16
> +
>   enum pm_pinctrl_config_param {
>   	PM_PINCTRL_CONFIG_SLEW_RATE = 0,
>   	PM_PINCTRL_CONFIG_BIAS_STATUS = 1,

Applied.
M


More information about the U-Boot mailing list