Skip to content

Commit 9077ac1

Browse files
sumanannaandersson
authored andcommitted
remoteproc/omap: Add support for system suspend/resume
This patch adds the support for system suspend/resume to the OMAP remoteproc driver so that the OMAP remoteproc devices can be suspended/resumed during a system suspend/resume. The support is added through the driver PM .suspend/.resume callbacks, and requires appropriate support from the OS running on the remote processors. The IPU & DSP remote processors typically have their own private modules like registers, internal memories, caches etc. The context of these modules need to be saved and restored properly for a suspend/resume to work. These are in general not accessible from the MPU, so the remote processors themselves have to implement the logic for the context save & restore of these modules. The OMAP remoteproc driver initiates a suspend by sending a mailbox message requesting the remote processor to save its context and enter into an idle/standby state. The remote processor should usually stop whatever processing it is doing to switch to a context save mode. The OMAP remoteproc driver detects the completion of the context save by checking the module standby status for the remoteproc device. It also stops any resources used by the remote processors like the timers. The timers need to be running only when the processor is active and executing, and need to be stopped otherwise to allow the timer driver to reach low-power states. The IOMMUs are automatically suspended by the PM core during the late suspend stage, after the remoteproc suspend process is completed by putting the remote processor cores into reset. Thereafter, the Linux kernel can put the domain into further lower power states as possible. The resume sequence undoes the operations performed in the PM suspend callback, by starting the timers and finally releasing the processors from reset. This requires that the remote processor side OS be able to distinguish a power-resume boot from a power-on/cold boot, restore the context of its private modules saved during the suspend phase, and resume executing code from where it was suspended. The IOMMUs would have been resumed by the PM core during early resume, so they are already enabled by the time remoteproc resume callback gets invoked. The remote processors should save their context into System RAM (DDR), as any internal memories are not guaranteed to retain context as it depends on the lowest power domain that the remote processor device is put into. The management of the DDR contents will be managed by the Linux kernel. Signed-off-by: Suman Anna <[email protected]> [[email protected]: converted to use ti-sysc instead of hwmod] Signed-off-by: Tero Kristo <[email protected]> Reviewed-by: Andrew F. Davis <[email protected]> Acked-by: Mathieu Poirier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent e28edc5 commit 9077ac1

File tree

2 files changed

+207
-2
lines changed

2 files changed

+207
-2
lines changed

drivers/remoteproc/omap_remoteproc.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515

