[PATCH 1/8] arm: socfpga: mailbox: Add RSU mailbox command support
dinesh.maniyam at altera.com
dinesh.maniyam at altera.com
Tue Jun 30 08:55:17 CEST 2026
From: Dinesh Maniyam <dinesh.maniyam at altera.com>
Add the Secure Device Manager (SDM) mailbox commands used by the Remote
System Update (RSU) feature on the Stratix 10 / Agilex family of SoCs:
GET_SUBPARTITION_TABLE, RSU_STATUS, RSU_UPDATE and QSPI_GET_DEVICE_INFO,
together with their PSCI secure-world (_psci) variants. When CADENCE_QSPI
is disabled the helpers return MBOX_FUNC_NOT_SUPPORTED.
Also add the RSU public headers (rsu.h, rsu_ll.h, rsu_misc.h,
rsu_flash_if.h, rsu_s10.h, rsu_smc.h, smc_s10.h, socfpga_rsu_dm.h and
rsu_console.h) describing the RSU session interface, the on-flash
sub-partition table (SPT) / configuration pointer block (CPB) layout and
the rsu_status_info structure the mailbox status command fills in.
Signed-off-by: Dinesh Maniyam <dinesh.maniyam at altera.com>
---
.../mach-socfpga/include/mach/mailbox_s10.h | 13 +
arch/arm/mach-socfpga/include/mach/rsu.h | 419 ++++++++++++++++++
.../mach-socfpga/include/mach/rsu_flash_if.h | 96 ++++
arch/arm/mach-socfpga/include/mach/rsu_ll.h | 94 ++++
arch/arm/mach-socfpga/include/mach/rsu_misc.h | 65 +++
arch/arm/mach-socfpga/include/mach/rsu_s10.h | 46 ++
arch/arm/mach-socfpga/include/mach/rsu_smc.h | 14 +
arch/arm/mach-socfpga/include/mach/smc_s10.h | 37 ++
.../include/mach/socfpga_rsu_dm.h | 18 +
arch/arm/mach-socfpga/mailbox_s10.c | 141 ++++++
include/rsu_console.h | 14 +
11 files changed, 957 insertions(+)
create mode 100644 arch/arm/mach-socfpga/include/mach/rsu.h
create mode 100644 arch/arm/mach-socfpga/include/mach/rsu_flash_if.h
create mode 100644 arch/arm/mach-socfpga/include/mach/rsu_ll.h
create mode 100644 arch/arm/mach-socfpga/include/mach/rsu_misc.h
create mode 100644 arch/arm/mach-socfpga/include/mach/rsu_s10.h
create mode 100644 arch/arm/mach-socfpga/include/mach/rsu_smc.h
create mode 100644 arch/arm/mach-socfpga/include/mach/smc_s10.h
create mode 100644 arch/arm/mach-socfpga/include/mach/socfpga_rsu_dm.h
create mode 100644 include/rsu_console.h
diff --git a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h
index 1a461de4819..5cb6ae4bcaf 100644
--- a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h
+++ b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h
@@ -92,6 +92,8 @@ enum ALT_SDM_MBOX_RESP_CODE {
MBOX_RESP_TIMEOUT = 0xB,
/* HW (i.e. QSPI) is not ready (initialized or configured) */
MBOX_RESP_HW_NOT_RDY = 0xC,
+ /* Mailbox function not supported in this build (e.g. no QSPI) */
+ MBOX_FUNC_NOT_SUPPORTED = 0xF,
/* Invalid license for IID registration */
MBOX_RESP_PUF_ACCCES_FAILED = 0x80,
MBOX_PUF_ENROLL_DISABLE = 0x81,
@@ -128,7 +130,11 @@ enum ALT_SDM_MBOX_RESP_CODE {
#define MBOX_QSPI_CLOSE 51
#define MBOX_QSPI_DIRECT 59
#define MBOX_REBOOT_HPS 71
+#define MBOX_GET_SUBPARTITION_TABLE 90
+#define MBOX_RSU_STATUS 91
+#define MBOX_RSU_UPDATE 92
#define MBOX_HPS_STAGE_NOTIFY 93
+#define MBOX_QSPI_GET_DEVICE_INFO 116 /* get QSPI size and erasesize */
/* Mailbox registers */
#define MBOX_CIN 0 /* command valid offset */
@@ -405,4 +411,11 @@ int mbox_reset_cold(void);
int mbox_hps_stage_notify(u32 execution_stage);
int mbox_get_fpga_config_status(u32 cmd);
int mbox_get_fpga_config_status_psci(u32 cmd);
+
+int mbox_qspi_get_device_info(u32 *resp_buf, u32 resp_buf_len);
+int mbox_rsu_get_spt_offset(u32 *resp_buf, u32 resp_buf_len);
+int mbox_rsu_status(u32 *resp_buf, u32 resp_buf_len);
+int mbox_rsu_status_psci(u32 *resp_buf, u32 resp_buf_len);
+int mbox_rsu_update(u32 *flash_offset);
+int mbox_rsu_update_psci(u32 *flash_offset);
#endif /* _MAILBOX_S10_H_ */
diff --git a/arch/arm/mach-socfpga/include/mach/rsu.h b/arch/arm/mach-socfpga/include/mach/rsu.h
new file mode 100644
index 00000000000..de4e47488bc
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu.h
@@ -0,0 +1,419 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ */
+
+#ifndef __RSU_H__
+#define __RSU_H__
+
+#include <asm/types.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+/* RSU Error Codes */
+#define EINTF 1
+#define ECFG 2
+#define ESLOTNUM 3
+#define EFORMAT 4
+#define EERASE 5
+#define EPROGRAM 6
+#define ECMP 7
+#define ESIZE 8
+#define ENAME 9
+#define EFILEIO 10
+#define ECALLBACK 11
+#define ELOWLEVEL 12
+#define EWRPROT 13
+#define EARGS 14
+#define ECORRUPTED_CPB 15
+#define ECORRUPTED_SPT 16
+
+#define STATE_DCIO_CORRUPTED 0xF004D00F
+#define STATE_CPB0_CORRUPTED 0xF004D010
+#define STATE_CPB0_CPB1_CORRUPTED 0xF004D011
+
+/* RSU Version Bitmasks */
+#define RSU_VERSION_CRT_IDX_MASK GENMASK(31, 28)
+#define RSU_VERSION_ERR_MASK GENMASK(27, 16)
+#define RSU_VERSION_DCMF_MASK GENMASK(7, 0)
+#define RSU_VERSION_ACMF_MASK GENMASK(15, 8)
+
+/* Macros for extracting RSU version fields */
+#define RSU_VERSION_CRT_DCMF_IDX(v) FIELD_GET(RSU_VERSION_CRT_IDX_MASK, (v))
+#define RSU_VERSION_ERROR_SOURCE(v) FIELD_GET(RSU_VERSION_ERR_MASK, (v))
+#define RSU_VERSION_ACMF_VERSION(v) FIELD_GET(RSU_VERSION_ACMF_MASK, (v))
+#define RSU_VERSION_DCMF_VERSION(v) FIELD_GET(RSU_VERSION_DCMF_MASK, (v))
+
+/* DCMF Version Bitmasks */
+#define DCMF_VERSION_MAJOR_MASK GENMASK(31, 24)
+#define DCMF_VERSION_MINOR_MASK GENMASK(23, 16)
+#define DCMF_VERSION_UPDATE_MASK GENMASK(15, 8)
+
+/* Macros for extracting DCMF version fields */
+#define DCMF_VERSION_MAJOR(v) FIELD_GET(DCMF_VERSION_MAJOR_MASK, (v))
+#define DCMF_VERSION_MINOR(v) FIELD_GET(DCMF_VERSION_MINOR_MASK, (v))
+#define DCMF_VERSION_UPDATE(v) FIELD_GET(DCMF_VERSION_UPDATE_MASK, (v))
+
+/**
+ * struct rsu_status_info - firmware status log info structure
+ * @current_image:address of image currently running in flash
+ * @fail_image: address of failed image in flash
+ * @state: the state of RSU system
+ * @version: the version number of RSU firmware
+ * @error_location: the error offset inside the failed image
+ * @error_details: error code
+ * @retry_counter: current image retry counter
+ *
+ * This structure is used to capture firmware status log information
+ */
+struct rsu_status_info {
+ u64 current_image;
+ u64 fail_image;
+ u32 state;
+ u32 version;
+ u32 error_location;
+ u32 error_details;
+ u32 retry_counter;
+};
+
+/**
+ * struct rsu_slot_info - slot information structure
+ * @name: a slot name
+ * @offset: a slot offset
+ * @size: the size of a slot
+ * @priority: the priority of a slot
+ *
+ * This structure is used to capture the slot information details
+ */
+struct rsu_slot_info {
+ char name[16];
+ u64 offset;
+ u32 size;
+ int priority;
+};
+
+/**
+ * rsu_init() - initialize flash driver, SPT and CPB data
+ * @filename: NULL for qspi
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_init(char *filename);
+
+/**
+ * rsu_exit() - free flash driver, clean SPT and CPB data
+ */
+void rsu_exit(void);
+
+/**
+ * rsu_slot_count() - get the number of slots defined
+ *
+ * Returns: the number of defined slots
+ */
+int rsu_slot_count(void);
+
+/**
+ * rsu_slot_by_name() - get slot number based on name
+ * @name: name of slot
+ *
+ * Return:slot number on success, or error code
+ */
+int rsu_slot_by_name(char *name);
+
+/**
+ * rsu_slot_get_info() - get the attributes of a slot
+ * @slot: slot number
+ * @info: pointer to info structure to be filled in
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_get_info(int slot, struct rsu_slot_info *info);
+
+/**
+ * rsu_slot_size() - get the size of a slot
+ * @slot: slot number
+ *
+ * Returns: the size of the slot in bytes, or error code
+ */
+int rsu_slot_size(int slot);
+
+/**
+ * rsu_slot_priority() - get the Decision CMF load priority of a slot
+ * @slot: slot number
+ *
+ * Priority of zero means the slot has no priority and is disabled.
+ * The slot with priority of one has the highest priority.
+ *
+ * Returns: the priority of the slot, or error code
+ */
+int rsu_slot_priority(int slot);
+
+/**
+ * rsu_slot_erase() - erase all data in a slot
+ * @slot: slot number
+ *
+ * Erase all data in a slot to prepare for programming. Remove the slot
+ * if it is in the CMF pointer block.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_erase(int slot);
+
+/**
+ * rsu_slot_program_buf() - program a slot from FPGA buffer data
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to program a slot using FPGA config data from a
+ * buffer and then enter the slot into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_program_factory_update_buf() - program a slot using factory update
+ * data from a buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer
+ *
+ * This function is used to program a slot using factory update data from a
+ * buffer and then enter the slot into CPB.
+ *
+ * Returns 0 on success, or error code
+ */
+int rsu_slot_program_factory_update_buf(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_program_buf_raw() - program a slot from raw buffer data
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to program a slot using raw data from a buffer,
+ * and then enter this slot into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf_raw(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_verify_buf() - verify FPGA config data in a slot against a
+ * buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_verify_buf_raw() - verify raw data in a slot against a buffer
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf_raw(int slot, void *buf, int size);
+
+/**
+ * rsu_slot_enable() - enable the selected slot
+ * @slot: slot number
+ *
+ * Set the selected slot as the highest priority. It will be the first
+ * slot to be tried after a power-on reset.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_enable(int slot);
+
+/**
+ * rsu_slot_disable() - disable the selected slot
+ * @slot: slot number
+ *
+ * Remove the selected slot from the priority scheme, but don't erase the
+ * slot data so that it can be re-enabled.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_disable(int slot);
+
+/**
+ * rsu_slot_load() - load the selected slot
+ * @slot: slot number
+ *
+ * This function is used to request the selected slot to be loaded
+ * immediately. On success, after a small delay, the system is rebooted.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load(int slot);
+
+/**
+ * rsu_slot_load_factory() - load the factory image
+ *
+ * This function is used to request the factory image to be loaded
+ * immediately. On success, after a small delay, the system is rebooted.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load_factory(void);
+
+/**
+ * rsu_slot_rename() - Rename the selected slot.
+ * @slot: slot number
+ * @name: new name for slot
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_rename(int slot, char *name);
+
+/**
+ * rsu_slot_delete() - Delete the selected slot.
+ * @slot: slot number
+ *
+ * Returns 0 on success, or Error Code
+ */
+int rsu_slot_delete(int slot);
+
+/**
+ * rsu_slot_create() - Create a new slot.
+ * @name: slot name
+ * @address: slot start address
+ * @size: slot size
+ *
+ * Returns 0 on success, or Error Code
+ */
+int rsu_slot_create(char *name, u64 address, unsigned int size);
+
+/**
+ * rsu_status_log() - Copy firmware status log to info struct
+ * @info: pointer to info struct to fill in
+ *
+ * Return 0 on success, or error code
+ */
+int rsu_status_log(struct rsu_status_info *info);
+
+/**
+ * rsu_notify() - report HPS software execution stage as a 16bit number
+ * @stage: software execution stage
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_notify(int stage);
+
+/**
+ * rsu_clear_error_status() - clear errors from the current RSU status log
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_clear_error_status(void);
+
+/**
+ * rsu_reset_retry_counter() - reset the retry counter
+ *
+ * This function is used to request the retry counter to be set to zero, so that
+ * the currently running image may be tried again after the next watchdog
+ * timeout.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_reset_retry_counter(void);
+
+/**
+ * rsu_dcmf_version() - retrieve the decision firmware version
+ * @versions: pointer to where the four DCMF versions will be stored
+ *
+ * This function is used to retrieve the version of each of the four DCMF copies
+ * in flash.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_dcmf_version(u32 *versions);
+
+/**
+ * rsu_max_retry() - retrieve the max_retry parameter
+ * @value: pointer to where the max_retry will be stored
+ *
+ * This function is used to retrieve the max_retry parameter from the decision
+ * firmware section in flash
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_max_retry(u8 *value);
+
+/**
+ * rsu_dcmf_status() - retrieve the decision firmware status
+ * @status: pointer to where the status will be stored
+ *
+ * This function is used to determine whether decision firmware copies are
+ * corrupted in flash, with the currently used decision firmware being used as
+ * reference. The status is an array of 4 values, one for each decision
+ * firmware copy. A 0 means the copy is fine, anything else means the copy is
+ * corrupted.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_dcmf_status(u16 *status);
+
+/**
+ * rsu_create_empty_cpb() - create a cpb with header field only
+ * This function is used to create a empty configuration pointer block
+ * (CPB) with the header field only.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_create_empty_cpb(void);
+
+/**
+ * rsu_restore_cpb() - restore the cpb from an address
+ * @address: the address which cpb is restored from.
+ *
+ * This function is used to restore a saved CPB from an address.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_restore_cpb(u64 address);
+
+/**
+ * rsu_save_cpb() - save cpb to the address
+ * @address: the address which cpb is saved to.
+ *
+ * This function is used to save CPB to an address.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_save_cpb(u64 address);
+
+/**
+ * rsu_restore_spt() - restore the spt from an address
+ * @address: the address which spt is restored from.
+ *
+ * This function is used to restore a saved SPT from an address
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_restore_spt(u64 address);
+
+/**
+ * rsu_save_spt() - save spt to the address
+ * @address: the address which spt is saved to.
+ *
+ * This function is used to save SPT to an address.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_save_spt(u64 address);
+
+/**
+ * rsu_running_factory() - determine if current running image is factory image
+ * @factory: set to non-zero value when running factory image, zero otherwise
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_running_factory(int *factory);
+#endif
diff --git a/arch/arm/mach-socfpga/include/mach/rsu_flash_if.h b/arch/arm/mach-socfpga/include/mach/rsu_flash_if.h
new file mode 100644
index 00000000000..7539708e66f
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_flash_if.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RSU SPI NOR access: driver model (DM_SPI_FLASH) vs legacy spi_flash_probe().
+ */
+#ifndef __RSU_FLASH_IF_H__
+#define __RSU_FLASH_IF_H__
+
+#include <linux/errno.h>
+#include <linux/kconfig.h>
+#include <linux/types.h>
+#include <spi_flash.h>
+
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
+#include <dm/device.h>
+#include <linux/mtd/spi-nor.h>
+
+static inline u32 rsu_mtd_size(struct udevice *dev)
+{
+ struct spi_nor *nor = dev_get_uclass_priv(dev);
+
+ return nor ? nor->size : 0;
+}
+
+static inline int rsu_mtd_read(struct udevice *dev, u32 off, size_t len,
+ void *buf)
+{
+ return spi_flash_read_dm(dev, off, len, buf);
+}
+
+static inline int rsu_mtd_write(struct udevice *dev, u32 off, size_t len,
+ const void *buf)
+{
+ return spi_flash_write_dm(dev, off, len, buf);
+}
+
+static inline int rsu_mtd_erase(struct udevice *dev, u32 off, size_t len)
+{
+ return spi_flash_erase_dm(dev, off, len);
+}
+
+static inline int rsu_mtd_probe(unsigned int bus, unsigned int cs,
+ struct udevice **devp)
+{
+ return spi_flash_probe_bus_cs(bus, cs, devp);
+}
+
+static inline void rsu_mtd_unclaim(struct udevice *dev)
+{
+ (void)dev;
+}
+
+#else
+
+static inline u32 rsu_mtd_size(struct spi_flash *flash)
+{
+ return flash->size;
+}
+
+static inline int rsu_mtd_read(struct spi_flash *flash, u32 off, size_t len,
+ void *buf)
+{
+ return spi_flash_read(flash, off, len, buf);
+}
+
+static inline int rsu_mtd_write(struct spi_flash *flash, u32 off, size_t len,
+ const void *buf)
+{
+ return spi_flash_write(flash, off, len, buf);
+}
+
+static inline int rsu_mtd_erase(struct spi_flash *flash, u32 off, size_t len)
+{
+ return spi_flash_erase(flash, off, len);
+}
+
+static inline int rsu_mtd_probe(unsigned int bus, unsigned int cs,
+ struct spi_flash **flashp)
+{
+ struct spi_flash *f;
+
+ f = spi_flash_probe(bus, cs, CONFIG_SF_DEFAULT_SPEED,
+ CONFIG_SF_DEFAULT_MODE);
+ if (!f)
+ return -ENODEV;
+ *flashp = f;
+ return 0;
+}
+
+static inline void rsu_mtd_unclaim(struct spi_flash *flash)
+{
+ spi_flash_free(flash);
+}
+
+#endif
+
+#endif /* __RSU_FLASH_IF_H__ */
diff --git a/arch/arm/mach-socfpga/include/mach/rsu_ll.h b/arch/arm/mach-socfpga/include/mach/rsu_ll.h
new file mode 100644
index 00000000000..b391597cbd0
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_ll.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ */
+
+#ifndef __RSU_LL_H__
+#define __RSU_LL_H__
+
+#include <asm/arch/rsu.h>
+#include <asm/types.h>
+
+/**
+ * struct rsu_ll_intf - RSU low level interface
+ * @exit: exit low level interface
+ * @partition.count: get number of slots
+ * @@partition.name: get name of a slot
+ * @partition.offset: get offset of a slot
+ * @partition.size: get size of a slot
+ * @partition.reserved: check if a slot is reserved
+ * @partition.readonly: check if a slot is read only
+ * @partition.rename: rename a slot
+ * @priority.get: get priority
+ * @priority.add: add priority
+ * @priority.remove: remove priority
+ * @data.read: read data from flash device
+ * @data.write: write date to flash device
+ * @data.erase: erase date from flash device
+ * @fw_ops.load: inform firmware to load image
+ * @fw_ops.status: get status from firmware
+ * @fw_ops.notify: send notify command to firmware
+ * @fw_ops.dcmf_status: get dcmf status
+ */
+struct rsu_ll_intf {
+ void (*exit)(void);
+ /** @priv: backend private data (e.g. QSPI session); NULL if unused */
+ void *priv;
+
+ struct {
+ int (*count)(void);
+ char* (*name)(int part_num);
+ u64 (*offset)(int part_num);
+ s64 (*factory_offset)(void);
+ u32 (*size)(int part_num);
+ int (*reserved)(int part_num);
+ int (*readonly)(int part_num);
+ int (*rename)(int part_num, char *name);
+ int (*delete)(int part_num);
+ int (*create)(char *name, u64 start, unsigned int size);
+ } partition;
+
+ struct {
+ int (*get)(int part_num);
+ int (*add)(int part_num);
+ int (*remove)(int part_num);
+ } priority;
+
+ struct {
+ int (*read)(int part_num, int offset, int bytes, void *buf);
+ int (*write)(int part_num, int offset, int bytes, void *buf);
+ int (*erase)(int part_num);
+ } data;
+
+ struct {
+ int (*load)(u64 offset);
+ int (*status)(struct rsu_status_info *info);
+ int (*notify)(u32 value);
+ int (*dcmf_version)(u32 *versions);
+ int (*dcmf_status)(u16 *status);
+ int (*max_retry)(u8 *value);
+ } fw_ops;
+
+ struct {
+ int (*empty)(void);
+ int (*restore)(u64 address);
+ int (*save)(u64 address);
+ int (*corrupted)(void);
+ } cpb_ops;
+
+ struct {
+ int (*restore)(u64 address);
+ int (*save)(u64 address);
+ int (*corrupted)(void);
+ } spt_ops;
+};
+
+/**
+ * rsu_ll_qspi_init() - low level qspi interface init
+ * @intf: pointer to pointer of low level interface
+ *
+ * Return: 0 on success, or error code
+ */
+int rsu_ll_qspi_init(struct rsu_ll_intf **intf);
+#endif
+
diff --git a/arch/arm/mach-socfpga/include/mach/rsu_misc.h b/arch/arm/mach-socfpga/include/mach/rsu_misc.h
new file mode 100644
index 00000000000..7777d652912
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_misc.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ */
+
+#ifndef __RSU_MISC_H__
+#define __RSU_MISC_H__
+
+#include <asm/arch/rsu_ll.h>
+
+/*
+ * A bitstream is a recurrent data structure composed of sections. Each section
+ * consists of 4KB blocks. The first block in a section is called the main
+ * descriptor and the first 32bit value in that descriptor identifies the
+ * section typpe, with 0x62294895 denoting a CMF section.
+ * The second block in a section is called a signature block. The last 256 bytes
+ * of the signature block are called the main image pointer, and contains up to
+ * four pointers to other sections in the bitstream. The entire signature block,
+ * including the main pointer area is protected by a 32-bit CRC.
+ *
+ * The slot size is used to determine if the bitstream was generated using a
+ * slot offset address of zero. The main image pointers of all the CMF sections
+ * identified in the bitstream are updated when programming into a slot if all
+ * of the pointers are less than the slot size.
+ */
+
+#define IMAGE_BLOCK_SZ 0x1000 /* Bitstream block size */
+#define SIG_BLOCK_PTR_OFFS 0x0F00 /* Signature block pointer offset */
+#define SIG_BLOCK_CRC_OFFS 0x0FFC /* Signature block CRC offset */
+#define CMF_MAGIC 0x62294895 /* Magic identifier for CMF sections */
+
+/* log level */
+enum rsu_log_level {
+ RSU_EMERG = 0,
+ RSU_ALERT,
+ RSU_CRIT,
+ RSU_ERR,
+ RSU_WARNING,
+ RSU_NOTICE,
+ RSU_INFO,
+ RSU_DEBUG
+};
+
+typedef int (*rsu_data_callback)(void *buf, int size);
+int rsu_cb_buf_init(void *buf, int size);
+void rsu_cb_buf_exit(void);
+int rsu_cb_buf(void *buf, int len);
+int rsu_cb_program_common(struct rsu_ll_intf *ll_intf, int slot,
+ rsu_data_callback callback, int rawdata);
+int rsu_cb_verify_common(struct rsu_ll_intf *ll_intf, int slot,
+ rsu_data_callback callback, int rawdata);
+
+int rsu_misc_is_rsvd_name(char *name);
+int rsu_misc_is_slot(struct rsu_ll_intf *ll_intf, int part_num);
+int rsu_misc_slot2part(struct rsu_ll_intf *ll_intf, int slot);
+int rsu_misc_writeprotected(int slot);
+void rsu_misc_safe_strcpy(char *dst, int dsz, char *src, int ssz);
+int rsu_misc_spt_checksum_enabled(void);
+
+void rsu_log(const enum rsu_log_level level, const char *format, ...);
+int smc_store_max_retry(u32 value);
+
+void swap_bits(char *data, int size);
+int pow(u32 x, u32 y);
+#endif
diff --git a/arch/arm/mach-socfpga/include/mach/rsu_s10.h b/arch/arm/mach-socfpga/include/mach/rsu_s10.h
new file mode 100644
index 00000000000..8218255eba7
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_s10.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2026 Altera Corporation <www.altera.com>
+ */
+#ifndef _RSU_S10_H_
+#define _RSU_S10_H_
+
+#define RSU_S10_CPB_MAGIC_NUMBER 0x57789609
+#define RSU_S10_SPT_MAGIC_NUMBER 0x57713427
+
+#define SPT0_INDEX 1
+#define SPT1_INDEX 3
+
+#define MAX_PART_NAME_LENGTH 16
+
+/* CMF pointer block */
+struct socfpga_rsu_s10_cpb {
+ u32 magic_number;
+ u32 header_size;
+ u32 total_size;
+ u32 reserved1;
+ u32 iptab_offset;
+ u32 nslots;
+ u32 reserved2;
+ u64 pointer_slot[508];
+};
+
+/* sub partition slot */
+struct socfpga_rsu_s10_spt_slot {
+ char name[MAX_PART_NAME_LENGTH];
+ u32 offset[2];
+ u32 length;
+ u32 flag;
+};
+
+/* sub partition table */
+struct socfpga_rsu_s10_spt {
+ u32 magic_number;
+ u32 version;
+ u32 entries;
+ u32 reserved[5];
+ struct socfpga_rsu_s10_spt_slot spt_slot[127];
+};
+
+#endif /* _RSU_S10_H_ */
diff --git a/arch/arm/mach-socfpga/include/mach/rsu_smc.h b/arch/arm/mach-socfpga/include/mach/rsu_smc.h
new file mode 100644
index 00000000000..89b66ee0b05
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_smc.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SMC / PSCI-visible RSU state (secure RAM). Keep separate from rsu_s10.h so
+ * normal-world RSU code does not declare secure-only symbols.
+ */
+#ifndef __RSU_SMC_H__
+#define __RSU_SMC_H__
+
+#include <asm/types.h>
+#include <asm/secure.h>
+
+extern u32 smc_rsu_update_address __secure_data;
+
+#endif
diff --git a/arch/arm/mach-socfpga/include/mach/smc_s10.h b/arch/arm/mach-socfpga/include/mach/smc_s10.h
new file mode 100644
index 00000000000..ba34b7c441f
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/smc_s10.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Intel Corporation. All rights reserved
+ * Copyright (C) 2026 Altera Corporation <www.altera.com>
+ */
+
+#define SMC_ARG0 0
+#define SMC_ARG1 (SMC_ARG0 + 1)
+#define SMC_ARG2 (SMC_ARG1 + 1)
+#define SMC_ARG3 (SMC_ARG2 + 1)
+#define SMC_RETURN_ARGS_MAX (SMC_ARG3 + 1)
+
+/*
+ * Macro functions for allocation and read/write of
+ * variables to be assigned to registers
+ */
+/* Allocate memory for variable */
+#define SMC_ALLOC_REG_MEM(var) unsigned long var[SMC_RETURN_ARGS_MAX]
+/* Clear variable */
+#define SMC_INIT_REG_MEM(var) \
+ do { \
+ int i; \
+ for (i = 0; i < SMC_RETURN_ARGS_MAX; i++) \
+ var[i] = 0; \
+ } while (0)
+/* Read variable */
+#define SMC_GET_REG_MEM(var, i) var[i]
+/* Write Variable */
+#define SMC_ASSIGN_REG_MEM(var, i, val) ((var)[i] = (val))
+/* Assign variables back to registers */
+#define SMC_RET_REG_MEM(var) \
+ asm volatile("ldr x0, %0\n" \
+ "ldr x1, %1\n" \
+ "ldr x2, %2\n" \
+ "ldr x3, %3\n" \
+ : : "m" (var[0]), "m" (var[1]), \
+ "m" (var[2]), "m" (var[3]) :)
diff --git a/arch/arm/mach-socfpga/include/mach/socfpga_rsu_dm.h b/arch/arm/mach-socfpga/include/mach/socfpga_rsu_dm.h
new file mode 100644
index 00000000000..05928f6aca0
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/socfpga_rsu_dm.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SoC FPGA RSU driver model (UCLASS_MISC) private data.
+ */
+#ifndef __SOCFPGA_RSU_DM_H__
+#define __SOCFPGA_RSU_DM_H__
+
+#include <asm/arch/rsu_ll.h>
+
+/**
+ * struct socfpga_rsu_priv - per-DM-device RSU session anchor
+ * @ll: active low-level interface while a console session holds the device
+ */
+struct socfpga_rsu_priv {
+ struct rsu_ll_intf *ll;
+};
+
+#endif /* __SOCFPGA_RSU_DM_H__ */
diff --git a/arch/arm/mach-socfpga/mailbox_s10.c b/arch/arm/mach-socfpga/mailbox_s10.c
index 5e8768168d3..d65a7313fd8 100644
--- a/arch/arm/mach-socfpga/mailbox_s10.c
+++ b/arch/arm/mach-socfpga/mailbox_s10.c
@@ -6,12 +6,14 @@
#include <asm/arch/clock_manager.h>
#include <asm/arch/mailbox_s10.h>
+#include <asm/arch/rsu.h>
#include <asm/arch/smc_api.h>
#include <asm/arch/system_manager.h>
#include <asm/io.h>
#include <asm/secure.h>
#include <asm/system.h>
#include <hang.h>
+#include <linux/bitfield.h>
#include <wait_bit.h>
#define MBOX_READL(reg) \
@@ -390,6 +392,145 @@ error:
return ret;
}
+
+int mbox_qspi_get_device_info(u32 *resp_buf, u32 resp_buf_len)
+{
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_XPL_BUILD) && IS_ENABLED(CONFIG_SPL_ATF)) {
+ ret = smc_send_mailbox(MBOX_QSPI_GET_DEVICE_INFO, 0, NULL, 0,
+ (u32 *)&resp_buf_len, (u32 *)resp_buf);
+ } else {
+ ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_GET_DEVICE_INFO,
+ MBOX_CMD_DIRECT, 0, NULL, 0,
+ (u32 *)&resp_buf_len, (u32 *)resp_buf);
+ }
+
+ if (ret) {
+ debug("%s: Failed to retrieve QSPI Device INFO: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ debug("Successfully retrieve QSPI Device INFO.\n");
+
+ return 0;
+}
+
+int mbox_rsu_get_spt_offset(u32 *resp_buf, u32 resp_buf_len)
+{
+#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_SPL_ATF)
+ return smc_send_mailbox(MBOX_GET_SUBPARTITION_TABLE, 0, NULL, 0,
+ (u32 *)&resp_buf_len, (u32 *)resp_buf);
+#else
+ return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_GET_SUBPARTITION_TABLE,
+ MBOX_CMD_DIRECT, 0, NULL, 0, (u32 *)&resp_buf_len,
+ (u32 *)resp_buf);
+#endif
+}
+
+int mbox_rsu_status(u32 *resp_buf, u32 resp_buf_len)
+{
+ int ret;
+ struct rsu_status_info *info = (struct rsu_status_info *)resp_buf;
+
+ info->retry_counter = -1;
+
+#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_SPL_ATF)
+ ret = smc_send_mailbox(MBOX_RSU_STATUS, 0, NULL, 0,
+ (u32 *)&resp_buf_len, (u32 *)resp_buf);
+#else
+ ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RSU_STATUS, MBOX_CMD_DIRECT, 0,
+ NULL, 0, (u32 *)&resp_buf_len, (u32 *)resp_buf);
+#endif
+
+ if (ret)
+ return ret;
+
+ if (info->retry_counter != -1)
+ if (!RSU_VERSION_ACMF_VERSION(info->version))
+ info->version |= FIELD_PREP(RSU_VERSION_ACMF_MASK, 1);
+
+ return ret;
+}
+
+#ifdef CONFIG_ARMV8_PSCI
+int __secure mbox_rsu_status_psci(u32 *resp_buf, u32 resp_buf_len)
+{
+ int ret;
+ struct rsu_status_info *info = (struct rsu_status_info *)resp_buf;
+ int adjust = (resp_buf_len >= 9);
+
+ if (adjust)
+ info->retry_counter = -1;
+
+ ret = mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_RSU_STATUS,
+ MBOX_CMD_DIRECT, 0, NULL, 0,
+ (u32 *)&resp_buf_len, (u32 *)resp_buf);
+
+ if (ret)
+ return ret;
+
+ if (!adjust)
+ return ret;
+
+ if (info->retry_counter != -1)
+ if (!RSU_VERSION_ACMF_VERSION(info->version))
+ info->version |= FIELD_PREP(RSU_VERSION_ACMF_MASK, 1);
+
+ return ret;
+}
+#endif /* CONFIG_ARMV8_PSCI */
+
+int mbox_rsu_update(u32 *flash_offset)
+{
+#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_SPL_ATF)
+ return smc_send_mailbox(MBOX_RSU_UPDATE, 2, (u32 *)flash_offset, 0,
+ 0, NULL);
+#else
+ return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RSU_UPDATE, MBOX_CMD_DIRECT, 2,
+ (u32 *)flash_offset, 0, 0, NULL);
+#endif
+}
+
+#ifdef CONFIG_ARMV8_PSCI
+int __secure mbox_rsu_update_psci(u32 *flash_offset)
+{
+ return mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_RSU_UPDATE,
+ MBOX_CMD_DIRECT, 2, (u32 *)flash_offset,
+ 0, 0, NULL);
+}
+#endif /* CONFIG_ARMV8_PSCI */
+
+#else
+int mbox_rsu_get_spt_offset(u32 *resp_buf, u32 resp_buf_len)
+{
+ return MBOX_FUNC_NOT_SUPPORTED;
+}
+
+int mbox_rsu_status(u32 *resp_buf, u32 resp_buf_len)
+{
+ return MBOX_FUNC_NOT_SUPPORTED;
+}
+
+#ifdef CONFIG_ARMV8_PSCI
+int __secure mbox_rsu_status_psci(u32 *resp_buf, u32 resp_buf_len)
+{
+ return MBOX_FUNC_NOT_SUPPORTED;
+}
+#endif /* CONFIG_ARMV8_PSCI */
+
+int mbox_rsu_update(u32 *flash_offset)
+{
+ return MBOX_FUNC_NOT_SUPPORTED;
+}
+
+#ifdef CONFIG_ARMV8_PSCI
+int __secure mbox_rsu_update_psci(u32 *flash_offset)
+{
+ return MBOX_FUNC_NOT_SUPPORTED;
+}
+#endif /* CONFIG_ARMV8_PSCI */
#endif /* CONFIG_CADENCE_QSPI */
int mbox_reset_cold(void)
diff --git a/include/rsu_console.h b/include/rsu_console.h
new file mode 100644
index 00000000000..4e851069183
--- /dev/null
+++ b/include/rsu_console.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Altera Corporation <www.altera.com>
+ *
+ * Public prototypes for SoC FPGA RSU console-helper functions.
+ */
+#ifndef _RSU_CONSOLE_H_
+#define _RSU_CONSOLE_H_
+
+int rsu_spt_cpb_list(int argc, char * const argv[]);
+int rsu_update(int argc, char * const argv[]);
+int rsu_dtb(int argc, char * const argv[]);
+
+#endif /* _RSU_CONSOLE_H_ */
--
2.43.7
More information about the U-Boot
mailing list