[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