This patch add scm call support to make hypervisor call to enable access of fw regions in ddr to mss subsystem on arm-v8 arch soc's. Signed-off-by: Avaneesh Kumar Dwivedi <akdwived@xxxxxxxxxxxxxx> --- drivers/firmware/qcom_scm-64.c | 25 +++++++ drivers/firmware/qcom_scm.c | 93 ++++++++++++++++++++++++++ drivers/firmware/qcom_scm.h | 3 + drivers/remoteproc/qcom_q6v5_pil.c | 129 ++++++++++++++++++++++++++++++++++++- include/linux/qcom_scm.h | 14 ++++ 5 files changed, 262 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 4a0f5ea..187fc00 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -358,3 +358,28 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) return ret ? : res.a1; } + +int __qcom_scm_assign_mem(struct device *dev, struct vmid_detail vmid) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = vmid.fw_phy; + desc.args[1] = vmid.size_fw; + desc.args[2] = vmid.from_phy; + desc.args[3] = vmid.size_from; + desc.args[4] = vmid.to_phy; + desc.args[5] = vmid.size_to; + desc.args[6] = 0; + + desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, + QCOM_SCM_VAL, QCOM_SCM_VAL); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, + QCOM_MEM_PROT_ASSIGN_ID, + &desc, &res); + + return ret ? : res.a1; +} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 893f953ea..f137f34 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -42,6 +42,18 @@ struct qcom_scm { static struct qcom_scm *__scm; +struct dest_vm_and_perm_info { + __le32 vm; + __le32 perm; + __le32 *ctx; + __le32 ctx_size; +}; + +struct fw_region_info { + __le64 addr; + __le64 size; +}; + static int qcom_scm_clk_enable(void) { int ret; @@ -292,6 +304,87 @@ int qcom_scm_pas_shutdown(u32 peripheral) } EXPORT_SYMBOL(qcom_scm_pas_shutdown); +/** + * qcom_scm_assign_mem() - Allocate and fill vmid detail of old + * new owners of memory region for fw and metadata etc, Which is + * further passed to hypervisor, which does translate intermediate + * physical address used by subsystems. + * @vmid: structure with pointers and size detail of old and new + * owners vmid detail. + * Return 0 on success. + */ +int qcom_scm_assign_mem(struct vmid_detail vmid) +{ + unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; + struct dest_vm_and_perm_info *to; + struct fw_region_info *fw_info; + __le64 fw_phy; + __le32 *from; + int ret = -ENOMEM; + int i; + + from = dma_alloc_attrs(__scm->dev, vmid.size_from, + &vmid.from_phy, GFP_KERNEL, dma_attrs); + if (!from) { + dev_err(__scm->dev, + "failed to allocate buffer to pass source vmid detail\n"); + return -ENOMEM; + } + to = dma_alloc_attrs(__scm->dev, vmid.size_to, + &vmid.to_phy, GFP_KERNEL, dma_attrs); + if (!to) { + dev_err(__scm->dev, + "failed to allocate buffer to pass destination vmid detail\n"); + goto free_src_buff; + } + fw_info = dma_alloc_attrs(__scm->dev, sizeof(*fw_info), + &fw_phy, GFP_KERNEL, dma_attrs); + if (!fw_info) { + dev_err(__scm->dev, + "failed to allocate buffer to pass firmware detail\n"); + goto free_dest_buff; + } + + /* copy detail of original owner of ddr region */ + /* in physically contigious buffer */ + memcpy(from, vmid.from, vmid.size_from); + + /* fill details of new owners of ddr region*/ + /* in physically contigious buffer */ + for (i = 0; i < (vmid.size_to / sizeof(__le32)); i++) { + to[i].vm = vmid.to[i]; + to[i].perm = vmid.permission[i]; + to[i].ctx = NULL; + to[i].ctx_size = 0; + } + + /* copy detail of firmware region whose mapping need to be done */ + /* in physically contigious buffer */ + fw_info->addr = vmid.fw_phy; + fw_info->size = vmid.size_fw; + + /* reuse fw_phy and size_fw members to fill address and size of */ + /* fw_info buffer */ + vmid.fw_phy = fw_phy; + vmid.size_to = sizeof(*to) * (vmid.size_to / sizeof(__le32)); + vmid.size_fw = sizeof(*fw_info); + ret = __qcom_scm_assign_mem(__scm->dev, vmid); + if (!ret) + goto free_fw_buff; + return ret; +free_fw_buff: + dma_free_attrs(__scm->dev, sizeof(*fw_info), fw_info, + fw_phy, dma_attrs); +free_dest_buff: + dma_free_attrs(__scm->dev, vmid.size_to, to, + vmid.to_phy, dma_attrs); +free_src_buff: + dma_free_attrs(__scm->dev, vmid.size_from, from, + vmid.from_phy, dma_attrs); + return ret; +} +EXPORT_SYMBOL(qcom_scm_assign_mem); + static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, unsigned long idx) { diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 3584b00..4665a11 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -55,6 +55,9 @@ extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral); extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset); +#define QCOM_SCM_SVC_MP 0xc +#define QCOM_MEM_PROT_ASSIGN_ID 0x16 +extern int __qcom_scm_assign_mem(struct device *dev, struct vmid_detail vmid); /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 8fd697a..62ad976 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -110,6 +110,7 @@ struct rproc_hexagon_res { struct qcom_mss_reg_res *active_supply; char **proxy_clk_names; char **active_clk_names; + int version; }; struct q6v5 { @@ -153,8 +154,28 @@ struct q6v5 { size_t mpss_size; struct qcom_rproc_subdev smd_subdev; + int version; }; +enum { + MSS_MSM8916, + MSS_MSM8974, + MSS_MSM8996, +}; + +enum { + ASSIGN_ACCESS_MSA, + REMOVE_ACCESS_MSA, + ASSIGN_SHARED_ACCESS_MSA, + REMOVE_SHARED_ACCESS_MSA, +}; + +#define VMID_HLOS 0x3 +#define VMID_MSS_MSA 0xF +#define PERM_READ 0x4 +#define PERM_WRITE 0x2 +#define PERM_EXEC 0x1 +#define PERM_RW (PERM_READ | PERM_WRITE) static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, const struct qcom_mss_reg_res *reg_res) { @@ -288,6 +309,54 @@ static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc, return &table; } +int hyp_mem_access(int id, phys_addr_t addr, size_t size) +{ + struct vmid_detail vmid; + int ret; + + switch (id) { + case ASSIGN_ACCESS_MSA: + vmid.from = (int[]) { VMID_HLOS }; + vmid.to = (int[]) { VMID_MSS_MSA }; + vmid.permission = (int[]) { PERM_READ | PERM_WRITE }; + vmid.size_from = vmid.size_to = 1 * sizeof(__le32); + break; + case REMOVE_ACCESS_MSA: + vmid.from = (int[]) { VMID_MSS_MSA }; + vmid.to = (int[]) { VMID_HLOS }; + vmid.permission = + (int[]) { PERM_READ | PERM_WRITE | PERM_EXEC }; + vmid.size_from = vmid.size_to = 1 * sizeof(__le32); + break; + case ASSIGN_SHARED_ACCESS_MSA: + vmid.from = (int[]) { VMID_HLOS }; + vmid.to = (int[]) { VMID_HLOS, VMID_MSS_MSA }; + vmid.permission = (int[]) { PERM_RW, PERM_RW }; + vmid.size_from = 1 * sizeof(__le32); + vmid.size_to = 2 * sizeof(__le32); + break; + case REMOVE_SHARED_ACCESS_MSA: + vmid.from = (int[]) { VMID_HLOS, VMID_MSS_MSA }; + vmid.to = (int[]) { VMID_HLOS }; + vmid.permission = + (int[]) { PERM_READ | PERM_WRITE | PERM_EXEC }; + vmid.size_from = 2 * sizeof(__le32); + vmid.size_to = 1 * sizeof(__le32); + break; + default: + break; + } + + vmid.fw_phy = addr; + vmid.size_fw = size; + ret = qcom_scm_assign_mem(vmid); + if (ret) + pr_err("%s: Failed to assign memory protection, ret = %d\n", + __func__, ret); + + return ret; +} + static int q6v5_load(struct rproc *rproc, const struct firmware *fw) { struct q6v5 *qproc = rproc->priv; @@ -461,6 +530,15 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) memcpy(ptr, fw->data, fw->size); + /* Hypervisor mapping to access metadata by modem */ + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(ASSIGN_ACCESS_MSA, phys, + ALIGN(fw->size, SZ_4K)) : 0; + if (ret) { + dev_err(qproc->dev, + "Failed to assign metadata memory, ret - %d\n", ret); + return -ENOMEM; + } writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG); writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); @@ -470,6 +548,13 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) else if (ret < 0) dev_err(qproc->dev, "MPSS header authentication failed: %d\n", ret); + /* Metadata authentication done, remove modem access */ + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(REMOVE_ACCESS_MSA, phys, + ALIGN(fw->size, SZ_4K)) : 0; + if (ret) + dev_err(qproc->dev, + "Failed to reclaim metadata memory, ret - %d\n", ret); dma_free_attrs(qproc->dev, fw->size, ptr, phys, dma_attrs); return ret < 0 ? ret : 0; @@ -578,6 +663,16 @@ static int q6v5_mpss_load(struct q6v5 *qproc) size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); if (!size) { boot_addr = relocate ? qproc->mpss_phys : min_addr; + /* Hypervisor mapping of modem fw */ + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(ASSIGN_SHARED_ACCESS_MSA, + boot_addr, qproc->mpss_size) : 0; + if (ret) { + dev_err(qproc->dev, + "Failed to assign fw memory access, ret - %d\n", + ret); + return -ENOMEM; + } writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); } @@ -636,6 +731,14 @@ static int q6v5_start(struct rproc *rproc) goto assert_reset; } + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(ASSIGN_ACCESS_MSA, qproc->mba_phys, + qproc->mba_size) : 0; + if (ret) { + dev_err(qproc->dev, + "Failed to assign mba memory access, ret - %d\n", ret); + goto assert_reset; + } writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); ret = q6v5proc_reset(qproc); @@ -657,16 +760,22 @@ static int q6v5_start(struct rproc *rproc) ret = q6v5_mpss_load(qproc); if (ret) - goto halt_axi_ports; + goto reclaim_mem; ret = wait_for_completion_timeout(&qproc->start_done, msecs_to_jiffies(5000)); if (ret == 0) { dev_err(qproc->dev, "start timed out\n"); ret = -ETIMEDOUT; - goto halt_axi_ports; + goto reclaim_mem; } + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(REMOVE_ACCESS_MSA, qproc->mba_phys, + qproc->mba_size) : 0; + if (ret) + dev_err(qproc->dev, + "Failed to reclaim mba memory, ret - %d\n", ret); qproc->running = true; q6v5_clk_disable(qproc->dev, qproc->proxy_clks, @@ -676,7 +785,20 @@ static int q6v5_start(struct rproc *rproc) return 0; +reclaim_mem: + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(REMOVE_SHARED_ACCESS_MSA, + qproc->mpss_phys, qproc->mpss_size) : 0; + if (ret) + dev_err(qproc->dev, + "Failed to reclaim fw memory, ret - %d\n", ret); halt_axi_ports: + ret = qproc->version == MSS_MSM8996 ? + hyp_mem_access(REMOVE_ACCESS_MSA, qproc->mba_phys, + qproc->mba_size) : 0; + if (ret) + dev_err(qproc->dev, + "Failed to reclaim mba memory, ret - %d\n", ret); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); @@ -1015,6 +1137,7 @@ static int q6v5_probe(struct platform_device *pdev) if (ret) goto free_rproc; + qproc->version = desc->version; ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); if (ret < 0) goto free_rproc; @@ -1090,6 +1213,7 @@ static int q6v5_remove(struct platform_device *pdev) "mem", NULL }, + .version = MSS_MSM8916, }; static const struct rproc_hexagon_res msm8974_mss = { @@ -1127,6 +1251,7 @@ static int q6v5_remove(struct platform_device *pdev) "mem", NULL }, + .version = MSS_MSM8974, }; static const struct of_device_id q6v5_of_match[] = { diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index cc32ab8..cb0b7ee 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -23,6 +23,19 @@ struct qcom_scm_hdcp_req { u32 val; }; +struct vmid_detail { + __le32 *from; + __le32 *to; + __le32 *permission; + __le32 size_from; + __le32 size_to; + __le32 size_fw; + __le64 fw_phy; + __le64 from_phy; + __le64 to_phy; + +}; + extern bool qcom_scm_is_available(void); extern bool qcom_scm_hdcp_available(void); @@ -36,6 +49,7 @@ extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); extern int qcom_scm_pas_auth_and_reset(u32 peripheral); extern int qcom_scm_pas_shutdown(u32 peripheral); +extern int qcom_scm_assign_mem(struct vmid_detail vmid); #define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0 #define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1 -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html