From fcf44934dfdd0d43eb8f08f413590a44cab3169d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 8 Jul 2026 08:53:53 +0200 Subject: arm64: versal2: Make hardware.h assembly safe The register address and bootmode defines in this header are useful from early assembly (e.g. lowlevel_init reading PMC_TAP_IDCODE), but the file could not be included from .S because the C-only declarations were not guarded. Reorder the header so all plain address/value defines are visible to both C and assembly, and move every C-only construct (the linux/bitops.h include, the register structs, the base pointer-cast macros and the platform enum) into a single #ifndef __ASSEMBLY__ block. No functional change for C consumers. Signed-off-by: Michal Simek Link: https://patch.msgid.link/de4de47ea457b285c5870e7ba25b4baf554561e7.1783493630.git.michal.simek@amd.com --- arch/arm/mach-versal2/include/mach/hardware.h | 82 +++++++++++++-------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/arch/arm/mach-versal2/include/mach/hardware.h b/arch/arm/mach-versal2/include/mach/hardware.h index 1bebf20910a..7977529fd6c 100644 --- a/arch/arm/mach-versal2/include/mach/hardware.h +++ b/arch/arm/mach-versal2/include/mach/hardware.h @@ -4,34 +4,6 @@ * Copyright (C) 2022 - 2025, Advanced Micro Devices, Inc. */ -#ifndef __ASSEMBLY__ -#include -#endif - -struct crlapb_regs { - u32 reserved0[67]; - u32 cpu_r5_ctrl; - u32 reserved; - u32 iou_switch_ctrl; /* 0x114 */ - u32 reserved1[13]; - u32 timestamp_ref_ctrl; /* 0x14c */ - u32 reserved3[108]; - u32 rst_cpu_r5; - u32 reserved2[17]; - u32 rst_timestamp; /* 0x348 */ -}; - -struct iou_scntrs_regs { - u32 counter_control_register; /* 0x0 */ - u32 reserved0[7]; - u32 base_frequency_id_register; /* 0x20 */ -}; - -struct crp_regs { - u32 reserved0[128]; - u32 boot_mode_usr; /* 0x200 */ -}; - #define VERSAL2_CRL_APB_BASEADDR 0xEB5E0000 #define VERSAL2_CRP_BASEADDR 0xF1260000 #define VERSAL2_IOU_SCNTR_SECURE 0xEC920000 @@ -41,10 +13,6 @@ struct crp_regs { #define IOU_SWITCH_CTRL_DIVISOR0_SHIFT 8 #define IOU_SCNTRS_CONTROL_EN 1 -#define crlapb_base ((struct crlapb_regs *)VERSAL2_CRL_APB_BASEADDR) -#define crp_base ((struct crp_regs *)VERSAL2_CRP_BASEADDR) -#define iou_scntr_secure ((struct iou_scntrs_regs *)VERSAL2_IOU_SCNTR_SECURE) - #define PMC_TAP 0xF11A0000 #define PMC_TAP_IDCODE (PMC_TAP + 0) @@ -76,15 +44,6 @@ struct crp_regs { #define PMC_MULTI_BOOT_REG 0xF1110004 #define PMC_MULTI_BOOT_MASK 0x1FFF -enum versal2_platform { - VERSAL2_SILICON = 0, - VERSAL2_SPP = 1, - VERSAL2_EMU = 2, - VERSAL2_QEMU = 3, - VERSAL2_SPP_MMD = 5, - VERSAL2_EMU_MMD = 6, -}; - #define VERSAL2_SLCR_BASEADDR 0xF1060000 #define VERSAL_AXI_MUX_SEL (VERSAL2_SLCR_BASEADDR + 0x504) #define VERSAL_OSPI_LINEAR_MODE BIT(1) @@ -108,3 +67,44 @@ enum versal2_platform { #define PMC_GLOBAL_PGGS3_REG 0xF111005C #define PMC_GLOBAL_PGGS4_REG 0xF1110060 + +#ifndef __ASSEMBLY__ +#include + +struct crlapb_regs { + u32 reserved0[67]; + u32 cpu_r5_ctrl; + u32 reserved; + u32 iou_switch_ctrl; /* 0x114 */ + u32 reserved1[13]; + u32 timestamp_ref_ctrl; /* 0x14c */ + u32 reserved3[108]; + u32 rst_cpu_r5; + u32 reserved2[17]; + u32 rst_timestamp; /* 0x348 */ +}; + +struct iou_scntrs_regs { + u32 counter_control_register; /* 0x0 */ + u32 reserved0[7]; + u32 base_frequency_id_register; /* 0x20 */ +}; + +struct crp_regs { + u32 reserved0[128]; + u32 boot_mode_usr; /* 0x200 */ +}; + +#define crlapb_base ((struct crlapb_regs *)VERSAL2_CRL_APB_BASEADDR) +#define crp_base ((struct crp_regs *)VERSAL2_CRP_BASEADDR) +#define iou_scntr_secure ((struct iou_scntrs_regs *)VERSAL2_IOU_SCNTR_SECURE) + +enum versal2_platform { + VERSAL2_SILICON = 0, + VERSAL2_SPP = 1, + VERSAL2_EMU = 2, + VERSAL2_QEMU = 3, + VERSAL2_SPP_MMD = 5, + VERSAL2_EMU_MMD = 6, +}; +#endif /* __ASSEMBLY__ */ -- cgit v1.3.1 From 5bca08783b54ce325fb649749de16edede0581d0 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 8 Jul 2026 08:53:54 +0200 Subject: arm64: versal2: Select GIC redistributor base at runtime The GIC redistributor base address depends on the part: the 2VM3654 device has 4 APU cores and 3 GIC ITS blocks, which shifts the redistributor region up compared to the base 8 core / single ITS part. A single U-Boot binary must therefore pick the right base at runtime instead of relying on the compile-time GICR_BASE used by the generic lowlevel_init(). Add a SoC specific lowlevel_init() that reads the PMC TAP IDCODE and selects GICR_BASE_2VM3654 for the 2VM3654 device, otherwise keeps the default GICR_BASE. CONFIG_ARMV8_MULTIENTRY is not supported on Versal Gen 2 so the generic slave handling is intentionally dropped. Signed-off-by: Michal Simek Link: https://patch.msgid.link/4c0dc2b6e29d300db15be0844879ab4d028d98a9.1783493630.git.michal.simek@amd.com --- arch/arm/mach-versal2/Makefile | 1 + arch/arm/mach-versal2/lowlevel_init.S | 40 +++++++++++++++++++++++++++++++++++ include/configs/amd_versal2.h | 9 ++++++++ 3 files changed, 50 insertions(+) create mode 100644 arch/arm/mach-versal2/lowlevel_init.S diff --git a/arch/arm/mach-versal2/Makefile b/arch/arm/mach-versal2/Makefile index 96497b1dfd0..73b3c8a6f35 100644 --- a/arch/arm/mach-versal2/Makefile +++ b/arch/arm/mach-versal2/Makefile @@ -8,3 +8,4 @@ obj-y += clk.o obj-y += cpu.o +obj-y += lowlevel_init.o diff --git a/arch/arm/mach-versal2/lowlevel_init.S b/arch/arm/mach-versal2/lowlevel_init.S new file mode 100644 index 00000000000..8bfe50b806e --- /dev/null +++ b/arch/arm/mach-versal2/lowlevel_init.S @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SoC specific lowlevel_init for AMD Versal Gen 2 + * + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include + +ENTRY(lowlevel_init) + mov x29, lr /* Save LR */ + +#if defined(CONFIG_GICV3) + branch_if_slave x0, 1f + ldr x0, =GICD_BASE + bl gic_init_secure +1: + /* + * Pick the redistributor base for this part. Default to GICR_BASE + * and switch to GICR_BASE_2VM3654 when the PMC TAP IDCODE matches + * the 2VM3654 device. + */ + ldr x0, =GICR_BASE + ldr x1, =PMC_TAP_IDCODE + ldr w1, [x1] + ldr w2, =GICR_IDCODE_2VM3654 + cmp w1, w2 + b.ne 3f + ldr x0, =GICR_BASE_2VM3654 +3: + bl gic_init_secure_percpu +#endif + + mov lr, x29 /* Restore LR */ + ret +ENDPROC(lowlevel_init) diff --git a/include/configs/amd_versal2.h b/include/configs/amd_versal2.h index a07e12bd146..00627dcd943 100644 --- a/include/configs/amd_versal2.h +++ b/include/configs/amd_versal2.h @@ -19,6 +19,15 @@ #define GICD_BASE 0xe2000000 #define GICR_BASE 0xe2060000 +/* + * The 2VM3654 part has 4 APU cores and 3 GIC ITS blocks (vs 8 cores and a + * single ITS on the base part), which moves the redistributor region up by + * the two extra ITS blocks. The right base is selected at runtime in + * lowlevel_init() based on the PMC TAP IDCODE. + */ +#define GICR_BASE_2VM3654 0xe20a0000 +#define GICR_IDCODE_2VM3654 0x04d98093 + /* Serial setup */ #define CFG_SYS_BAUDRATE_TABLE \ { 4800, 9600, 19200, 38400, 57600, 115200 } -- cgit v1.3.1 From 40727c4f38d675ea833561c7188970696f012c6a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 2 Jul 2026 12:08:25 +0200 Subject: board: xilinx: Generate FWU capsule dfu_alt_info from fwu_platform_hook() Commit 371a6c1744f3 ("board: xilinx: Add capsule and FWU support") and commit 818c06faa119 ("board: amd: Add capsule and FWU support") introduced a separate set_dfu_alt_info() for the FWU multi-bank case (Versal and Versal Gen 2 respectively) which set the dfu_alt_info environment variable directly, while configure_capsule_updates() handled the non-FWU case by filling update_info.dfu_string. Fold the FWU multi-bank generation (fwu_gen_alt_info_from_mtd() over the nor0 MTD partitions) into fwu_platform_hook() and drop the standalone set_dfu_alt_info(). Signed-off-by: Michal Simek Link: https://patch.msgid.link/33c118b8177f8d0dc182a6f4c5cffedca4e02448.1782986903.git.michal.simek@amd.com --- board/amd/versal2/board.c | 39 --------------------------------------- board/xilinx/common/board.c | 37 ++++++++++++++++++++++++++++++++++++- board/xilinx/versal/board.c | 39 --------------------------------------- 3 files changed, 36 insertions(+), 79 deletions(-) diff --git a/board/amd/versal2/board.c b/board/amd/versal2/board.c index 2afd283b8dd..15b79bbea69 100644 --- a/board/amd/versal2/board.c +++ b/board/amd/versal2/board.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -362,8 +361,6 @@ enum env_location env_get_location(enum env_operation op, int prio) #define DFU_ALT_BUF_LEN SZ_1K -#if defined(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) && \ - !defined(CONFIG_FWU_MULTI_BANK_UPDATE) static void mtd_found_part(u32 *base, u32 *size) { struct mtd_info *part, *mtd; @@ -440,42 +437,6 @@ void configure_capsule_updates(void) update_info.dfu_string = strdup(buf); debug("Capsule DFU: %s\n", update_info.dfu_string); } -#endif - -#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) - -/* Generate dfu_alt_info from partitions */ -void set_dfu_alt_info(char *interface, char *devstr) -{ - int ret; - struct mtd_info *mtd; - - /* - * It is called multiple times for every image - * per bank that's why enough to set it up once. - */ - if (env_get("dfu_alt_info")) - return; - - ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN); - memset(buf, 0, DFU_ALT_BUF_LEN); - - mtd_probe_devices(); - - mtd = get_mtd_device_nm("nor0"); - if (IS_ERR_OR_NULL(mtd)) - return; - - ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd); - if (ret < 0) { - log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret); - return; - } - log_debug("Make dfu_alt_info: '%s'\n", buf); - - env_set("dfu_alt_info", buf); -} -#endif int spi_get_env_dev(void) { diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c index 52a2e8767d8..f45b879736e 100644 --- a/board/xilinx/common/board.c +++ b/board/xilinx/common/board.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2014 - 2022, Xilinx, Inc. - * (C) Copyright 2022 - 2025, Advanced Micro Devices, Inc. + * (C) Copyright 2022 - 2026, Advanced Micro Devices, Inc. * * Michal Simek */ @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -22,6 +24,7 @@ #endif #include #include +#include #include #include #include @@ -65,6 +68,8 @@ struct efi_capsule_update_info update_info = { .images = fw_images, }; +#define DFU_ALT_BUF_LEN SZ_1K + #endif /* EFI_HAVE_CAPSULE_SUPPORT */ #define EEPROM_HEADER_MAGIC 0xdaaddeed @@ -846,6 +851,36 @@ int fwu_platform_hook(struct udevice *dev, struct fwu_data *data) /* Copy image type GUID */ memcpy(&fw_images[0].image_type_id, &img_entry->image_type_guid, 16); + /* + * Generate the capsule DFU string from the FWU metadata. This has to + * happen here, and not in configure_capsule_updates() called from + * board_late_init(), because the FWU data is only populated by + * fwu_boottime_checks() at EVT_POST_PREBOOT. + */ + { + ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN); + struct mtd_info *mtd; + int ret; + + memset(buf, 0, DFU_ALT_BUF_LEN); + + mtd_probe_devices(); + + mtd = get_mtd_device_nm("nor0"); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd); + if (ret < 0) { + log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret); + return ret; + } + log_debug("Make dfu_alt_info: '%s'\n", buf); + + update_info.dfu_string = strdup(buf); + debug("Capsule DFU: %s\n", update_info.dfu_string); + } + if (IS_ENABLED(CONFIG_EFI_ESRT)) { efi_status_t ret; diff --git a/board/xilinx/versal/board.c b/board/xilinx/versal/board.c index 0537517b1b2..e3e1085fce7 100644 --- a/board/xilinx/versal/board.c +++ b/board/xilinx/versal/board.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -314,8 +313,6 @@ enum env_location env_get_location(enum env_operation op, int prio) #define DFU_ALT_BUF_LEN SZ_1K -#if defined(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) && \ - !defined(CONFIG_FWU_MULTI_BANK_UPDATE) static void mtd_found_part(u32 *base, u32 *size) { struct mtd_info *part, *mtd; @@ -392,39 +389,3 @@ void configure_capsule_updates(void) update_info.dfu_string = strdup(buf); debug("Capsule DFU: %s\n", update_info.dfu_string); } -#endif - -#if defined(CONFIG_FWU_MULTI_BANK_UPDATE) - -/* Generate dfu_alt_info from partitions */ -void set_dfu_alt_info(char *interface, char *devstr) -{ - int ret; - struct mtd_info *mtd; - - /* - * It is called multiple times for every image - * per bank that's why enough to set it up once. - */ - if (env_get("dfu_alt_info")) - return; - - ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN); - memset(buf, 0, DFU_ALT_BUF_LEN); - - mtd_probe_devices(); - - mtd = get_mtd_device_nm("nor0"); - if (IS_ERR_OR_NULL(mtd)) - return; - - ret = fwu_gen_alt_info_from_mtd(buf, DFU_ALT_BUF_LEN, mtd); - if (ret < 0) { - log_err("Error: Failed to generate dfu_alt_info. (%d)\n", ret); - return; - } - log_debug("Make dfu_alt_info: '%s'\n", buf); - - env_set("dfu_alt_info", buf); -} -#endif -- cgit v1.3.1 From 93332d403a97592c509731af470405058e88904e Mon Sep 17 00:00:00 2001 From: Suraj Kakade Date: Thu, 9 Jul 2026 14:07:26 +0530 Subject: spi: zynqmp_gqspi: fix quad mode bus conflict Starting the GENFIFO after each CMD and ADDR entry creates a gap between the address phase (TX) and data phase (RX). For read commands without dummy cycles, the controller is still driving DQ2/DQ3 HIGH when the flash switches to output mode, causing a bus conflict. Split the GENFIFO helper into a write-only variant and a start-and-wait variant. Queue CMD, ADDR and dummy entries without starting the GENFIFO. The data phase appends its entry and starts the queued sequence, keeping the address-to-data transition continuous. Since genfifo_cmd() no longer triggers after each entry, operations without a data phase have no trigger path. Call start_gen_fifo() from exec_op() before deasserting chip select to execute the queued CMD and ADDR entries. Fixes: 22cca1730ec4 ("spi: zynqmp_gqspi: Add support for ZynqMP qspi driver") Signed-off-by: Suraj Kakade Signed-off-by: Padmarao Begari Signed-off-by: Michal Simek Link: https://patch.msgid.link/20260709083757.1851446-1-suraj.hanumantkakade@amd.com --- drivers/spi/zynqmp_gqspi.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c index 2a095d0c58e..f572f2b3f15 100644 --- a/drivers/spi/zynqmp_gqspi.c +++ b/drivers/spi/zynqmp_gqspi.c @@ -283,16 +283,21 @@ static u32 zynqmp_qspi_genfifo_mode(u8 buswidth) } } -static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv, - u32 gqspi_fifo_reg) +static void zynqmp_qspi_write_gen_fifo(struct zynqmp_qspi_priv *priv, + u32 gqspi_fifo_reg) { struct zynqmp_qspi_regs *regs = priv->regs; - u32 config_reg, ier; - int ret = 0; log_content("%s, GFIFO_CMD: 0x%X\n", __func__, gqspi_fifo_reg); writel(gqspi_fifo_reg, ®s->genfifo); +} + +static int zynqmp_qspi_start_gen_fifo(struct zynqmp_qspi_priv *priv) +{ + struct zynqmp_qspi_regs *regs = priv->regs; + u32 config_reg, ier; + int ret = 0; config_reg = readl(®s->confr); /* Manual start if needed */ @@ -310,6 +315,15 @@ static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv, if (ret) log_warning("%s, Timeout\n", __func__); + return ret; +} + +static int zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv, + u32 gqspi_fifo_reg) +{ + zynqmp_qspi_write_gen_fifo(priv, gqspi_fifo_reg); + + return zynqmp_qspi_start_gen_fifo(priv); } static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on) @@ -573,7 +587,7 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv) gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(op->cmd.buswidth); gen_fifo_cmd |= GQSPI_GFIFO_TX; gen_fifo_cmd |= op->cmd.opcode; - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + zynqmp_qspi_write_gen_fifo(priv, gen_fifo_cmd); /* Send address */ for (i = 0; i < op->addr.nbytes; i++) { @@ -584,7 +598,7 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv) gen_fifo_cmd |= GQSPI_GFIFO_TX; gen_fifo_cmd |= addr; - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + zynqmp_qspi_write_gen_fifo(priv, gen_fifo_cmd); } /* Send dummy */ @@ -596,7 +610,7 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv) gen_fifo_cmd &= ~(GQSPI_GFIFO_TX | GQSPI_GFIFO_RX); gen_fifo_cmd |= GQSPI_GFIFO_DATA_XFR_MASK; gen_fifo_cmd |= dummy_cycles; - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + zynqmp_qspi_write_gen_fifo(priv, gen_fifo_cmd); } } @@ -644,7 +658,9 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv) while (priv->len) { len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd); - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + ret = zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + if (ret) + return ret; if (gen_fifo_cmd & GQSPI_GFIFO_EXP_MASK) ret = zynqmp_qspi_fill_tx_fifo(priv, 1 << len); @@ -666,6 +682,7 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv, struct zynqmp_qspi_regs *regs = priv->regs; u32 last_bits; u32 *traverse = buf; + int ret; while (priv->len) { len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd); @@ -674,7 +691,9 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv, priv->bytes_to_receive = (1 << len); else priv->bytes_to_receive = len; - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + ret = zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + if (ret) + return ret; /* Manual start */ config_reg = readl(®s->confr); @@ -741,7 +760,9 @@ static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv, while (priv->len) { zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd); - zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + ret = zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd); + if (ret) + return ret; } ret = wait_for_bit_le32(&dma_regs->dmaisr, @@ -886,6 +907,8 @@ static int zynqmp_qspi_exec_op(struct spi_slave *slave, ret = zynqmp_qspi_genfifo_fill_rx(priv); else if (op->data.dir == SPI_MEM_DATA_OUT) ret = zynqmp_qspi_genfifo_fill_tx(priv); + else + ret = zynqmp_qspi_start_gen_fifo(priv); zynqmp_qspi_chipselect(priv, 0); -- cgit v1.3.1 From 90ddf602cf96885a97581146a5e375ce5d125a39 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 Apr 2026 14:03:46 +0200 Subject: xilinx: mbv: Disable CPU driver in SPL Disable CONFIG_SPL_CPU to remove the CPU driver from SPL. The CPU driver provides runtime CPU information and enumeration which is not needed during the early SPL boot phase, saving significant code space. spl/u-boot-spl: all -922 data -208 rodata -100 text -614 Signed-off-by: Michal Simek Link: https://patch.msgid.link/96dcbd196206f48aa4552624b562124c11cafbce.1777550623.git.michal.simek@amd.com --- configs/xilinx_mbv32_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/xilinx_mbv32_defconfig b/configs/xilinx_mbv32_defconfig index 097d6095df2..75ce445fafb 100644 --- a/configs/xilinx_mbv32_defconfig +++ b/configs/xilinx_mbv32_defconfig @@ -38,6 +38,7 @@ CONFIG_SPL_SYS_MALLOC=y CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x80000000 CONFIG_SPL_SYS_MALLOC_SIZE=0x200000 +# CONFIG_SPL_CPU is not set # CONFIG_CMD_MII is not set CONFIG_CMD_SNTP=y CONFIG_CMD_TIMER=y -- cgit v1.3.1 From 84e3df261f3958abf604b9704f01b33aad21b701 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 Apr 2026 14:03:47 +0200 Subject: xilinx: mbv: Disable arch-specific memory functions in SPL Disable CONFIG_SPL_USE_ARCH_MEMCPY, CONFIG_SPL_USE_ARCH_MEMMOVE, and CONFIG_SPL_USE_ARCH_MEMSET to use generic C implementations instead of RISC-V assembly versions. The generic implementations are smaller for SPL's constrained code size requirements. spl/u-boot-spl: all -446 text -446 Signed-off-by: Michal Simek Link: https://patch.msgid.link/4885b1983ec158ed98e727091be38f7de104108e.1777550623.git.michal.simek@amd.com --- configs/xilinx_mbv32_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configs/xilinx_mbv32_defconfig b/configs/xilinx_mbv32_defconfig index 75ce445fafb..d068eeef234 100644 --- a/configs/xilinx_mbv32_defconfig +++ b/configs/xilinx_mbv32_defconfig @@ -1,4 +1,7 @@ CONFIG_RISCV=y +# CONFIG_SPL_USE_ARCH_MEMCPY is not set +# CONFIG_SPL_USE_ARCH_MEMMOVE is not set +# CONFIG_SPL_USE_ARCH_MEMSET is not set CONFIG_SYS_MALLOC_LEN=0xe00000 CONFIG_NR_DRAM_BANKS=1 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y -- cgit v1.3.1 From 74467fdf1d5c283a1698dadbf7d433a96076e722 Mon Sep 17 00:00:00 2001 From: Sandeep Gundlupet Raju Date: Tue, 5 May 2026 08:34:07 +0200 Subject: xilinx: mbv: Enable SPL_REMAKE_ELF support For MicroBlaze-V SPI boot, the u-boot-spl.elf should be embedded into the BRAM (Block RAM) of bitstream or rcdo. This requires SPL_REMAKE_ELF to be enabled so that the SPL ELF is generated during the build. Enable SPL_REMAKE_ELF for MB-V devices to support this flow. Signed-off-by: Sandeep Gundlupet Raju Signed-off-by: Michal Simek Link: https://patch.msgid.link/aa144c8002ba86154dcb62624a1d9d7cbe8c2cdf.1777962845.git.michal.simek@amd.com --- configs/xilinx_mbv32_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/xilinx_mbv32_defconfig b/configs/xilinx_mbv32_defconfig index d068eeef234..82bbba6d0f0 100644 --- a/configs/xilinx_mbv32_defconfig +++ b/configs/xilinx_mbv32_defconfig @@ -42,6 +42,7 @@ CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x80000000 CONFIG_SPL_SYS_MALLOC_SIZE=0x200000 # CONFIG_SPL_CPU is not set +CONFIG_SPL_REMAKE_ELF=y # CONFIG_CMD_MII is not set CONFIG_CMD_SNTP=y CONFIG_CMD_TIMER=y -- cgit v1.3.1 From 4710d760b96f0f5833f87384cc3bed8027b8c71f Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 22 Jul 2026 14:16:37 +0200 Subject: xilinx: zynqmp: Setup BOARD_SIZE_LIMIT for mini configurations Setup BOARD_SIZE_LIMIT to 128KB (131072 bytes) for ZynqMP mini/qspi configurations and enable CONFIG_SKIP_RELOCATE as there is no need for relocation. For configurations running from OCM (xilinx_zynqmp_mini_defconfig and xilinx_zynqmp_mini_qspi_defconfig), move TEXT_BASE from 0xFFFC0000 to 0xFFFE0000. The initial 128KB of OCM (0xFFFC0000-0xFFFDFFFF) is used for data buffer. Memory layout in OCM (256KB total): - 0xFFFC0000 - 0xFFFDFFFF: Data buffer (128KB) - 0xFFFE0000 - 0xFFFFFFFF: U-Boot code + stack (128KB) - TEXT_BASE at 0xFFFE0000 (code grows up) - EARLY SP at 0xFFFE0000 (stack grows down) EARLY SP is released when new stack location is calculated before U-Boot prompt that's why no conflict with Data buffer location. Signed-off-by: Michal Simek Link: https://patch.msgid.link/6d91337d971fa9e22f6f57371f8bc656ea6bb4a3.1784722595.git.michal.simek@amd.com --- configs/xilinx_zynqmp_mini_defconfig | 5 ++++- configs/xilinx_zynqmp_mini_qspi_defconfig | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/configs/xilinx_zynqmp_mini_defconfig b/configs/xilinx_zynqmp_mini_defconfig index 20e2ae0f003..8b1d8cc0439 100644 --- a/configs/xilinx_zynqmp_mini_defconfig +++ b/configs/xilinx_zynqmp_mini_defconfig @@ -2,7 +2,7 @@ CONFIG_ARM=y CONFIG_SYS_CONFIG_NAME="xilinx_zynqmp_mini" CONFIG_SYS_ICACHE_OFF=y CONFIG_ARCH_ZYNQMP=y -CONFIG_TEXT_BASE=0xFFFC0000 +CONFIG_TEXT_BASE=0xFFFE0000 CONFIG_SYS_MALLOC_LEN=0x1a00 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xfffe0000 @@ -14,7 +14,10 @@ CONFIG_SYS_MEM_RSVD_FOR_MMU=y # CONFIG_PSCI_RESET is not set CONFIG_SYS_MEMTEST_START=0x00000000 CONFIG_SYS_MEMTEST_END=0x00001000 +CONFIG_SKIP_RELOCATE=y CONFIG_REMAKE_ELF=y +CONFIG_HAS_BOARD_SIZE_LIMIT=y +CONFIG_BOARD_SIZE_LIMIT=131072 # CONFIG_LEGACY_IMAGE_FORMAT is not set # CONFIG_AUTOBOOT is not set CONFIG_SYS_CBSIZE=1024 diff --git a/configs/xilinx_zynqmp_mini_qspi_defconfig b/configs/xilinx_zynqmp_mini_qspi_defconfig index 03366a3b05a..9457dd0a672 100644 --- a/configs/xilinx_zynqmp_mini_qspi_defconfig +++ b/configs/xilinx_zynqmp_mini_qspi_defconfig @@ -2,7 +2,7 @@ CONFIG_ARM=y CONFIG_SYS_CONFIG_NAME="xilinx_zynqmp_mini" CONFIG_SYS_ICACHE_OFF=y CONFIG_ARCH_ZYNQMP=y -CONFIG_TEXT_BASE=0xFFFC0000 +CONFIG_TEXT_BASE=0xFFFE0000 CONFIG_SYS_MALLOC_LEN=0x1b00 CONFIG_NR_DRAM_BANKS=1 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y @@ -19,8 +19,10 @@ CONFIG_XILINX_MINI=y CONFIG_SYS_MEM_RSVD_FOR_MMU=y CONFIG_ZYNQMP_NO_DDR=y # CONFIG_PSCI_RESET is not set -# CONFIG_EXPERT is not set +CONFIG_SKIP_RELOCATE=y CONFIG_REMAKE_ELF=y +CONFIG_HAS_BOARD_SIZE_LIMIT=y +CONFIG_BOARD_SIZE_LIMIT=131072 # CONFIG_LEGACY_IMAGE_FORMAT is not set # CONFIG_AUTOBOOT is not set # CONFIG_ARCH_FIXUP_FDT_MEMORY is not set -- cgit v1.3.1 From f81d620a82ffb3787806dcbc99bb4602fe04a337 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 22 Jul 2026 14:16:38 +0200 Subject: xilinx: zynqmp: Enable SPL for zynqmp mini The only purpose to enable SPL is to simplify testing of mini configuration. SPL initializes the system also with DDR that mini configuration can directly run. psu_init already targets zcu102 like for mini_qspi (symlink zynqmp-mini -> zynqmp-zcu102-rev1.0). Signed-off-by: Michal Simek Link: https://patch.msgid.link/9c9a2699c97241fa599b65fd336699ace7577833.1784722595.git.michal.simek@amd.com --- configs/xilinx_zynqmp_mini_defconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/configs/xilinx_zynqmp_mini_defconfig b/configs/xilinx_zynqmp_mini_defconfig index 8b1d8cc0439..35385d0079a 100644 --- a/configs/xilinx_zynqmp_mini_defconfig +++ b/configs/xilinx_zynqmp_mini_defconfig @@ -8,7 +8,12 @@ CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xfffe0000 CONFIG_ENV_SIZE=0x80 CONFIG_DEFAULT_DEVICE_TREE="zynqmp-mini" +CONFIG_SPL_STACK=0xfffffffc +CONFIG_SPL_BSS_MAX_SIZE=0x80000 CONFIG_SYS_LOAD_ADDR=0x8000000 +CONFIG_SPL=y +# CONFIG_SPL_FS_FAT is not set +# CONFIG_SPL_LIBDISK_SUPPORT is not set CONFIG_XILINX_MINI=y CONFIG_SYS_MEM_RSVD_FOR_MMU=y # CONFIG_PSCI_RESET is not set @@ -25,6 +30,14 @@ CONFIG_SYS_PBSIZE=1049 # CONFIG_DISPLAY_CPUINFO is not set # CONFIG_BOARD_LATE_INIT is not set CONFIG_CLOCKS=y +CONFIG_SPL_MAX_SIZE=0x40000 +# CONFIG_SPL_BINMAN_SYMBOLS is not set +# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set +CONFIG_SPL_HAVE_INIT_STACK=y +CONFIG_SPL_SYS_MALLOC=y +CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y +CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x20000000 +CONFIG_SPL_SYS_MALLOC_SIZE=0x1000000 # CONFIG_CMDLINE_EDITING is not set # CONFIG_AUTO_COMPLETE is not set # CONFIG_SYS_LONGHELP is not set @@ -53,10 +66,12 @@ CONFIG_SYS_ALT_MEMTEST=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_CACHE=y # CONFIG_CMD_SLEEP is not set +CONFIG_SPL_OF_CONTROL=y CONFIG_OF_EMBED=y CONFIG_ENV_RELOC_GD_ENV_ADDR=y CONFIG_NO_NET=y # CONFIG_DM_DEVICE_REMOVE is not set +CONFIG_SPL_DM_SEQ_ALIAS=y # CONFIG_SIMPLE_BUS is not set # CONFIG_DM_MAILBOX is not set # CONFIG_MMC is not set -- cgit v1.3.1 From 63c228d0a0efd89fd808cb1286238f5628db3769 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 24 Jul 2026 07:18:47 +0200 Subject: firmware: zynqmp: Remove duplicated sys_proto.h header asm/arch/sys_proto.h is included twice that's why remove one. Signed-off-by: Michal Simek Link: https://patch.msgid.link/cd3c11a7e09ed8db3945910cdfed0eb860dcd61e.1784870326.git.michal.simek@amd.com --- drivers/firmware/firmware-zynqmp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 6052a31b5b4..0783aa9a8ab 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -22,7 +22,6 @@ #if defined(CONFIG_ZYNQMP_IPI) #include -#include #define PMUFW_PAYLOAD_ARG_CNT 8 -- cgit v1.3.1 From 661b6e44a446953b472679345ced92a2bb68c4f9 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 24 Jul 2026 07:25:13 +0200 Subject: xilinx: versal2: Add PM UFS firmware interface Add firmware interface for UFS access but also keep IO interface when firmware is not enabled. Signed-off-by: Michal Simek Link: https://patch.msgid.link/6419fe4f6f1f451aee624c7762b7aa690d50e498.1784870711.git.michal.simek@amd.com --- arch/arm/mach-versal2/cpu.c | 24 ++++++++ arch/arm/mach-versal2/include/mach/sys_proto.h | 7 ++- drivers/firmware/firmware-zynqmp.c | 79 +++++++++++++++++++++++--- drivers/ufs/ufs-amd-versal2.c | 4 +- include/zynqmp_firmware.h | 13 +++-- 5 files changed, 112 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-versal2/cpu.c b/arch/arm/mach-versal2/cpu.c index 6cc6592b0fc..01efa7b7928 100644 --- a/arch/arm/mach-versal2/cpu.c +++ b/arch/arm/mach-versal2/cpu.c @@ -160,6 +160,30 @@ u8 __weak versal2_get_bootmode(void) return bootmode; } +int __weak zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value) +{ + *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY); + return 0; +} + +int __weak zynqmp_pm_ufs_sram_csr_read(u32 *value) +{ + *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; +} + +int __weak zynqmp_pm_ufs_sram_csr_write(u32 *value) +{ + writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; +} + +int __weak zynqmp_pm_ufs_cal_reg(u32 *value) +{ + *value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET); + return 0; +} + void versal2_timer_setup(void) { u32 val; diff --git a/arch/arm/mach-versal2/include/mach/sys_proto.h b/arch/arm/mach-versal2/include/mach/sys_proto.h index b8d12d1dd3b..c1dfd184d86 100644 --- a/arch/arm/mach-versal2/include/mach/sys_proto.h +++ b/arch/arm/mach-versal2/include/mach/sys_proto.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2021 - 2022, Xilinx, Inc. - * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. */ #ifndef _ASM_ARCH_SYS_PROTO_H @@ -22,4 +22,9 @@ u8 versal2_get_bootmode(void); /* EL3 clock/timer register setup, called from board_early_init_r() */ void versal2_timer_setup(void); +int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value); +int zynqmp_pm_ufs_sram_csr_read(u32 *value); +int zynqmp_pm_ufs_sram_csr_write(u32 *value); +int zynqmp_pm_ufs_cal_reg(u32 *value); + #endif /* _ASM_ARCH_SYS_PROTO_H */ diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 0783aa9a8ab..2bdb1c4d9f8 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -177,26 +177,89 @@ unsigned int zynqmp_firmware_version(void) #if defined(CONFIG_ARCH_VERSAL2) int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value) { - *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY); - return 0; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, + IOCTL_READ_REG, TXRX_CFGRDY_OFFSET, 0, 0, + 0, ret_payload); + if (ret) + return ret; + + *value = ret_payload[1]; + + return ret; } int zynqmp_pm_ufs_sram_csr_read(u32 *value) { - *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); - return 0; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, + IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0, + 0, ret_payload); + if (ret) + return ret; + + *value = ret_payload[1]; + + return ret; } int zynqmp_pm_ufs_sram_csr_write(u32 *value) { - writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); - return 0; + int ret; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_MASK_WRITE_REG); + if (ret) { + printf("%s: IOCTL_MASK_WRITE_REG is not supported : %d\n" + , __func__, ret); + return 0; + } + + ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, + IOCTL_MASK_WRITE_REG, SRAM_CSR_OFFSET, + GENMASK(2, 1), *value, 0, NULL); + if (ret) + return ret; + + return ret; } int zynqmp_pm_ufs_cal_reg(u32 *value) { - *value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET); - return 0; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG); + if (ret) { + printf("%s: IOCTL_READ_REG is not supported : %d\n" + , __func__, ret); + return 0; + } + + ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_EFUSE_CACHE, + IOCTL_READ_REG, UFS_CAL_1_OFFSET, 0, 0, + 0, ret_payload); + if (ret) + return ret; + + *value = ret_payload[1]; + + return ret; } #endif /* CONFIG_ARCH_VERSAL2 */ diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c index 3369d32d924..25af9dd63cc 100644 --- a/drivers/ufs/ufs-amd-versal2.c +++ b/drivers/ufs/ufs-amd-versal2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2024-2025 Advanced Micro Devices, Inc. + * Copyright (C) 2024-2026 Advanced Micro Devices, Inc. */ #include @@ -14,6 +14,8 @@ #include #include +#include + #include "ufs.h" #include "ufshcd-dwc.h" #include "ufshci-dwc.h" diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h index 0e545e3db1b..f753a67ac27 100644 --- a/include/zynqmp_firmware.h +++ b/include/zynqmp_firmware.h @@ -465,10 +465,6 @@ int zynqmp_mmio_read(const u32 address, u32 *value); int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value); int zynqmp_pm_feature(const u32 api_id); u32 zynqmp_pm_get_bootmode_reg(void); -int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value); -int zynqmp_pm_ufs_sram_csr_read(u32 *value); -int zynqmp_pm_ufs_sram_csr_write(u32 *value); -int zynqmp_pm_ufs_cal_reg(u32 *value); u32 zynqmp_pm_get_pmc_multi_boot_reg(void); u32 zynqmp_pm_get_pmc_global_pggs_reg(u32 reg_addr); @@ -535,7 +531,14 @@ extern smc_call_handler_t __data smc_call_handler; #define PM_DEV_OSPI (0x1822402aU) -#define PM_REG_PGGS3 0x30004003 +#define PM_REGNODE_PMC_IOU_SLCR 0x30000002 +#define PM_REGNODE_EFUSE_CACHE 0x30000003 +#define PM_REG_PGGS3 0x30004003 + +#define SRAM_CSR_OFFSET 0x104C +#define TXRX_CFGRDY_OFFSET 0x1054 +#define UFS_CAL_1_OFFSET 0xBE8 + #define PMC_GLOBAL_PGGS3_REG_NODE 0x1824C005 #endif /* _ZYNQMP_FIRMWARE_H_ */ -- cgit v1.3.1 From 9e8a0f54b0231d11e481fbf7832a8780e6201b71 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 Jul 2026 08:49:16 +0200 Subject: ufs: amd-versal2: Perform controller reset in the init callback Move the controller/PHY reset and SRAM-bypass sequence from hce_enable_notify(PRE_CHANGE) into ufs_versal2_init(). The reset sequence releases the host controller from reset, and the UFS core reads the capability and version registers right after .init() and before ufshcd_hba_enable() (the only caller of hce_enable_notify()). Doing the reset in .init() ensures those registers are read after the controller is out of reset, and keeps the driver aligned with Linux. ufs_versal2_phy_init() stays in POST_CHANGE. Signed-off-by: Michal Simek Link: https://patch.msgid.link/6b4cbfa6cc58bef86da86971e4b27726a4e510f0.1785394153.git.michal.simek@amd.com --- drivers/ufs/ufs-amd-versal2.c | 86 +++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c index 25af9dd63cc..986147848bd 100644 --- a/drivers/ufs/ufs-amd-versal2.c +++ b/drivers/ufs/ufs-amd-versal2.c @@ -301,7 +301,7 @@ static int ufs_versal2_init(struct ufs_hba *hba) struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); struct clk clk; unsigned long core_clk_rate = 0; - u32 cal; + u32 cal, sram_csr; int ret = 0; priv->phy_mode = UFSHCD_DWC_PHY_MODE_ROM; @@ -331,6 +331,41 @@ static int ufs_versal2_init(struct ufs_hba *hba) return PTR_ERR(priv->rstphy); } + /* Assert RST_UFS Reset for UFS block in PMX_IOU */ + ret = reset_assert(priv->rstc); + if (ret) { + dev_err(hba->dev, "host reset assert failed, err = %d\n", ret); + return ret; + } + + /* Assert PHY reset */ + ret = reset_assert(priv->rstphy); + if (ret) { + dev_err(hba->dev, "phy reset assert failed, err = %d\n", ret); + return ret; + } + + ret = zynqmp_pm_ufs_sram_csr_read(&sram_csr); + if (ret) + return ret; + + if (!priv->phy_mode) { + sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK; + sram_csr |= SRAM_CSR_BYPASS_MASK; + } else { + dev_err(hba->dev, "Invalid phy-mode %d.\n", priv->phy_mode); + return -EINVAL; + } + + ret = zynqmp_pm_ufs_sram_csr_write(&sram_csr); + if (ret) + return ret; + + /* De Assert RST_UFS Reset for UFS block in PMX_IOU */ + ret = reset_deassert(priv->rstc); + if (ret) + dev_err(hba->dev, "host reset deassert failed, err = %d\n", ret); + ret = zynqmp_pm_ufs_cal_reg(&cal); if (ret) return ret; @@ -346,57 +381,12 @@ static int ufs_versal2_init(struct ufs_hba *hba) static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) { - struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); - u32 sram_csr; - int ret; - - switch (status) { - case PRE_CHANGE: - /* Assert RST_UFS Reset for UFS block in PMX_IOU */ - ret = reset_assert(priv->rstc); - if (ret) { - dev_err(hba->dev, "ufshc reset assert failed, err = %d\n", ret); - return ret; - } - - /* Assert PHY reset */ - ret = reset_assert(priv->rstphy); - if (ret) { - dev_err(hba->dev, "ufsphy reset assert failed, err = %d\n", ret); - return ret; - } - - ret = zynqmp_pm_ufs_sram_csr_read(&sram_csr); - if (ret) - return ret; - - if (!priv->phy_mode) { - sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK; - sram_csr |= SRAM_CSR_BYPASS_MASK; - } else { - dev_err(hba->dev, "Invalid phy-mode %d.\n", priv->phy_mode); - return -EINVAL; - } - - ret = zynqmp_pm_ufs_sram_csr_write(&sram_csr); - if (ret) - return ret; - - /* De Assert RST_UFS Reset for UFS block in PMX_IOU */ - ret = reset_deassert(priv->rstc); - if (ret) - dev_err(hba->dev, "ufshc reset deassert failed, err = %d\n", ret); + int ret = 0; - break; - case POST_CHANGE: + if (status == POST_CHANGE) { ret = ufs_versal2_phy_init(hba); if (ret) dev_err(hba->dev, "Phy init failed (%d)\n", ret); - - break; - default: - ret = -EINVAL; - break; } return ret; -- cgit v1.3.1 From f2294a98b28627b7b2075d5d19d2909de6b27667 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 Jul 2026 08:49:17 +0200 Subject: xilinx: versal2: Clean UFS firmware interface The M-PHY and SRAM readiness poll loops move out of the UFS controller driver and into the firmware backend, exposed as blocking waits with a caller-owned timeout budget. This keeps the UFS driver backend-agnostic and lets an other backends offload the wait to the platform in a single call instead of round-tripping every poll. set_sram_bypass() now performs the SRAM_CSR read-modify-write in the firmware layer. Hook up functions are also update to reflect their usage. Signed-off-by: Michal Simek Link: https://patch.msgid.link/26bc9aa2c02921690e126ab3401f7974ef04ad6e.1785394153.git.michal.simek@amd.com --- arch/arm/mach-versal2/cpu.c | 34 ++++++++---- arch/arm/mach-versal2/include/mach/hardware.h | 5 ++ arch/arm/mach-versal2/include/mach/sys_proto.h | 8 +-- drivers/firmware/firmware-zynqmp.c | 72 +++++++++++++++---------- drivers/ufs/ufs-amd-versal2.c | 73 ++++++-------------------- 5 files changed, 95 insertions(+), 97 deletions(-) diff --git a/arch/arm/mach-versal2/cpu.c b/arch/arm/mach-versal2/cpu.c index 01efa7b7928..d72f66f4fba 100644 --- a/arch/arm/mach-versal2/cpu.c +++ b/arch/arm/mach-versal2/cpu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -160,25 +161,40 @@ u8 __weak versal2_get_bootmode(void) return bootmode; } -int __weak zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value) +/* + * Wait for the M-PHY TX/RX config-ready status to settle (all bits cleared) or + * @timeout_us to elapse. The direct-MMIO fallback owns the poll loop, mirroring + * the EEMI backend; the timeout budget is owned by the caller. + */ +int __weak zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us) { - *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY); - return 0; + return wait_for_bit_le32((void *)(uintptr_t)(PMXC_SLCR_BASE_ADDRESS + + PMXC_TX_RX_CFG_RDY), + TX_RX_CFG_RDY_MASK, false, timeout_us / 1000, + false); } -int __weak zynqmp_pm_ufs_sram_csr_read(u32 *value) +int __weak zynqmp_pm_wait_sram_init_done(u32 timeout_us) { - *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); - return 0; + return wait_for_bit_le32((void *)(uintptr_t)(PMXC_SLCR_BASE_ADDRESS + + PMXC_SRAM_CSR), + SRAM_CSR_INIT_DONE_MASK, true, timeout_us / 1000, + false); } -int __weak zynqmp_pm_ufs_sram_csr_write(u32 *value) +int __weak zynqmp_pm_set_sram_bypass(void) { - writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + u32 sram_csr; + + sram_csr = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK; + sram_csr |= SRAM_CSR_BYPASS_MASK; + writel(sram_csr, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; } -int __weak zynqmp_pm_ufs_cal_reg(u32 *value) +int __weak zynqmp_pm_get_ufs_calibration_values(u32 *value) { *value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET); return 0; diff --git a/arch/arm/mach-versal2/include/mach/hardware.h b/arch/arm/mach-versal2/include/mach/hardware.h index 7977529fd6c..4ff732d660f 100644 --- a/arch/arm/mach-versal2/include/mach/hardware.h +++ b/arch/arm/mach-versal2/include/mach/hardware.h @@ -65,6 +65,11 @@ #define PMXC_SRAM_CSR 0x4C #define PMXC_TX_RX_CFG_RDY 0x54 +#define SRAM_CSR_INIT_DONE_MASK BIT(0) +#define SRAM_CSR_EXT_LD_DONE_MASK BIT(1) +#define SRAM_CSR_BYPASS_MASK BIT(2) +#define TX_RX_CFG_RDY_MASK GENMASK(3, 0) + #define PMC_GLOBAL_PGGS3_REG 0xF111005C #define PMC_GLOBAL_PGGS4_REG 0xF1110060 diff --git a/arch/arm/mach-versal2/include/mach/sys_proto.h b/arch/arm/mach-versal2/include/mach/sys_proto.h index c1dfd184d86..d678adf9c26 100644 --- a/arch/arm/mach-versal2/include/mach/sys_proto.h +++ b/arch/arm/mach-versal2/include/mach/sys_proto.h @@ -22,9 +22,9 @@ u8 versal2_get_bootmode(void); /* EL3 clock/timer register setup, called from board_early_init_r() */ void versal2_timer_setup(void); -int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value); -int zynqmp_pm_ufs_sram_csr_read(u32 *value); -int zynqmp_pm_ufs_sram_csr_write(u32 *value); -int zynqmp_pm_ufs_cal_reg(u32 *value); +int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us); +int zynqmp_pm_wait_sram_init_done(u32 timeout_us); +int zynqmp_pm_set_sram_bypass(void); +int zynqmp_pm_get_ufs_calibration_values(u32 *value); #endif /* _ASM_ARCH_SYS_PROTO_H */ diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 2bdb1c4d9f8..fae66ccb3d8 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -19,6 +19,7 @@ #include #include #include +#include #if defined(CONFIG_ZYNQMP_IPI) #include @@ -175,51 +176,60 @@ unsigned int zynqmp_firmware_version(void) }; #if defined(CONFIG_ARCH_VERSAL2) -int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value) +/* + * Poll the M-PHY TX/RX config-ready status until it settles or @timeout_us + * elapses. Legacy EEMI firmware only offers the per-read status primitive, so + * the poll loop lives here rather than in the UFS driver; the timeout budget is + * owned by the caller. + */ +int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us) { u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - if (!value) - return -EINVAL; + while (timeout_us--) { + ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, + IOCTL_READ_REG, TXRX_CFGRDY_OFFSET, 0, 0, + 0, ret_payload); + if (ret) + return ret; - ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, - IOCTL_READ_REG, TXRX_CFGRDY_OFFSET, 0, 0, - 0, ret_payload); - if (ret) - return ret; + if (!(ret_payload[1] & TX_RX_CFG_RDY_MASK)) + return 0; - *value = ret_payload[1]; + udelay(1); + } - return ret; + return -ETIMEDOUT; } -int zynqmp_pm_ufs_sram_csr_read(u32 *value) +int zynqmp_pm_wait_sram_init_done(u32 timeout_us) { u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - if (!value) - return -EINVAL; + while (timeout_us--) { + ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, + IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0, + 0, ret_payload); + if (ret) + return ret; - ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, - IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0, - 0, ret_payload); - if (ret) - return ret; + if (ret_payload[1] & SRAM_CSR_INIT_DONE_MASK) + return 0; - *value = ret_payload[1]; + udelay(1); + } - return ret; + return -ETIMEDOUT; } -int zynqmp_pm_ufs_sram_csr_write(u32 *value) +int zynqmp_pm_set_sram_bypass(void) { + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 sram_csr; int ret; - if (!value) - return -EINVAL; - ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_MASK_WRITE_REG); if (ret) { printf("%s: IOCTL_MASK_WRITE_REG is not supported : %d\n" @@ -228,15 +238,21 @@ int zynqmp_pm_ufs_sram_csr_write(u32 *value) } ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, - IOCTL_MASK_WRITE_REG, SRAM_CSR_OFFSET, - GENMASK(2, 1), *value, 0, NULL); + IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0, + 0, ret_payload); if (ret) return ret; - return ret; + sram_csr = ret_payload[1]; + sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK; + sram_csr |= SRAM_CSR_BYPASS_MASK; + + return xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR, + IOCTL_MASK_WRITE_REG, SRAM_CSR_OFFSET, + GENMASK(2, 1), sram_csr, 0, NULL); } -int zynqmp_pm_ufs_cal_reg(u32 *value) +int zynqmp_pm_get_ufs_calibration_values(u32 *value) { u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c index 986147848bd..48854792cbf 100644 --- a/drivers/ufs/ufs-amd-versal2.c +++ b/drivers/ufs/ufs-amd-versal2.c @@ -20,10 +20,6 @@ #include "ufshcd-dwc.h" #include "ufshci-dwc.h" -#define SRAM_CSR_INIT_DONE_MASK BIT(0) -#define SRAM_CSR_EXT_LD_DONE_MASK BIT(1) -#define SRAM_CSR_BYPASS_MASK BIT(2) - #define MPHY_FAST_RX_AFE_CAL BIT(2) #define MPHY_FW_CALIB_CFG_VAL BIT(8) @@ -31,8 +27,6 @@ #define MPHY_RX_OVRD_VAL BIT(2) #define MPHY_RX_ACK_MASK BIT(0) -#define TX_RX_CFG_RDY_MASK GENMASK(3, 0) - #define TIMEOUT_MICROSEC 1000000L struct ufs_versal2_priv { @@ -229,7 +223,6 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba) static int ufs_versal2_phy_init(struct ufs_hba *hba) { struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); - u32 reg, time_left; int ret; static const struct ufshcd_dme_attr_val rmmi_attrs[] = { { UIC_ARG_MIB(CBREFCLKCTRL2), CBREFREFCLK_GATE_OVR_EN, DME_LOCAL }, @@ -238,24 +231,15 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL } }; - /* Wait for Tx/Rx config_rdy */ - time_left = TIMEOUT_MICROSEC; - do { - time_left--; - ret = zynqmp_pm_ufs_get_txrx_cfgrdy(®); - if (ret) - return ret; - - reg &= TX_RX_CFG_RDY_MASK; - if (!reg) - break; - - mdelay(5); - } while (time_left); - - if (!time_left) { + /* + * Wait for Tx/Rx config_rdy. The poll loop lives in the firmware + * backend (IO, EEMI or SCMI) so this driver stays backend-agnostic; + * the timeout budget stays here with the consumer. + */ + ret = zynqmp_pm_wait_mphy_tx_rx_config_ready(TIMEOUT_MICROSEC); + if (ret) { dev_err(hba->dev, "Tx/Rx configuration signal busy.\n"); - return -ETIMEDOUT; + return ret; } ret = ufshcd_dwc_dme_set_attrs(hba, rmmi_attrs, ARRAY_SIZE(rmmi_attrs)); @@ -269,24 +253,11 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) return ret; } - /* Wait for SRAM init done */ - time_left = TIMEOUT_MICROSEC; - do { - time_left--; - ret = zynqmp_pm_ufs_sram_csr_read(®); - if (ret) - return ret; - - reg &= SRAM_CSR_INIT_DONE_MASK; - if (reg) - break; - - mdelay(5); - } while (time_left); - - if (!time_left) { + /* Wait for SRAM init done (poll handled by the firmware backend). */ + ret = zynqmp_pm_wait_sram_init_done(TIMEOUT_MICROSEC); + if (ret) { dev_err(hba->dev, "SRAM initialization failed.\n"); - return -ETIMEDOUT; + return ret; } ret = ufs_versal2_setup_phy(hba); @@ -301,7 +272,7 @@ static int ufs_versal2_init(struct ufs_hba *hba) struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); struct clk clk; unsigned long core_clk_rate = 0; - u32 cal, sram_csr; + u32 cal; int ret = 0; priv->phy_mode = UFSHCD_DWC_PHY_MODE_ROM; @@ -345,28 +316,18 @@ static int ufs_versal2_init(struct ufs_hba *hba) return ret; } - ret = zynqmp_pm_ufs_sram_csr_read(&sram_csr); - if (ret) + ret = zynqmp_pm_set_sram_bypass(); + if (ret) { + dev_err(hba->dev, "Bypass SRAM interface failed, err = %d\n", ret); return ret; - - if (!priv->phy_mode) { - sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK; - sram_csr |= SRAM_CSR_BYPASS_MASK; - } else { - dev_err(hba->dev, "Invalid phy-mode %d.\n", priv->phy_mode); - return -EINVAL; } - ret = zynqmp_pm_ufs_sram_csr_write(&sram_csr); - if (ret) - return ret; - /* De Assert RST_UFS Reset for UFS block in PMX_IOU */ ret = reset_deassert(priv->rstc); if (ret) dev_err(hba->dev, "host reset deassert failed, err = %d\n", ret); - ret = zynqmp_pm_ufs_cal_reg(&cal); + ret = zynqmp_pm_get_ufs_calibration_values(&cal); if (ret) return ret; -- cgit v1.3.1 From fafbcbdc977a7cc9cd686642a15935966500c05a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 Jul 2026 08:49:18 +0200 Subject: ufs: amd-versal2: Remove ZYNQMP_FIRMWARE dependency There are EEMI firmware hooks available but they are also IO versions of it that's why remove ZYNQMP_FIRMWARE depency from UFS driver. Signed-off-by: Michal Simek Link: https://patch.msgid.link/85cbacdc7492890c76396974d24fff607c2088ad.1785394153.git.michal.simek@amd.com --- drivers/ufs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig index c1b84bd7559..6dffdc00b32 100644 --- a/drivers/ufs/Kconfig +++ b/drivers/ufs/Kconfig @@ -10,7 +10,7 @@ config UFS config UFS_AMD_VERSAL2 bool "AMD Versal Gen 2 UFS controller platform driver" - depends on UFS && ZYNQMP_FIRMWARE + depends on UFS && ARCH_VERSAL2 help This selects the AMD specific additions to UFSHCD platform driver. UFS host on AMD needs some vendor specific configuration before accessing -- cgit v1.3.1 From 86cc9f2fb5637d64e2da78c43fdccf4c50daef8c Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 10 Aug 2026 13:37:01 +0200 Subject: MAINTAINERS: Add missing fragment for Versal Gen 2 Versal Gen 2 and especially march-versal2 is not covered by own fragment that's why add it. Signed-off-by: Michal Simek Link: https://patch.msgid.link/11956eca5ebd0b3626b7e2d0fa52331cbd3dc1ec.1786361819.git.michal.simek@amd.com --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e1379a6a3e7..eb48eea55c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -821,6 +821,12 @@ F: drivers/spi/cadence_ospi_versal.c F: drivers/watchdog/xilinx_wwdt.c N: (? +S: Maintained +T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-microblaze.git +F: arch/arm/mach-versal2/ + ARM VERSATILE EXPRESS DRIVERS M: Liviu Dudau S: Maintained -- cgit v1.3.1