[U-Boot] [RESEND PATCHv1 2/5] arm: socfpga: stratix10: add RSU support for Stratix10 SoC

richard.gong at linux.intel.com richard.gong at linux.intel.com
Thu Oct 24 14:48:27 UTC 2019


From: Richard Gong <richard.gong at 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 at 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 at 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 at 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



More information about the U-Boot mailing list