1.Add vcp suspend and resume callback

Signed-off-by: Xiangzhi Tang <[email protected]>
---
 drivers/remoteproc/mtk_vcp_common.c       | 114 ++++++++++++++++++++++
 drivers/remoteproc/mtk_vcp_common.h       |   6 ++
 drivers/remoteproc/mtk_vcp_rproc.c        |  59 +++++++++++
 drivers/remoteproc/mtk_vcp_rproc.h        |   2 +
 include/linux/remoteproc/mtk_vcp_public.h |   1 +
 5 files changed, 182 insertions(+)

diff --git a/drivers/remoteproc/mtk_vcp_common.c 
b/drivers/remoteproc/mtk_vcp_common.c
index f3b506034e95..f4952fbace3c 100644
--- a/drivers/remoteproc/mtk_vcp_common.c
+++ b/drivers/remoteproc/mtk_vcp_common.c
@@ -190,6 +190,11 @@ bool is_vcp_ready(struct mtk_vcp_device *vcp,
        return vcp_is_core_ready(vcp, core_id);
 }
 
+bool is_vcp_suspending(struct mtk_vcp_device *vcp)
+{
+       return vcp->vcp_cluster->is_suspending ? true : false;
+}
+
 int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
                            enum vcp_core_id core_id)
 {
@@ -252,6 +257,95 @@ int wait_core_hart_shutdown(struct mtk_vcp_device *vcp,
        return ret;
 }
 
+void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_id)
+{
+       u32 status;
+       u32 stop_ctrl;
+       int ret;
+
+       stop_ctrl = (vcp->vcp_cluster->twohart[core_id] ?
+                    (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT | 
B_CORE_AXIS_BUSY) :
+                    (B_CORE_GATED | B_HART0_HALT | B_CORE_AXIS_BUSY));
+
+       switch (core_id) {
+       case VCP_ID:
+               ret = readl_poll_timeout(vcp->vcp_cluster->cfg + R_CORE0_STATUS,
+                                        status,
+                                        (status & stop_ctrl) == (stop_ctrl & 
~B_CORE_AXIS_BUSY),
+                                        USEC_PER_MSEC,
+                                        CORE_HART_SHUTDOWN_TIMEOUT_MS * 
USEC_PER_MSEC);
+               if (ret) {
+                       dev_err(vcp->dev, "wait [%s] core stop timeout, current 
status 0x%x\n",
+                               core_id ? "MMUP_ID" : "VCP_ID", status);
+                       return;
+               }
+               break;
+       case MMUP_ID:
+               ret = readl_poll_timeout(vcp->vcp_cluster->cfg + R_CORE1_STATUS,
+                                        status,
+                                        (status & stop_ctrl) == (stop_ctrl & 
~B_CORE_AXIS_BUSY),
+                                        USEC_PER_MSEC,
+                                        CORE_HART_SHUTDOWN_TIMEOUT_MS * 
USEC_PER_MSEC);
+               if (ret) {
+                       dev_err(vcp->dev, "wait [%s] core stop timeout, current 
status 0x%x\n",
+                               core_id ? "MMUP_ID" : "VCP_ID", status);
+                       return;
+               }
+               break;
+       default:
+               dev_err(vcp->dev, "%s(), No Support core id\n", __func__);
+               break;
+       }
+}
+
+static bool vcp_get_suspend_resume_status(struct mtk_vcp_device *vcp)
+{
+       if (vcp->vcp_cluster->core_nums > MMUP_ID)
+               return !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & 
VCP_AP_SUSPEND) &&
+                      !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & 
MMUP_AP_SUSPEND);
+
+       return !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & 
VCP_AP_SUSPEND);
+}
+
+void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend)
+{
+       bool status;
+       int ret;
+
+       if (suspend) {
+               writel(B_CORE0_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR2);
+               writel(SUSPEND_MAGIC, vcp->vcp_cluster->cfg + 
VCP_C0_GPR0_SUSPEND_RESUME);
+               if (vcp->vcp_cluster->core_nums > MMUP_ID) {
+                       writel(B_CORE1_SUSPEND, vcp->vcp_cluster->cfg_core + 
AP_R_GPR3);
+                       writel(SUSPEND_MAGIC, vcp->vcp_cluster->cfg + 
VCP_C1_GPR0_SUSPEND_RESUME);
+               }
+       } else {
+               writel(B_CORE0_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR2);
+               writel(RESUME_MAGIC, vcp->vcp_cluster->cfg + 
VCP_C0_GPR0_SUSPEND_RESUME);
+               if (vcp->vcp_cluster->core_nums > MMUP_ID) {
+                       writel(B_CORE1_RESUME, vcp->vcp_cluster->cfg_core + 
AP_R_GPR3);
+                       writel(RESUME_MAGIC, vcp->vcp_cluster->cfg + 
VCP_C1_GPR0_SUSPEND_RESUME);
+               }
+       }
+
+       writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET);
+
+       ret = read_poll_timeout(vcp_get_suspend_resume_status,
+                               status, (status == suspend),
+                               USEC_PER_MSEC,
+                               SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+                               false, vcp);
+       if (ret)
+               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(struct mtk_vcp_device *vcp,
                           enum vcp_feature_id id,
                           struct notifier_block *nb)
