feat(ethos-n)!: add protected NPU firmware setup

When the Arm(R) Ethos(TM)-N NPU driver is built with TZMP1 support, the
NPU should use the firmware that has been loaded into the protected
memory by BL2. The Linux Kernel NPU driver in the non-secure world is
not allowed to configure the NPU to do this in a TZMP1 build so the SiP
service will now configure the NPU to boot with the firmware in the
protected memory.

BREAKING CHANGE: The Linux Kernel NPU driver can no longer directly
configure and boot the NPU in a TZMP1 build. The API version has
therefore been given a major version bump with this change.

Signed-off-by: Mikael Olsson <mikael.olsson@arm.com>
Change-Id: I65d00f54b3ade3665d7941e270da7a3dec02281a
diff --git a/drivers/arm/ethosn/ethosn_smc.c b/drivers/arm/ethosn/ethosn_smc.c
index d1b21e0..6558566 100644
--- a/drivers/arm/ethosn/ethosn_smc.c
+++ b/drivers/arm/ethosn/ethosn_smc.c
@@ -51,7 +51,11 @@
 #define SEC_AUXCTLR_STASHING_VAL	U(0xA5000000)
 
 #define SEC_DEL_REG			U(0x0004)
+#if ARM_ETHOSN_NPU_TZMP1
+#define SEC_DEL_VAL			U(0x808)
+#else
 #define SEC_DEL_VAL			U(0x80C)
+#endif
 #define SEC_DEL_EXCC_MASK		U(0x20)
 
 #define SEC_SECCTLR_REG			U(0x0010)
@@ -62,7 +66,9 @@
 #define SEC_DEL_ADDR_EXT_VAL            U(0x1)
 
 #define SEC_SYSCTRL0_REG		U(0x0018)
+#define SEC_SYSCTRL0_CPU_WAIT		U(1)
 #define SEC_SYSCTRL0_SLEEPING		U(1U << 4)
+#define SEC_SYSCTRL0_INITVTOR_MASK	U(0x1FFFFF80)
 #define SEC_SYSCTRL0_SOFT_RESET		U(3U << 29)
 #define SEC_SYSCTRL0_HARD_RESET		U(1U << 31)
 
@@ -88,6 +94,8 @@
 #define SEC_NPU_ID_REG			U(0xF000)
 #define SEC_NPU_ID_ARCH_VER_SHIFT	U(0X10)
 
+#define FIRMWARE_STREAM_INDEX           U(0x0)
+#define PLE_STREAM_INDEX		U(0x4)
 #define INPUT_STREAM_INDEX              U(0x6)
 #define INTERMEDIATE_STREAM_INDEX       U(0x7)
 #define OUTPUT_STREAM_INDEX             U(0x8)
@@ -98,6 +106,14 @@
 #if ARM_ETHOSN_NPU_TZMP1
 CASSERT(ARM_ETHOSN_NPU_FW_IMAGE_BASE > 0U, assert_ethosn_invalid_fw_image_base);
 static const struct ethosn_big_fw *big_fw;
+
+#define FW_INITVTOR_ADDR(big_fw) \
+	((ETHOSN_FW_VA_BASE + big_fw->vector_table_offset) & \
+	 SEC_SYSCTRL0_INITVTOR_MASK)
+
+#define SYSCTRL0_INITVTOR_ADDR(value) \
+	(value & SEC_SYSCTRL0_INITVTOR_MASK)
+
 #endif
 
 static bool ethosn_get_device_and_core(uintptr_t core_addr,
@@ -140,6 +156,9 @@
 	size_t i;
 	uint32_t streams[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 
+	streams[FIRMWARE_STREAM_INDEX] = ARM_ETHOSN_NPU_PROT_FW_NSAID;
+	streams[PLE_STREAM_INDEX] = ARM_ETHOSN_NPU_PROT_FW_NSAID;
+
 	if (is_protected) {
 		streams[INPUT_STREAM_INDEX] = ARM_ETHOSN_NPU_PROT_DATA_NSAID;
 		streams[INTERMEDIATE_STREAM_INDEX] =
@@ -154,6 +173,13 @@
 			      streams[i]);
 	}
 }
