Эх сурвалжийг харах

msm: camera: icp: Add support for non secure FW load

Starting from waipio, ICP [lx7] FW is to loaded using
secure PIL. For debug purposes also added support for
non secure image loading for lx7.

CRs-Fixed: 2722486
Change-Id: I30026fb8e65f323ab73336e0ae787a2359bc934f
Signed-off-by: Karthik Anantha Ram <[email protected]>
Karthik Anantha Ram 4 жил өмнө
parent
commit
a503554e2f

+ 2 - 1
Kbuild

@@ -155,7 +155,8 @@ camera-$(CONFIG_SPECTRA_ICP) += \
 	drivers/cam_icp/icp_hw/bps_hw/bps_soc.o \
 	drivers/cam_icp/cam_icp_subdev.o \
 	drivers/cam_icp/cam_icp_context.o \
-	drivers/cam_icp/hfi.o
+	drivers/cam_icp/hfi.o \
+	drivers/cam_icp/utils/cam_icp_utils.o
 
 camera-$(CONFIG_SPECTRA_JPEG) += \
 	drivers/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.o \

+ 6 - 128
drivers/cam_icp/icp_hw/a5_hw/a5_core.c

@@ -9,11 +9,8 @@
 #include <linux/videodev2.h>
 #include <linux/uaccess.h>
 #include <linux/platform_device.h>
-#include <linux/firmware.h>
 #include <linux/delay.h>
 #include <linux/timer.h>
-#include <linux/elf.h>
-#include <linux/iopoll.h>
 #include <media/cam_icp.h>
 #include "cam_io_util.h"
 #include "cam_a5_hw_intf.h"
@@ -29,6 +26,7 @@
 #include "cam_icp_hw_mgr_intf.h"
 #include "cam_cpas_api.h"
 #include "cam_debug_util.h"
+#include "cam_icp_utils.h"
 
 #define PC_POLL_DELAY_US 100
 #define PC_POLL_TIMEOUT_US 10000
@@ -54,133 +52,12 @@ static int cam_a5_cpas_vote(struct cam_a5_device_core_info *core_info,
 	return rc;
 }
 
