]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
pinctrl: zynqmp: Add SPL support
authorSean Anderson <sean.anderson@linux.dev>
Thu, 29 Jan 2026 18:40:11 +0000 (13:40 -0500)
committerMichal Simek <michal.simek@amd.com>
Fri, 13 Feb 2026 07:16:25 +0000 (08:16 +0100)
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@linux.dev>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Link: https://lore.kernel.org/r/20260129184011.3932790-1-sean.anderson@linux.dev
drivers/firmware/firmware-zynqmp.c
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/pinctrl-zynqmp.c
include/zynqmp_firmware.h

index f8a9945c1daa092c5ca960a63814758d23dc0c90..fb583580ebeca6181af34671ea46278439d33c9e 100644 (file)
@@ -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
index ea90713ec6ca8c26f6be4c1c02d95f4465688432..73dd5ff4a79315793ba0617f6ca52fc796f886c4 100644 (file)
@@ -386,6 +386,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"
index 33ff7b95ef222ae9f1f2e872e060e566d2a34c03..4fb6cef3113f72fb858c011e5856bc945451e2fe 100644 (file)
@@ -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/
index 7c11ac4c8b83acc047d0ee40cac2de8733904e01..0b936684f8a1a1834d3f5e067bf01e69de1db60e 100644 (file)
 #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
index 05df49f292a188a8e36c4467ba8baafc0abb91cb..f5e72625e535eb06bc1d0ad5816c2babac761494 100644 (file)
@@ -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,