+
+static void ethosn_configure_vector_table(uintptr_t core_addr)
+{
+	mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_SYSCTRL0_REG),
+			FW_INITVTOR_ADDR(big_fw));
+}
+
 #endif
 
 static void ethosn_configure_events(uintptr_t core_addr)
@@ -307,6 +333,31 @@
 	return timeout < ETHOSN_RESET_TIMEOUT_US;
 }
 
+static int ethosn_core_boot_fw(uintptr_t core_addr)
+{
+#if ARM_ETHOSN_NPU_TZMP1
+	const uintptr_t sysctrl0_reg = ETHOSN_CORE_SEC_REG(core_addr, SEC_SYSCTRL0_REG);
+	const uint32_t sysctrl0_val = mmio_read_32(sysctrl0_reg);
+	const bool waiting = (sysctrl0_val & SEC_SYSCTRL0_CPU_WAIT);
+
+	if (!waiting) {
+		WARN("ETHOSN: Firmware is already running.\n");
+		return ETHOSN_INVALID_STATE;
+	}
+
+	if (SYSCTRL0_INITVTOR_ADDR(sysctrl0_val) != FW_INITVTOR_ADDR(big_fw)) {
+		WARN("ETHOSN: Unknown vector table won't boot firmware.\n");
+		return ETHOSN_INVALID_CONFIGURATION;
+	}
+
+	mmio_clrbits_32(sysctrl0_reg, SEC_SYSCTRL0_CPU_WAIT);
+
+	return ETHOSN_SUCCESS;
+#else
+	return ETHOSN_NOT_SUPPORTED;
+#endif
+}
+
 static int ethosn_core_full_reset(const struct ethosn_device_t *device,
 				  const struct ethosn_core_t *core,
 				  bool hard_reset,
@@ -341,6 +392,10 @@
 	ethosn_configure_stream_addr_extends(device, core->addr);
 	ethosn_configure_stream_attr_ctlr(core->addr);
 
+#if ARM_ETHOSN_NPU_TZMP1
+	ethosn_configure_vector_table(core->addr);
+#endif
+
 	ethosn_delegate_to_ns(core->addr);
 
 	return ETHOSN_SUCCESS;
@@ -407,6 +462,8 @@
 						     is_protected,
 						     aux_features,
 						     handle);
+	case ETHOSN_FNUM_BOOT_FW:
+		SMC_RET1(handle, ethosn_core_boot_fw(core->addr));
 	default:
 		WARN("ETHOSN: Unimplemented SMC call: 0x%x\n", fid);
 		SMC_RET1(handle, SMC_UNK);
@@ -470,7 +527,7 @@
 		x4 &= 0xFFFFFFFF;
 	}
 
-	if (!is_ethosn_fid(smc_fid) || (fid > ETHOSN_FNUM_GET_FW_PROP)) {
+	if (!is_ethosn_fid(smc_fid) || (fid > ETHOSN_FNUM_BOOT_FW)) {
 		WARN("ETHOSN: Unknown SMC call: 0x%x\n", smc_fid);
 		SMC_RET1(handle, SMC_UNK);
 	}
@@ -509,6 +566,11 @@
 	}
 
 	dev = ETHOSN_GET_DEVICE(0U);
+	if (dev->has_reserved_memory) {
+		ERROR("ETHOSN: TZMP1 doesn't support using reserved memory\n");
+		return ETHOSN_FAILURE;
+	}
+
 	arch_ver = ethosn_core_read_arch_version(dev->cores[0U].addr);
 	big_fw = (struct ethosn_big_fw *)ARM_ETHOSN_NPU_FW_IMAGE_BASE;