1616
#include <linux/kernel.h>
1717
#include <linux/module.h>
18+
#include <linux/clk.h>
19+
#include <linux/clk/ti.h>
1820
#include <linux/err.h>
21+
#include <linux/io.h>
1922
#include <linux/of_device.h>
2023
#include <linux/of_reserved_mem.h>
2124
#include <linux/platform_device.h>
2225
#include <linux/dma-mapping.h>
2326
#include <linux/remoteproc.h>
2427
#include <linux/mailbox_client.h>
28+
#include <linux/omap-iommu.h>
2529
#include <linux/omap-mailbox.h>
2630
#include <linux/regmap.h>
2731
#include <linux/mfd/syscon.h>
@@ -81,6 +85,9 @@ struct omap_rproc_timer {
8185
* @timers: timer(s) info used by rproc
8286
* @rproc: rproc handle
8387
* @reset: reset handle
88+
* @pm_comp: completion primitive to sync for suspend response
89+
* @fck: functional clock for the remoteproc
90+
* @suspend_acked: state machine flag to store the suspend request ack
8491
*/
8592
struct omap_rproc {
8693
struct mbox_chan *mbox;
@@ -92,6 +99,9 @@ struct omap_rproc {
9299
struct omap_rproc_timer *timers;
93100
struct rproc *rproc;
94101
struct reset_control *reset;
102+
struct completion pm_comp;
103+
struct clk *fck;
104+
bool suspend_acked;
95105
};
96106

97107
/**
@@ -371,6 +381,12 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data)
371381
case RP_MBOX_ECHO_REPLY:
372382
dev_info(dev, "received echo reply from %s\n", name);
373383
break;
384+
case RP_MBOX_SUSPEND_ACK:
385+
/* Fall through */
386+
case RP_MBOX_SUSPEND_CANCEL:
387+
oproc->suspend_acked = msg == RP_MBOX_SUSPEND_ACK;
388+
complete(&oproc->pm_comp);
389+
break;
374390
default:
375391
if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG)
376392
return;
@@ -560,6 +576,168 @@ static const struct rproc_ops omap_rproc_ops = {
560576
.da_to_va = omap_rproc_da_to_va,
561577
};
562578

579+
#ifdef CONFIG_PM
580+
static bool _is_rproc_in_standby(struct omap_rproc *oproc)
581+
{
582+
return ti_clk_is_in_standby(oproc->fck);
583+
}
584+
585+
/* 1 sec is long enough time to let the remoteproc side suspend the device */
586+
#define DEF_SUSPEND_TIMEOUT 1000
587+
static int _omap_rproc_suspend(struct rproc *rproc)
588+
{
589+
struct device *dev = rproc->dev.parent;
590+
struct omap_rproc *oproc = rproc->priv;
591+
unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT);
592+
unsigned long ta = jiffies + to;
593+
int ret;
594+
595+
reinit_completion(&oproc->pm_comp);
596+
oproc->suspend_acked = false;
597+
ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_SUSPEND_SYSTEM);
598+
if (ret < 0) {
599+
dev_err(dev, "PM mbox_send_message failed: %d\n", ret);
600+
return ret;
601+
}
602+
603+
ret = wait_for_completion_timeout(&oproc->pm_comp, to);
604+
if (!oproc->suspend_acked)
605+
return -EBUSY;
606+
607+
/*
608+
* The remoteproc side is returning the ACK message before saving the
609+
* context, because the context saving is performed within a SYS/BIOS
610+
* function, and it cannot have any inter-dependencies against the IPC
611+
* layer. Also, as the SYS/BIOS needs to preserve properly the processor
612+
* register set, sending this ACK or signalling the completion of the
613+
* context save through a shared memory variable can never be the
614+
* absolute last thing to be executed on the remoteproc side, and the
615+
* MPU cannot use the ACK message as a sync point to put the remoteproc
616+
* into reset. The only way to ensure that the remote processor has
617+
* completed saving the context is to check that the module has reached
618+
* STANDBY state (after saving the context, the SYS/BIOS executes the
619+
* appropriate target-specific WFI instruction causing the module to
620+
* enter STANDBY).
621+
*/
622+
while (!_is_rproc_in_standby(oproc)) {
623+
if (time_after(jiffies, ta))
624+
return -ETIME;
625+
schedule();
626+
}
627+
628+
ret = reset_control_assert(oproc->reset);
629+
if (ret) {
630+
dev_err(dev, "reset assert during suspend failed %d\n", ret);
631+
return ret;
632+
}
633+
634+
ret = omap_rproc_disable_timers(rproc, false);
635+
if (ret) {
636+
dev_err(dev, "disabling timers during suspend failed %d\n",
637+
ret);
638+
goto enable_device;
639+
}
640+
641+
return 0;
642+
643+
enable_device:
644+
reset_control_deassert(oproc->reset);
645+
return ret;
646+
}
647+
648+
static int _omap_rproc_resume(struct rproc *rproc)
649+
{
650+
struct device *dev = rproc->dev.parent;
651+
struct omap_rproc *oproc = rproc->priv;
652+
int ret;
653+
654+
/* boot address could be lost after suspend, so restore it */
655+
if (oproc->boot_data) {
656+
ret = omap_rproc_write_dsp_boot_addr(rproc);
657+
if (ret) {
658+
dev_err(dev, "boot address restore failed %d\n", ret);
659+
goto out;
660+
}
661+
}
662+
663+
ret = omap_rproc_enable_timers(rproc, false);
664+
if (ret) {
665+
dev_err(dev, "enabling timers during resume failed %d\n", ret);
666+
goto out;
667+
}
668+
669+
ret = reset_control_deassert(oproc->reset);
670+
if (ret) {
671+
dev_err(dev, "reset deassert during resume failed %d\n", ret);
672+
goto disable_timers;
673+
}
674+
675+
return 0;
676+
677+
disable_timers:
678+
omap_rproc_disable_timers(rproc, false);
679+
out:
680+
return ret;
681+
}
682+
683+
static int __maybe_unused omap_rproc_suspend(struct device *dev)
684+
{
685+
struct platform_device *pdev = to_platform_device(dev);
686+
struct rproc *rproc = platform_get_drvdata(pdev);
687+
int ret = 0;
688+
689+
mutex_lock(&rproc->lock);
690+
if (rproc->state == RPROC_OFFLINE)
691+
goto out;
692+
693+
if (rproc->state == RPROC_SUSPENDED)
694+
goto out;
695+
696+
if (rproc->state != RPROC_RUNNING) {
697+
ret = -EBUSY;
698+
goto out;
699+
}
700+
701+
ret = _omap_rproc_suspend(rproc);
702+
if (ret) {
703+
dev_err(dev, "suspend failed %d\n", ret);
704+
goto out;
705+
}
706+
707+
rproc->state = RPROC_SUSPENDED;
708+
out:
709+
mutex_unlock(&rproc->lock);
710+
return ret;
711+
}
712+
713+
static int __maybe_unused omap_rproc_resume(struct device *dev)
714+
{
715+
struct platform_device *pdev = to_platform_device(dev);
716+
struct rproc *rproc = platform_get_drvdata(pdev);
717+
int ret = 0;
718+
719+
mutex_lock(&rproc->lock);
720+
if (rproc->state == RPROC_OFFLINE)
721+
goto out;
722+
723+
if (rproc->state != RPROC_SUSPENDED) {
724+
ret = -EBUSY;
725+
goto out;
726+
}
727+
728+
ret = _omap_rproc_resume(rproc);
729+
if (ret) {
730+
dev_err(dev, "resume failed %d\n", ret);
731+
goto out;
732+
}
733+
734+
rproc->state = RPROC_RUNNING;
735+
out:
736+
mutex_unlock(&rproc->lock);
737+
return ret;
738+
}
739+
#endif /* CONFIG_PM */
740+
563741
static const struct omap_rproc_mem_data ipu_mems[] = {
564742
{ .name = "l2ram", .dev_addr = 0x20000000 },
565743
{ },
@@ -818,6 +996,14 @@ static int omap_rproc_probe(struct platform_device *pdev)
818996
if (ret)
819997
goto free_rproc;
820998

999+
init_completion(&oproc->pm_comp);
1000+
1001+
oproc->fck = devm_clk_get(&pdev->dev, 0);
1002+
if (IS_ERR(oproc->fck)) {
1003+
ret = PTR_ERR(oproc->fck);
1004+
goto free_rproc;
1005+
}
1006+
8211007
ret = of_reserved_mem_device_init(&pdev->dev);
8221008
if (ret) {
8231009
dev_warn(&pdev->dev, "device does not have specific CMA pool.\n");
@@ -851,11 +1037,16 @@ static int omap_rproc_remove(struct platform_device *pdev)
8511037
return 0;
8521038
}
8531039

1040+
static const struct dev_pm_ops omap_rproc_pm_ops = {
1041+
SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume)
1042+
};
1043+
8541044
static struct platform_driver omap_rproc_driver = {
8551045
.probe = omap_rproc_probe,
8561046
.remove = omap_rproc_remove,
8571047
.driver = {
8581048
.name = "omap-rproc",
1049+
.pm = &omap_rproc_pm_ops,
8591050
.of_match_table = omap_rproc_of_match,
8601051
},
8611052
};

