AMD/Xilinx changes for v2026.10-rc3

Versal Gen 2:
- Add support for 2VM3654 device
- Add missing MAINTAINERS fragment

AMD:
- Fix fwu dfu_string generation not to alter with generic DFU

Firmware/ufs:
- UFS interface cleanup
- Remove ZynqMP firmware dependency from UFS

zynqmp_gqspi:
- Fix quad mode bus conflict

MB-V:
- shrink SPL size and enforce size limits
- remake ELF from BIN
This commit is contained in:
Tom Rini
2026-08-12 08:31:54 -06:00
18 changed files with 388 additions and 243 deletions
+6
View File
@@ -821,6 +821,12 @@ F: drivers/spi/cadence_ospi_versal.c
F: drivers/watchdog/xilinx_wwdt.c
N: (?<!uni)versal
ARM VERSAL GEN 2
M: Michal Simek <michal.simek@amd.com>
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 <liviu.dudau@foss.arm.com>
S: Maintained
+1
View File
@@ -8,3 +8,4 @@
obj-y += clk.o
obj-y += cpu.o
obj-y += lowlevel_init.o
+40
View File
@@ -11,6 +11,7 @@
#include <malloc.h>
#include <time.h>
#include <vsprintf.h>
#include <wait_bit.h>
#include <asm/armv8/mmu.h>
#include <asm/cache.h>
#include <asm/global_data.h>
@@ -160,6 +161,45 @@ u8 __weak versal2_get_bootmode(void)
return bootmode;
}
/*
* 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)
{
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_wait_sram_init_done(u32 timeout_us)
{
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_set_sram_bypass(void)
{
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_get_ufs_calibration_values(u32 *value)
{
*value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET);
return 0;
}
void versal2_timer_setup(void)
{
u32 val;
+46 -41
View File
@@ -4,34 +4,6 @@
* Copyright (C) 2022 - 2025, Advanced Micro Devices, Inc.
*/
#ifndef __ASSEMBLY__
#include <linux/bitops.h>
#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)
@@ -106,5 +65,51 @@ enum versal2_platform {
#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
#ifndef __ASSEMBLY__
#include <linux/bitops.h>
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__ */
@@ -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_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 */
+40
View File
@@ -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 <asm-offsets.h>
#include <config.h>
#include <linux/linkage.h>
#include <asm/macro.h>
#include <asm/arch/hardware.h>
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)
-39
View File
@@ -11,7 +11,6 @@
#include <env.h>
#include <efi_loader.h>
#include <fdtdec.h>
#include <fwu.h>
#include <init.h>
#include <env_internal.h>
#include <log.h>
@@ -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)
{
+36 -1
View File
@@ -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 <michal.simek@amd.com>
*/
@@ -14,6 +14,8 @@
#include <init.h>
#include <jffs2/load_kernel.h>
#include <log.h>
#include <memalign.h>
#include <mtd.h>
#include <asm/io.h>
#include <asm/global_data.h>
#include <asm/sections.h>
@@ -22,6 +24,7 @@
#endif
#include <dm/uclass.h>
#include <i2c.h>
#include <linux/err.h>
#include <linux/sizes.h>
#include <malloc.h>
#include <memtop.h>
@@ -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;
-39
View File
@@ -10,7 +10,6 @@
#include <env.h>
#include <efi_loader.h>
#include <fdtdec.h>
#include <fwu.h>
#include <init.h>
#include <env_internal.h>
#include <log.h>
@@ -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
+5
View File
@@ -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
@@ -38,6 +41,8 @@ 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_SPL_REMAKE_ELF=y
# CONFIG_CMD_MII is not set
CONFIG_CMD_SNTP=y
CONFIG_CMD_TIMER=y
+19 -1
View File
@@ -2,19 +2,27 @@ 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
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
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
@@ -22,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
@@ -50,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
+4 -2
View File
@@ -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
+91 -13
View File
@@ -19,10 +19,10 @@
#include <asm/ptrace.h>
#include <asm/system.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#if defined(CONFIG_ZYNQMP_IPI)
#include <mailbox.h>
#include <asm/arch/sys_proto.h>
#define PMUFW_PAYLOAD_ARG_CNT 8
@@ -176,28 +176,106 @@ 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)
{
*value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY);
return 0;
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
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;
if (!(ret_payload[1] & TX_RX_CFG_RDY_MASK))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
int zynqmp_pm_ufs_sram_csr_read(u32 *value)
int zynqmp_pm_wait_sram_init_done(u32 timeout_us)
{
*value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
return 0;
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
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;
if (ret_payload[1] & SRAM_CSR_INIT_DONE_MASK)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
int zynqmp_pm_ufs_sram_csr_write(u32 *value)
int zynqmp_pm_set_sram_bypass(void)
{
writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
return 0;
u32 ret_payload[PAYLOAD_ARG_CNT];
u32 sram_csr;
int ret;
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_READ_REG, SRAM_CSR_OFFSET, 0, 0,
0, ret_payload);
if (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)
{
*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 */
+33 -10
View File
@@ -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, &regs->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(&regs->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(&regs->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);
+1 -1
View File
@@ -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
+43 -90
View File
@@ -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 <clk.h>
@@ -14,14 +14,12 @@
#include <linux/time.h>
#include <reset.h>
#include <asm/arch/sys_proto.h>
#include "ufs.h"
#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)
@@ -29,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 {
@@ -227,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 },
@@ -236,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(&reg);
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));
@@ -267,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(&reg);
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);
@@ -329,7 +302,32 @@ static int ufs_versal2_init(struct ufs_hba *hba)
return PTR_ERR(priv->rstphy);
}
ret = zynqmp_pm_ufs_cal_reg(&cal);
/* 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_set_sram_bypass();
if (ret) {
dev_err(hba->dev, "Bypass SRAM interface failed, err = %d\n", 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_get_ufs_calibration_values(&cal);
if (ret)
return ret;
@@ -344,57 +342,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;
int ret = 0;
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);
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;
+9
View File
@@ -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 }
+8 -5
View File
@@ -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_ */