dev_err(dev, "%s: can not start core 1 before core 0\n",
__func__);
ret = -EPERM;
@@ -646,7 +649,8 @@ static int k3_r5_rproc_stop(struct rproc *rproc)
/* do not allow core 0 to stop before core 1 */
core1 = list_last_entry(&cluster->cores, struct k3_r5_core,
elem);
- if (core != core1 && core1->rproc->state != RPROC_OFFLINE) {
+ if (core != core1 && core1->rproc->state != RPROC_OFFLINE &&
+ core1->rproc->state != RPROC_SUSPENDED) {
dev_err(dev, "%s: can not stop core 0 before core 1\n",
__func__);
ret = -EPERM;
@@ -1238,6 +1242,117 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc)
return ret;
}
+static int k3_r5_rproc_suspend(struct k3_r5_rproc *kproc)
+{
+ unsigned int rproc_state = kproc->rproc->state;
+ int ret;
+
+ if (rproc_state != RPROC_RUNNING && rproc_state != RPROC_ATTACHED)
+ return 0;
+
+ if (rproc_state == RPROC_RUNNING)
+ ret = rproc_shutdown(kproc->rproc);
+ else
+ ret = rproc_detach(kproc->rproc);
+
+ if (ret) {
+ dev_err(kproc->dev, "Failed to %s rproc (%d)\n",
+ (rproc_state == RPROC_RUNNING) ? "shutdown" : "detach",
+ ret);
+ return ret;
+ }
+
+ kproc->rproc->state = RPROC_SUSPENDED;
+
+ return ret;
+}
+
+static int k3_r5_rproc_resume(struct k3_r5_rproc *kproc)
+{
+ int ret;
+
+ if (kproc->rproc->state != RPROC_SUSPENDED)
+ return 0;
+
+ ret = k3_r5_rproc_configure_mode(kproc);
+ if (ret < 0)
+ return -EBUSY;
+
+ /*
+ * ret > 0 for IPC-only mode
+ * ret == 0 for remote proc mode
+ */
+ if (ret == 0) {
+ /*
+ * remote proc looses its configuration when powered off.
+ * So, we have to configure it again on resume.
+ */
+ ret = k3_r5_rproc_configure(kproc);
+ if (ret < 0) {
+ dev_err(kproc->dev, "k3_r5_rproc_configure failed (%d)\n", ret);
+ return -EBUSY;
+ }
+ }
+
+ return rproc_boot(kproc->rproc);
+}
+
+static int k3_r5_cluster_pm_notifier_call(struct notifier_block *bl,
+ unsigned long state, void *unused)
+{
+ struct k3_r5_cluster *cluster = container_of(bl, struct k3_r5_cluster,
+ pm_notifier);
+ struct k3_r5_core *core;
+ int ret;
+
+ switch (state) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_RESTORE_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ /* core1 should be suspended before core0 */
+ list_for_each_entry_reverse(core, &cluster->cores, elem) {
+ /*
+ * In LOCKSTEP mode, rproc is allocated only for
+ * core0
+ */
+ if (core->rproc) {
+ ret = k3_r5_rproc_suspend(core->rproc->priv);
+ if (ret)
+ dev_warn(core->dev,
+ "k3_r5_rproc_suspend failed (%d)\n", ret);
+ }
+
+ ret = ti_sci_proc_release(core->tsp);
+ if (ret)
+ dev_warn(core->dev, "ti_sci_proc_release failed (%d)\n", ret);
+ }
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ /* core0 should be started before core1 */
+ list_for_each_entry(core, &cluster->cores, elem) {
+ ret = ti_sci_proc_request(core->tsp);
+ if (ret)
+ dev_warn(core->dev, "ti_sci_proc_request failed (%d)\n", ret);
+
+ /*
+ * In LOCKSTEP mode, rproc is allocated only for
+ * core0
+ */
+ if (core->rproc) {
+ ret = k3_r5_rproc_resume(core->rproc->priv);
+ if (ret)
+ dev_warn(core->dev,
+ "k3_r5_rproc_resume failed (%d)\n", ret);
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
static int k3_r5_cluster_rproc_init(struct platform_device *pdev)
{
struct k3_r5_cluster *cluster = platform_get_drvdata(pdev);
@@ -1336,6 +1451,9 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev)
}
}
+ cluster->pm_notifier.notifier_call = k3_r5_cluster_pm_notifier_call;
+ register_pm_notifier(&cluster->pm_notifier);
+
return 0;
err_split:
@@ -1402,6 +1520,7 @@ static void k3_r5_cluster_rproc_exit(void *data)
rproc_free(rproc);
core->rproc = NULL;
}
+ unregister_pm_notifier(&cluster->pm_notifier);
}
static int k3_r5_core_of_get_internal_memories(struct platform_device *pdev,