@@ -416,6 +510,16 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, 
enum vcp_feature_id id)
        bool suspend_status;
        int ret;
 
+       ret = read_poll_timeout(is_vcp_suspending,
+                               suspend_status, !suspend_status,
+                               USEC_PER_MSEC,
+                               SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+                               false, vcp);
+       if (ret) {
+               dev_err(vcp->dev, "%s blocked by vcp suspend\n", __func__);
+               return ret;
+       }
+
        if (vcp->vcp_cluster->feature_enable[id]) {
                dev_err(vcp->dev, "%s feature(id=%d) already enabled\n",
                        __func__, id);
@@ -443,6 +547,16 @@ static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, 
enum vcp_feature_id id
        bool suspend_status;
        int ret;
 
+       ret = read_poll_timeout(is_vcp_suspending,
+                               suspend_status, !suspend_status,
+                               USEC_PER_MSEC,
+                               SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC,
+                               false, vcp);
+       if (ret) {
+               dev_err(vcp->dev, "%s blocked by vcp suspend\n", __func__);
+               return ret;
+       }
+
        if (!vcp->vcp_cluster->feature_enable[id]) {
                dev_err(vcp->dev, "%s feature(id=%d) already disabled\n",
                        __func__, id);
diff --git a/drivers/remoteproc/mtk_vcp_common.h 
b/drivers/remoteproc/mtk_vcp_common.h
index 8b19fcb78a79..eff2199c7610 100644
--- a/drivers/remoteproc/mtk_vcp_common.h
+++ b/drivers/remoteproc/mtk_vcp_common.h
@@ -16,9 +16,12 @@
 #define VCP_READY_TIMEOUT_MS 3000
 #define VCP_IPI_DEV_READY_TIMEOUT 1000
 #define CORE_HART_SHUTDOWN_TIMEOUT_MS 10
+#define SUSPEND_WAIT_TIMEOUT_MS 100
 
 /* VCP platform definition */
 #define DMA_MAX_MASK_BIT 33
+#define RESUME_MAGIC 0x12345678
+#define SUSPEND_MAGIC 0x87654321
 #define PIN_OUT_C_SIZE_SLEEP_0 2
 
 /* VCP load image definition */
@@ -269,5 +272,8 @@ int vcp_A_register_feature(struct mtk_vcp_device *vcp,
 int vcp_A_deregister_feature(struct mtk_vcp_device *vcp,
                             enum vcp_feature_id id);
 
+bool is_vcp_suspending(struct mtk_vcp_device *vcp);
 int 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_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 833a0dc69d9c..a44a8081e7a3 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"
@@ -67,6 +68,57 @@ 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);
+
+       for (u32 id = RTOS_FEATURE_ID + 1; id < NUM_FEATURE_ID; id++) {
+               if (vcp->vcp_cluster->feature_enable[id]) {
+                       dev_err(vcp->dev, "%s Check feature(id=%d) 
statue(%d)\n",
+                               __func__, id,
+                               vcp->vcp_cluster->feature_enable[id]);
+                       return -EINVAL;
+               }
+       }
+
+       if (!vcp->vcp_cluster->is_suspending) {
+               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);
+       }
+       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) {
+               pm_runtime_get_sync(dev);
+
+               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;
@@ -436,6 +488,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_feature = vcp_A_register_feature,
                .vcp_deregister_feature = vcp_A_deregister_feature,
                .vcp_register_notify = vcp_A_register_notify,
@@ -458,6 +511,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},
        {}
@@ -471,6 +529,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 600715b77124..e3a76e368fd7 100644
--- a/drivers/remoteproc/mtk_vcp_rproc.h
+++ b/drivers/remoteproc/mtk_vcp_rproc.h
@@ -23,6 +23,7 @@
  * @msg_vcp_ready1: core1 ready ipi msg data
  * @slp_ipi_ack_data: sleep ipi msg data
  * @feature_enable: feature status 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
@@ -46,6 +47,7 @@ struct mtk_vcp_of_cluster {
        u32 msg_vcp_ready1;
        u32 slp_ipi_ack_data;
        bool feature_enable[NUM_FEATURE_ID];
+       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 b9e1d86685fd..dbdcf5aa0f99 100644
--- a/include/linux/remoteproc/mtk_vcp_public.h
+++ b/include/linux/remoteproc/mtk_vcp_public.h
@@ -100,6 +100,7 @@ struct mtk_vcp_ipi_ops {
 };
 
 struct mtk_vcp_ops {
+       bool (*vcp_is_suspending)(struct mtk_vcp_device *vcp);
        bool (*vcp_is_ready)(struct mtk_vcp_device *vcp,
                             enum vcp_feature_id id);
        int (*vcp_register_feature)(struct mtk_vcp_device *vcp,
-- 
2.46.0


Reply via email to