meson/gxl: Configure and boot SCP

On Amlogic gxl (s905x) SOC, in order to use SCP, bl31 has to send bl30
and bl301 firmware along with their SHA256 hash over scpi.

Signed-off-by: Remi Pommarel <repk@triplefau.lt>
diff --git a/plat/meson/gxl/gxl_bl31_setup.c b/plat/meson/gxl/gxl_bl31_setup.c
index 0e52f6c..7fe0529 100644
--- a/plat/meson/gxl/gxl_bl31_setup.c
+++ b/plat/meson/gxl/gxl_bl31_setup.c
@@ -19,6 +19,8 @@
  * BL31 from BL2.
  */
 static entry_point_info_t bl33_image_ep_info;
+static image_info_t bl30_image_info;
+static image_info_t bl301_image_info;
 
 /*******************************************************************************
  * Return a pointer to the 'entry_point_info' structure of the next image for
@@ -50,19 +52,20 @@
  * tables. BL2 has flushed this information to memory, so we are guaranteed
  * to pick up good data.
  ******************************************************************************/
-struct gxbb_bl31_param {
+struct gxl_bl31_param {
 	param_header_t h;
 	image_info_t *bl31_image_info;
 	entry_point_info_t *bl32_ep_info;
 	image_info_t *bl32_image_info;
 	entry_point_info_t *bl33_ep_info;
 	image_info_t *bl33_image_info;
+	image_info_t *scp_image_info[];
 };
 
 void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
 				u_register_t arg2, u_register_t arg3)
 {
-	struct gxbb_bl31_param *from_bl2;
+	struct gxl_bl31_param *from_bl2;
 
 	/* Initialize the console to provide early debug support */
 	gxbb_console_init();
@@ -74,7 +77,7 @@
 	assert(arg1 == GXBB_BL31_PLAT_PARAM_VAL);
 
 	/* Check that params passed from BL2 are not NULL. */
-	from_bl2 = (struct gxbb_bl31_param *) arg0;
+	from_bl2 = (struct gxl_bl31_param *) arg0;
 
 	/* Check params passed from BL2 are not NULL. */
 	assert(from_bl2 != NULL);
@@ -91,6 +94,9 @@
 		ERROR("BL31: BL33 entrypoint not obtained from BL2\n");
 		panic();
 	}
+
+	bl30_image_info = *from_bl2->scp_image_info[0];
+	bl301_image_info = *from_bl2->scp_image_info[1];
 }
 
 void bl31_plat_arch_setup(void)
@@ -100,6 +106,14 @@
 	enable_mmu_el3(0);
 }
 
+static inline void gxl_scp_boot(void)
+{
+	scpi_upload_scp_fw(bl30_image_info.image_base,
+			bl30_image_info.image_size, 0);
+	scpi_upload_scp_fw(bl301_image_info.image_base,
+			bl301_image_info.image_size, 1);
+}
+
 /*******************************************************************************
  * GICv2 driver setup information
  ******************************************************************************/
@@ -140,5 +154,7 @@
 	gicv2_pcpu_distif_init();
 	gicv2_cpuif_enable();
 
-//	gxbb_thermal_unknown();
+	gxl_scp_boot();
+
+	gxbb_thermal_unknown();
 }
diff --git a/plat/meson/gxl/gxl_private.h b/plat/meson/gxl/gxl_private.h
index 910a42c..913cbf6 100644
--- a/plat/meson/gxl/gxl_private.h
+++ b/plat/meson/gxl/gxl_private.h
@@ -8,6 +8,7 @@
 #define GXBB_PRIVATE_H
 
 #include <stdint.h>
+#include <stddef.h>
 
 /* Utility functions */
 unsigned int plat_gxbb_calc_core_pos(u_register_t mpidr);
@@ -29,6 +30,7 @@
 uint32_t scpi_efuse_read(void *dst, uint32_t base, uint32_t size);
 void scpi_unknown_thermal(uint32_t arg0, uint32_t arg1,
 			  uint32_t arg2, uint32_t arg3);
+void scpi_upload_scp_fw(uintptr_t addr, size_t size, int send);
 
 /* Peripherals */
 void gxbb_thermal_unknown(void);