-static int32_t cam_icp_validate_fw(const uint8_t *elf)
-{
-	struct elf32_hdr *elf_hdr;
-
-	if (!elf) {
-		CAM_ERR(CAM_ICP, "Invalid params");
-		return -EINVAL;
-	}
-
-	elf_hdr = (struct elf32_hdr *)elf;
-
-	if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG)) {
-		CAM_ERR(CAM_ICP, "ICP elf identifier is failed");
-		return -EINVAL;
-	}
-
-	/* check architecture */
-	if (elf_hdr->e_machine != EM_ARM) {
-		CAM_ERR(CAM_ICP, "unsupported arch");
-		return -EINVAL;
-	}
-
-	/* check elf bit format */
-	if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) {
-		CAM_ERR(CAM_ICP, "elf doesn't support 32 bit format");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int32_t cam_icp_get_fw_size(const uint8_t *elf, uint32_t *fw_size)
-{
-	int32_t rc = 0;
-	int32_t i = 0;
-	uint32_t num_prg_hdrs;
-	unsigned char *icp_prg_hdr_tbl;
-	uint32_t seg_mem_size = 0;
-	struct elf32_hdr *elf_hdr;
-	struct elf32_phdr *prg_hdr;
-
-	if (!elf || !fw_size) {
-		CAM_ERR(CAM_ICP, "invalid args");
-		return -EINVAL;
-	}
-
-	*fw_size = 0;
-
-	elf_hdr = (struct elf32_hdr *)elf;
-	num_prg_hdrs = elf_hdr->e_phnum;
-	icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
-	prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
-
-	if (!prg_hdr) {
-		CAM_ERR(CAM_ICP, "failed to get elf program header attr");
-		return -EINVAL;
-	}
-
-	CAM_DBG(CAM_ICP, "num_prg_hdrs = %d", num_prg_hdrs);
-	for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
-		if (prg_hdr->p_flags == 0)
-			continue;
-
-		seg_mem_size = (prg_hdr->p_memsz + prg_hdr->p_align - 1) &
-					~(prg_hdr->p_align - 1);
-		seg_mem_size += prg_hdr->p_vaddr;
-		CAM_DBG(CAM_ICP, "memsz:%x align:%x addr:%x seg_mem_size:%x",
-			(int)prg_hdr->p_memsz, (int)prg_hdr->p_align,
-			(int)prg_hdr->p_vaddr, (int)seg_mem_size);
-		if (*fw_size < seg_mem_size)
-			*fw_size = seg_mem_size;
-
-	}
-
-	if (*fw_size == 0) {
-		CAM_ERR(CAM_ICP, "invalid elf fw file");
-		return -EINVAL;
-	}
-
-	return rc;
-}
-
-static int32_t cam_icp_program_fw(const uint8_t *elf,
-		struct cam_a5_device_core_info *core_info)
-{
-	int32_t rc = 0;
-	uint32_t num_prg_hdrs;
-	unsigned char *icp_prg_hdr_tbl;
-	int32_t i = 0;
-	u8 *dest;
-	u8 *src;
-	struct elf32_hdr *elf_hdr;
-	struct elf32_phdr *prg_hdr;
-
-	elf_hdr = (struct elf32_hdr *)elf;
-	num_prg_hdrs = elf_hdr->e_phnum;
-	icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
-	prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
-
-	if (!prg_hdr) {
-		CAM_ERR(CAM_ICP, "failed to get elf program header attr");
-		return -EINVAL;
-	}
-
-	for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
-		if (prg_hdr->p_flags == 0)
-			continue;
-
-		CAM_DBG(CAM_ICP, "Loading FW header size: %u",
-			prg_hdr->p_filesz);
-		if (prg_hdr->p_filesz != 0) {
-			src = (u8 *)((u8 *)elf + prg_hdr->p_offset);
-			dest = (u8 *)(((u8 *)core_info->fw_kva_addr) +
-				prg_hdr->p_vaddr);
-
-			memcpy_toio(dest, src, prg_hdr->p_filesz);
-		}
-	}
-
-	return rc;
-}
-
 static int32_t cam_a5_download_fw(void *device_priv)
 {
 	int32_t rc = 0;
 	uint32_t fw_size;
 	const uint8_t *fw_start = NULL;
+	struct cam_icp_proc_params a5_params;
 	struct cam_hw_info *a5_dev = device_priv;
 	struct cam_hw_soc_info *soc_info = NULL;
 	struct cam_a5_device_core_info *core_info = NULL;
@@ -198,6 +75,7 @@ static int32_t cam_a5_download_fw(void *device_priv)
 	hw_info = core_info->a5_hw_info;
 	pdev = soc_info->pdev;
 	cam_a5_soc_info = soc_info->soc_private;
+	a5_params.skip_seg = false;
 
 	if (cam_a5_soc_info->fw_name) {
 		CAM_INFO(CAM_ICP, "Downloading firmware %s",
@@ -224,13 +102,13 @@ static int32_t cam_a5_download_fw(void *device_priv)
 	}
 
 	fw_start = core_info->fw_elf->data;
-	rc = cam_icp_validate_fw(fw_start);
+	rc = cam_icp_validate_fw(fw_start, EM_ARM);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "fw elf validation failed");
 		goto fw_download_failed;
 	}
 
-	rc = cam_icp_get_fw_size(fw_start, &fw_size);
+	rc = cam_icp_get_fw_size(fw_start, &fw_size, &a5_params);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "unable to get fw size");
 		goto fw_download_failed;
@@ -243,7 +121,7 @@ static int32_t cam_a5_download_fw(void *device_priv)
 		goto fw_download_failed;
 	}
 
-	rc = cam_icp_program_fw(fw_start, core_info);
+	rc = cam_icp_program_fw(fw_start, core_info->fw_kva_addr, &a5_params);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "fw program is failed");
 		goto fw_download_failed;

+ 3 - 0
drivers/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c

@@ -3183,6 +3183,9 @@ static int cam_icp_mgr_proc_boot(struct cam_icp_hw_mgr *hw_mgr)
 		args.firmware.iova = hw_mgr->hfi_mem.fw_buf.iova;
 		args.firmware.kva = hw_mgr->hfi_mem.fw_buf.kva;
 		args.firmware.len = hw_mgr->hfi_mem.fw_buf.len;
