1.Add vcp suspend and resume callback 2.Add sleep lock ipi cmd flow for lock vcp status while feature using VCP
Signed-off-by: Xiangzhi Tang <[email protected]> --- drivers/remoteproc/mtk_vcp_common.c | 175 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 29 ++++ drivers/remoteproc/mtk_vcp_rproc.c | 66 ++++++++ drivers/remoteproc/mtk_vcp_rproc.h | 4 + include/linux/remoteproc/mtk_vcp_public.h | 1 + 5 files changed, 275 insertions(+) diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_vcp_common.c index 9767f5ff15a0..0bd071f73b23 100644 --- a/drivers/remoteproc/mtk_vcp_common.c +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -364,6 +364,127 @@ u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, return retry; } +bool is_vcp_suspending(struct mtk_vcp_device *vcp) +{ + return vcp->vcp_cluster->is_suspending ? true : false; +} + +void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_id) +{ + u32 core_halt; + u32 core_axi; + u32 core_status; + u32 status; + + /* make sure vcp is in idle state */ + int timeout = SUSPEND_WAIT_TIMEOUT_MS; + + while (--timeout) { + switch (core_id) { + case VCP_ID: + core_status = readl(vcp->vcp_cluster->cfg + R_CORE0_STATUS); + status = (vcp->vcp_cluster->twohart[VCP_ID] ? + (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT) : + (B_CORE_GATED | B_HART0_HALT)); + break; + case MMUP_ID: + core_status = readl(vcp->vcp_cluster->cfg + R_CORE1_STATUS); + status = (vcp->vcp_cluster->twohart[MMUP_ID] ? + (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT) : + (B_CORE_GATED | B_HART0_HALT)); + break; + case VCP_CORE_TOTAL: + default: + break; + } + + core_halt = ((core_status & status) == status); + core_axi = core_status & (B_CORE_AXIS_BUSY); + + if (core_halt && !core_axi) { + dev_err(vcp->dev, "[%s] core status 0x%x, GPIC 0x%x flag 0x%x\n", + core_id ? "MMUP_ID" : "VCP_ID", core_status, + readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET), + readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC)); + break; + } + usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX); + } + + if (timeout == 0) { + dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n", + core_id ? "MMUP_ID" : "VCP_ID", core_status); + } +} + +void vcp_wait_rdy_signal(struct mtk_vcp_device *vcp, bool rdy) +{ + u32 rdy_signal; + int ret; + + if (!IS_ERR((void const *)vcp->vcp_cluster->vcp_rdy)) { + if (rdy) + ret = read_poll_timeout_atomic(readl, rdy_signal, rdy_signal & READY_BIT, + USEC_PER_MSEC, + VCP_SYNC_TIMEOUT_MS * USEC_PER_MSEC, + false, + vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7); + else + ret = read_poll_timeout_atomic(readl, rdy_signal, !(rdy_signal & READY_BIT), + USEC_PER_MSEC, + VCP_SYNC_TIMEOUT_MS * USEC_PER_MSEC, + false, + vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7); + if (ret < 0) + dev_err(vcp->dev, "wait vcp %s timeout 0x%x\n", + rdy ? "set rdy bit" : "clr rdy bit", + readl(vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7)); + } else { + dev_err(vcp->dev, "illegal vcp rdy signal\n"); + } +} + +void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend) +{ + int timeout = SUSPEND_WAIT_TIMEOUT_MS; + + if (suspend) { + writel(B_CORE0_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR2); + writel(B_CORE1_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR3); + writel(SUSPEND_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME_FLAG); + writel(SUSPEND_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME_FLAG); + writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + } else { + writel(B_CORE0_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR2); + writel(B_CORE1_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR3); + writel(RESUME_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME_FLAG); + writel(RESUME_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME_FLAG); + writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + } + + while (--timeout) { + if (suspend && + (readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & (VCP_AP_SUSPEND)) && + (readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & (MMUP_AP_SUSPEND))) + break; + else if (!suspend && + !(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & (VCP_AP_SUSPEND)) && + !(readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & (MMUP_AP_SUSPEND))) + break; + usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX); + } + if (timeout <= 0) { + dev_err(vcp->dev, "vcp %s timeout GPIC 0x%x 0x%x 0x%x 0x%x flag 0x%x 0x%x\n", + suspend ? "suspend" : "resume", + readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET), + readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_CLR), + readl(vcp->vcp_cluster->cfg_core + AP_R_GPR2), + readl(vcp->vcp_cluster->cfg_core + AP_R_GPR3), + readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC), + readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC)); + } +} + void vcp_A_register_notify(enum feature_id id, struct notifier_block *nb) { @@ -524,7 +645,23 @@ static int reset_vcp(struct mtk_vcp_device *vcp) static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id) { + int ret; + bool suspend_status; + struct slp_ctrl_data ipi_data; + mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + read_poll_timeout_atomic(is_vcp_suspending, + suspend_status, !suspend_status, + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (suspend_status) { + dev_warn(vcp->dev, "%s blocked by vcp suspend, pwclkcnt(%d)\n", + __func__, + vcp->vcp_cluster->pwclkcnt); + return -ETIMEDOUT; + } + if (vcp->vcp_cluster->pwclkcnt == 0) { if (!is_vcp_ready_by_coreid(VCP_CORE_TOTAL)) { if (reset_vcp(vcp)) { @@ -534,6 +671,17 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id) } } vcp->vcp_cluster->pwclkcnt++; + if (id != RTOS_FEATURE_ID) { + ipi_data.cmd = SLP_WAKE_LOCK; + ipi_data.feature = id; + ret = vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + &ipi_data, PIN_OUT_C_SIZE_SLEEP_0, 500); + if (ret < 0) { + dev_warn(vcp->dev, "%s ipc_send_compl failed. ret %d\n", + __func__, ret); + return ret; + } + } mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex); return 0; @@ -541,7 +689,34 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id) static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id id) { + int ret; + bool suspend_status; + struct slp_ctrl_data ipi_data; + mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + read_poll_timeout_atomic(is_vcp_suspending, + suspend_status, !suspend_status, + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (suspend_status) { + dev_warn(vcp->dev, "%s blocked by vcp suspend, pwclkcnt(%d)\n", + __func__, + vcp->vcp_cluster->pwclkcnt); + return -ETIMEDOUT; + } + + if (id != RTOS_FEATURE_ID) { + ipi_data.cmd = SLP_WAKE_UNLOCK; + ipi_data.feature = id; + ret = vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + &ipi_data, PIN_OUT_C_SIZE_SLEEP_0, 500); + if (ret < 0) { + dev_err(vcp->dev, "%s ipc_send_compl failed. ret %d\n", + __func__, ret); + return ret; + } + } vcp->vcp_cluster->pwclkcnt--; if (vcp->vcp_cluster->pwclkcnt < 0) { for (u32 i = 0; i < NUM_FEATURE_ID; i++) diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_vcp_common.h index 4a4393b2ae1f..42deda362b6c 100644 --- a/drivers/remoteproc/mtk_vcp_common.h +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -18,9 +18,13 @@ #define VCP_IPI_DEV_READY_TIMEOUT 1000 #define USDELAY_RANGE_MIN 1000 #define USDELAY_RANGE_MAX 2000 +#define SUSPEND_WAIT_TIMEOUT_MS 100 /* vcp platform define */ #define DMA_MAX_MASK_BIT 33 +#define RESUME_IPI_MAGIC 0x12345678 +#define SUSPEND_IPI_MAGIC 0x87654321 +#define PIN_OUT_C_SIZE_SLEEP_0 2 /* vcp load image define */ #define VCM_IMAGE_MAGIC (0x58881688) @@ -98,6 +102,15 @@ enum vcp_core_id { VCP_CORE_TOTAL = 2, }; +/* vcp sleep cmd flag sync with VCP FW */ +enum { + SLP_WAKE_LOCK = 0, + SLP_WAKE_UNLOCK, + SLP_STATUS_DBG, + SLP_SUSPEND, + SLP_RESUME, +}; + /* vcp kernel smc server id */ enum mtk_tinysys_vcp_kernel_op { MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET = 0, @@ -155,6 +168,17 @@ struct vcp_reserve_mblock { size_t size; }; +/** + * struct slp_ctrl_data - sleep ctrl data sync with AP and VCP + * + * @feature: Feature id + * @cmd: sleep cmd flag. + */ +struct slp_ctrl_data { + u32 feature; + u32 cmd; +}; + /** * struct vcp_work_struct - vcp notify work structure. * @@ -230,6 +254,8 @@ void vcp_A_register_notify(enum feature_id id, struct notifier_block *nb); void vcp_A_unregister_notify(enum feature_id id, struct notifier_block *nb); +bool is_vcp_suspending(struct mtk_vcp_device *vcp); + /* vcp common reserved memory APIs */ int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp); phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id); @@ -252,4 +278,7 @@ int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, /* vcp common core hart shutdown API */ u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id); +void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_id); +void vcp_wait_rdy_signal(struct mtk_vcp_device *vcp, bool rdy); +void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend); #endif diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vcp_rproc.c index 4aa0ed47abd7..133518bedd76 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.c +++ b/drivers/remoteproc/mtk_vcp_rproc.c @@ -11,6 +11,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/suspend.h> #include <linux/remoteproc.h> #include "mtk_vcp_common.h" @@ -73,12 +74,68 @@ struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp) } EXPORT_SYMBOL_GPL(vcp_get_ipidev); +static int mtk_vcp_suspend(struct device *dev) +{ + struct mtk_vcp_device *vcp = platform_get_drvdata(to_platform_device(dev)); + + vcp_extern_notify(VCP_ID, VCP_EVENT_SUSPEND); + vcp_extern_notify(MMUP_ID, VCP_EVENT_SUSPEND); + + if (!vcp->vcp_cluster->is_suspending && + vcp->vcp_cluster->pwclkcnt) { + vcp->vcp_cluster->is_suspending = true; + vcp->vcp_cluster->vcp_ready[VCP_ID] = false; + vcp->vcp_cluster->vcp_ready[MMUP_ID] = false; + + flush_workqueue(vcp->vcp_cluster->vcp_workqueue); + + vcp_wait_suspend_resume(vcp, true); + vcp_wait_core_stop(vcp, VCP_ID); + vcp_wait_core_stop(vcp, MMUP_ID); + + pm_runtime_put_sync(dev); + + /* wait vcp clr rdy bit */ + vcp_wait_rdy_signal(vcp, false); + } + vcp->vcp_cluster->is_suspending = true; + + return 0; +} + +static int mtk_vcp_resume(struct device *dev) +{ + struct mtk_vcp_device *vcp = platform_get_drvdata(to_platform_device(dev)); + + if (vcp->vcp_cluster->is_suspending && + vcp->vcp_cluster->pwclkcnt) { + pm_runtime_get_sync(dev); + + /* wait vcp set rdy bit */ + vcp_wait_rdy_signal(vcp, true); + vcp_wait_suspend_resume(vcp, false); + } + vcp->vcp_cluster->is_suspending = false; + + vcp_extern_notify(MMUP_ID, VCP_EVENT_RESUME); + vcp_extern_notify(VCP_ID, VCP_EVENT_RESUME); + + return 0; +} + static int mtk_vcp_start(struct rproc *rproc) { struct mtk_vcp_device *vcp = (struct mtk_vcp_device *)rproc->priv; struct arm_smccc_res res; int ret; + ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + NULL, NULL, &vcp->vcp_cluster->slp_ipi_ack_data); + if (ret) { + dev_err(vcp->dev, "Failed to register IPI_OUT_C_SLEEP_0\n"); + goto slp_ipi_unregister; + } + ret = vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_0, (void *)vcp_A_ready_ipi_handler, vcp, &vcp->vcp_cluster->msg_vcp_ready0); @@ -118,6 +175,8 @@ static int mtk_vcp_start(struct rproc *rproc) vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_1); vcp0_ready_ipi_unregister: vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_0); +slp_ipi_unregister: + vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_OUT_C_SLEEP_0); return ret; } @@ -435,6 +494,7 @@ static struct mtk_vcp_ipi_ops mt8196_vcp_ipi_ops = { static const struct mtk_vcp_of_data mt8196_of_data = { .ops = { .vcp_is_ready = is_vcp_ready, + .vcp_is_suspending = is_vcp_suspending, .vcp_register_notify = vcp_A_register_notify, .vcp_unregister_notify = vcp_A_unregister_notify, .vcp_register_feature = vcp_A_register_feature, @@ -455,6 +515,11 @@ static const struct mtk_vcp_of_data mt8196_of_data = { }, }; +static const struct dev_pm_ops mtk_vcp_rproc_pm_ops = { + .suspend_noirq = mtk_vcp_suspend, + .resume_noirq = mtk_vcp_resume, +}; + static const struct of_device_id mtk_vcp_of_match[] = { { .compatible = "mediatek,mt8196-vcp", .data = &mt8196_of_data}, {} @@ -468,6 +533,7 @@ static struct platform_driver mtk_vcp_device = { .driver = { .name = "mtk-vcp", .of_match_table = mtk_vcp_of_match, + .pm = pm_ptr(&mtk_vcp_rproc_pm_ops), }, }; diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vcp_rproc.h index e36612256b63..3713977e4171 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.h +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -22,7 +22,9 @@ * @sram_offset: core sram memory layout * @msg_vcp_ready0: core0 ready ipi msg data * @msg_vcp_ready1: core1 ready ipi msg data + * @slp_ipi_ack_data: sleep ipi msg data * @pwclkcnt: power and clock config count data + * @is_suspending: suspend status flag * @vcp_ready: vcp core status flag * @share_mem_iova: shared memory iova base * @share_mem_size: shared memory size @@ -45,7 +47,9 @@ struct mtk_vcp_of_cluster { u32 sram_offset[VCP_CORE_TOTAL]; u32 msg_vcp_ready0; u32 msg_vcp_ready1; + u32 slp_ipi_ack_data; int pwclkcnt; + bool is_suspending; bool vcp_ready[VCP_CORE_TOTAL]; dma_addr_t share_mem_iova; size_t share_mem_size; diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remoteproc/mtk_vcp_public.h index 5bd562d1ae62..5a859a3bc1eb 100644 --- a/include/linux/remoteproc/mtk_vcp_public.h +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -107,6 +107,7 @@ struct mtk_vcp_ipi_ops { struct mtk_vcp_ops { bool (*vcp_is_ready)(enum feature_id id); + bool (*vcp_is_suspending)(struct mtk_vcp_device *vcp); void (*vcp_register_notify)(enum feature_id id, struct notifier_block *nb); void (*vcp_unregister_notify)(enum feature_id id, struct notifier_block *nb); int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id id); -- 2.46.0