drivers/remoteproc/omap_remoteproc.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Remote processor messaging
33
*
4-
* Copyright (C) 2011 Texas Instruments, Inc.
4+
* Copyright (C) 2011-2020 Texas Instruments, Inc.
55
* Copyright (C) 2011 Google, Inc.
66
* All rights reserved.
77
*
@@ -57,6 +57,16 @@
5757
* @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the
5858
* recovery mechanism (to some extent).
5959
*
60+
* @RP_MBOX_SUSPEND_AUTO: auto suspend request for the remote processor
61+
*
62+
* @RP_MBOX_SUSPEND_SYSTEM: system suspend request for the remote processor
63+
*
64+
* @RP_MBOX_SUSPEND_ACK: successful response from remote processor for a
65+
* suspend request
66+
*
67+
* @RP_MBOX_SUSPEND_CANCEL: a cancel suspend response from a remote processor
68+
* on a suspend request
69+
*
6070
* Introduce new message definitions if any here.
6171
*
6272
* @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core
@@ -70,7 +80,11 @@ enum omap_rp_mbox_messages {
7080
RP_MBOX_ECHO_REQUEST = 0xFFFFFF03,
7181
RP_MBOX_ECHO_REPLY = 0xFFFFFF04,
7282
RP_MBOX_ABORT_REQUEST = 0xFFFFFF05,
73-
RP_MBOX_END_MSG = 0xFFFFFF06,
83+
RP_MBOX_SUSPEND_AUTO = 0xFFFFFF10,
84+
RP_MBOX_SUSPEND_SYSTEM = 0xFFFFFF11,
85+
RP_MBOX_SUSPEND_ACK = 0xFFFFFF12,
86+
RP_MBOX_SUSPEND_CANCEL = 0xFFFFFF13,
87+
RP_MBOX_END_MSG = 0xFFFFFF14,
7488
};
7589

7690
#endif /* _OMAP_RPMSG_H */

0 commit comments

Comments
 (0)