remoteproc: k3-m4: Introduce PM suspend/resume handlers

Introduce suspend/resume handling to m4 remoteproc driver. The driver
registers for pm notifications. And on SUSPEND_PREPARE event a i
RP_MBOX_SUSPEND_SYSTEM mailbox message is sent to the remote core.
a) If the remote core does n't respond to the SUSPEND message, the
driver sets a device on constraint and returns.
b) If the remote core responds with 'RP_MBOX_SUSPEND_ACK', the driver
initiates a stop of the remote core and returns from the suspend
handler.
c) On the other hand, if the remote core responds with
RP_MBOX_SUSPEND_AUTO or RP_MBOX_SUSPEND_CANCEL, the driver simply
returns.

On the Resume path, driver queries DM to determine the state of the
remote core and if it is Off, turns on the remote core.

Signed-off-by: Hari Nagalla <hnagalla@ti.com>
Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
This commit is contained in:
Hari Nagalla
2024-07-11 15:15:57 +02:00
committed by Heinrich Toews
parent 0436682951
commit 1f4ef45bc2
+152
View File
@@ -14,9 +14,11 @@
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/remoteproc.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include "omap_remoteproc.h"
#include "remoteproc_internal.h"
@@ -92,6 +94,14 @@ struct k3_m4_rproc {
struct mbox_chan *mbox;
struct mbox_client client;
struct completion shut_comp;
struct completion suspend_comp;
struct notifier_block pm_notifier;
u32 suspend_status;
};
enum core_status {
CORE_IS_OFF = 0,
CORE_IS_ON
};
/**
@@ -116,6 +126,34 @@ static int is_core_in_wfi(struct k3_m4_rproc *core)
return (stat & PROC_BOOT_STATUS_FLAG_M4_WFI);
}
/**
* get_core_status - local utility function to check core status
* @core: remote core pointer used for checking core status
* @cstatus: core status
*
* This utility function is invoked by the resume handler to get remote core
* status.
*/
static int get_core_status(struct k3_m4_rproc *core, enum core_status *cstatus)
{
const struct ti_sci_handle *ti_sci = core->ti_sci;
bool r_state = false, c_state = false;
int ret;
ret = ti_sci->ops.dev_ops.is_on(ti_sci, core->ti_sci_id, &r_state, &c_state);
if (ret) {
dev_err(core->dev, "ti_sci call to get dev status failed\n");
return ret;
}
if (c_state)
*cstatus = CORE_IS_ON;
else
*cstatus = CORE_IS_OFF;
return 0;
}
/**
* k3_m4_rproc_mbox_callback() - inbound mailbox message handler
* @client: mailbox client pointer used for requesting the mailbox channel
@@ -155,6 +193,17 @@ static void k3_m4_rproc_mbox_callback(struct mbox_client *client, void *data)
dev_dbg(dev, "received shutdown_ack from %s\n", name);
complete(&kproc->shut_comp);
break;
case RP_MBOX_SUSPEND_ACK:
dev_dbg(dev, "received suspend_ack from %s\n", name);
kproc->suspend_status = RP_MBOX_SUSPEND_ACK;
complete(&kproc->suspend_comp);
break;
case RP_MBOX_SUSPEND_CANCEL:
case RP_MBOX_SUSPEND_AUTO:
dev_dbg(dev, "received suspend_cancel from %s\n", name);
kproc->suspend_status = RP_MBOX_SUSPEND_CANCEL;
complete(&kproc->suspend_comp);
break;
default:
/* silently handle all other valid messages */
if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG)
@@ -684,6 +733,104 @@ static int k3_m4_rproc_detach(struct rproc *rproc)
return 0;
}
static int k3_m4_suspend(struct rproc *rproc)
{
struct k3_m4_rproc *kproc = rproc->priv;
unsigned long msg = RP_MBOX_SUSPEND_SYSTEM;
unsigned long to = msecs_to_jiffies(5000);
struct dev_pm_qos_request qos_req;
struct device *dev = kproc->dev;
int ret;
kproc->suspend_status = 0;
reinit_completion(&kproc->suspend_comp);
ret = mbox_send_message(kproc->mbox, (void *)msg);
if (ret < 0) {
dev_err(dev, "PM mbox_send_message failed: %d\n", ret);
return ret;
}
ret = wait_for_completion_timeout(&kproc->suspend_comp, to);
if (ret == 0) {
dev_err(dev, "%s: timedout waiting for rproc completion event\n", __func__);
// Set constraint to keep the device on
dev_pm_qos_add_request(kproc->dev, &qos_req, DEV_PM_QOS_RESUME_LATENCY, 0);
return 0;
};
dev_dbg(dev, "%s: suspend ack from remote 0x%x\n", __func__, kproc->suspend_status);
if (kproc->suspend_status == RP_MBOX_SUSPEND_ACK) {
const struct ti_sci_handle *ti_sci = kproc->ti_sci;
// shutdown the remote core
rproc_shutdown(rproc);
ret = ti_sci->ops.dev_ops.put_device(ti_sci, kproc->ti_sci_id);
if (ret) {
dev_err(dev, "module-reset assert failed, ret = %d\n", ret);
if (reset_control_deassert(kproc->reset))
dev_warn(dev, "local-reset deassert back failed\n");
}
kproc->rproc->state = RPROC_SUSPENDED;
} else if (kproc->suspend_status == RP_MBOX_SUSPEND_CANCEL) {
kproc->rproc->state = RPROC_SUSPENDED;
}
return 0;
}
static int k3_m4_resume(struct rproc *rproc)
{
struct k3_m4_rproc *kproc = rproc->priv;
enum core_status cstatus = CORE_IS_OFF;
unsigned long msg = RP_MBOX_ECHO_REQUEST;
struct device *dev = kproc->dev;
int ret;
ret = get_core_status(kproc, &cstatus);
if (cstatus == CORE_IS_OFF) {
dev_info(dev, "Core is off in resume\n");
rproc_boot(rproc);
} else {
dev_err(dev, "Core is on in resume\n");
msg = RP_MBOX_ECHO_REQUEST;
ret = mbox_send_message(kproc->mbox, (void *)msg);
if (ret < 0) {
dev_err(dev, "PM mbox_send_message failed: %d\n", ret);
return ret;
}
}
kproc->rproc->state = RPROC_RUNNING;
return 0;
}
/* PM notifier call.
* This is a callback function for PM notifications. On a resume completion
* i.e after all the resume driver calls are handled on PM_POST_SUSPEND,
* on a deep sleep the remote core is rebooted.
*/
static int m4_pm_notifier_call(struct notifier_block *bl, unsigned long state, void *unused)
{
struct k3_m4_rproc *kproc = container_of(bl, struct k3_m4_rproc, pm_notifier);
struct rproc *rproc = kproc->rproc;
switch (state) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
return k3_m4_suspend(rproc);
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
if (rproc->state == RPROC_SUSPENDED)
return k3_m4_resume(rproc);
break;
}
return 0;
}
static const struct rproc_ops k3_m4_rproc_ops = {
.start = k3_m4_rproc_start,
.stop = k3_m4_rproc_stop,
@@ -748,6 +895,7 @@ static int k3_m4_rproc_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(kproc->tsp),
"failed to construct ti-sci proc control\n");
init_completion(&kproc->shut_comp);
init_completion(&kproc->suspend_comp);
ret = ti_sci_proc_request(kproc->tsp);
if (ret < 0)
@@ -784,6 +932,10 @@ static int k3_m4_rproc_probe(struct platform_device *pdev)
rproc->ops->get_loaded_rsc_table = k3_m4_get_loaded_rsc_table;
} else {
dev_info(dev, "configured M4 for remoteproc mode\n");
/* register for PM notifiers */
kproc->pm_notifier.notifier_call = m4_pm_notifier_call;
register_pm_notifier(&kproc->pm_notifier);
/*
* ensure the M4 local reset is asserted to ensure the core
* doesn't execute bogus code in .prepare() when the module