summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-06-08 08:36:57 -0600
committerTom Rini <[email protected]>2026-06-08 08:36:57 -0600
commit5d4d6e331d3f056ecc7ab11b72098a3cf4fdb099 (patch)
treebd545cc373e2646007a97ab90bdb5be52f481982
parent9e87893c24d1f21350e69bc222e6a3214c933b3b (diff)
parent8efa173b389e5cef6eece991351442baea0264fd (diff)
Merge tag 'xilinx-for-v2026.10-rc1-v2' of https://source.denx.de/u-boot/custodians/u-boot-microblaze into next
AMD/Xilinx/FPGA changes for v2026.10-rc1 v2 zynqmp: - Clean up USB gadget configuration mbv: - Reduce SPL size versal: - Add support for A/B capsule update versal2: - Add support for A/B capsule update reset: - Introduce reset_reset_bulk() generic interface - zynqmp: Implement reset_reset_bulk() interface spi: - cadence: Switch to reset_reset_bulk() interface
-rw-r--r--arch/arm/mach-versal/include/mach/hardware.h3
-rw-r--r--arch/arm/mach-versal2/include/mach/hardware.h3
-rw-r--r--arch/sandbox/dts/test.dts10
-rw-r--r--arch/sandbox/include/asm/reset.h3
-rw-r--r--board/amd/versal2/board.c131
-rw-r--r--board/xilinx/common/board.c73
-rw-r--r--board/xilinx/versal/board.c42
-rw-r--r--board/xilinx/zynqmp/zynqmp.c4
-rw-r--r--configs/amd_versal2_virt_defconfig15
-rw-r--r--configs/xilinx_mbv32_defconfig2
-rw-r--r--configs/xilinx_versal_virt_defconfig2
-rw-r--r--configs/xilinx_zynqmp_virt_defconfig1
-rw-r--r--drivers/firmware/firmware-zynqmp.c54
-rw-r--r--drivers/reset/reset-uclass.c34
-rw-r--r--drivers/reset/reset-zynqmp.c11
-rw-r--r--drivers/reset/sandbox-reset-test.c14
-rw-r--r--drivers/reset/sandbox-reset.c58
-rw-r--r--drivers/spi/cadence_qspi.c16
-rw-r--r--include/configs/amd_versal2.h5
-rw-r--r--include/reset-uclass.h19
-rw-r--r--include/reset.h49
-rw-r--r--include/zynqmp_firmware.h4
-rw-r--r--test/dm/reset.c107
23 files changed, 637 insertions, 23 deletions
diff --git a/arch/arm/mach-versal/include/mach/hardware.h b/arch/arm/mach-versal/include/mach/hardware.h
index b5f80a8e3a9..66eca18998a 100644
--- a/arch/arm/mach-versal/include/mach/hardware.h
+++ b/arch/arm/mach-versal/include/mach/hardware.h
@@ -100,3 +100,6 @@ struct crp_regs {
#define MIO_PIN_12 0xF1060030
#define BANK0_OUTPUT 0xF1020040
#define BANK0_TRI 0xF1060200
+
+#define PMC_GLOBAL_PGGS3_REG 0xF111005C
+#define PMC_GLOBAL_PGGS4_REG 0xF1110060
diff --git a/arch/arm/mach-versal2/include/mach/hardware.h b/arch/arm/mach-versal2/include/mach/hardware.h
index 81a0df89357..1bebf20910a 100644
--- a/arch/arm/mach-versal2/include/mach/hardware.h
+++ b/arch/arm/mach-versal2/include/mach/hardware.h
@@ -105,3 +105,6 @@ enum versal2_platform {
#define PMXC_UFS_CAL_1_OFFSET 0xBE8
#define PMXC_SRAM_CSR 0x4C
#define PMXC_TX_RX_CFG_RDY 0x54
+
+#define PMC_GLOBAL_PGGS3_REG 0xF111005C
+#define PMC_GLOBAL_PGGS4_REG 0xF1110060
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 0887de4333b..074e5c06ec8 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1530,10 +1530,16 @@
#reset-cells = <1>;
};
+ resetc_fb: reset-ctl-fallback {
+ compatible = "sandbox,reset-ctl-fallback-only";
+ #reset-cells = <1>;
+ };
+
reset-ctl-test {
compatible = "sandbox,reset-ctl-test";
- resets = <&resetc 100>, <&resetc 2>, <&resetc 20>, <&resetc 40>;
- reset-names = "other", "test", "test2", "test3";
+ resets = <&resetc 100>, <&resetc 2>, <&resetc 20>, <&resetc 40>,
+ <&resetc_fb 5>;
+ reset-names = "other", "test", "test2", "test3", "fallback";
};
rng {
diff --git a/arch/sandbox/include/asm/reset.h b/arch/sandbox/include/asm/reset.h
index f0709b41c09..2890e0dc09b 100644
--- a/arch/sandbox/include/asm/reset.h
+++ b/arch/sandbox/include/asm/reset.h
@@ -10,6 +10,7 @@ struct udevice;
int sandbox_reset_query(struct udevice *dev, unsigned long id);
int sandbox_reset_is_requested(struct udevice *dev, unsigned long id);
+int sandbox_reset_get_count(struct udevice *dev, unsigned long id);
int sandbox_reset_test_get(struct udevice *dev);
int sandbox_reset_test_get_devm(struct udevice *dev);
@@ -19,6 +20,8 @@ int sandbox_reset_test_assert(struct udevice *dev);
int sandbox_reset_test_assert_bulk(struct udevice *dev);
int sandbox_reset_test_deassert(struct udevice *dev);
int sandbox_reset_test_deassert_bulk(struct udevice *dev);
+int sandbox_reset_test_reset(struct udevice *dev);
+int sandbox_reset_test_reset_bulk(struct udevice *dev);
int sandbox_reset_test_free(struct udevice *dev);
int sandbox_reset_test_release_bulk(struct udevice *dev);
diff --git a/board/amd/versal2/board.c b/board/amd/versal2/board.c
index 81daba1c5ef..ec28e60c410 100644
--- a/board/amd/versal2/board.c
+++ b/board/amd/versal2/board.c
@@ -1,17 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 - 2022, Xilinx, Inc.
- * Copyright (C) 2022 - 2025, Advanced Micro Devices, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
*
* Michal Simek <[email protected]>
*/
#include <cpu_func.h>
+#include <dfu.h>
+#include <env.h>
+#include <efi_loader.h>
#include <fdtdec.h>
+#include <fwu.h>
#include <init.h>
#include <env_internal.h>
#include <log.h>
#include <malloc.h>
+#include <memalign.h>
+#include <mmc.h>
+#include <mtd.h>
#include <time.h>
#include <asm/cache.h>
#include <asm/global_data.h>
@@ -26,6 +33,7 @@
#include "../../xilinx/common/board.h"
#include <linux/bitfield.h>
+#include <linux/sizes.h>
#include <debug_uart.h>
#include <generated/dt.h>
#include <linux/ioport.h>
@@ -348,6 +356,10 @@ int board_late_init(void)
int ret;
u32 multiboot;
+ if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) &&
+ !IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
+ configure_capsule_updates();
+
if (!(gd->flags & GD_FLG_ENV_DEFAULT)) {
debug("Saved variables - Skipping\n");
return 0;
@@ -476,3 +488,120 @@ enum env_location env_get_location(enum env_operation op, int prio)
}
}
#endif
+
+#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;
+
+ mtd_probe_devices();
+
+ mtd = get_mtd_device_nm("nor0");
+ if (!IS_ERR_OR_NULL(mtd)) {
+ list_for_each_entry(part, &mtd->partitions, node) {
+ debug("0x%012llx-0x%012llx : \"%s\"\n",
+ part->offset, part->offset + part->size,
+ part->name);
+
+ if (*base >= part->offset &&
+ *base < part->offset + part->size) {
+ debug("Found my partition: %d/%s\n",
+ part->index, part->name);
+ *base = part->offset;
+ *size = part->size;
+ break;
+ }
+ }
+ }
+}
+
+void configure_capsule_updates(void)
+{
+ int bootseq = 0, len = 0;
+ u32 multiboot = versal2_multi_boot();
+ u32 bootmode = versal2_get_bootmode();
+
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
+
+ memset(buf, 0, DFU_ALT_BUF_LEN);
+
+ multiboot = env_get_hex("multiboot", multiboot);
+
+ switch (bootmode) {
+ case EMMC_MODE:
+ case SD_MODE:
+ case SD1_LSHFT_MODE:
+ case SD_MODE1:
+ bootseq = mmc_get_env_dev();
+
+ len += snprintf(buf + len, DFU_ALT_BUF_LEN, "mmc %d=boot",
+ bootseq);
+
+ if (multiboot)
+ len += snprintf(buf + len, DFU_ALT_BUF_LEN,
+ "%04d", multiboot);
+
+ len += snprintf(buf + len, DFU_ALT_BUF_LEN, ".bin fat %d 1",
+ bootseq);
+ break;
+ case QSPI_MODE_24BIT:
+ case QSPI_MODE_32BIT:
+ case OSPI_MODE:
+ {
+ u32 base = multiboot * SZ_32K;
+ u32 size = 0x1500000;
+ u32 limit = size;
+
+ mtd_found_part(&base, &limit);
+
+ len += snprintf(buf + len, DFU_ALT_BUF_LEN,
+ "sf 0:0=boot.bin raw 0x%x 0x%x",
+ base, limit);
+ }
+ break;
+ default:
+ return;
+ }
+
+ 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
diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c
index 89562ef77fc..52a2e8767d8 100644
--- a/board/xilinx/common/board.c
+++ b/board/xilinx/common/board.c
@@ -14,8 +14,12 @@
#include <init.h>
#include <jffs2/load_kernel.h>
#include <log.h>
+#include <asm/io.h>
#include <asm/global_data.h>
#include <asm/sections.h>
+#if defined(CONFIG_ARCH_VERSAL) || defined(CONFIG_ARCH_VERSAL2)
+#include <asm/arch/hardware.h>
+#endif
#include <dm/uclass.h>
#include <i2c.h>
#include <linux/sizes.h>
@@ -30,6 +34,8 @@
#include <rng.h>
#include <slre.h>
#include <soc.h>
+#include <zynqmp_firmware.h>
+#include <linux/bitfield.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <u-boot/uuid.h>
@@ -718,7 +724,74 @@ phys_addr_t board_get_usable_ram_top(phys_size_t total_size)
return reg + size;
}
+#endif
+
+#if defined(CONFIG_FWU_MULTI_BANK_UPDATE)
+
+#if defined(CONFIG_ARCH_VERSAL) || defined(CONFIG_ARCH_VERSAL2)
+/*
+ * The Versal and Versal Gen 2 PMC Global pggs4 register contains below
+ * information in each byte as:
+ *
+ * Byte[3]: Magic number
+ * Byte[2]: Boot counter value
+ * Byte[1]: Boot partition value - boot index
+ * Byte[0]: Rollback counter value
+ */
+
+#define MAGIC_NUM 0x1D
+#define MAGIC_MASK GENMASK(31, 24)
+#define BOOTINDEX_MASK GENMASK(15, 8)
+
+static int plat_get_boot_index(void)
+{
+ u32 val;
+
+ if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE))
+ val = zynqmp_pm_get_pmc_global_pggs_reg(PMC_GLOBAL_PGGS4_REG);
+ else
+ val = readl(PMC_GLOBAL_PGGS4_REG);
+
+ if (FIELD_GET(MAGIC_MASK, val) != MAGIC_NUM) {
+ log_err("FWU requires PMC magic number 0x%x\n", MAGIC_NUM);
+ return -EINVAL;
+ }
+
+ return FIELD_GET(BOOTINDEX_MASK, val);
+}
+#endif
+
+int fwu_plat_get_alt_num(struct udevice __always_unused *dev,
+ efi_guid_t *image_id, u8 *alt_num)
+{
+ int ret;
+
+ ret = fwu_mtd_get_alt_num(image_id, alt_num, "nor0");
+ debug("%s: return %d\n", __func__, ret);
+
+ return ret;
+}
+void fwu_plat_get_bootidx(uint *boot_idx)
+{
+ int ret;
+ u32 active_idx;
+
+ ret = fwu_get_active_index(&active_idx);
+ if (ret < 0)
+ printf("%s: failed to read active index\n", __func__);
+
+ ret = plat_get_boot_index();
+ if (ret < 0) {
+ *boot_idx = 0;
+ printf("%s: failed and setup boot index to 0\n", __func__);
+ } else {
+ *boot_idx = ret;
+ }
+
+ debug("%s: boot_idx: %d, active_idx: %d\n",
+ __func__, *boot_idx, active_idx);
+}
#endif
#if IS_ENABLED(CONFIG_BOARD_RNG_SEED)
diff --git a/board/xilinx/versal/board.c b/board/xilinx/versal/board.c
index 9371c30ea27..8666f2ceff4 100644
--- a/board/xilinx/versal/board.c
+++ b/board/xilinx/versal/board.c
@@ -10,6 +10,7 @@
#include <env.h>
#include <efi_loader.h>
#include <fdtdec.h>
+#include <fwu.h>
#include <init.h>
#include <env_internal.h>
#include <log.h>
@@ -293,7 +294,8 @@ int board_late_init(void)
{
int ret;
- if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT))
+ if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) &&
+ !IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE))
configure_capsule_updates();
if (!(gd->flags & GD_FLG_ENV_DEFAULT)) {
@@ -374,6 +376,8 @@ 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;
@@ -450,3 +454,39 @@ 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
diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c
index a1d8ae26673..eb41f84c198 100644
--- a/board/xilinx/zynqmp/zynqmp.c
+++ b/board/xilinx/zynqmp/zynqmp.c
@@ -541,10 +541,6 @@ int board_late_init(void)
{
int ret, multiboot;
-#if defined(CONFIG_USB_ETHER) && !defined(CONFIG_USB_GADGET_DOWNLOAD)
- usb_ether_init();
-#endif
-
if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT))
configure_capsule_updates();
diff --git a/configs/amd_versal2_virt_defconfig b/configs/amd_versal2_virt_defconfig
index 61e3671ea4f..00ccc81a83b 100644
--- a/configs/amd_versal2_virt_defconfig
+++ b/configs/amd_versal2_virt_defconfig
@@ -1,10 +1,11 @@
CONFIG_ARM=y
CONFIG_COUNTER_FREQUENCY=100000000
CONFIG_POSITION_INDEPENDENT=y
-CONFIG_SYS_INIT_SP_BSS_OFFSET=0x180000
+CONFIG_SYS_INIT_SP_BSS_OFFSET=0x200000
CONFIG_ARCH_VERSAL2=y
CONFIG_TEXT_BASE=0x40000000
-CONFIG_SYS_MALLOC_F_LEN=0x100000
+CONFIG_SYS_MALLOC_LEN=0x4000000
+CONFIG_SYS_MALLOC_F_LEN=0x180000
CONFIG_NR_DRAM_BANKS=36
CONFIG_DEFAULT_DEVICE_TREE="amd-versal2-virt"
CONFIG_OF_LIBFDT_OVERLAY=y
@@ -17,6 +18,10 @@ CONFIG_PCI=y
CONFIG_SYS_MEMTEST_START=0x00000000
CONFIG_SYS_MEMTEST_END=0x00001000
CONFIG_REMAKE_ELF=y
+CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
+CONFIG_EFI_CAPSULE_ON_DISK=y
+CONFIG_EFI_CAPSULE_ON_DISK_EARLY=y
+CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
CONFIG_EFI_HTTP_BOOT=y
CONFIG_FIT=y
CONFIG_FIT_VERBOSE=y
@@ -81,7 +86,12 @@ CONFIG_SIMPLE_PM_BUS=y
CONFIG_CLK_CCF=y
CONFIG_CLK_SCMI=y
CONFIG_CLK_VERSAL=y
+CONFIG_DFU_TIMEOUT=y
+CONFIG_DFU_MMC=y
+CONFIG_DFU_MTD=y
CONFIG_DFU_RAM=y
+CONFIG_DFU_SF=y
+CONFIG_SYS_DFU_DATA_BUF_SIZE=0x1800000
CONFIG_ARM_FFA_TRANSPORT=y
CONFIG_SCMI_FIRMWARE=y
CONFIG_FPGA_XILINX=y
@@ -106,6 +116,7 @@ CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ZYNQ=y
CONFIG_ZYNQ_SDHCI_MIN_FREQ=100000
CONFIG_MTD=y
+CONFIG_DM_MTD=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_GIGADEVICE=y
CONFIG_SPI_FLASH_ISSI=y
diff --git a/configs/xilinx_mbv32_defconfig b/configs/xilinx_mbv32_defconfig
index 4ff3bd6b687..9d0111fe8e5 100644
--- a/configs/xilinx_mbv32_defconfig
+++ b/configs/xilinx_mbv32_defconfig
@@ -18,6 +18,8 @@ CONFIG_BOOT_SCRIPT_OFFSET=0x0
CONFIG_TARGET_XILINX_MBV=y
# CONFIG_RISCV_ISA_F is not set
# CONFIG_SPL_SMP is not set
+# CONFIG_AVAILABLE_HARTS is not set
+CONFIG_SPL_OPTIMIZE_INLINING=y
CONFIG_REMAKE_ELF=y
# CONFIG_EFI_LOADER is not set
CONFIG_FIT=y
diff --git a/configs/xilinx_versal_virt_defconfig b/configs/xilinx_versal_virt_defconfig
index 7c9b1577875..a94468d9b9b 100644
--- a/configs/xilinx_versal_virt_defconfig
+++ b/configs/xilinx_versal_virt_defconfig
@@ -80,6 +80,7 @@ CONFIG_SIMPLE_PM_BUS=y
CONFIG_CLK_VERSAL=y
CONFIG_DFU_TIMEOUT=y
CONFIG_DFU_MMC=y
+CONFIG_DFU_MTD=y
CONFIG_DFU_RAM=y
CONFIG_DFU_SF=y
CONFIG_SYS_DFU_DATA_BUF_SIZE=0x1800000
@@ -106,6 +107,7 @@ CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ZYNQ=y
CONFIG_ZYNQ_SDHCI_MIN_FREQ=100000
CONFIG_MTD=y
+CONFIG_DM_MTD=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_SOFT_RESET=y
CONFIG_SPI_FLASH_SOFT_RESET_ON_BOOT=y
diff --git a/configs/xilinx_zynqmp_virt_defconfig b/configs/xilinx_zynqmp_virt_defconfig
index a13ae0c857b..c5472112b91 100644
--- a/configs/xilinx_zynqmp_virt_defconfig
+++ b/configs/xilinx_zynqmp_virt_defconfig
@@ -222,6 +222,7 @@ CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_MANUFACTURER="Xilinx"
CONFIG_USB_GADGET_VENDOR_NUM=0x03FD
CONFIG_USB_GADGET_PRODUCT_NUM=0x0300
+CONFIG_USB_GADGET_DOWNLOAD=y
CONFIG_USB_ETHER=y
CONFIG_USB_ETH_CDC=y
CONFIG_VIDEO=y
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index fb583580ebe..ea14ed4ef95 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -3,7 +3,7 @@
* Xilinx Zynq MPSoC Firmware driver
*
* Copyright (C) 2018-2019 Xilinx, Inc.
- * Copyright (C) 2022 - 2025, Advanced Micro Devices, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
*/
#include <asm/arch/hardware.h>
@@ -197,6 +197,58 @@ int zynqmp_pm_ufs_cal_reg(u32 *value)
*value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET);
return 0;
}
+#endif /* CONFIG_ARCH_VERSAL2 */
+
+#if defined(CONFIG_ARCH_VERSAL) || defined(CONFIG_ARCH_VERSAL2)
+u32 zynqmp_pm_get_pmc_global_pggs_reg(u32 reg_addr)
+{
+ int ret;
+ u32 value = 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ if (reg_addr == PMC_GLOBAL_PGGS3_REG) {
+ value = 0;
+ } else if (reg_addr == PMC_GLOBAL_PGGS4_REG) {
+ value = 1;
+ } else {
+ printf("%s: not supported pggs register 0x%x\n",
+ __func__, reg_addr);
+ return 0;
+ }
+
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_PGGS);
+ if (ret) {
+ 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;
+ }
+
+ /* find node ID from the pggs3 offset */
+ value = PM_REG_PGGS3 + value;
+
+ ret = xilinx_pm_request(PM_IOCTL, value,
+ IOCTL_READ_REG, 0, 0, 0, 0,
+ ret_payload);
+ if (ret) {
+ printf("%s: node 0x%x get pggs register failed\n",
+ __func__, value);
+ return 0;
+ }
+ } else {
+ ret = xilinx_pm_request(PM_IOCTL, PMC_GLOBAL_PGGS3_REG_NODE,
+ IOCTL_READ_PGGS, value, 0, 0, 0,
+ ret_payload);
+ if (ret) {
+ printf("%s: node 0x%x get pggs register failed\n",
+ __func__, PMC_GLOBAL_PGGS3_REG_NODE);
+ return 0;
+ }
+ }
+
+ return ret_payload[1];
+}
#endif
int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value)
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c
index fe4cebf54f1..c199e3e5da7 100644
--- a/drivers/reset/reset-uclass.c
+++ b/drivers/reset/reset-uclass.c
@@ -13,6 +13,7 @@
#include <reset-uclass.h>
#include <dm/devres.h>
#include <dm/lists.h>
+#include <linux/delay.h>
static inline struct reset_ops *reset_dev_ops(struct udevice *dev)
{
@@ -225,6 +226,39 @@ int reset_deassert_bulk(struct reset_ctl_bulk *bulk)
return 0;
}
+int reset_reset(struct reset_ctl *reset_ctl, ulong delay_us)
+{
+ struct reset_ops *ops = reset_dev_ops(reset_ctl->dev);
+ int ret;
+
+ debug("%s(reset_ctl=%p, delay_us=%lu)\n", __func__, reset_ctl,
+ delay_us);
+
+ if (ops->rst_reset)
+ return ops->rst_reset(reset_ctl, delay_us);
+
+ ret = reset_assert(reset_ctl);
+ if (ret < 0)
+ return ret;
+
+ udelay(delay_us);
+
+ return reset_deassert(reset_ctl);
+}
+
+int reset_reset_bulk(struct reset_ctl_bulk *bulk, ulong delay_us)
+{
+ int i, ret;
+
+ for (i = 0; i < bulk->count; i++) {
+ ret = reset_reset(&bulk->resets[i], delay_us);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
int reset_status(struct reset_ctl *reset_ctl)
{
struct reset_ops *ops = reset_dev_ops(reset_ctl->dev);
diff --git a/drivers/reset/reset-zynqmp.c b/drivers/reset/reset-zynqmp.c
index d04e8eef3bb..2b58f3a75b4 100644
--- a/drivers/reset/reset-zynqmp.c
+++ b/drivers/reset/reset-zynqmp.c
@@ -45,6 +45,16 @@ static int zynqmp_reset_deassert(struct reset_ctl *rst)
PM_RESET_ACTION_RELEASE);
}
+static int zynqmp_reset_reset(struct reset_ctl *rst, ulong delay_us)
+{
+ struct zynqmp_reset_priv *priv = dev_get_priv(rst->dev);
+
+ dev_dbg(rst->dev, "%s(rst=%p) (id=%lu)\n", __func__, rst, rst->id);
+
+ return zynqmp_pm_reset_assert(priv->reset_id + rst->id,
+ PM_RESET_ACTION_PULSE);
+}
+
static int zynqmp_reset_request(struct reset_ctl *rst)
{
struct zynqmp_reset_priv *priv = dev_get_priv(rst->dev);
@@ -74,6 +84,7 @@ const struct reset_ops zynqmp_reset_ops = {
.request = zynqmp_reset_request,
.rst_assert = zynqmp_reset_assert,
.rst_deassert = zynqmp_reset_deassert,
+ .rst_reset = zynqmp_reset_reset,
};
static const struct udevice_id zynqmp_reset_ids[] = {
diff --git a/drivers/reset/sandbox-reset-test.c b/drivers/reset/sandbox-reset-test.c
index dfacb764bc7..64c205596c5 100644
--- a/drivers/reset/sandbox-reset-test.c
+++ b/drivers/reset/sandbox-reset-test.c
@@ -96,6 +96,20 @@ int sandbox_reset_test_deassert_bulk(struct udevice *dev)
return reset_deassert_bulk(sbrt->bulkp);
}
+int sandbox_reset_test_reset(struct udevice *dev)
+{
+ struct sandbox_reset_test *sbrt = dev_get_priv(dev);
+
+ return reset_reset(sbrt->ctlp, 0);
+}
+
+int sandbox_reset_test_reset_bulk(struct udevice *dev)
+{
+ struct sandbox_reset_test *sbrt = dev_get_priv(dev);
+
+ return reset_reset_bulk(sbrt->bulkp, 0);
+}
+
int sandbox_reset_test_free(struct udevice *dev)
{
struct sandbox_reset_test *sbrt = dev_get_priv(dev);
diff --git a/drivers/reset/sandbox-reset.c b/drivers/reset/sandbox-reset.c
index 1c0ea7390df..12812f0f340 100644
--- a/drivers/reset/sandbox-reset.c
+++ b/drivers/reset/sandbox-reset.c
@@ -9,12 +9,14 @@
#include <reset-uclass.h>
#include <asm/io.h>
#include <asm/reset.h>
+#include <linux/delay.h>
#define SANDBOX_RESET_SIGNALS 101
struct sandbox_reset_signal {
bool asserted;
bool requested;
+ int reset_count;
};
struct sandbox_reset {
@@ -31,6 +33,7 @@ static int sandbox_reset_request(struct reset_ctl *reset_ctl)
return -EINVAL;
sbr->signals[reset_ctl->id].requested = true;
+ sbr->signals[reset_ctl->id].reset_count = 0;
return 0;
}
@@ -66,6 +69,21 @@ static int sandbox_reset_deassert(struct reset_ctl *reset_ctl)
return 0;
}
+static int sandbox_reset_reset(struct reset_ctl *reset_ctl, ulong delay_us)
+{
+ struct sandbox_reset *sbr = dev_get_priv(reset_ctl->dev);
+
+ debug("%s(reset_ctl=%p, delay_us=%lu)\n", __func__, reset_ctl,
+ delay_us);
+
+ sbr->signals[reset_ctl->id].asserted = true;
+ udelay(delay_us);
+ sbr->signals[reset_ctl->id].asserted = false;
+ sbr->signals[reset_ctl->id].reset_count++;
+
+ return 0;
+}
+
static int sandbox_reset_bind(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
@@ -90,6 +108,7 @@ static const struct reset_ops sandbox_reset_reset_ops = {
.rfree = sandbox_reset_free,
.rst_assert = sandbox_reset_assert,
.rst_deassert = sandbox_reset_deassert,
+ .rst_reset = sandbox_reset_reset,
};
U_BOOT_DRIVER(sandbox_reset) = {
@@ -102,6 +121,33 @@ U_BOOT_DRIVER(sandbox_reset) = {
.ops = &sandbox_reset_reset_ops,
};
+/*
+ * Second sandbox reset controller for tests: same assert/deassert
+ * behaviour as sandbox_reset, but no rst_reset so reset_reset() uses
+ * the core assert / udelay / deassert fallback (reset_count never bumps).
+ */
+static const struct udevice_id sandbox_reset_fallback_ids[] = {
+ { .compatible = "sandbox,reset-ctl-fallback-only" },
+ { }
+};
+
+static const struct reset_ops sandbox_reset_fallback_reset_ops = {
+ .request = sandbox_reset_request,
+ .rfree = sandbox_reset_free,
+ .rst_assert = sandbox_reset_assert,
+ .rst_deassert = sandbox_reset_deassert,
+};
+
+U_BOOT_DRIVER(sandbox_reset_fallback) = {
+ .name = "sandbox_reset_fallback",
+ .id = UCLASS_RESET,
+ .of_match = sandbox_reset_fallback_ids,
+ .bind = sandbox_reset_bind,
+ .probe = sandbox_reset_probe,
+ .priv_auto = sizeof(struct sandbox_reset),
+ .ops = &sandbox_reset_fallback_reset_ops,
+};
+
int sandbox_reset_query(struct udevice *dev, unsigned long id)
{
struct sandbox_reset *sbr = dev_get_priv(dev);
@@ -125,3 +171,15 @@ int sandbox_reset_is_requested(struct udevice *dev, unsigned long id)
return sbr->signals[id].requested;
}
+
+int sandbox_reset_get_count(struct udevice *dev, unsigned long id)
+{
+ struct sandbox_reset *sbr = dev_get_priv(dev);
+
+ debug("%s(dev=%p, id=%ld)\n", __func__, dev, id);
+
+ if (id >= SANDBOX_RESET_SIGNALS)
+ return -EINVAL;
+
+ return sbr->signals[id].reset_count;
+}
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 2a4a49c5f1c..984d4a39ded 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -31,6 +31,8 @@
#define CQSPI_DISABLE_STIG_MODE BIT(0)
#define CQSPI_DMA_MODE BIT(1)
+#define CQSPI_RESET_DELAY_US 10
+
__weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
@@ -256,19 +258,9 @@ static int cadence_spi_probe(struct udevice *bus)
priv->resets = devm_reset_bulk_get_optional(bus);
if (priv->resets) {
- /* Assert all OSPI reset lines */
- ret = reset_assert_bulk(priv->resets);
- if (ret) {
- dev_err(bus, "Failed to assert OSPI reset: %d\n", ret);
- return ret;
- }
-
- udelay(10);
-
- /* Deassert all OSPI reset lines */
- ret = reset_deassert_bulk(priv->resets);
+ ret = reset_reset_bulk(priv->resets, CQSPI_RESET_DELAY_US);
if (ret) {
- dev_err(bus, "Failed to deassert OSPI reset: %d\n", ret);
+ dev_err(bus, "Failed to reset OSPI: %d\n", ret);
return ret;
}
}
diff --git a/include/configs/amd_versal2.h b/include/configs/amd_versal2.h
index fccc786219f..a07e12bd146 100644
--- a/include/configs/amd_versal2.h
+++ b/include/configs/amd_versal2.h
@@ -23,6 +23,11 @@
#define CFG_SYS_BAUDRATE_TABLE \
{ 4800, 9600, 19200, 38400, 57600, 115200 }
+/* GUID for capsule updatable firmware image */
+#define XILINX_BOOT_IMAGE_GUID \
+ EFI_GUID(0xed9e7fcf, 0x47b3, 0x40cd, 0xb6, 0xe3, \
+ 0x56, 0x5f, 0x14, 0x67, 0x6d, 0x82)
+
#if defined(CONFIG_CMD_DFU)
#define DFU_DEFAULT_POLL_TIMEOUT 300
#define DFU_ALT_INFO_RAM \
diff --git a/include/reset-uclass.h b/include/reset-uclass.h
index 9a0696dd1e3..7af090b60b5 100644
--- a/include/reset-uclass.h
+++ b/include/reset-uclass.h
@@ -77,6 +77,25 @@ struct reset_ops {
*/
int (*rst_deassert)(struct reset_ctl *reset_ctl);
/**
+ * rst_reset - Reset a HW module.
+ *
+ * This optional function triggers a reset pulse on the reset line.
+ * If not implemented, reset_reset() falls back to rst_assert(),
+ * udelay(@delay_us), then rst_deassert(); that delay is therefore
+ * observed only on the fallback path.
+ *
+ * When rst_reset is provided, @delay_us is controller-specific: the
+ * implementation should honour it if the hardware needs a minimum
+ * assertion time before release. It may ignore @delay_us when the
+ * pulse shape is fixed elsewhere (for example a firmware pulse).
+ *
+ * @reset_ctl: The reset signal to pulse.
+ * @delay_us: Minimum delay in microseconds between assert and
+ * deassert where applicable; see above.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*rst_reset)(struct reset_ctl *reset_ctl, ulong delay_us);
+ /**
* rst_status - Check reset signal status.
*
* @reset_ctl: The reset signal to check.
diff --git a/include/reset.h b/include/reset.h
index 036a786d2ac..58574b983f6 100644
--- a/include/reset.h
+++ b/include/reset.h
@@ -321,6 +321,45 @@ int reset_deassert(struct reset_ctl *reset_ctl);
int reset_deassert_bulk(struct reset_ctl_bulk *bulk);
/**
+ * reset_reset - Reset a HW module by asserting and deasserting a reset signal.
+ *
+ * This function will assert and then deassert the specified reset signal,
+ * thus resetting the affected HW module. This is a convenience function
+ * that combines reset_assert() and reset_deassert().
+ *
+ * If the controller implements struct reset_ops.rst_reset, that callback
+ * is used and @delay_us is interpreted as documented there. Otherwise the
+ * core performs reset_assert(), udelay(@delay_us), then reset_deassert().
+ *
+ * @reset_ctl: A reset control struct that was previously successfully
+ * requested by reset_get_by_*().
+ * @delay_us: Delay in microseconds between assert and deassert on the
+ * fallback path; meaning is driver-specific when rst_reset is used.
+ * Use 0 for no delay on the fallback path.
+ * Return: 0 if OK, or a negative error code.
+ */
+int reset_reset(struct reset_ctl *reset_ctl, ulong delay_us);
+
+/**
+ * reset_reset_bulk - Reset all HW modules in a reset control bulk struct.
+ *
+ * This calls reset_reset() on each entry in order. Each line therefore
+ * completes its own assert/delay/deassert (or controller rst_reset) before
+ * the next entry starts. That matches Linux reset_control_bulk_reset().
+ *
+ * When several lines must stay asserted together for @delay_us (typical
+ * multi-reset controllers), use reset_assert_bulk(), udelay(@delay_us),
+ * and reset_deassert_bulk() instead.
+ *
+ * @bulk: A reset control bulk struct that was previously successfully
+ * requested by reset_get_bulk().
+ * @delay_us: Delay in microseconds passed to each reset_reset(); see
+ * reset_reset() and struct reset_ops.rst_reset.
+ * Return: 0 if OK, or a negative error code.
+ */
+int reset_reset_bulk(struct reset_ctl_bulk *bulk, ulong delay_us);
+
+/**
* rst_status - Check reset signal status.
*
* @reset_ctl: The reset signal to check.
@@ -443,6 +482,16 @@ static inline int reset_deassert_bulk(struct reset_ctl_bulk *bulk)
return 0;
}
+static inline int reset_reset(struct reset_ctl *reset_ctl, ulong delay_us)
+{
+ return -ENOSYS;
+}
+
+static inline int reset_reset_bulk(struct reset_ctl_bulk *bulk, ulong delay_us)
+{
+ return -ENOSYS;
+}
+
static inline int reset_status(struct reset_ctl *reset_ctl)
{
return -ENOTSUPP;
diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h
index f5e72625e53..0e545e3db1b 100644
--- a/include/zynqmp_firmware.h
+++ b/include/zynqmp_firmware.h
@@ -470,6 +470,7 @@ 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);
/* Type of Config Object */
#define PM_CONFIG_OBJECT_TYPE_BASE 0x1U
@@ -534,4 +535,7 @@ extern smc_call_handler_t __data smc_call_handler;
#define PM_DEV_OSPI (0x1822402aU)
+#define PM_REG_PGGS3 0x30004003
+#define PMC_GLOBAL_PGGS3_REG_NODE 0x1824C005
+
#endif /* _ZYNQMP_FIRMWARE_H_ */
diff --git a/test/dm/reset.c b/test/dm/reset.c
index dceb6a1dad3..91fa7ff723b 100644
--- a/test/dm/reset.c
+++ b/test/dm/reset.c
@@ -19,6 +19,9 @@
/* This is the other reset phandle specifier handled by bulk */
#define OTHER_RESET_ID 2
+/* Line on reset-ctl-fallback (sandbox,reset-ctl-fallback-only); see test.dts */
+#define FALLBACK_RESET_ID 5
+
/* Base test of the reset uclass */
static int dm_test_reset_base(struct unit_test_state *uts)
{
@@ -120,6 +123,110 @@ static int dm_test_reset_devm(struct unit_test_state *uts)
}
DM_TEST(dm_test_reset_devm, UTF_SCAN_FDT);
+static int dm_test_reset_reset(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+ &dev_reset));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(sandbox_reset_test_get(dev_test));
+
+ /* Verify reset_count starts at 0 */
+ ut_asserteq(0, sandbox_reset_get_count(dev_reset, TEST_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_assert(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_reset(dev_test));
+
+ /* Verify reset was pulsed (count incremented) */
+ ut_asserteq(1, sandbox_reset_get_count(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_free(dev_test));
+
+ return 0;
+}
+DM_TEST(dm_test_reset_reset, UTF_SCAN_FDT);
+
+/*
+ * reset_reset() fallback path: controller has no rst_reset op, so the
+ * core does assert -> udelay -> deassert. rst_reset-only accounting
+ * (reset_count) stays zero. Leave the line asserted before reset_reset()
+ * so we verify the fallback actually pulses it back to deasserted.
+ */
+static int dm_test_reset_reset_fallback_path(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset_fb;
+ struct udevice *dev_test;
+ struct reset_ctl ctl;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl-fallback",
+ &dev_reset_fb));
+ ut_asserteq(0, sandbox_reset_query(dev_reset_fb, FALLBACK_RESET_ID));
+ ut_asserteq(0, sandbox_reset_get_count(dev_reset_fb, FALLBACK_RESET_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(reset_get_by_name(dev_test, "fallback", &ctl));
+ ut_asserteq_ptr(ctl.dev, dev_reset_fb);
+ ut_asserteq(FALLBACK_RESET_ID, ctl.id);
+
+ ut_assertok(reset_assert(&ctl));
+ ut_asserteq(1, sandbox_reset_query(dev_reset_fb, FALLBACK_RESET_ID));
+ ut_asserteq(0, sandbox_reset_get_count(dev_reset_fb, FALLBACK_RESET_ID));
+
+ ut_assertok(reset_reset(&ctl, 1));
+ ut_asserteq(0, sandbox_reset_get_count(dev_reset_fb, FALLBACK_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset_fb, FALLBACK_RESET_ID));
+
+ ut_assertok(reset_free(&ctl));
+
+ return 0;
+}
+DM_TEST(dm_test_reset_reset_fallback_path, UTF_SCAN_FDT);
+
+static int dm_test_reset_reset_bulk(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+ &dev_reset));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(sandbox_reset_test_get_bulk(dev_test));
+
+ /* Verify reset_count starts at 0 */
+ ut_asserteq(0, sandbox_reset_get_count(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_get_count(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_assert_bulk(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_reset_bulk(dev_test));
+
+ /* Verify resets were pulsed (counts incremented) */
+ ut_asserteq(1, sandbox_reset_get_count(dev_reset, TEST_RESET_ID));
+ ut_asserteq(1, sandbox_reset_get_count(dev_reset, OTHER_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_release_bulk(dev_test));
+
+ return 0;
+}
+DM_TEST(dm_test_reset_reset_bulk, UTF_SCAN_FDT);
+
static int dm_test_reset_bulk(struct unit_test_state *uts)
{
struct udevice *dev_reset;