All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC
@ 2019-09-09 17:35 richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 1/5] arm: socfpga: stratix10: add RSU mailbox support richard.gong at linux.intel.com
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: richard.gong at linux.intel.com @ 2019-09-09 17:35 UTC (permalink / raw)
  To: u-boot

From: Richard Gong <richard.gong@intel.com>

The Intel Remote System Update (RSU) provides a way for users to update
the QSPI configuration bitstream of a Intel Stratix10 SoC device with
significantly reduced risk of corrupting the bitstream storage and
bricking the system.

The patchset adds RSU support which allows user to perform a complete set
of RSU operations via provided console commands.

The patches have reviewed by other colleagues at Intel.

Richard Gong (5):
  arm: socfpga: stratix10: add RSU mailbox support
  arm: socfpga: stratix10: add RSU support for Stratix10 SoC
  arm: socfpga: stratix10: add environment variables for RSU support
  arm: socfpga: stratix10: add console commands for RSU support
  arm: socfpga: enable RSU build

 arch/arm/mach-socfpga/Makefile                   |    6 +
 arch/arm/mach-socfpga/include/mach/mailbox_s10.h |   36 +-
 arch/arm/mach-socfpga/include/mach/rsu.h         |  244 +++++
 arch/arm/mach-socfpga/include/mach/rsu_ll.h      |   71 ++
 arch/arm/mach-socfpga/include/mach/rsu_misc.h    |   46 +
 arch/arm/mach-socfpga/include/mach/rsu_s10.h     |   46 +
 arch/arm/mach-socfpga/mailbox_s10.c              |   45 +
 arch/arm/mach-socfpga/misc_s10.c                 |    9 +
 arch/arm/mach-socfpga/rsu.c                      |  569 ++++++++++++
 arch/arm/mach-socfpga/rsu_ll_qspi.c              | 1033 ++++++++++++++++++++++
 arch/arm/mach-socfpga/rsu_misc.c                 |  527 +++++++++++
 arch/arm/mach-socfpga/rsu_s10.c                  |  817 +++++++++++++++++
 12 files changed, 3439 insertions(+), 10 deletions(-)
 create mode 100644 arch/arm/mach-socfpga/include/mach/rsu.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/rsu.c
 create mode 100644 arch/arm/mach-socfpga/rsu_ll_qspi.c
 create mode 100644 arch/arm/mach-socfpga/rsu_misc.c
 create mode 100644 arch/arm/mach-socfpga/rsu_s10.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [U-Boot] [PATCHv1 1/5] arm: socfpga: stratix10: add RSU mailbox support
  2019-09-09 17:35 [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC richard.gong at linux.intel.com
@ 2019-09-09 17:35 ` richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 2/5] arm: socfpga: stratix10: add RSU support for Stratix10 SoC richard.gong at linux.intel.com
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: richard.gong at linux.intel.com @ 2019-09-09 17:35 UTC (permalink / raw)
  To: u-boot

From: Richard Gong <richard.gong@intel.com>

Add Remote System Update (RSU) related mailbox support. This includes
RSU_STATUS which reports status of bitstream loaded by Configuration
Management Firmware (CMF), RSU_UPDATE which will invokes CMF to load new
bitstream, GET_SUBPARTITION_TABLE which will query CMF on the start
address of sub-partition table, HPS_STAGE_NOTIFY which notifies firmware
the stage of HPS code execution and related functions to support Secure
Monitor Call (SMC).

Signed-off-by: Richard Gong <richard.gong@intel.com>
Signed-off-by: Radu Bacrau <radu.bacrau@intel.com>
Signed-off-by: Ley Foon Tan <ley.foon.tan@intel.com>
Signed-off-by: Chin Liang See <chin.liang.see@intel.com>
---
 arch/arm/mach-socfpga/include/mach/mailbox_s10.h | 36 +++++++++++++------
 arch/arm/mach-socfpga/mailbox_s10.c              | 45 ++++++++++++++++++++++++
 2 files changed, 71 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h
index ae728a5..979fd06 100644
--- a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h
+++ b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h
@@ -77,16 +77,20 @@ enum ALT_SDM_MBOX_RESP_CODE {
 };
 
 /* Mailbox command list */
-#define MBOX_RESTART		2
-#define MBOX_CONFIG_STATUS	4
-#define MBOX_RECONFIG		6
-#define MBOX_RECONFIG_MSEL	7
-#define MBOX_RECONFIG_DATA	8
-#define MBOX_RECONFIG_STATUS	9
-#define MBOX_QSPI_OPEN		50
-#define MBOX_QSPI_CLOSE		51
-#define MBOX_QSPI_DIRECT	59
-#define MBOX_REBOOT_HPS		71
+#define MBOX_RESTART			2
+#define MBOX_CONFIG_STATUS		4
+#define MBOX_RECONFIG			6
+#define MBOX_RECONFIG_MSEL		7
+#define MBOX_RECONFIG_DATA		8
+#define MBOX_RECONFIG_STATUS		9
+#define MBOX_QSPI_OPEN			50
+#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
 
 /* Mailbox registers */
 #define MBOX_CIN			0	/* command valid offset */
@@ -130,6 +134,11 @@ enum ALT_SDM_MBOX_RESP_CODE {
 #define RCF_SOFTFUNC_STATUS_SEU_ERROR			BIT(3)
 #define RCF_PIN_STATUS_NSTATUS				BIT(31)
 
+/* Defines for HPS_STAGE_NOTIFY */
+#define HPS_EXECUTION_STATE_FSBL	0
+#define HPS_EXECUTION_STATE_SSBL	1
+#define HPS_EXECUTION_STATE_OS		2
+
 int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg, u8 urgent,
 		  u32 *resp_buf_len, u32 *resp_buf);
 int mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
@@ -146,6 +155,13 @@ int mbox_qspi_open(void);
 #endif
 
 int mbox_reset_cold(void);
+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);
+int mbox_hps_stage_notify(u32 execution_stage);
+int mbox_hps_stage_notify_psci(u32 execution_stage);
 int mbox_get_fpga_config_status(u32 cmd);
 int mbox_get_fpga_config_status_psci(u32 cmd);
 #endif /* _MAILBOX_S10_H_ */
diff --git a/arch/arm/mach-socfpga/mailbox_s10.c b/arch/arm/mach-socfpga/mailbox_s10.c
index 4498ab5..6774390 100644
--- a/arch/arm/mach-socfpga/mailbox_s10.c
+++ b/arch/arm/mach-socfpga/mailbox_s10.c
@@ -342,6 +342,39 @@ int mbox_reset_cold(void)
 	return 0;
 }
 
+int mbox_rsu_get_spt_offset(u32 *resp_buf, u32 resp_buf_len)
+{
+	return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_GET_SUBPARTITION_TABLE,
+			     MBOX_CMD_DIRECT, 0, NULL, 0, (u32 *)&resp_buf_len,
+			     (u32 *)resp_buf);
+}
+
+int mbox_rsu_status(u32 *resp_buf, u32 resp_buf_len)
+{
+	return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RSU_STATUS, MBOX_CMD_DIRECT, 0,
+			     NULL, 0, (u32 *)&resp_buf_len, (u32 *)resp_buf);
+}
+
+int __secure mbox_rsu_status_psci(u32 *resp_buf, u32 resp_buf_len)
+{
+	return mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_RSU_STATUS,
+				  MBOX_CMD_DIRECT, 0, NULL, 0,
+				  (u32 *)&resp_buf_len, (u32 *)resp_buf);
+}
+
+int mbox_rsu_update(u32 *flash_offset)
+{
+	return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RSU_UPDATE, MBOX_CMD_DIRECT, 2,
+			     (u32 *)flash_offset, 0, 0, NULL);
+}
+
+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);
+}
+
 /* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
 static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
 {
@@ -380,6 +413,12 @@ static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
 	return MBOX_CFGSTAT_STATE_CONFIG;
 }
 
+int  __secure mbox_hps_stage_notify_psci(u32 execution_stage)
+{
+	return mbox_send_cmd_psci(MBOX_ID_UBOOT, MBOX_HPS_STAGE_NOTIFY,
+			     MBOX_CMD_DIRECT, 1, &execution_stage, 0, 0, NULL);
+}
+
 int mbox_get_fpga_config_status(u32 cmd)
 {
 	return mbox_get_fpga_config_status_common(cmd);
@@ -405,6 +444,12 @@ int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
 			       resp_buf_len, resp_buf);
 }
 
+int mbox_hps_stage_notify(u32 execution_stage)
+{
+	return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_HPS_STAGE_NOTIFY,
+			     MBOX_CMD_DIRECT, 1, &execution_stage, 0, 0, NULL);
+}
+
 int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
 {
 	return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [U-Boot] [PATCHv1 2/5] arm: socfpga: stratix10: add RSU support for Stratix10 SoC
  2019-09-09 17:35 [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 1/5] arm: socfpga: stratix10: add RSU mailbox support richard.gong at linux.intel.com
@ 2019-09-09 17:35 ` richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 3/5] arm: socfpga: stratix10: add environment variables for RSU support richard.gong at linux.intel.com
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: richard.gong at linux.intel.com @ 2019-09-09 17:35 UTC (permalink / raw)
  To: u-boot

From: Richard Gong <richard.gong@intel.com>

The Intel Remote System Update (RSU) provides a way for users to update
the QSPI configuration bitstream of an Intel Stratix 10 SoC device with
significantly reduced risk of corrupting the bitstream storage and
bricking the system.

This patch adds RSU support which allows user to perform a complete set
of RSU operations:
	1. Provides support for creating the initial flash images for a
	   system to support RSU.
	2. Allows several production images to be tried in a specific
	   order until one of them is successful.
	3. Loads a factory image if no production image is available, or
	   all production images failed.
	4. Provides users with the ability to add and remove production
	   images.
	5. Provides user with the ability to change the order in which
	   production images are loaded.
	6. Provides user with the ability to load a specific image from
	   flash. The image is a production or factory image.
	7. Provides user with information on which image is currently
	   running, and what errors were encountered by RSU.