+		args.use_sec_pil = false;
+	} else {
+		args.use_sec_pil = true;
 	}
 
 	args.irq_cb.data = hw_mgr;

+ 2 - 0
drivers/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h

@@ -54,6 +54,7 @@ struct cam_icp_irq_cb {
  * @firmware.len: length of firmware region
  * @irq_cb: irq callback
  * @debug_enabled: processor will be booted with debug enabled
+ * @use_sec_pil: If set to true, use secure PIL for load
  */
 struct cam_icp_boot_args {
 	struct {
@@ -64,6 +65,7 @@ struct cam_icp_boot_args {
 
 	struct cam_icp_irq_cb irq_cb;
 	bool debug_enabled;
+	bool use_sec_pil;
 };
 
 /**

+ 275 - 10
drivers/cam_icp/icp_hw/lx7_hw/lx7_core.c

@@ -3,7 +3,6 @@
  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
  */
 
-#include <linux/firmware.h>
 #include <linux/of_address.h>
 #include <linux/qcom_scm.h>
 #include <linux/soc/qcom/mdt_loader.h>
@@ -16,6 +15,7 @@
 #include "cam_icp_hw_intf.h"
 #include "hfi_intf.h"
 #include "hfi_sys_defs.h"
+#include "cam_icp_utils.h"
 #include "lx7_core.h"
 #include "lx7_reg.h"
 #include "lx7_soc.h"
@@ -27,6 +27,9 @@
 
 #define ICP_FW_NAME_MAX_SIZE    32
 
+#define PC_POLL_DELAY_US 100
+#define PC_POLL_TIMEOUT_US 10000
+
 static int cam_lx7_ubwc_configure(struct cam_hw_soc_info *soc_info)
 {
 	int i = 0, rc, ddr_type;
@@ -296,6 +299,12 @@ static void prepare_boot(struct cam_hw_info *lx7_info,
 	struct cam_lx7_core_info *core_info = lx7_info->core_info;
 	unsigned long flags;
 
+	if (!args->use_sec_pil) {
+		core_info->fw_params.fw_buf = args->firmware.iova;
+		core_info->fw_params.fw_kva_addr = args->firmware.kva;
+		core_info->fw_params.fw_buf_len = args->firmware.len;
+	}
+
 	spin_lock_irqsave(&lx7_info->hw_lock, flags);
 	core_info->irq_cb.data = args->irq_cb.data;
 	core_info->irq_cb.cb = args->irq_cb.cb;
@@ -307,12 +316,218 @@ static void prepare_shutdown(struct cam_hw_info *lx7_info)
 	struct cam_lx7_core_info *core_info = lx7_info->core_info;
 	unsigned long flags;
 
+	core_info->fw_params.fw_buf = 0x0;
+	core_info->fw_params.fw_kva_addr = 0x0;
+	core_info->fw_params.fw_buf_len = 0x0;
+	core_info->use_sec_pil = false;
+
 	spin_lock_irqsave(&lx7_info->hw_lock, flags);
 	core_info->irq_cb.data = NULL;
 	core_info->irq_cb.cb = NULL;
 	spin_unlock_irqrestore(&lx7_info->hw_lock, flags);
 }
 
+/* Used if ICP_SYS is not protected */
+static int __cam_lx7_power_collapse(struct cam_hw_info *lx7_info)
+{
+	uint32_t status = 0;
+	void __iomem *base;
+
+	if (!lx7_info) {
+		CAM_ERR(CAM_ICP, "invalid lx7 dev info");
+		return -EINVAL;
+	}
+
+	base = lx7_info->soc_info.reg_map[LX7_SYS_BASE].mem_base;
+
+	/**
+	 * Need to poll here to confirm that FW has triggered WFI
+	 * and Host can then proceed. No interrupt is expected
+	 * from FW at this time.
+	 */
+	if (readl_poll_timeout(base + ICP_LX7_SYS_STATUS,
+				status, status & ICP_LX7_STANDBYWFI,
+				PC_POLL_DELAY_US, PC_POLL_TIMEOUT_US)) {
+		CAM_ERR(CAM_ICP, "WFI poll timed out: status=0x%08x", status);
+		return -ETIMEDOUT;
+	}
+
+	cam_io_w_mb(0x0, base + ICP_LX7_SYS_CONTROL);
+	return 0;
+}
+
+/* Used if ICP_SYS is not protected */
+static int __cam_lx7_power_resume(struct cam_hw_info *lx7_info)
+{
+	void __iomem *base;
+	struct lx7_soc_info    *soc_priv;
+	struct cam_hw_soc_info *soc_info;
+
+	if (!lx7_info) {
+		CAM_ERR(CAM_ICP, "invalid lx7 dev info");
+		return -EINVAL;
+	}
+
+	soc_info = &lx7_info->soc_info;
+	soc_priv = soc_info->soc_private;
+	base = lx7_info->soc_info.reg_map[LX7_SYS_BASE].mem_base;
+
+	cam_io_w_mb(ICP_LX7_FUNC_RESET,
+		base + ICP_LX7_SYS_RESET);
+
+	if (soc_priv->icp_qos_val)
+		cam_io_w_mb(soc_priv->icp_qos_val,
+			base + ICP_LX7_SYS_ACCESS);
+
+	cam_io_w_mb(ICP_LX7_EN_CPU,
+		base + ICP_LX7_SYS_CONTROL);
+
+	return 0;
+}
+
+/* Used for non secure FW load */
+static int32_t __cam_non_sec_load_fw(void *device_priv)
+{
+	int32_t rc = 0;
+	uint32_t fw_size;
+	struct cam_icp_proc_params lx7_params;
+	char firmware_name[ICP_FW_NAME_MAX_SIZE] = {0};
+	const char               *fw_name;
+	const uint8_t            *fw_start = NULL;
+	struct cam_hw_info       *lx7_dev = device_priv;
+	struct cam_hw_soc_info   *soc_info = NULL;
+	struct cam_lx7_core_info *core_info = NULL;
+	struct platform_device   *pdev = NULL;
+
+	if (!device_priv) {
+		CAM_ERR(CAM_ICP, "Invalid cam_dev_info");
+		return -EINVAL;
+	}
+
+	soc_info = &lx7_dev->soc_info;
+	core_info = (struct cam_lx7_core_info *)lx7_dev->core_info;
+	pdev = soc_info->pdev;
+
+	/**
+	 * Do not attempt to map 0xE0400000 and 0xE0420000 as these
+	 * addresses are routed internally by the core. These segments
+	 * are used by the firmware to make use of the rom packing feature.
+	 */
+	lx7_params.skip_seg = true;
+	lx7_params.vaddr[0] = 0xE0400000;
+	lx7_params.vaddr[1] = 0xE0420000;
+
+	rc = of_property_read_string(pdev->dev.of_node, "fw_name",
+		&fw_name);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "FW image name not found");
+		return rc;
+	}
+
+	/* Account for ".elf" size [4 characters] */
+	if (strlen(fw_name) >= (ICP_FW_NAME_MAX_SIZE - 4)) {
+		CAM_ERR(CAM_ICP, "Invalid fw name %s", fw_name);
+		return -EINVAL;
+	}
+
+	scnprintf(firmware_name, ARRAY_SIZE(firmware_name),
+		"%s.elf", fw_name);
+
+	rc = request_firmware(&core_info->fw_params.fw_elf,
+		firmware_name, &pdev->dev);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "Failed to locate %s fw: %d",
+			firmware_name, rc);
+		return rc;
+	}
+
+	if (!core_info->fw_params.fw_elf) {
+		CAM_ERR(CAM_ICP, "Invalid elf size");
+		rc = -EINVAL;
+		goto fw_download_failed;
+	}
+
+	fw_start = core_info->fw_params.fw_elf->data;
+
+	rc = cam_icp_validate_fw(fw_start, EM_XTENSA);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "fw elf validation failed");
+		goto fw_download_failed;
+	}
+
+	rc = cam_icp_get_fw_size(fw_start, &fw_size, &lx7_params);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "unable to get fw size");
+		goto fw_download_failed;
+	}
+
+	if (core_info->fw_params.fw_buf_len < fw_size) {
+		CAM_ERR(CAM_ICP, "mismatch in fw size: %u %llu",
+			fw_size, core_info->fw_params.fw_buf_len);
+		rc = -EINVAL;
+		goto fw_download_failed;
+	}
+
+	rc = cam_icp_program_fw(fw_start,
+		core_info->fw_params.fw_kva_addr,
+		&lx7_params);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "fw program is failed");
+		goto fw_download_failed;
+	}
+
+fw_download_failed:
+	release_firmware(core_info->fw_params.fw_elf);
+	return rc;
+}
+
+/* Used for non secure FW load */
+static int cam_lx7_non_sec_boot(
+	struct cam_hw_info *lx7_info,
+	struct cam_icp_boot_args *args,
+	uint32_t arg_size)
+{
+	int rc;
+
+	if (!lx7_info || !args) {
+		CAM_ERR(CAM_ICP,
+			"invalid args: lx7_dev=%pK args=%pK",
+			lx7_info, args);
+		return -EINVAL;
+	}
+
+	if (arg_size != sizeof(struct cam_icp_boot_args)) {
+		CAM_ERR(CAM_ICP, "invalid boot args size");
+		return -EINVAL;
+	}
+
+	if (lx7_info->soc_info.num_mem_block != LX7_BASE_MAX) {
+		CAM_ERR(CAM_ICP, "check ICP SYS reg config in DT..");
+		return -EINVAL;
+	}
+
+	prepare_boot(lx7_info, args);
+
+	rc = __cam_non_sec_load_fw(lx7_info);
+	if (rc) {
+		CAM_ERR(CAM_ICP,
+			"firmware download failed rc=%d", rc);
+		goto err;
+	}
+
+	rc = __cam_lx7_power_resume(lx7_info);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "lx7 boot failed rc=%d", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	prepare_shutdown(lx7_info);
+	return rc;
+}
+
 #if IS_REACHABLE(CONFIG_QCOM_MDT_LOADER)
 static int __load_firmware(struct platform_device *pdev)
 {
@@ -410,6 +625,7 @@ static int cam_lx7_boot(struct cam_hw_info *lx7_info,
 			uint32_t arg_size)
 {
 	int rc;
+	struct cam_lx7_core_info *core_info = NULL;
 
 	if (!IS_REACHABLE(CONFIG_QCOM_MDT_LOADER))
 		return -EOPNOTSUPP;
@@ -426,6 +642,7 @@ static int cam_lx7_boot(struct cam_hw_info *lx7_info,
 		return -EINVAL;
 	}
 
+	core_info = (struct cam_lx7_core_info *)lx7_info->core_info;
 	prepare_boot(lx7_info, args);
 
 #if IS_REACHABLE(CONFIG_QCOM_MDT_LOADER)
@@ -442,6 +659,7 @@ static int cam_lx7_boot(struct cam_hw_info *lx7_info,
 		goto err;
 	}
 
+	core_info->use_sec_pil = true;
 	return 0;
 err:
 	prepare_shutdown(lx7_info);
@@ -450,19 +668,66 @@ err:
 
 static int cam_lx7_shutdown(struct cam_hw_info *lx7_info)
 {
+	int rc = 0;
+	struct cam_lx7_core_info *core_info =
+		(struct cam_lx7_core_info *)lx7_info->core_info;
+
 	prepare_shutdown(lx7_info);
 
-	return qcom_scm_pas_shutdown(CAM_FW_PAS_ID);
+	if (core_info->use_sec_pil)
+		rc = qcom_scm_pas_shutdown(CAM_FW_PAS_ID);
+	else {
+		void __iomem *base;
+
+		base = lx7_info->soc_info.reg_map[LX7_SYS_BASE].mem_base;
+		cam_io_w_mb(0x0, base + ICP_LX7_SYS_CONTROL);
+	}
+
+	return rc;
+}
+
+/* API controls collapse/resume of ICP */
+static int cam_lx7_core_control(
+	struct cam_hw_info *lx7_info,
+	uint32_t state)
+{
+	int rc = 0;
+	struct cam_lx7_core_info *core_info =
+		(struct cam_lx7_core_info *)lx7_info->core_info;
+
+	if (core_info->use_sec_pil) {
+		rc = qcom_scm_set_remote_state(state, CAM_FW_PAS_ID);
+		if (rc)
+			CAM_ERR(CAM_ICP, "remote state set to %s failed rc=%d",
+				state == TZ_STATE_RESUME ? "resume" : "suspend", rc);
+	} else {
+		if (state == TZ_STATE_RESUME) {
+			rc = __cam_lx7_power_resume(lx7_info);
+			if (rc)
+				CAM_ERR(CAM_ICP, "lx7 resume failed rc=%d", rc);
+		} else {
+			rc = __cam_lx7_power_collapse(lx7_info);
+			if (rc)
+				CAM_ERR(CAM_ICP, "lx7 collapse failed rc=%d", rc);
+		}
+	}
+
+	return rc;
 }
 
-static int set_remote_state(uint32_t state)
+static inline int cam_lx7_download_fw(
+	struct cam_hw_info *lx7_info,
+	struct cam_icp_boot_args *args,
+	uint32_t arg_size)
 {
 	int rc;
 
-	rc = qcom_scm_set_remote_state(state, CAM_FW_PAS_ID);
-	if (rc)
-		CAM_ERR(CAM_ICP, "remote state set to %s failed rc=%d",
-			state == TZ_STATE_RESUME ? "resume" : "suspend", rc);
+	if (args->use_sec_pil)
+		rc = cam_lx7_boot(
+			lx7_info, args, arg_size);
+	else
+		rc = cam_lx7_non_sec_boot(
+			lx7_info, args, arg_size);
 
 	return rc;
 }
@@ -524,13 +789,13 @@ int cam_lx7_process_cmd(void *priv, uint32_t cmd_type,
 		rc = cam_lx7_shutdown(lx7_info);
 		break;
 	case CAM_ICP_CMD_PROC_BOOT:
-		rc = cam_lx7_boot(lx7_info, args, arg_size);
+		rc = cam_lx7_download_fw(lx7_info, args, arg_size);
 		break;
 	case CAM_ICP_CMD_POWER_COLLAPSE:
-		rc = set_remote_state(TZ_STATE_SUSPEND);
+		rc = cam_lx7_core_control(lx7_info, TZ_STATE_SUSPEND);
 		break;
 	case CAM_ICP_CMD_POWER_RESUME:
-		rc = set_remote_state(TZ_STATE_RESUME);
+		rc = cam_lx7_core_control(lx7_info, TZ_STATE_RESUME);
 		break;
 	case CAM_ICP_CMD_VOTE_CPAS:
 		rc = cam_lx7_cpas_vote(lx7_info->core_info, args);

+ 15 - 3
drivers/cam_icp/icp_hw/lx7_hw/lx7_core.h

@@ -9,16 +9,28 @@
 #include "cam_hw_intf.h"
 #include "cam_icp_hw_intf.h"
 
-#define LX7_CSR_BASE  0
-#define LX7_CIRQ_BASE 1
-
 #define UNSUPPORTED_PROC_PAS_ID   30
 #define CAM_FW_PAS_ID             33
 
+enum cam_lx7_reg_base {
+	LX7_CSR_BASE,
+	LX7_CIRQ_BASE,
+	LX7_SYS_BASE,
+	LX7_BASE_MAX,
+};
+
 struct cam_lx7_core_info {
 	struct cam_icp_irq_cb irq_cb;
 	uint32_t cpas_handle;
 	bool cpas_start;
+	bool use_sec_pil;
+	struct {
+		const struct firmware *fw_elf;
+		void *fw;
+		uint32_t fw_buf;
+		uintptr_t fw_kva_addr;
+		uint64_t fw_buf_len;
+	} fw_params;
 };
 
 int cam_lx7_hw_init(void *priv, void *args, uint32_t arg_size);

+ 10 - 0
drivers/cam_icp/icp_hw/lx7_hw/lx7_reg.h

@@ -6,6 +6,16 @@
 #ifndef _CAM_LX7_REG_H_
 #define _CAM_LX7_REG_H_
 
+/* ICP_SYS - Protected reg space defined in AC policy */
+#define ICP_LX7_SYS_RESET      0x0
+#define ICP_LX7_SYS_CONTROL    0x4
+#define ICP_LX7_SYS_STATUS     0xC
+#define ICP_LX7_SYS_ACCESS     0x10
+
+#define ICP_LX7_STANDBYWFI     (1 << 7)
+#define ICP_LX7_EN_CPU         (1 << 9)
+#define ICP_LX7_FUNC_RESET     (1 << 4)
+
 #define ICP_LX7_CIRQ_OB_MASK   0x0
 #define ICP_LX7_CIRQ_OB_CLEAR  0x4
 #define ICP_LX7_CIRQ_OB_STATUS 0xc

+ 17 - 0
drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.c

@@ -65,6 +65,20 @@ static int cam_lx7_ubwc_config_get(struct lx7_soc_info *lx7_soc_info,
 	return 0;
 }
 
+static inline void cam_lx7_qos_get(
+	struct lx7_soc_info *lx7_soc_info,
+	struct device_node *np)
+{
+	int rc;
+
+	rc = of_property_read_u32(np, "qos-val",
+		&lx7_soc_info->icp_qos_val);
+	if (rc < 0) {
+		CAM_WARN(CAM_ICP, "QoS need not be set");
+		lx7_soc_info->icp_qos_val = 0;
+	}
+}
+
 static int cam_lx7_dt_properties_get(struct cam_hw_soc_info *soc_info)
 {
 	int rc;
@@ -82,6 +96,9 @@ static int cam_lx7_dt_properties_get(struct cam_hw_soc_info *soc_info)
 		return rc;
 	}
 
+	cam_lx7_qos_get(soc_info->soc_private,
+		soc_info->pdev->dev.of_node);
+
 	return 0;
 }
 

+ 1 - 0
drivers/cam_icp/icp_hw/lx7_hw/lx7_soc.h

@@ -13,6 +13,7 @@
 #define UBWC_CONFIG_MAX 2
 
 struct lx7_soc_info {
+	uint32_t icp_qos_val;
 	struct {
 		uint32_t ipe_fetch[UBWC_CONFIG_MAX];
 		uint32_t ipe_write[UBWC_CONFIG_MAX];

+ 142 - 0
drivers/cam_icp/utils/cam_icp_utils.c

@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "cam_icp_utils.h"
+
+int32_t cam_icp_validate_fw(const uint8_t *elf,
+	uint32_t machine_id)
+{
+	struct elf32_hdr *elf_hdr;
+
+	if (!elf) {
+		CAM_ERR(CAM_ICP, "Invalid params");
+		return -EINVAL;
+	}
+
+	elf_hdr = (struct elf32_hdr *)elf;
+
+	if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG)) {
+		CAM_ERR(CAM_ICP, "ICP elf identifier is failed");
+		return -EINVAL;
+	}
+
+	/* check architecture */
+	if (elf_hdr->e_machine != machine_id) {
+		CAM_ERR(CAM_ICP, "unsupported arch: 0x%x", elf_hdr->e_machine);
+		return -EINVAL;
+	}
+
+	/* check elf bit format */
+	if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) {
+		CAM_ERR(CAM_ICP, "elf doesn't support 32 bit format");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int32_t cam_icp_get_fw_size(
+	const uint8_t *elf, uint32_t *fw_size,
+	struct cam_icp_proc_params *params)
+{
+	int32_t rc = 0;
+	int32_t i = 0;
+	uint32_t num_prg_hdrs;
+	unsigned char *icp_prg_hdr_tbl;
+	uint32_t seg_mem_size = 0;
+	struct elf32_hdr *elf_hdr;
+	struct elf32_phdr *prg_hdr;
+
+	if (!elf || !fw_size || !params) {
+		CAM_ERR(CAM_ICP, "invalid args");
+		return -EINVAL;
+	}
+
+	*fw_size = 0;
+
+	elf_hdr = (struct elf32_hdr *)elf;
+	num_prg_hdrs = elf_hdr->e_phnum;
+	icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
+	prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
+
+	if (!prg_hdr) {
+		CAM_ERR(CAM_ICP, "failed to get elf program header attr");
+		return -EINVAL;
+	}
+
+	CAM_DBG(CAM_ICP, "num_prg_hdrs = %d", num_prg_hdrs);
+	for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
+		if (prg_hdr->p_flags == 0)
+			continue;
+
+		if ((params->skip_seg) &&
+			((prg_hdr->p_vaddr == params->vaddr[0]) ||
+			(prg_hdr->p_vaddr ==  params->vaddr[1])))
+			continue;
+
+		seg_mem_size = (prg_hdr->p_memsz + prg_hdr->p_align - 1) &
+					~(prg_hdr->p_align - 1);
+		seg_mem_size += prg_hdr->p_vaddr;
+		CAM_DBG(CAM_ICP, "memsz:%x align:%x addr:%x seg_mem_size:%x",
+			(int)prg_hdr->p_memsz, (int)prg_hdr->p_align,
+			(int)prg_hdr->p_vaddr, (int)seg_mem_size);
+		if (*fw_size < seg_mem_size)
+			*fw_size = seg_mem_size;
+
+	}
+
+	if (*fw_size == 0) {
+		CAM_ERR(CAM_ICP, "invalid elf fw file");
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+int32_t cam_icp_program_fw(const uint8_t *elf,
+	uintptr_t fw_kva_addr,
+	struct cam_icp_proc_params *params)
+{
+	int32_t rc = 0;
+	uint32_t num_prg_hdrs;
+	unsigned char *icp_prg_hdr_tbl;
+	int32_t i = 0;
+	u8 *dest;
+	u8 *src;
+	struct elf32_hdr *elf_hdr;
+	struct elf32_phdr *prg_hdr;
+
+	elf_hdr = (struct elf32_hdr *)elf;
+	num_prg_hdrs = elf_hdr->e_phnum;
+	icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
+	prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
+
+	if (!prg_hdr) {
+		CAM_ERR(CAM_ICP, "failed to get elf program header attr");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
+		if (prg_hdr->p_flags == 0)
+			continue;
+
+		if ((params->skip_seg) &&
+			((prg_hdr->p_vaddr == params->vaddr[0]) ||
+			(prg_hdr->p_vaddr ==  params->vaddr[1])))
+			continue;
+
+		CAM_DBG(CAM_ICP, "Loading FW header size: %u",
+			prg_hdr->p_filesz);
+		if (prg_hdr->p_filesz != 0) {
+			src = (u8 *)((u8 *)elf + prg_hdr->p_offset);
+			dest = (u8 *)(((u8 *)fw_kva_addr) +
+				prg_hdr->p_vaddr);
+
+			memcpy_toio(dest, src, prg_hdr->p_filesz);
+		}
+	}
+
+	return rc;
+}

+ 42 - 0
drivers/cam_icp/utils/cam_icp_utils.h

@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CAM_ICP_UTILS_H_
+#define _CAM_ICP_UTILS_H_
+
+#include <linux/firmware.h>
+#include <linux/elf.h>
+#include <linux/iopoll.h>
+
+#include "cam_debug_util.h"
+
+/**
+ * struct cam_icp_proc_params - ICP [a5/lx7] specific params
+ * @skip_seg :  If set skip segments listed in vaddr field
+ * @vaddr    :  vaddr of segments to be skipped
+ */
+struct cam_icp_proc_params {
+	bool        skip_seg;
+	uint32_t    vaddr[2];
+};
+
+/**
+ * @brief : Validate FW elf image
+ */
+int32_t cam_icp_validate_fw(const uint8_t *elf, uint32_t machine_id);
+
+/**
+ * @brief : Get FW elf size
+ */
+int32_t cam_icp_get_fw_size(const uint8_t *elf, uint32_t *fw_size,
+	struct cam_icp_proc_params *params);
+
+/**
+ * @brief : Program FW memory
+ */
+int32_t cam_icp_program_fw(const uint8_t *elf,
+	uintptr_t fw_kva_addr, struct cam_icp_proc_params *params);
+
+#endif /* _CAM_ICP_UTILS_H_ */