diff --git a/plat/meson/gxl/gxl_scpi.c b/plat/meson/gxl/gxl_scpi.c
index daa495e..13d6524 100644
--- a/plat/meson/gxl/gxl_scpi.c
+++ b/plat/meson/gxl/gxl_scpi.c
@@ -9,11 +9,13 @@
 #include <plat/common/platform.h>
 #include <platform_def.h>
 #include <string.h>
+#include <crypto/sha_dma.h>
 
 #include "gxl_private.h"
 
 #define SIZE_SHIFT	20
 #define SIZE_MASK	0x1FF
+#define SIZE_FWBLK	0x200UL
 
 /*
  * Note: The Amlogic SCP firmware uses the legacy SCPI protocol.
@@ -24,12 +26,16 @@
 #define SCPI_CMD_JTAG_SET_STATE		0xC0
 #define SCPI_CMD_EFUSE_READ		0xC2
 
+#define SCPI_CMD_COPY_FW 0xd4
+#define SCPI_CMD_SET_FW_ADDR 0xd3
+#define SCPI_CMD_FW_SIZE 0xd2
+
 static inline uint32_t scpi_cmd(uint32_t command, uint32_t size)
 {
 	return command | (size << SIZE_SHIFT);
 }
 
-void scpi_secure_message_send(uint32_t command, uint32_t size)
+static void scpi_secure_message_send(uint32_t command, uint32_t size)
 {
 	mhu_secure_message_send(scpi_cmd(command, size));
 }
@@ -133,5 +139,73 @@
 	mmio_write_32(GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD + 0xC, arg3);
 	mhu_secure_message_send(scpi_cmd(0xC3, 16));
 	mhu_secure_message_wait();
+	mhu_secure_message_end();
+}
+
+static inline void scpi_copy_scp_data(uint8_t *data, size_t len)
+{
+	void *dst = (void *)GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD;
+	size_t sz;
+
+	mmio_write_32(GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD, len);
+	scpi_secure_message_send(SCPI_CMD_FW_SIZE, len);
+	mhu_secure_message_wait();
+
+	for (sz = 0; sz < len; sz += SIZE_FWBLK) {
+		memcpy(dst, data + sz, MIN(SIZE_FWBLK, len - sz));
+		mhu_secure_message_send(SCPI_CMD_COPY_FW);
+	}
+}
+
+static inline void scpi_set_scp_addr(uint64_t addr, size_t len)
+{
+	volatile uint64_t *dst = (uint64_t *)GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD;
+
+	/*
+	 * It is ok as GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD is mapped as
+	 * non cachable
+	 */
+	*dst = addr;
+	scpi_secure_message_send(SCPI_CMD_SET_FW_ADDR, sizeof(addr));
+	mhu_secure_message_wait();
+
+	mmio_write_32(GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD, len);
+	scpi_secure_message_send(SCPI_CMD_FW_SIZE, len);
+	mhu_secure_message_wait();
+}
+
+static inline void scpi_send_fw_hash(uint8_t hash[], size_t len)
+{
+	void *dst = (void *)GXBB_MHU_SECURE_AP_TO_SCP_PAYLOAD;
+
+	memcpy(dst, hash, len);
+	mhu_secure_message_send(0xd0);
+	mhu_secure_message_send(0xd1);
+	mhu_secure_message_send(0xd5);
 	mhu_secure_message_end();
 }
+
+/**
+ * Upload a FW to SCP.
+ *
+ * @param addr: firmware data address
+ * @param size: size of firmware
+ * @param send: If set, actually copy the firmware in SCP memory otherwise only
+ *  send the firmware address.
+ */
+void scpi_upload_scp_fw(uintptr_t addr, size_t size, int send)
+{
+	struct asd_ctx ctx;
+
+	asd_sha_init(&ctx, ASM_SHA256);
+	asd_sha_update(&ctx, (void *)addr, size);
+	asd_sha_finalize(&ctx);
+
+	mhu_secure_message_start();
+	if (send == 0)
+		scpi_set_scp_addr(addr, size);
+	else
+		scpi_copy_scp_data((void *)addr, size);
+
+	scpi_send_fw_hash(ctx.digest, sizeof(ctx.digest));
+}