Signed-off-by: Richard Gong <richard.gong@intel.com>
---
 arch/arm/mach-socfpga/include/mach/rsu.h      |  244 ++++++
 arch/arm/mach-socfpga/include/mach/rsu_ll.h   |   71 ++
 arch/arm/mach-socfpga/include/mach/rsu_misc.h |   46 ++
 arch/arm/mach-socfpga/rsu.c                   |  569 ++++++++++++++
 arch/arm/mach-socfpga/rsu_ll_qspi.c           | 1033 +++++++++++++++++++++++++
 arch/arm/mach-socfpga/rsu_misc.c              |  527 +++++++++++++
 6 files changed, 2490 insertions(+)
 create mode 100644 arch/arm/mach-socfpga/include/mach/rsu.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/rsu.c
 create mode 100644 arch/arm/mach-socfpga/rsu_ll_qspi.c
 create mode 100644 arch/arm/mach-socfpga/rsu_misc.c

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 0000000..3b1868a
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#ifndef __RSU_H__
+#define __RSU_H__
+
+#include <asm/types.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
+
+/* RSU Notify Bitmasks */
+#define RSU_NOTIFY_IGNORE_STAGE         BIT(18)
+#define RSU_NOTIFY_CLEAR_ERROR_STATUS   BIT(17)
+#define RSU_NOTIFY_RESET_RETRY_COUNTER  BIT(16)
+
+/**
+ * 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_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_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);
+#endif
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 0000000..1a5b2a3
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_ll.h
@@ -0,0 +1,71 @@
+/* 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
+ */
+struct rsu_ll_intf {
+	void (*exit)(void);
+
+	struct {
+		int (*count)(void);
+		char* (*name)(int part_num);
+		u64 (*offset)(int part_num);
+		u32 (*size)(int part_num);
+		int (*reserved)(int part_num);
+		int (*readonly)(int part_num);
+		int (*rename)(int part_num, char *name);
+	} 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);
+	} fw_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 0000000..c173008
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_misc.h
@@ -0,0 +1,46 @@
+/* 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>
+
+#define IMAGE_BLOCK_SZ	0x1000	/* Size of a bitstream data block */
+#define IMAGE_PTR_BLOCK 0x1000	/* Offset to the PTR BLOCK */
+#define IMAGE_PTR_START	0x1F00	/* Offset to main image pointers */
+#define IMAGE_PTR_CRC	0x1FFC	/* Offset to CRC for PTR BLOCK */
+#define IMAGE_PTR_END   0x1FFF  /* End of PTR BLOCK */
+
+/* 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);
+
+void rsu_log(const enum rsu_log_level level, const char *format, ...);
+#endif
diff --git a/arch/arm/mach-socfpga/rsu.c b/arch/arm/mach-socfpga/rsu.c
new file mode 100644
index 0000000..ef37e8c
--- /dev/null
+++ b/arch/arm/mach-socfpga/rsu.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <asm/arch/rsu.h>
+#include <asm/arch/rsu_misc.h>
+
+struct rsu_ll_intf *ll_intf;
+
+/**
+ * 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)
+{
+	int ret;
+
+	if (ll_intf) {
+		rsu_log(RSU_ERR, "ll_intf initialized\n");
+		return -EINTF;
+	}
+
+	ret = rsu_ll_qspi_init(&ll_intf);
+	if (ret) {
+		rsu_exit();
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * rsu_exit() - free flash driver, clean SPT and CPB data
+ */
+void rsu_exit(void)
+{
+	if (ll_intf && ll_intf->exit)
+		ll_intf->exit();
+
+	ll_intf = NULL;
+}
+
+/**
+ * rsu_slot_count() - get the number of slots defined
+ *
+ * Returns: the number of defined slots
+ */
+int rsu_slot_count(void)
+{
+	int partitions;
+	int cnt = 0;
+	int x;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	partitions = ll_intf->partition.count();
+
+	for (x = 0; x < partitions; x++) {
+		if (rsu_misc_is_slot(ll_intf, x))
+			cnt++;
+	}
+
+	return cnt;
+}
+
+/**
+ * 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)
+{
+	int partitions;
+	int cnt = 0;
+	int x;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (!name)
+		return -EARGS;
+
+	partitions = ll_intf->partition.count();
+
+	for (x = 0; x < partitions; x++) {
+		if (rsu_misc_is_slot(ll_intf, x)) {
+			if (!strcmp(name, ll_intf->partition.name(x)))
+				return cnt;
+			cnt++;
+		}
+	}
+
+	return -ENAME;
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (!info)
+		return -EARGS;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -EINVAL;
+
+	rsu_misc_safe_strcpy(info->name, sizeof(info->name),
+			     ll_intf->partition.name(part_num),
+			     sizeof(info->name));
+
+	info->offset = ll_intf->partition.offset(part_num);
+	info->size = ll_intf->partition.size(part_num);
+	info->priority = ll_intf->priority.get(part_num);
+
+	return 0;
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	return ll_intf->partition.size(part_num);
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	return ll_intf->priority.get(part_num);
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_misc_writeprotected(slot)) {
+		rsu_log(RSU_ERR, "Trying to erase a write protected slot\n");
+		return -EWRPROT;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.remove(part_num))
+		return -ELOWLEVEL;
+
+	if (ll_intf->data.erase(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * 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)
+{
+	int ret;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_program_common(ll_intf, slot, rsu_cb_buf, 0);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to program buf data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+	return ret;
+}
+
+/**
+ * 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,
+ * the slot is not entered into CPB.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_program_buf_raw(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_program_common(ll_intf, slot, rsu_cb_buf, 1);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to program raw data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+	return ret;
+}
+
+/**
+ * rsu_slot_verify_buf() - verify FPGA config data in a slot
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to verify FPGA configuration data in a selected
+ * slot against the provided buffer.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_verify_common(ll_intf, slot, rsu_cb_buf, 0);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to verify buffer data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+
+	return ret;
+}
+
+/**
+ * rsu_slot_verify_buf_raw() - verify raw data in a slot
+ * @slot: slot number
+ * @buf: pointer to data buffer
+ * @size: bytes to read from buffer, in hex value
+ *
+ * This function is used to verify raw data in a selected slot against
+ * the provided buffer.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_verify_buf_raw(int slot, void *buf, int size)
+{
+	int ret;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (rsu_cb_buf_init(buf, size)) {
+		rsu_log(RSU_ERR, "Bad buf/size arguments\n");
+		return -EARGS;
+	}
+
+	ret = rsu_cb_verify_common(ll_intf, slot, rsu_cb_buf, 1);
+	if (ret) {
+		rsu_log(RSU_ERR, "fail to verify raw data\n");
+		return ret;
+	}
+
+	rsu_cb_buf_exit();
+	return ret;
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.remove(part_num))
+		return -ELOWLEVEL;
+
+	if (ll_intf->priority.add(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.remove(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * 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 the system is rebooted after a short delay.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load(int slot)
+{
+	int part_num;
+	u64 offset;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	offset = ll_intf->partition.offset(part_num);
+
+	if (ll_intf->priority.get(part_num) <= 0) {
+		rsu_log(RSU_ERR, "Try to reboot from an erased slot\n");
+		return -EERASE;
+	}
+
+	return ll_intf->fw_ops.load(offset);
+}
+
+/**
+ * rsu_slot_load_factory() - load the factory image
+ *
+ * This function is used to request the factory image to be loaded
+ * immediately. On success, the system is rebooted after a short delay.
+ *
+ * Returns: 0 on success, or error code
+ */
+int rsu_slot_load_factory(void)
+{
+	int part_num;
+	int partitions;
+	u64 offset;
+	char name[] = "FACTORY_IMAGE";
+
+	if (!ll_intf)
+		return -EINTF;
+
+	partitions = ll_intf->partition.count();
+	for (part_num = 0; part_num < partitions; part_num++) {
+		if (!strcmp(name, ll_intf->partition.name(part_num)))
+			break;
+	}
+
+	if (part_num >= partitions) {
+		rsu_log(RSU_ERR, "No FACTORY_IMAGE partition defined\n");
+		return -EFORMAT;
+	}
+
+	offset = ll_intf->partition.offset(part_num);
+	return ll_intf->fw_ops.load(offset);
+}
+
+/**
+ * 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)
+{
+	int part_num;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0 || slot >= rsu_slot_count()) {
+		rsu_log(RSU_ERR, "invalid slot number\n");
+		return -ESLOTNUM;
+	}
+
+	if (!name)
+		return -EARGS;
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (rsu_misc_is_rsvd_name(name)) {
+		rsu_log(RSU_ERR, "Partition rename uses a reserved name\n");
+		return -ENAME;
+	}
+
+	if (ll_intf->partition.rename(part_num, name))
+		return -ENAME;
+
+	return 0;
+}
+
+/**
+ * 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)
+{
+	if (!ll_intf)
+		return -EINTF;
+
+	return ll_intf->fw_ops.status(info);
+}
diff --git a/arch/arm/mach-socfpga/rsu_ll_qspi.c b/arch/arm/mach-socfpga/rsu_ll_qspi.c
new file mode 100644
index 0000000..6ceb150
--- /dev/null
+++ b/arch/arm/mach-socfpga/rsu_ll_qspi.c
@@ -0,0 +1,1033 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <asm/arch/mailbox_s10.h>
+#include <asm/arch/rsu.h>
+#include <asm/arch/rsu_misc.h>
+#include <spi.h>
+#include <spi_flash.h>
+
+#define SPT_MAGIC_NUMBER	0x57713427
+#define SPT_FLAG_RESERVED	1
+#define SPT_FLAG_READONLY	2
+
+#define CPB_MAGIC_NUMBER	0x57789609
+#define CPB_HEADER_SIZE		24
+
+#define ERASED_ENTRY		((u64)-1)
+#define SPENT_ENTRY		((u64)0)
+
+#define SPT_VERSION		0
+#define LIBRSU_VER		0
+
+/**
+ * struct sub_partition_table_partition - SPT partition structure
+ * @name: sub-partition name
+ * @offset: sub-partition start offset
+ * @length: sub-partition length
+ * @flags: sub-partition flags
+ */
+struct sub_partition_table_partition {
+	char name[16];
+	u64 offset;
+	u32 length;
+	u32 flags;
+};
+
+/**
+ * struct sub_partition_table - sub partition table structure
+ * @magic_number: the magic number
+ * @version: version number
+ * @partitions: number of entries
+ * @revd: reserved
+ * @sub_partition_table_partition.partition: SPT partition array
+ */
+struct sub_partition_table {
+	u32 magic_number;
+	u32 version;
+	u32 partitions;
+	u32 rsvd[5];
+	struct sub_partition_table_partition partition[127];
+};
+
+/**
+ * union cmf_pointer_block - CMF pointer block
+ * @header.magic_number: CMF pointer block magic number
+ * @header.header_size: size of CMF pointer block header
+ * @header.cpb_size: size of CMF pointer block
+ * @header.cpb_backup_offset: offset of CMF pointer block
+ * @header.image_ptr_offset: offset of image pointers
+ * @header.image_ptr_slots: number of image pointer slots
+ * @data: image pointer slot array
+ */
+union cmf_pointer_block {
+	struct {
+		u32 magic_number;
+		u32 header_size;
+		u32 cpb_size;
+		u32 cpb_backup_offset;
+		u32 image_ptr_offset;
+		u32 image_ptr_slots;
+	} header;
+	char data[4 * 1024];
+};
+
+static union cmf_pointer_block cpb;
+static struct sub_partition_table spt;
+static u64 *cpb_slots;
+struct spi_flash *flash;
+static u32 spt0_offset;
+static u32 spt1_offset;
+
+/**
+ * get_part_offset() - get a selected partition offset
+ * @part_num: the selected partition number
+ * @offset: the partition offset
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int get_part_offset(int part_num, u64 *offset)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	*offset = spt.partition[part_num].offset;
+
+	return 0;
+}
+
+/**
+ * read_dev() - read data from flash
+ * @offset: the offset which read from flash
+ * @buf: buffer for read data
+ * @len: the size of data which read from flash
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int read_dev(u64 offset, void *buf, int len)
+{
+	int ret;
+
+	ret = spi_flash_read(flash, (u32)offset, len, buf);
+	if (ret) {
+		rsu_log(RSU_ERR, "read flash error=%i\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * write_dev() - write data to flash
+ * @offset: the offset which data will written to
+ * @buf: the written data
+ * @len: the size of data which write to flash
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int write_dev(u64 offset, void *buf, int len)
+{
+	int ret;
+
+	ret = spi_flash_write(flash, (u32)offset, len, buf);
+	if (ret) {
+		rsu_log(RSU_ERR, "write flash error=%i\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * erase_dev() - erase data at flash
+ * @offset: the offset from which data will be erased
+ * @len: the size of data to be erased
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int erase_dev(u64 offset, int len)
+{
+	int ret;
+
+	ret = spi_flash_erase(flash, (u32)offset, len);
+	if (ret) {
+		rsu_log(RSU_ERR, "erase flash error=%i\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * read_part() - read a selected partition data
+ * @part_num: the selected partition number
+ * @offset: the offset from which data will be read
+ * @buf: buffer contains the read data
+ * @len: the size of data to be read
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int read_part(int part_num, u64 offset, void *buf, int len)
+{
+	u64 part_offset;
+
+	if (get_part_offset(part_num, &part_offset))
+		return -1;
+
+	if (offset < 0 || len < 0 ||
+	    (offset + len) > spt.partition[part_num].length)
+		return -1;
+
+	return read_dev(part_offset + offset, buf, len);
+}
+
+/**
+ * write_part() - write a selected partition data
+ * @part_num: the selected partition number
+ * @offset: the offset to which data will be written
+ * @buf: data to be written to
+ * @len: the size of data to be written
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int write_part(int part_num, u64 offset, void *buf, int len)
+{
+	u64 part_offset;
+
+	if (get_part_offset(part_num, &part_offset))
+		return -1;
+
+	if (offset < 0 || len < 0 ||
+	    (offset + len) > spt.partition[part_num].length)
+		return -1;
+
+	return write_dev(part_offset + offset, buf, len);
+}
+
+/**
+ * erase_part() - erase a selected partition data
+ * @part_num: the selected partition number
+ *
+ * Return: 0 on success, or -ve for error
+ */
+static int erase_part(int part_num)
+{
+	u64 part_offset;
+
+	if (get_part_offset(part_num, &part_offset))
+		return -1;
+
+	return erase_dev(part_offset, spt.partition[part_num].length);
+}
+
+/**
+ * writeback_spt() - write back SPT
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int writeback_spt(void)
+{
+	int x;
+	int updates = 0;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "SPT0") &&
+		    strcmp(spt.partition[x].name, "SPT1"))
+			continue;
+
+		if (erase_part(x)) {
+			rsu_log(RSU_ERR, "failed to erase SPTx");
+			return -1;
+		}
+
+		spt.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(x, 0, &spt, sizeof(spt))) {
+			rsu_log(RSU_ERR, "failed to write SPTx table");
+			return -1;
+		}
+
+		spt.magic_number = (u32)SPT_MAGIC_NUMBER;
+		if (write_part(x, 0, &spt.magic_number,
+			       sizeof(spt.magic_number))) {
+			rsu_log(RSU_ERR, "failed to write SPTx magic #");
+			return -1;
+		}
+
+		updates++;
+	}
+
+	if (updates != 2) {
+		rsu_log(RSU_ERR, "didn't find two SPTs");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * check_spt() - check if SPT is valid
+ *
+ * Return: 0 for valid SPT, or -1 for not
+ */
+static int check_spt(void)
+{
+	int x;
+	int max_len = sizeof(spt.partition[0].name);
+	int spt0_found = 0;
+	int spt1_found = 0;
+	int cpb0_found = 0;
+	int cpb1_found = 0;
+
+	/*
+	 * Make sure the SPT names are '\0' terminated. Truncate last byte
+	 * if the name uses all available bytes.  Perform validity check on
+	 * entries.
+	 */
+
+	rsu_log(RSU_DEBUG,
+		"MAX length of a name = %i bytes\n", max_len - 1);
+
+	if (spt.version > SPT_VERSION) {
+		rsu_log(RSU_WARNING, "SPT version %i is greater than %i\n",
+			spt.version, SPT_VERSION);
+		rsu_log(RSU_WARNING,
+			"RSU Version %i - update to for newer features\n",
+			LIBRSU_VER);
+	}
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strnlen(spt.partition[x].name, max_len) >= max_len)
+			spt.partition[x].name[max_len - 1] = '\0';
+
+		rsu_log(RSU_DEBUG, "RSU %-16s %016llX - %016llX (%X)\n",
+			spt.partition[x].name, spt.partition[x].offset,
+			(spt.partition[x].offset +
+			spt.partition[x].length - 1),
+			spt.partition[x].flags);
+
+		if (strcmp(spt.partition[x].name, "SPT0") == 0)
+			spt0_found = 1;
+		else if (strcmp(spt.partition[x].name, "SPT1") == 0)
+			spt1_found = 1;
+		else if (strcmp(spt.partition[x].name, "CPB0") == 0)
+			cpb0_found = 1;
+		else if (strcmp(spt.partition[x].name, "CPB1") == 0)
+			cpb1_found = 1;
+	}
+
+	if (!spt0_found || !spt1_found || !cpb0_found || !cpb1_found) {
+		rsu_log(RSU_ERR, "Missing a critical entry in the SPT\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * load_spt() - retrieve SPT from flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int load_spt(void)
+{
+	int spt0_good = 0;
+	int spt1_good = 0;
+
+	rsu_log(RSU_DEBUG, "reading SPT1\n");
+	if (read_dev(spt1_offset, &spt, sizeof(spt)) == 0 &&
+	    spt.magic_number == SPT_MAGIC_NUMBER) {
+		if (check_spt() == 0)
+			spt1_good = 1;
+		else
+			rsu_log(RSU_ERR, "SPT1 validity check failed\n");
+	} else {
+		rsu_log(RSU_ERR, "Bad SPT1 magic number 0x%08X\n",
+			spt.magic_number);
+	}
+
+	rsu_log(RSU_DEBUG, "reading SPT0\n");
+	if (read_dev(spt0_offset, &spt, sizeof(spt)) == 0 &&
+	    spt.magic_number == SPT_MAGIC_NUMBER) {
+		if (check_spt() == 0)
+			spt0_good = 1;
+		else
+			rsu_log(RSU_ERR, "SPT0 validity check failed\n");
+	} else {
+		rsu_log(RSU_ERR, "Bad SPT0 magic number 0x%08X\n",
+			spt.magic_number);
+	}
+
+	if (spt0_good && spt1_good) {
+		rsu_log(RSU_INFO, "SPTs are GOOD!!!\n");
+		return 0;
+	}
+
+	if (spt0_good) {
+		rsu_log(RSU_WARNING, "warning: Restoring SPT1\n");
+		if (erase_dev(spt1_offset, 32 * 1024)) {
+			rsu_log(RSU_ERR, "Erase SPT1 region failed\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)0xFFFFFFFF;
+		if (write_dev(spt1_offset, &spt, sizeof(spt))) {
+			rsu_log(RSU_ERR, "Unable to write SPT1 table\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)SPT_MAGIC_NUMBER;
+		if (write_dev(spt1_offset, &spt.magic_number,
+			      sizeof(spt.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to wr SPT1 magic #\n");
+			return -1;
+		}
+
+		return 0;
+	}
+
+	if (spt1_good) {
+		if (read_dev(spt1_offset, &spt, sizeof(spt)) ||
+		    spt.magic_number != SPT_MAGIC_NUMBER || check_spt()) {
+			rsu_log(RSU_ERR, "Failed to load SPT1\n");
+			return -1;
+		}
+
+		rsu_log(RSU_WARNING, "Restoring SPT0");
+
+		if (erase_dev(spt0_offset, 32 * 1024)) {
+			rsu_log(RSU_ERR, "Erase SPT0 region failed\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)0xFFFFFFFF;
+		if (write_dev(spt1_offset, &spt, sizeof(spt))) {
+			rsu_log(RSU_ERR, "Unable to write SPT0 table\n");
+			return -1;
+		}
+
+		spt.magic_number = (u32)SPT_MAGIC_NUMBER;
+		if (write_dev(spt1_offset, &spt.magic_number,
+			      sizeof(spt.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to wr SPT0 magic #\n");
+			return -1;
+		}
+
+		return 0;
+	}
+
+	rsu_log(RSU_ERR, "no valid SPT0 and SPT1 found\n");
+	return -1;
+}
+
+/**
+ * check_cpb() - check if CPB is valid
+ *
+ * Return: 0 for the valid CPB, or -1 for not
+ */
+static int check_cpb(void)
+{
+	int x, y;
+
+	if (cpb.header.header_size > CPB_HEADER_SIZE) {
+		rsu_log(RSU_WARNING,
+			"CPB header is larger than expected\n");
+		return -1;
+	}
+
+	for (x = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] != ERASED_ENTRY &&
+		    cpb_slots[x] != SPENT_ENTRY) {
+			for (y = 0; y < spt.partitions; y++) {
+				if (cpb_slots[x] ==
+				    spt.partition[y].offset) {
+					rsu_log(RSU_DEBUG,
+						"cpb_slots[%i] = %s\n",
+						x, spt.partition[y].name);
+					break;
+				}
+			}
+
+			if (y >= spt.partitions)
+				rsu_log(RSU_DEBUG,
+					"cpb_slots[%i] = %016llX ???",
+					x, cpb_slots[x]);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * load_cpb() - retrieve CPB from flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int load_cpb(void)
+{
+	int x;
+	int cpb0_part = -1;
+	int cpb0_good = 0;
+	int cpb1_part = -1;
+	int cpb1_good = 0;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "CPB0") == 0)
+			cpb0_part = x;
+		else if (strcmp(spt.partition[x].name, "CPB1") == 0)
+			cpb1_part = x;
+
+		if (cpb0_part >= 0 && cpb1_part >= 0)
+			break;
+	}
+
+	if (cpb0_part < 0 || cpb1_part < 0) {
+		rsu_log(RSU_ERR, "Missing CPB0/1 partition\n");
+		return -1;
+	}
+
+	rsu_log(RSU_DEBUG, "Reading CPB1\n");
+	if (read_part(cpb1_part, 0, &cpb, sizeof(cpb)) == 0 &&
+	    cpb.header.magic_number == CPB_MAGIC_NUMBER) {
+		cpb_slots = (u64 *)
+			     &cpb.data[cpb.header.image_ptr_offset];
+		if (check_cpb() == 0)
+			cpb1_good = 1;
+	} else {
+		rsu_log(RSU_ERR, "Bad CPB1 is bad\n");
+	}
+
+	rsu_log(RSU_DEBUG, "Reading CPB0\n");
+	if (read_part(cpb0_part, 0, &cpb, sizeof(cpb)) == 0 &&
+	    cpb.header.magic_number == CPB_MAGIC_NUMBER) {
+		cpb_slots = (u64 *)
+			     &cpb.data[cpb.header.image_ptr_offset];
+		if (check_cpb() == 0)
+			cpb0_good = 1;
+	} else {
+		rsu_log(RSU_ERR, "Bad CPB0 is bad\n");
+	}
+
+	if (cpb0_good && cpb1_good) {
+		rsu_log(RSU_INFO, "CPBs are GOOD!!!\n");
+		cpb_slots = (u64 *)
+			     &cpb.data[cpb.header.image_ptr_offset];
+		return 0;
+	}
+
+	if (cpb0_good) {
+		rsu_log(RSU_WARNING, "Restoring CPB1\n");
+		if (erase_part(cpb1_part)) {
+			rsu_log(RSU_ERR, "Failed erase CPB1\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(cpb1_part, 0, &cpb, sizeof(cpb))) {
+			rsu_log(RSU_ERR, "Unable to write CPB1 table\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)CPB_MAGIC_NUMBER;
+		if (write_part(cpb1_part, 0, &cpb.header.magic_number,
+			       sizeof(cpb.header.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to write CPB1 magic number\n");
+			return -1;
+		}
+
+		cpb_slots = (u64 *)&cpb.data[cpb.header.image_ptr_offset];
+		return 0;
+	}
+
+	if (cpb1_good) {
+		if (read_part(cpb1_part, 0, &cpb, sizeof(cpb)) ||
+		    cpb.header.magic_number != CPB_MAGIC_NUMBER) {
+			rsu_log(RSU_ERR, "Unable to load CPB1\n");
+			return -1;
+		}
+
+		rsu_log(RSU_WARNING, "Restoring CPB0\n");
+		if (erase_part(cpb0_part)) {
+			rsu_log(RSU_ERR, "Failed erase CPB0\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(cpb0_part, 0, &cpb, sizeof(cpb))) {
+			rsu_log(RSU_ERR, "Unable to write CPB0 table\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)CPB_MAGIC_NUMBER;
+		if (write_part(cpb0_part, 0, &cpb.header.magic_number,
+			       sizeof(cpb.header.magic_number))) {
+			rsu_log(RSU_ERR, "Unable to write CPB0 magic number\n");
+			return -1;
+		}
+
+		cpb_slots = (u64 *)&cpb.data[cpb.header.image_ptr_offset];
+		return 0;
+	}
+
+	rsu_log(RSU_ERR, "No valid CPB0 or CPB1 found\n");
+	return -1;
+}
+
+/**
+ * update_cpb() - update CPB@flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int update_cpb(int slot, u64 ptr)
+{
+	int x;
+	int updates = 0;
+
+	if (slot < 0 || slot > cpb.header.image_ptr_slots)
+		return -1;
+
+	if ((cpb_slots[slot] & ptr) != ptr)
+		return -1;
+
+	cpb_slots[slot] = ptr;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "CPB0") &&
+		    strcmp(spt.partition[x].name, "CPB1"))
+			continue;
+
+		if (write_part(x, 0, &cpb, sizeof(cpb)))
+			return -1;
+
+		updates++;
+	}
+
+	if (updates != 2) {
+		rsu_log(RSU_ERR, "Did not find two CPBs\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * writeback_cpb() - write CPB back to flash
+ *
+ * Return: 0 on success, or -1 for error
+ */
+static int writeback_cpb(void)
+{
+	int x;
+	int updates = 0;
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strcmp(spt.partition[x].name, "CPB0") &&
+		    strcmp(spt.partition[x].name, "CPB1"))
+			continue;
+
+		if (erase_part(x)) {
+			rsu_log(RSU_ERR, "Unable to ease CPBx\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)0xFFFFFFFF;
+		if (write_part(x, 0, &cpb, sizeof(cpb))) {
+			rsu_log(RSU_ERR, "Unable to write CPBx table\n");
+			return -1;
+		}
+
+		cpb.header.magic_number = (u32)CPB_MAGIC_NUMBER;
+		if (write_part(x, 0, &cpb.header.magic_number,
+			       sizeof(cpb.header.magic_number))) {
+			rsu_log(RSU_ERR,
+				"Unable to write CPBx magic number\n");
+			return -1;
+		}
+
+		updates++;
+	}
+
+	if (updates != 2) {
+		rsu_log(RSU_ERR, "Did not find two CPBs\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * partition_count() - get the partition count
+ *
+ * Return: the number of partition@flash
+ */
+static int partition_count(void)
+{
+	return spt.partitions;
+}
+
+/**
+ * partition_name() - get a selected partition name
+ * @part_num: the selected partition number
+ *
+ * Return: partition name on success, or "BAD" on error
+ */
+static char *partition_name(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return "BAD";
+
+	return spt.partition[part_num].name;
+}
+
+/**
+ * partition_offset() - get a selected partition offset
+ * @part_num: the selected partition number
+ *
+ * Return: offset on success, or -1 on error
+ */
+static u64 partition_offset(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	return spt.partition[part_num].offset;
+}
+
+/**
+ * partition_size() - get a selected partition size
+ * @part_num: the selected partition number
+ *
+ * Return: the partition size for success, or -1 for error
+ */
+static u32 partition_size(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	return spt.partition[part_num].length;
+}
+
+/**
+ * partition_reserved() - check if a selected partition is reserved
+ * @part_num: the selected partition number
+ *
+ * Return: 1 for reserved partition, or 0 for not
+ */
+static int partition_reserved(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return 0;
+
+	return (spt.partition[part_num].flags & SPT_FLAG_RESERVED) ? 1 : 0;
+}
+
+/**
+ * partition_readonly() - check if a selected partition is read only
+ * @part_num: the selected partition number
+ *
+ * Return: 1 for read only partition, or 0 for not
+ */
+static int partition_readonly(int part_num)
+{
+	if (part_num < 0 || part_num >= spt.partitions)
+		return 0;
+
+	return (spt.partition[part_num].flags & SPT_FLAG_READONLY) ? 1 : 0;
+}
+
+/**
+ * partition_rename() - rename the selected partition name
+ * @part_num: the selected partition
+ * @name: the new name
+ *
+ * Return: 0 for success, or -1 on error
+ */
+static int partition_rename(int part_num, char *name)
+{
+	int x;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	if (strnlen(name, sizeof(spt.partition[0].name)) >=
+	    sizeof(spt.partition[0].name)) {
+		rsu_log(RSU_ERR,
+			"Partition name is too long - limited to %li",
+			sizeof(spt.partition[0].name) - 1);
+		return -1;
+	}
+
+	for (x = 0; x < spt.partitions; x++) {
+		if (strncmp(spt.partition[x].name, name,
+			    sizeof(spt.partition[0].name) - 1) == 0) {
+			rsu_log(RSU_ERR,
+				"Partition rename already in use\n");
+			return -1;
+		}
+	}
+
+	rsu_misc_safe_strcpy(spt.partition[part_num].name,
+			     sizeof(spt.partition[0].name),
+			     name, sizeof(spt.partition[0].name));
+
+	if (writeback_spt())
+		return -1;
+
+	if (load_spt())
+		return -1;
+
+	return 0;
+}
+
+/**
+ * priority_get() - get the selected partition's priority
+ * @part_num: the selected partition number
+ *
+ * Return: 0 for success, or -1 on error
+ */
+static int priority_get(int part_num)
+{
+	int x;
+	int priority = 0;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	for (x = cpb.header.image_ptr_slots; x > 0; x--) {
+		if (cpb_slots[x - 1] != ERASED_ENTRY &&
+		    cpb_slots[x - 1] != SPENT_ENTRY) {
+			priority++;
+			if (cpb_slots[x - 1] ==
+			    spt.partition[part_num].offset)
+				return priority;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * priority_add() - enable the selected partition's priority
+ * @part_num: the selected partition number
+ *
+ * Return: 0 for success, or  -1 on error
+ */
+static int priority_add(int part_num)
+{
+	int x;
+	int y;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	for (x = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] == ERASED_ENTRY) {
+			if (update_cpb(x,
+				       spt.partition[part_num].offset)) {
+				load_cpb();
+				return -1;
+			}
+			return load_cpb();
+		}
+	}
+
+	rsu_log(RSU_DEBUG, "Compressing CPB\n");
+
+	for (x = 0, y = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] != ERASED_ENTRY &&
+		    cpb_slots[x] != SPENT_ENTRY) {
+			cpb_slots[y++] = cpb_slots[x];
+		}
+	}
+
+	if (y < cpb.header.image_ptr_slots)
+		cpb_slots[y++] = spt.partition[part_num].offset;
+	else
+		return -1;
+
+	while (y < cpb.header.image_ptr_slots)
+		cpb_slots[y++] = ERASED_ENTRY;
+
+	if (writeback_cpb() || load_cpb())
+		return -1;
+
+	return 0;
+}
+
+/**
+ * priority_remove() - remove the selected partition's priority
+ * @part_num: the selected partition number
+ *
+ * Return: 0 for success, or -1 on error
+ */
+static int priority_remove(int part_num)
+{
+	int x;
+
+	if (part_num < 0 || part_num >= spt.partitions)
+		return -1;
+
+	for (x = 0; x < cpb.header.image_ptr_slots; x++) {
+		if (cpb_slots[x] == spt.partition[part_num].offset)
+			if (update_cpb(x, SPENT_ENTRY)) {
+				load_cpb();
+				return -1;
+			}
+	}
+
+	return load_cpb();
+}
+
+/**
+ * data_read() - read data from flash
+ * @part_num: partition number
+ * @offset: offset which data will be read from
+ * @bytes: data size in byte which will be read
+ * @buf: pointer to buffer contains to be read data
+ *
+ * Return: 0 for success, or error code
+ */
+static int data_read(int part_num, int offset, int bytes, void *buf)
+{
+	return read_part(part_num, offset, buf, bytes);
+}
+
+/**
+ * data_write() - write data to flash
+ * @part_num: partition number
+ * @part_num: offset which data will be written to
+ * @bytes: data size in bytes which will be written
+ * @buf: pointer to buffer contains to be written data
+ *
+ * Return: 0 for success, or error code
+ */
+static int data_write(int part_num, int offset, int bytes, void *buf)
+{
+	return write_part(part_num, offset, buf, bytes);
+}
+
+/**
+ * data_erase() - erase flash data
+ * @part_num: partition number
+ *
+ * Return: 0 for success, or error code
+ */
+static int data_erase(int part_num)
+{
+	return erase_part(part_num);
+}
+
+/**
+ * image_load() - load production or factory image
+ * @offset: the image offset
+ *
+ * Return: 0 for success, or error code
+ */
+static int image_load(u64 offset)
+{
+	u32 flash_offset[2];
+
+	flash_offset[0] = lower_32_bits(offset);
+	flash_offset[1] = upper_32_bits(offset);
+
+	rsu_log(RSU_DEBUG, "RSU_DEBUG: RSU updated to 0x%08x%08x\n",
+		flash_offset[1], flash_offset[0]);
+
+	if (mbox_rsu_update(flash_offset))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * status_log() - get firmware status info
+ * @info: pointer to rsu_status_info
+ *
+ * Return: 0 for success, or error code
+ */
+static int status_log(struct rsu_status_info *info)
+{
+	if (mbox_rsu_status((u32 *)info,
+			    sizeof(struct rsu_status_info))) {
+		rsu_log(RSU_ERR,
+			"RSU: Firmware or flash content not supporting RSU\n");
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static void ll_exit(void)
+{
+	if (flash) {
+		spi_flash_free(flash);
+		flash = NULL;
+	}
+}
+
+static struct rsu_ll_intf qspi_ll_intf = {
+	.exit = ll_exit,
+
+	.partition.count = partition_count,
+	.partition.name = partition_name,
+	.partition.offset = partition_offset,
+	.partition.size = partition_size,
+	.partition.reserved = partition_reserved,
+	.partition.readonly = partition_readonly,
+	.partition.rename = partition_rename,
+
+	.priority.get = priority_get,
+	.priority.add = priority_add,
+	.priority.remove = priority_remove,
+
+	.data.read = data_read,
+	.data.write = data_write,
+	.data.erase = data_erase,
+
+	.fw_ops.load = image_load,
+	.fw_ops.status = status_log
+};
+
+int rsu_ll_qspi_init(struct rsu_ll_intf **intf)
+{
+	u32 spt_offset[4];
+
+	/* retrieve data from flash */
+	flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
+				CONFIG_SF_DEFAULT_CS,
+				CONFIG_SF_DEFAULT_SPEED,
+				CONFIG_SF_DEFAULT_MODE);
+	if (!flash) {
+		rsu_log(RSU_ERR, "SPI probe failed.\n");
+		return -ENODEV;
+	}
+
+	/* get the offset from firmware */
+	if (mbox_rsu_get_spt_offset(spt_offset, 4)) {
+		rsu_log(RSU_ERR, "Error from mbox_rsu_get_spt_offset\n");
+		return -ECOMM;
+	}
+
+	spt0_offset = spt_offset[1];
+	spt1_offset = spt_offset[3];
+	rsu_log(RSU_DEBUG, "SPT0 offset 0x%08x\n", spt0_offset);
+	rsu_log(RSU_DEBUG, "SPT1 offset 0x%08x\n", spt1_offset);
+
+	if (load_spt()) {
+		rsu_log(RSU_ERR, "Bad SPT\n");
+		return -1;
+	}
+
+	if (load_cpb()) {
+		rsu_log(RSU_ERR, "Bad CPB\n");
+		return -1;
+	}
+
+	*intf = &qspi_ll_intf;
+
+	return 0;
+}
diff --git a/arch/arm/mach-socfpga/rsu_misc.c b/arch/arm/mach-socfpga/rsu_misc.c
new file mode 100644
index 0000000..74760e9
--- /dev/null
+++ b/arch/arm/mach-socfpga/rsu_misc.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <asm/arch/rsu.h>
+#include <asm/arch/rsu_ll.h>
+#include <asm/arch/rsu_misc.h>
+#include <asm/types.h>
+#include <u-boot/zlib.h>
+#include <errno.h>
+
+#define LOG_BUF_SIZE	1024
+
+static char *cb_buffer;
+static int cb_buffer_togo;
+
+static char *reserved_names[] = {
+	"BOOT_INFO",
+	"FACTORY_IMAGE",
+	"SPT",
+	"SPT0",
+	"SPT1",
+	"CPB",
+	"CPB0",
+	"CPB1",
+	""
+};
+
+struct pointer_block {
+	u32 num_ptrs;
+	u32 RSVD0;
+	u64 ptrs[4];
+	u8 RSVD1[0xd4];
+	u32 crc;
+};
+
+/**
+ * swap_bits() - swap bits
+ * @data: pointer point to data
+ * @len: data length
+ */
+static void swap_bits(char *data, int len)
+{
+	int x, y;
+	char tmp;
+
+	for (x = 0; x < len; x++) {
+		tmp = 0;
+		for (y = 0; y < 8; y++) {
+			tmp <<= 1;
+			if (data[x] & 1)
+				tmp |= 1;
+			data[x] >>= 1;
+		}
+		data[x] = tmp;
+	}
+}
+
+/**
+ * rsu_misc_image_adjust() - adjust values in the pointer block
+ *
+ * @block - pointer to the start of the second 4KB block of an image
+ * @info - rsu_slot_info structure for target slot
+ *
+ * Adjust values in the 256 byte pointer block for the offset of the
+ * slot being programmed. The pointer block is in the second 4KB block
+ * of the image. The pointer block contains a CRC of the entire 4KB block.
+ *
+ * Returns 0 on success and block was adjusted, or -1 on error
+ */
+int rsu_misc_image_adjust(void *block, struct rsu_slot_info *info)
+{
+	u32 calc_crc;
+	int x;
+	char *data = (char *)block;
+	struct pointer_block *ptr_blk = (struct pointer_block *)(data
+					 + IMAGE_PTR_START
+					 - IMAGE_PTR_BLOCK);
+
+	/*
+	 * Check CRC on 4KB block before proceeding.  All bytes must be
+	 * bit-swapped before they can used in zlib CRC32 library function
+	 * The CRC value is stored in big endian in the bitstream.
+	 */
+	swap_bits(block, IMAGE_BLOCK_SZ);
+	calc_crc = crc32(0, block, IMAGE_PTR_CRC - IMAGE_BLOCK_SZ);
+	if (be32_to_cpu(ptr_blk->crc) != calc_crc) {
+		rsu_log(RSU_ERR, "Bad CRC. Calc = %08X / Fm Block = %08x",
+			calc_crc, be32_to_cpu(ptr_blk->crc));
+		return -1;
+	}
+	swap_bits(block, IMAGE_BLOCK_SZ);
+
+	/*
+	 * Adjust main image pointers after they pass a validity test.
+	 * Return -1 if an error is found, or 0 if the block looks OK
+	 * (adjusted or not adjusted)
+	 */
+	if (ptr_blk->num_ptrs == 0)
+		return 0;
+
+	if (ptr_blk->num_ptrs > 4) {
+		rsu_log(RSU_ERR, "Invalid number of pointers in block\n");
+		return -1;
+	}
+
+	for (x = 0; x < ptr_blk->num_ptrs; x++) {
+		if (ptr_blk->ptrs[x] > (u64)info->size) {
+			rsu_log(RSU_DEBUG, "A ptr > 0x%llX, not adjust\n",
+				(u64)info->size);
+
+			for (x = 0; x < ptr_blk->num_ptrs; x++) {
+				if (ptr_blk->ptrs[x] < info->offset ||
+				    ptr_blk->ptrs[x] >=
+				    (info->offset + info->size)) {
+					rsu_log(RSU_ERR,
+						"ptr not in the slot\n");
+					return -1;
+				}
+			}
+
+			return 0;
+		}
+	}
+
+	for (x = 0; x < ptr_blk->num_ptrs; x++)
+		ptr_blk->ptrs[x] += info->offset;
+
+	/* Update CRC in block */
+	swap_bits(block, IMAGE_BLOCK_SZ);
+	calc_crc = crc32(0, block, IMAGE_PTR_CRC - IMAGE_BLOCK_SZ);
+	ptr_blk->crc = cpu_to_be32(calc_crc);
+	swap_bits(block, IMAGE_BLOCK_SZ);
+
+	return 0;
+}
+
+/**
+ * rsu_misc_is_rsvd_name() - check if a reserved name
+ *
+ * @name: name to check
+ *
+ * Returns 1 if a reserved name, or 0 for not
+ */
+int rsu_misc_is_rsvd_name(char *name)
+{
+	int x;
+
+	for (x = 0; reserved_names[x][0] != '\0'; x++)
+		if (strcmp(name, reserved_names[x]) == 0)
+			return 1;
+
+	return 0;
+}
+
+/**
+ * rsu_misc_is_slot() - check if a read only or reserved partition
+ * @ll_intf: pointer to ll_intf
+ * @part_num: partition number
+ *
+ * Return 1 if not read only or not reserved, or 0 for is
+ */
+int rsu_misc_is_slot(struct rsu_ll_intf *ll_intf, int part_num)
+{
+	int readonly = ll_intf->partition.readonly(part_num);
+	int reserved = ll_intf->partition.reserved(part_num);
+
+	if (readonly || reserved)
+		return 0;
+
+	if (rsu_misc_is_rsvd_name(ll_intf->partition.name(part_num)))
+		return 0;
+
+	return 1;
+}
+
+/**
+ * rsu_misc_slot2part() - get partition number from the slot
+ * @ll_intf: pointer to ll_intf
+ * @slot: slot number
+ *
+ * Return 0 if success, or -1 for error
+ */
+int rsu_misc_slot2part(struct rsu_ll_intf *ll_intf, int slot)
+{
+	int partitions;
+	int cnt = 0;
+
+	partitions = ll_intf->partition.count();
+
+	for (int x = 0; x < partitions; x++) {
+		if (rsu_misc_is_slot(ll_intf, x)) {
+			if (slot == cnt)
+				return x;
+			cnt++;
+		}
+	}
+
+	return -1;
+}
+
+/**
+ * rsu_misc_writeprotected() - check if a slot is protected
+ * @slot: the number of slot to be checked
+ *
+ * Return 1 if a slot is protected, 0 for not
+ */
+int rsu_misc_writeprotected(int slot)
+{
+	char *protected;
+	int protected_slot_numb;
+
+	/* protect works only for slot 0-31 */
+	if (slot > 31)
+		return 0;
+
+	protected = env_get("rsu_protected_slot");
+	if (!strcmp(protected, "")) {
+		/* user doesn't set protected slot */
+		return 0;
+	}
+
+	protected_slot_numb = (int)simple_strtol(protected, NULL, 0);
+	if (protected_slot_numb < 0 || protected_slot_numb > 31) {
+		rsu_log(RSU_WARNING,
+			"protected slot works only on the first 32 slots\n");
+		return 0;
+	}
+
+	if (protected_slot_numb == slot)
+		return 1;
+	else
+		return 0;
+}
+
+/**
+ * rsu_misc_safe_strcpy() - buffer copy
+ * @dst: pointer to dst
+ * @dsz: dst buffer size
+ * @src: pointer to src
+ * @ssz: src buffer size
+ */
+void rsu_misc_safe_strcpy(char *dst, int dsz, char *src, int ssz)
+{
+	int len;
+
+	if (!dst || dsz <= 0)
+		return;
+
+	if (!src || ssz <= 0) {
+		dst[0] = '\0';
+		return;
+	}
+
+	len = strnlen(src, ssz);
+	if (len >= dsz)
+		len = dsz - 1;
+
+	memcpy(dst, src, len);
+	dst[len] = '\0';
+}
+
+/**
+ * rsu_cb_buf_init() - initialize buffer parameters
+ * @buf: pointer to buf
+ * @size: size of buffer
+ *
+ * Return 0 if success, or -1 for error
+ */
+int rsu_cb_buf_init(void *buf, int size)
+{
+	if (!buf || size <= 0)
+		return -1;
+
+	cb_buffer = (char *)buf;
+	cb_buffer_togo = size;
+
+	return 0;
+}
+
+/**
+ * rsu_cb_buf_exit() - reset buffer parameters
+ */
+void rsu_cb_buf_exit(void)
+{
+	cb_buffer = NULL;
+	cb_buffer_togo = -1;
+}
+
+/**
+ * rsu_cb_buf() - copy data to buffer
+ * @buf: pointer to data buffer
+ * @len: size of data buffer
+ *
+ * Return the buffer data size
+ */
+int rsu_cb_buf(void *buf, int len)
+{
+	int read_len;
+
+	if (!cb_buffer_togo)
+		return 0;
+
+	if (!cb_buffer || cb_buffer_togo < 0 || !buf || len < 0)
+		return -1;
+
+	if (cb_buffer_togo < len)
+		read_len = cb_buffer_togo;
+	else
+		read_len = len;
+
+	memcpy(buf, cb_buffer, read_len);
+
+	cb_buffer += read_len;
+	cb_buffer_togo -= read_len;
+
+	if (!cb_buffer_togo)
+		cb_buffer = NULL;
+
+	return read_len;
+}
+
+/**
+ * rsu_cb_program_common - callback to program flash
+ * @ll_intf: pointer to ll_intf
+ * @slot: slot number
+ * @callback: callback function pointer
+ * @rawdata: flag (raw data or not)
+ *
+ * Return 0 if success, or error code
+ */
+int rsu_cb_program_common(struct rsu_ll_intf *ll_intf, int slot,
+			  rsu_data_callback callback, int rawdata)
+{
+	int part_num;
+	int offset;
+	unsigned char buf[IMAGE_BLOCK_SZ];
+	unsigned char vbuf[IMAGE_BLOCK_SZ];
+	int cnt, c, done;
+	int x;
+	struct rsu_slot_info info;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	if (slot < 0)
+		return -ESLOTNUM;
+
+	if (rsu_misc_writeprotected(slot)) {
+		rsu_log(RSU_ERR,
+			"Trying to program a write protected slot\n");
+		return -EWRPROT;
+	}
+
+	if (rsu_slot_get_info(slot, &info)) {
+		rsu_log(RSU_ERR, "Unable to read slot info\n");
+		return -ESLOTNUM;
+	}
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (ll_intf->priority.get(part_num) > 0) {
+		rsu_log(RSU_ERR,
+			"Trying to program a slot already in use\n");
+		return -EPROGRAM;
+	}
+
+	if (!callback)
+		return -EARGS;
+
+	offset = 0;
+	done = 0;
+
+	while (!done) {
+		cnt = 0;
+		while (cnt < IMAGE_BLOCK_SZ) {
+			c = callback(buf + cnt, IMAGE_BLOCK_SZ - cnt);
+			if (c == 0) {
+				done = 1;
+				break;
+			} else if (c < 0) {
+				return -ECALLBACK;
+			}
+			cnt += c;
+		}
+
+		if (cnt == 0)
+			break;
+
+		if (!rawdata && offset == IMAGE_PTR_BLOCK &&
+		    cnt == IMAGE_BLOCK_SZ &&
+		    rsu_misc_image_adjust(buf, &info))
+			return -EPROGRAM;
+
+		if ((offset + cnt) > ll_intf->partition.size(part_num)) {
+			rsu_log(RSU_ERR,
+				"Trying to program too much data into slot\n");
+			return -ESIZE;
+		}
+
+		if (ll_intf->data.write(part_num, offset, cnt, buf))
+			return -ELOWLEVEL;
+
+		if (ll_intf->data.read(part_num, offset, cnt, vbuf))
+			return -ELOWLEVEL;
+
+		for (x = 0; x < cnt; x++)
+			if (vbuf[x] != buf[x]) {
+				rsu_log(RSU_DEBUG,
+					"Expect %02X, got %02X @ 0x%08X\n",
+					buf[x], vbuf[x], offset + x);
+				return -ECMP;
+			}
+
+		offset += cnt;
+	}
+
+	if (!rawdata && ll_intf->priority.add(part_num))
+		return -ELOWLEVEL;
+
+	return 0;
+}
+
+/**
+ * rsu_cb_verify_common() - callback for data verification
+ * @ll_intf: pointer to ll_intf
+ * @slot: slot number
+ * @callback: callback function pointer
+ * @rawdata: flag (raw data or not)
+ *
+ * Return 0 if success, or error code
+ */
+int rsu_cb_verify_common(struct rsu_ll_intf *ll_intf, int slot,
+			 rsu_data_callback callback, int rawdata)
+{
+	int part_num;
+	int offset;
+	unsigned char buf[IMAGE_BLOCK_SZ];
+	unsigned char vbuf[IMAGE_BLOCK_SZ];
+	int cnt, c, done;
+	int x;
+
+	if (!ll_intf)
+		return -EINTF;
+
+	part_num = rsu_misc_slot2part(ll_intf, slot);
+	if (part_num < 0)
+		return -ESLOTNUM;
+
+	if (!rawdata && ll_intf->priority.get(part_num) <= 0) {
+		rsu_log(RSU_ERR, "Trying to verify a slot not in use\n");
+		return -EERASE;
+	}
+
+	if (!callback)
+		return -EARGS;
+
+	offset = 0;
+	done = 0;
+
+	while (!done) {
+		cnt = 0;
+		while (cnt < IMAGE_BLOCK_SZ) {
+			c = callback(buf + cnt, IMAGE_BLOCK_SZ - cnt);
+			if (c == 0) {
+				done = 1;
+				break;
+			} else if (c < 0) {
+				return -ECALLBACK;
+			}
+
+			cnt += c;
+		}
+
+		if (cnt == 0)
+			break;
+
+		if (ll_intf->data.read(part_num, offset, cnt, vbuf))
+			return -ELOWLEVEL;
+
+		for (x = 0; x < cnt; x++) {
+			if (!rawdata && (offset + x) >= IMAGE_PTR_START &&
+			    (offset + x) <= IMAGE_PTR_END)
+				continue;
+
+			if (vbuf[x] != buf[x]) {
+				rsu_log(RSU_ERR,
+					"Expect %02X, got %02X @ 0x%08X",
+					buf[x], vbuf[x], offset + x);
+				return -ECMP;
+			}
+		}
+		offset += cnt;
+	}
+
+	return 0;
+}
+
+/*
+ * rsu_log() - display rsu log message
+ * @level: log level
+ * @format: log message format
+ */
+void rsu_log(const enum rsu_log_level level, const char *format, ...)
+{
+	va_list args;
+	int log_level;
+	char printbuffer[LOG_BUF_SIZE];
+
+	log_level = (int)simple_strtol(env_get("rsu_log_level"), NULL, 0);
+
+	if (level >= log_level)
+		return;
+
+	va_start(args, format);
+	vscnprintf(printbuffer, sizeof(printbuffer), format, args);
+	va_end(args);
+	puts(printbuffer);
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [U-Boot] [PATCHv1 3/5] arm: socfpga: stratix10: add environment variables for RSU support
  2019-09-09 17:35 [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 1/5] arm: socfpga: stratix10: add RSU mailbox support richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 2/5] arm: socfpga: stratix10: add RSU support for Stratix10 SoC richard.gong at linux.intel.com
@ 2019-09-09 17:35 ` richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 4/5] arm: socfpga: stratix10: add console commands " richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 5/5] arm: socfpga: enable RSU build richard.gong at linux.intel.com
  4 siblings, 0 replies; 6+ messages in thread
From: richard.gong at linux.intel.com @ 2019-09-09 17:35 UTC (permalink / raw)
  To: u-boot

From: Richard Gong <richard.gong@intel.com>

Add two RSU environment variables:
1. rsu_log_level
	the variable is unsigned integer and its default value is
	RSU_DEBUG (7), which only show log with RSU_INFO,RSU_WARNING and
	RSU_ERR.

	To enable all logs (RSU_ERR, RSU_WARNING, RSU_INFO and RSU_DEBUG),
	you need set log level to 8 or above via “setenv rsu_log_level 8”.

	To disable all logs, you need set log level to 3 or below.

2. rsu_protected_slot
	by default there is no protected RSU slot, you need run
	"setenv rsu_protected_slot <slot_num>” to set a slot protected,
	and “setenv rsu_protected_slot <a space>” to unset a protected
	slot.

Signed-off-by: Richard Gong <richard.gong@intel.com>
---
 arch/arm/mach-socfpga/misc_s10.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/mach-socfpga/misc_s10.c b/arch/arm/mach-socfpga/misc_s10.c
index 0a5fab1..c9a6f00 100644
--- a/arch/arm/mach-socfpga/misc_s10.c
+++ b/arch/arm/mach-socfpga/misc_s10.c
@@ -21,6 +21,8 @@
 
 #include <dt-bindings/reset/altr,rst-mgr-s10.h>
 
+#define RSU_DEFAULT_LOG_LEVEL  7
+
 DECLARE_GLOBAL_DATA_PTR;
 
 static struct socfpga_system_manager *sysmgr_regs =
@@ -136,10 +138,17 @@ int print_cpuinfo(void)
 int arch_misc_init(void)
 {
 	char qspi_string[13];
+	char level[4];
+
+	snprintf(level, sizeof(level), "%u", RSU_DEFAULT_LOG_LEVEL);
 
 	sprintf(qspi_string, "<0x%08x>", cm_get_qspi_controller_clk_hz());
 	env_set("qspi_clock", qspi_string);
 
+	/* setup for RSU */
+	env_set("rsu_protected_slot", "");
+	env_set("rsu_log_level", level);
+
 	socfpga_set_phymode();
 	return 0;
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [U-Boot] [PATCHv1 4/5] arm: socfpga: stratix10: add console commands for RSU support
  2019-09-09 17:35 [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC richard.gong at linux.intel.com
                   ` (2 preceding siblings ...)
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 3/5] arm: socfpga: stratix10: add environment variables for RSU support richard.gong at linux.intel.com
@ 2019-09-09 17:35 ` richard.gong at linux.intel.com
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 5/5] arm: socfpga: enable RSU build richard.gong at linux.intel.com
  4 siblings, 0 replies; 6+ messages in thread
From: richard.gong at linux.intel.com @ 2019-09-09 17:35 UTC (permalink / raw)
  To: u-boot

From: Richard Gong <richard.gong@intel.com>

The Intel Remote System Update (RSU) provides a way for users to update
the QSPI configuration bitstream of a Intel Stratix 10 SoC device with
significantly reduced risk of corrupting the bitstream storage and
bricking the system.

This patch provides console commands to exercises the RSU APIs at
Intel Stratix10 SoC. The commands use the term "slot" to refer to a
sub-partition which is intended to contain a production image, and the
term "priority" to refer to the fact that the images are loaded by
firmware in the defined order.

The provided console commands are below, you can also refer to the
header file rsu.h for the details of RSU APIs:
	slot_count
	slot_by_name
	slot_get_info
	slot_size
	slot_priority
	slot_erase
	slot_program_buf
	slot_program_buf_raw
	slot_verify_buf
	slot_verify_buf_raw
	slot_enable
	slot_disable
	slot_load
	slot_load_factory
	slot_rename
	status_log
	list
	dtb
	update
	notify
	clear_error_status
	reset_retry_counter

Signed-off-by: Richard Gong <richard.gong@intel.com>
Signed-off-by: Radu Bacrau <radu.bacrau@intel.com>
Signed-off-by: Chin Liang See <chin.liang.see@intel.com>
---
 arch/arm/mach-socfpga/include/mach/rsu_s10.h |  46 ++
 arch/arm/mach-socfpga/rsu_s10.c              | 817 +++++++++++++++++++++++++++
 2 files changed, 863 insertions(+)
 create mode 100644 arch/arm/mach-socfpga/include/mach/rsu_s10.h
 create mode 100644 arch/arm/mach-socfpga/rsu_s10.c

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 0000000..9a4ec53
--- /dev/null
+++ b/arch/arm/mach-socfpga/include/mach/rsu_s10.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ */
+#ifndef _RSU_S10_H_
+#define _RSU_S10_H_
+
+extern u32 smc_rsu_update_address;
+
+#define RSU_S10_CPB_MAGIC_NUMBER	0x57789609
+#define RSU_S10_SPT_MAGIC_NUMBER	0x57713427
+
+#define SPT0_INDEX	1
+#define SPT1_INDEX	3
+
+/* 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[16];
+	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/rsu_s10.c b/arch/arm/mach-socfpga/rsu_s10.c
new file mode 100644
index 0000000..8127e08
--- /dev/null
+++ b/arch/arm/mach-socfpga/rsu_s10.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019 Intel Corporation
+ *
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <asm/arch/mailbox_s10.h>
+#include <asm/arch/rsu.h>
+#include <asm/arch/rsu_s10.h>
+#include <spi.h>
+#include <spi_flash.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct socfpga_rsu_s10_cpb rsu_cpb = {0};
+struct socfpga_rsu_s10_spt rsu_spt = {0};
+u32 rsu_spt0_offset = 0, rsu_spt1_offset = 0;
+
+static int initialized;
+
+static int rsu_print_status(void)
+{
+	struct rsu_status_info status_info;
+
+	if (mbox_rsu_status((u32 *)&status_info, sizeof(status_info))) {
+		puts("RSU: Firmware or flash content not supporting RSU\n");
+		return -ENOTSUPP;
+	}
+	puts("RSU: Remote System Update Status\n");
+	printf("Current Image\t: 0x%08llx\n", status_info.current_image);
+	printf("Last Fail Image\t: 0x%08llx\n", status_info.fail_image);
+	printf("State\t\t: 0x%08x\n", status_info.state);
+	printf("Version\t\t: 0x%08x\n", status_info.version);
+	printf("Error location\t: 0x%08x\n", status_info.error_location);
+	printf("Error details\t: 0x%08x\n", status_info.error_details);
+	if (status_info.version)
+		printf("Retry counter\t: 0x%08x\n", status_info.retry_counter);
+
+	return 0;
+}
+
+static void rsu_print_spt_slot(void)
+{
+	int i;
+
+	puts("RSU: Sub-partition table content\n");
+	for (i = 0; i < rsu_spt.entries; i++) {
+		printf("%16s\tOffset:0x%08x%08x\tLength:0x%08x\tFlag:0x%08x\n",
+		       rsu_spt.spt_slot[i].name,
+		       rsu_spt.spt_slot[i].offset[1],
+		       rsu_spt.spt_slot[i].offset[0],
+		       rsu_spt.spt_slot[i].length,
+		       rsu_spt.spt_slot[i].flag);
+	}
+}
+
+static void rsu_print_cpb_slot(void)
+{
+	int i, j = 1;
+
+	puts("RSU: CMF pointer block's image pointer list\n");
+	for (i = rsu_cpb.nslots - 1; i >= 0; i--) {
+		if (rsu_cpb.pointer_slot[i] != ~0 &&
+		    rsu_cpb.pointer_slot[i] != 0) {
+			printf("Priority %d Offset: 0x%016llx nslot: %d\n",
+			       j, rsu_cpb.pointer_slot[i], i);
+			j++;
+		}
+	}
+}
+
+static u32 rsu_spt_slot_find_cpb(void)
+{
+	int i;
+
+	for (i = 0; i < rsu_spt.entries; i++) {
+		if (strstr(rsu_spt.spt_slot[i].name, "CPB0"))
+			return rsu_spt.spt_slot[i].offset[0];
+	}
+	puts("RSU: Cannot find SPT0 entry from sub-partition table\n");
+	return 0;
+}
+
+static u32 rsu_get_boot_part_len(void)
+{
+	int i;
+	u32 offset = 0, len = 0;
+
+	/* look for last entry that has largest offset */
+	for (i = 0; i < rsu_spt.entries; i++) {
+		if (rsu_spt.spt_slot[i].offset[0] > offset) {
+			offset = rsu_spt.spt_slot[i].offset[0];
+			len = rsu_spt.spt_slot[i].length;
+		}
+	}
+
+	/* With the len, we shall know the boot partition size */
+	len += offset;
+	return roundup(len, 64 << 10);	/* align to 64kB, flash sector size */
+}
+
+int rsu_spt_cpb_list(int argc, char * const argv[])
+{
+	u32 spt_offset[4];
+	u32 cpb_offset;
+	int err;
+	struct spi_flash *flash;
+
+	if (argc != 1)
+		return CMD_RET_USAGE;
+
+	/* print the RSU status */
+	err = rsu_print_status();
+	if (err)
+		return err;
+
+	/* retrieve the sub-partition table (spt) offset from firmware */
+	if (mbox_rsu_get_spt_offset(spt_offset, 4)) {
+		puts("RSU: Error from mbox_rsu_get_spt_offset\n");
+		return -ECOMM;
+	}
+	rsu_spt0_offset = spt_offset[SPT0_INDEX];
+	rsu_spt1_offset = spt_offset[SPT1_INDEX];
+
+	/* update into U-Boot env so we can update into DTS later */
+	env_set_hex("rsu_sbt0", rsu_spt0_offset);
+	env_set_hex("rsu_sbt1", rsu_spt1_offset);
+	printf("RSU: Sub-partition table 0 offset 0x%08x\n", rsu_spt0_offset);
+	printf("RSU: Sub-partition table 1 offset 0x%08x\n", rsu_spt1_offset);
+
+	/* retrieve sub-partition table (spt) from flash */
+	flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
+				CONFIG_SF_DEFAULT_CS,
+				CONFIG_SF_DEFAULT_SPEED,
+				CONFIG_SF_DEFAULT_MODE);
+	if (!flash) {
+		puts("RSU: SPI probe failed.\n");
+		return -ENODEV;
+	}
+	if (spi_flash_read(flash, rsu_spt0_offset, sizeof(rsu_spt), &rsu_spt)) {
+		puts("RSU: spi_flash_read failed\n");
+		return -EIO;
+	}
+
+	/* valid the sub-partition table (spt) magic number */
+	if (rsu_spt.magic_number != RSU_S10_SPT_MAGIC_NUMBER) {
+		printf("RSU: SPT magic number is not match\n");
+		return -EFAULT;
+	}
+
+	/* list the sub-partition table (spt) content */
+	rsu_print_spt_slot();
+
+	/* locate where is CMF pointer block (cpb) */
+	cpb_offset = rsu_spt_slot_find_cpb();
+	if (!cpb_offset)
+		return -ENXIO;
+	printf("RSU: CMF pointer block offset 0x%08x\n", cpb_offset);
+
+	/* retrieve CMF pointer block (cpb) from flash */
+	if (spi_flash_read(flash, cpb_offset, sizeof(rsu_cpb), &rsu_cpb)) {
+		puts("RSU: spi_flash_read failed\n");
+		return -EIO;
+	}
+
+	/* valid the CMF pointer block (cpb) magic number */
+	if (rsu_cpb.magic_number != RSU_S10_CPB_MAGIC_NUMBER) {
+		printf("RSU: CMF pointer block magic number not match 0x%08x\n",
+		       rsu_cpb.magic_number);
+		return -EFAULT;
+	}
+
+	/* list the CMF pointer block (cpb) content */
+	rsu_print_cpb_slot();
+
+	return 0;
+}
+
+int rsu_update(int argc, char * const argv[])
+{
+	u32 flash_offset[2];
+	u64 addr;
+	char *endp;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	addr = simple_strtoul(argv[1], &endp, 16);
+
+	flash_offset[0] = lower_32_bits(addr);
+	flash_offset[1] = upper_32_bits(addr);
+
+	printf("RSU: RSU update to 0x%08x%08x\n",
+	       flash_offset[1], flash_offset[0]);
+	mbox_rsu_update(flash_offset);
+	return 0;
+}
+
+int rsu_dtb(int argc, char * const argv[])
+{
+	char flash0_string[100];
+	const char *fdt_flash0;
+	int nodeoffset, len;
+	u32 reg[2];
+	int err;
+
+	/* Extracting RSU info from bitstream */
+	err = rsu_spt_cpb_list(argc, argv);
+	if (err == -ENOTSUPP)
+		return 0;
+	else if (err)
+		return err;
+
+	/* Extract the flash0's reg from Linux DTB */
+	nodeoffset = fdt_path_offset(working_fdt, "/__symbols__");
+	if (nodeoffset < 0) {
+		puts("DTB: __symbols__ node not found\n");
+		puts("Ensure you load kernel dtb and fdt addr\n");
+		return -ENODEV;
+	}
+	fdt_flash0 = fdt_getprop(working_fdt, nodeoffset, "qspi_boot", &len);
+	if (!fdt_flash0) {
+		puts("DTB: qspi_boot alias node not found. Check your dts\n");
+		return -ENODEV;
+	}
+	strcpy(flash0_string, fdt_flash0);
+	printf("DTB: qspi_boot node@%s\n", flash0_string);
+
+	/* assemble new reg value for boot partition */
+	len = rsu_get_boot_part_len();
+	reg[0] = cpu_to_fdt32(rsu_spt0_offset);
+	reg[1] = cpu_to_fdt32(len  - rsu_spt0_offset);
+
+	/* update back to Linux DTB */
+	nodeoffset = fdt_path_offset(working_fdt, flash0_string);
+	if (nodeoffset < 0) {
+		printf("DTB: %s node not found\n", flash0_string);
+		return -ENODEV;
+	}
+	return fdt_setprop(working_fdt, nodeoffset, "reg", reg, sizeof(reg));
+}
+
+static int slot_count(int argc, char * const argv[])
+{
+	int count;
+
+	if (argc != 1)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	count = rsu_slot_count();
+	if (count < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Number of slots = %d.\n", count);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_by_name(int argc, char * const argv[])
+{
+	char *name = argv[1];
+	int slot;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = rsu_slot_by_name(name);
+	if (slot < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot name '%s' is %d.\n", name, slot);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_get_info(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	struct rsu_slot_info info;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	ret = rsu_slot_get_info(slot, &info);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	printf("NAME: %s\n", info.name);
+	printf("OFFSET: 0x%016llX\n", info.offset);
+	printf("SIZE: 0x%08X\n", info.size);
+	if (info.priority)
+		printf("PRIORITY: %i\n", info.priority);
+	else
+		printf("PRIORITY: [disabled]\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_size(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	int size;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	size = rsu_slot_size(slot);
+	if (size < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d size = %d.\n", slot, size);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_priority(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	int priority;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	priority = rsu_slot_priority(slot);
+	if (priority < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d priority = %d.\n", slot, priority);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_erase(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	ret = rsu_slot_erase(slot);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d erased.\n", slot);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_program_buf(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	u64 address;
+	int size;
+	int ret;
+	int addr_lo;
+	int addr_hi;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	address = simple_strtoul(argv[2], &endp, 16);
+	size = simple_strtoul(argv[3], &endp, 16);
+
+	ret = rsu_slot_program_buf(slot, (void *)address, size);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	addr_hi = upper_32_bits(address);
+	addr_lo = lower_32_bits(address);
+	printf("Slot %d was programmed with buffer=0x%08x%08x size=%d.\n",
+	       slot, addr_hi, addr_lo, size);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_program_buf_raw(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	u64 address;
+	int size;
+	int ret;
+	int addr_lo;
+	int addr_hi;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	address = simple_strtoul(argv[2], &endp, 16);
+	size = simple_strtoul(argv[3], &endp, 16);
+
+	ret = rsu_slot_program_buf_raw(slot, (void *)address, size);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	addr_hi = upper_32_bits(address);
+	addr_lo = lower_32_bits(address);
+	printf("Slot %d was programmed with raw buffer=0x%08x%08x size=%d.\n",
+	       slot, addr_hi, addr_lo, size);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_verify_buf(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	u64 address;
+	int size;
+	int ret;
+	int addr_lo;
+	int addr_hi;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	address = simple_strtoul(argv[2], &endp, 16);
+	size = simple_strtoul(argv[3], &endp, 16);
+
+	ret = rsu_slot_verify_buf(slot, (void *)address, size);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	addr_hi = upper_32_bits(address);
+	addr_lo = lower_32_bits(address);
+	printf("Slot %d was verified with buffer=0x%08x%08x size=%d.\n",
+	       slot, addr_hi, addr_lo, size);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_verify_buf_raw(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	u64 address;
+	int size;
+	int ret;
+	int addr_lo;
+	int addr_hi;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	address = simple_strtoul(argv[2], &endp, 16);
+	size = simple_strtoul(argv[3], &endp, 16);
+
+	ret = rsu_slot_verify_buf_raw(slot, (void *)address, size);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	addr_hi = upper_32_bits(address);
+	addr_lo = lower_32_bits(address);
+	printf("Slot %d was verified with raw buffer=0x%08x%08x size=%d.\n",
+	       slot, addr_hi, addr_lo, size);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_enable(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	ret = rsu_slot_enable(slot);
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d enabled.\n", slot);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_disable(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	ret = rsu_slot_disable(slot);
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d disabled.\n", slot);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_load(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	ret = rsu_slot_load(slot);
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d loading.\n", slot);
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_load_factory(int argc, char * const argv[])
+{
+	int ret;
+
+	if (argc != 1)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	ret = rsu_slot_load_factory();
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Factory loading.\n");
+	return CMD_RET_SUCCESS;
+}
+
+static int slot_rename(int argc, char * const argv[])
+{
+	int slot;
+	char *endp;
+	char *name;
+	int ret;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	slot = simple_strtoul(argv[1], &endp, 16);
+	name = argv[2];
+
+	ret = rsu_slot_rename(slot, name);
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Slot %d renamed to %s.\n", slot, name);
+	return CMD_RET_SUCCESS;
+}
+
+static int status_log(int argc, char * const argv[])
+{
+	struct rsu_status_info info;
+	int ret;
+
+	if (argc != 1)
+		return CMD_RET_USAGE;
+
+	if (!initialized) {
+		if (rsu_init(NULL))
+			return CMD_RET_FAILURE;
+
+		initialized = 1;
+	}
+
+	ret = rsu_status_log(&info);
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	printf("Current Image\t: 0x%08llx\n", info.current_image);
+	printf("Last Fail Image\t: 0x%08llx\n", info.fail_image);
+	printf("State\t\t: 0x%08x\n", info.state);
+	printf("Version\t\t: 0x%08x\n", info.version);
+	printf("Error location\t: 0x%08x\n", info.error_location);
+	printf("Error details\t: 0x%08x\n", info.error_details);
+	if (info.version)
+		printf("Retry counter\t: 0x%08x\n", info.retry_counter);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int rsu_notify(int argc, char * const argv[])
+{
+	u32 stage;
+	char *endp;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	stage = simple_strtoul(argv[1], &endp, 16) & GENMASK(0, 15);
+	ret = mbox_hps_stage_notify(stage);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int clear_error_status(int argc, char * const argv[])
+{
+	int arg;
+	int ret;
+
+	arg = RSU_NOTIFY_IGNORE_STAGE | RSU_NOTIFY_CLEAR_ERROR_STATUS;
+	ret = mbox_hps_stage_notify(arg);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int reset_retry_counter(int argc, char * const argv[])
+{
+	int arg;
+	int ret;
+
+	arg = RSU_NOTIFY_IGNORE_STAGE | RSU_NOTIFY_RESET_RETRY_COUNTER;
+	ret = mbox_hps_stage_notify(arg);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+struct func_t {
+	const char *cmd_string;
+	int (*func_ptr)(int cmd_argc, char * const cmd_argv[]);
+};
+
+static const struct func_t rsu_func_t[] = {
+	{"dtb", rsu_dtb},
+	{"list", rsu_spt_cpb_list},
+	{"slot_by_name", slot_by_name},
+	{"slot_count", slot_count},
+	{"slot_disable", slot_disable},
+	{"slot_enable", slot_enable},
+	{"slot_erase", slot_erase},
+	{"slot_get_info", slot_get_info},
+	{"slot_load", slot_load},
+	{"slot_load_factory", slot_load_factory},
+	{"slot_priority", slot_priority},
+	{"slot_program_buf", slot_program_buf},
+	{"slot_program_buf_raw", slot_program_buf_raw},
+	{"slot_rename", slot_rename},
+	{"slot_size", slot_size},
+	{"slot_verify_buf", slot_verify_buf},
+	{"slot_verify_buf_raw", slot_verify_buf_raw},
+	{"status_log", status_log},
+	{"update", rsu_update},
+	{"notify", rsu_notify},
+	{"clear_error_status", clear_error_status},
+	{"reset_retry_counter", reset_retry_counter}
+};
+
+int do_rsu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	const char *cmd;
+	int i;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	cmd = argv[1];
+	--argc;
+	++argv;
+
+	for (i = 0; i < ARRAY_SIZE(rsu_func_t); i++) {
+		if (!strcmp(cmd, rsu_func_t[i].cmd_string))
+			return rsu_func_t[i].func_ptr(argc, argv);
+	}
+
+	return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(rsu, 5, 1, do_rsu,
+	   "SoCFPGA Stratix10 SoC Remote System Update",
+	   "dtb   - Update Linux DTB qspi-boot parition offset with spt0 value\n"
+	   "list  - List down the available bitstreams in flash\n"
+	   "slot_by_name <name> - find slot by name and display the slot number\n"
+	   "slot_count - display the slot count\n"
+	   "slot_disable <slot> - remove slot from CPB\n"
+	   "slot_enable <slot> - make slot the highest priority\n"
+	   "slot_erase <slot> - erase slot\n"
+	   "slot_get_info <slot> - display slot information\n"
+	   "slot_load <slot> - load slot immediately\n"
+	   "slot_load_factory - load factory immediately\n"
+	   "slot_priority <slot> - display slot priority\n"
+	   "slot_program_buf <slot> <buffer> <size> - program buffer into slot, and make it highest priority\n"
+	   "slot_program_buf_raw <slot> <buffer> <size> - program raw buffer into slot\n"
+	   "slot_rename <slot> <name> - rename slot\n"
+	   "slot_size <slot> - display slot size\n"
+	   "slot_verify_buf <slot> <buffer> <size> - verify slot contents against buffer\n"
+	   "slot_verify_buf_raw <slot> <buffer> <size> - verify slot contents against raw buffer\n"
+	   "status_log - display RSU status\n"
+	   "update <flash_offset> - Initiate firmware to load bitstream as specified by flash_offset\n"
+	   "notify <value> - Let SDM know the current state of HPS software\n"
+	   "clear_error_status - clear the RSU error status\n"
+	   "reset_retry_counter - reset the RSU retry counter\n"
+	   ""
+);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [U-Boot] [PATCHv1 5/5] arm: socfpga: enable RSU build
  2019-09-09 17:35 [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC richard.gong at linux.intel.com
                   ` (3 preceding siblings ...)
  2019-09-09 17:35 ` [U-Boot] [PATCHv1 4/5] arm: socfpga: stratix10: add console commands " richard.gong at linux.intel.com
@ 2019-09-09 17:35 ` richard.gong at linux.intel.com
  4 siblings, 0 replies; 6+ messages in thread
From: richard.gong at linux.intel.com @ 2019-09-09 17:35 UTC (permalink / raw)
  To: u-boot

From: Richard Gong <richard.gong@intel.com>

Add build support for RSU.

Signed-off-by: Richard Gong <richard.gong@intel.com>
---
 arch/arm/mach-socfpga/Makefile | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile
index fc1181c..17ec0bf 100644
--- a/arch/arm/mach-socfpga/Makefile
+++ b/arch/arm/mach-socfpga/Makefile
@@ -33,6 +33,12 @@ obj-y	+= mailbox_s10.o
 obj-y	+= misc_s10.o
 obj-y	+= mmu-arm64_s10.o
 obj-y	+= reset_manager_s10.o
+ifndef CONFIG_SPL_BUILD
+obj-y   += rsu.o
+obj-y   += rsu_ll_qspi.o
+obj-y   += rsu_misc.o
+obj-y   += rsu_s10.o
+endif
 obj-y	+= system_manager_s10.o
 obj-y	+= timer_s10.o
 obj-y	+= wrap_pinmux_config_s10.o
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-09-09 17:35 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-09 17:35 [U-Boot] [PATCHv1 0/5] support remote system update on Intel Stratix10 SoC richard.gong at linux.intel.com
2019-09-09 17:35 ` [U-Boot] [PATCHv1 1/5] arm: socfpga: stratix10: add RSU mailbox support richard.gong at linux.intel.com
2019-09-09 17:35 ` [U-Boot] [PATCHv1 2/5] arm: socfpga: stratix10: add RSU support for Stratix10 SoC richard.gong at linux.intel.com
2019-09-09 17:35 ` [U-Boot] [PATCHv1 3/5] arm: socfpga: stratix10: add environment variables for RSU support richard.gong at linux.intel.com
2019-09-09 17:35 ` [U-Boot] [PATCHv1 4/5] arm: socfpga: stratix10: add console commands " richard.gong at linux.intel.com
2019-09-09 17:35 ` [U-Boot] [PATCHv1 5/5] arm: socfpga: enable RSU build richard.gong at linux.intel.com

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.