diff mbox series

[2/4] remoteproc: k3-r5: Introduce PM suspend/resume handlers

Message ID 20240621150058.319524-3-richard.genoud@bootlin.com (mailing list archive)
State New
Headers show
Series remoteproc: k3-r5: Introduce suspend to ram support | expand

Commit Message

Richard GENOUD June 21, 2024, 3 p.m. UTC
This patch adds the support for system suspend/resume to the ti_k3_R5
remoteproc driver.

In order to save maximum power, the approach here is to shutdown
completely the cores that were started by the kernel (i.e. those in
RUNNING state).
Those which were started before the kernel (in attached mode) will be
detached.

The pm_notifier mechanism is used here because the remote procs firmwares
have to be reloaded at resume, and thus the driver must have access to
the file system were the firmware is stored.

On suspend, the running remote procs are stopped, the attached remote
procs are detached and processor control released.

On resume, the reverse operation is done.

Based on work from: Hari Nagalla <hnagalla@ti.com>

Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
 drivers/remoteproc/ti_k3_r5_remoteproc.c | 123 ++++++++++++++++++++++-
 1 file changed, 121 insertions(+), 2 deletions(-)

Comments

Mathieu Poirier June 28, 2024, 8:48 p.m. UTC | #1
On Fri, Jun 21, 2024 at 05:00:56PM +0200, Richard Genoud wrote:
> This patch adds the support for system suspend/resume to the ti_k3_R5
> remoteproc driver.
> 
> In order to save maximum power, the approach here is to shutdown
> completely the cores that were started by the kernel (i.e. those in
> RUNNING state).
> Those which were started before the kernel (in attached mode) will be
> detached.
> 
> The pm_notifier mechanism is used here because the remote procs firmwares
> have to be reloaded at resume, and thus the driver must have access to
> the file system were the firmware is stored.
> 
> On suspend, the running remote procs are stopped, the attached remote
> procs are detached and processor control released.
> 
> On resume, the reverse operation is done.
> 
> Based on work from: Hari Nagalla <hnagalla@ti.com>
> 
> Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
> ---
>  drivers/remoteproc/ti_k3_r5_remoteproc.c | 123 ++++++++++++++++++++++-
>  1 file changed, 121 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c
> index 39a47540c590..1f18b08618c8 100644
> --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c
> +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c
> @@ -20,6 +20,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/remoteproc.h>
> +#include <linux/suspend.h>
>  #include <linux/reset.h>
>  #include <linux/slab.h>
>  
> @@ -112,6 +113,7 @@ struct k3_r5_cluster {
>  	struct list_head cores;
>  	wait_queue_head_t core_transition;
>  	const struct k3_r5_soc_data *soc_data;
> +	struct notifier_block pm_notifier;
>  };
>  
>  /**
> @@ -577,7 +579,8 @@ static int k3_r5_rproc_start(struct rproc *rproc)
>  		/* do not allow core 1 to start before core 0 */
>  		core0 = list_first_entry(&cluster->cores, struct k3_r5_core,
>  					 elem);
> -		if (core != core0 && core0->rproc->state == RPROC_OFFLINE) {
> +		if (core != core0 && (core0->rproc->state == RPROC_OFFLINE ||
> +				      core0->rproc->state == RPROC_SUSPENDED)) {

If I understand correctly, this is to address a possible race condition between
user space wanting to start core1 via sysfs while the system is being suspended.
Is this correct?  If so, please add a comment to explain what is going on.
Otherwise a comment is obviously needed.

>  			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,
Richard GENOUD July 1, 2024, 7:30 a.m. UTC | #2
Le 28/06/2024 à 22:48, Mathieu Poirier a écrit :
> On Fri, Jun 21, 2024 at 05:00:56PM +0200, Richard Genoud wrote:
>> This patch adds the support for system suspend/resume to the ti_k3_R5
>> remoteproc driver.
>>
>> In order to save maximum power, the approach here is to shutdown
>> completely the cores that were started by the kernel (i.e. those in
>> RUNNING state).
>> Those which were started before the kernel (in attached mode) will be
>> detached.
>>
>> The pm_notifier mechanism is used here because the remote procs firmwares
>> have to be reloaded at resume, and thus the driver must have access to
>> the file system were the firmware is stored.
>>
>> On suspend, the running remote procs are stopped, the attached remote
>> procs are detached and processor control released.
>>
>> On resume, the reverse operation is done.
>>
>> Based on work from: Hari Nagalla <hnagalla@ti.com>
>>
>> Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
>> ---
>>   drivers/remoteproc/ti_k3_r5_remoteproc.c | 123 ++++++++++++++++++++++-
>>   1 file changed, 121 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c
>> index 39a47540c590..1f18b08618c8 100644
>> --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c
>> +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c
>> @@ -20,6 +20,7 @@
>>   #include <linux/platform_device.h>
>>   #include <linux/pm_runtime.h>
>>   #include <linux/remoteproc.h>
>> +#include <linux/suspend.h>
>>   #include <linux/reset.h>
>>   #include <linux/slab.h>
>>   
>> @@ -112,6 +113,7 @@ struct k3_r5_cluster {
>>   	struct list_head cores;
>>   	wait_queue_head_t core_transition;
>>   	const struct k3_r5_soc_data *soc_data;
>> +	struct notifier_block pm_notifier;
>>   };
>>   
>>   /**
>> @@ -577,7 +579,8 @@ static int k3_r5_rproc_start(struct rproc *rproc)
>>   		/* do not allow core 1 to start before core 0 */
>>   		core0 = list_first_entry(&cluster->cores, struct k3_r5_core,
>>   					 elem);
>> -		if (core != core0 && core0->rproc->state == RPROC_OFFLINE) {
>> +		if (core != core0 && (core0->rproc->state == RPROC_OFFLINE ||
>> +				      core0->rproc->state == RPROC_SUSPENDED)) {
> 
> If I understand correctly, this is to address a possible race condition between
> user space wanting to start core1 via sysfs while the system is being suspended.
> Is this correct?  If so, please add a comment to explain what is going on.
> Otherwise a comment is obviously needed.
Yes, you're right, I'll add a comment on the race condition at suspend.

> 
>>   			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,

Thanks !
kernel test robot July 1, 2024, 7:02 p.m. UTC | #3
Hi Richard,

kernel test robot noticed the following build warnings:

[auto build test WARNING on remoteproc/rproc-next]
[also build test WARNING on linus/master v6.10-rc6 next-20240701]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Richard-Genoud/remoteproc-k3-r5-Fix-IPC-only-mode-detection/20240625-201619
base:   git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux.git rproc-next
patch link:    https://lore.kernel.org/r/20240621150058.319524-3-richard.genoud%40bootlin.com
patch subject: [PATCH 2/4] remoteproc: k3-r5: Introduce PM suspend/resume handlers
config: arm64-randconfig-003-20240701
compiler: aarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build):

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202407020242.9zgLSOC0-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/remoteproc/ti_k3_r5_remoteproc.c:117: warning: Function parameter or struct member 'pm_notifier' not described in 'k3_r5_cluster'


vim +117 drivers/remoteproc/ti_k3_r5_remoteproc.c

7508ea19b20da8 Suman Anna     2020-11-18  101  
6dedbd1d544389 Suman Anna     2020-10-02  102  /**
6dedbd1d544389 Suman Anna     2020-10-02  103   * struct k3_r5_cluster - K3 R5F Cluster structure
6dedbd1d544389 Suman Anna     2020-10-02  104   * @dev: cached device pointer
6dedbd1d544389 Suman Anna     2020-10-02  105   * @mode: Mode to configure the Cluster - Split or LockStep
6dedbd1d544389 Suman Anna     2020-10-02  106   * @cores: list of R5 cores within the cluster
61f6f68447aba0 Apurva Nandan  2024-04-30  107   * @core_transition: wait queue to sync core state changes
7508ea19b20da8 Suman Anna     2020-11-18  108   * @soc_data: SoC-specific feature data for a R5FSS
6dedbd1d544389 Suman Anna     2020-10-02  109   */
6dedbd1d544389 Suman Anna     2020-10-02  110  struct k3_r5_cluster {
6dedbd1d544389 Suman Anna     2020-10-02  111  	struct device *dev;
6dedbd1d544389 Suman Anna     2020-10-02  112  	enum cluster_mode mode;
6dedbd1d544389 Suman Anna     2020-10-02  113  	struct list_head cores;
61f6f68447aba0 Apurva Nandan  2024-04-30  114  	wait_queue_head_t core_transition;
7508ea19b20da8 Suman Anna     2020-11-18  115  	const struct k3_r5_soc_data *soc_data;
2d0e76f2a8f41f Richard Genoud 2024-06-21  116  	struct notifier_block pm_notifier;
6dedbd1d544389 Suman Anna     2020-10-02 @117  };
6dedbd1d544389 Suman Anna     2020-10-02  118
diff mbox series

Patch

diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c
index 39a47540c590..1f18b08618c8 100644
--- a/drivers/remoteproc/ti_k3_r5_remoteproc.c
+++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c
@@ -20,6 +20,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/remoteproc.h>
+#include <linux/suspend.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
 
@@ -112,6 +113,7 @@  struct k3_r5_cluster {
 	struct list_head cores;
 	wait_queue_head_t core_transition;
 	const struct k3_r5_soc_data *soc_data;
+	struct notifier_block pm_notifier;
 };
 
 /**
@@ -577,7 +579,8 @@  static int k3_r5_rproc_start(struct rproc *rproc)
 		/* do not allow core 1 to start before core 0 */
 		core0 = list_first_entry(&cluster->cores, struct k3_r5_core,
 					 elem);
-		if (core != core0 && core0->rproc->state == RPROC_OFFLINE) {
+		if (core != core0 && (core0->rproc->state == RPROC_OFFLINE ||
+				      core0->rproc->state == RPROC_SUSPENDED)) {
 			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,