Skip to content

Commit 5f31b23

Browse files
sumanannaandersson
authored andcommitted
remoteproc/omap: Add support for runtime auto-suspend/resume
This patch enhances the PM support in the OMAP remoteproc driver to support the runtime auto-suspend. A remoteproc may not be required to be running all the time, and typically will need to be active only during certain usecases. As such, to save power, it should be turned off during potential long periods of inactivity between usecases. This suspend and resume of the device is a relatively heavy process in terms of latencies, so a remoteproc should be suspended only after a certain period of prolonged inactivity. The OMAP remoteproc driver leverages the runtime pm framework's auto_suspend feature to accomplish this functionality. This feature is automatically enabled when a remote processor has successfully booted. The 'autosuspend_delay_ms' for each device dictates the inactivity period/time to wait for before suspending the device. The runtime auto-suspend design relies on marking the last busy time on every communication (virtqueue kick) to and from the remote processor. When there has been no activity for 'autosuspend_delay_ms' time, the runtime PM framework invokes the driver's runtime pm suspend callback to suspend the device. The remote processor will be woken up on the initiation of the next communication message through the runtime pm resume callback. The current auto-suspend design also allows a remote processor to deny a auto-suspend attempt, if it wishes to, by sending a NACK response to the initial suspend request message sent to the remote processor as part of the suspend process. The auto-suspend request is also only attempted if the remote processor is idled and in standby at the time of inactivity timer expiry. This choice is made to avoid unnecessary messaging, and the auto-suspend is simply rescheduled to be attempted again after a further lapse of autosuspend_delay_ms. The runtime pm callbacks functionality in this patch reuses most of the core logic from the suspend/resume support code, and make use of an additional auto_suspend flag to differentiate the logic in common code from system suspend. The system suspend/resume sequences are also updated to reflect the proper pm_runtime statuses, and also to really perform a suspend/resume only if the remoteproc has not been auto-suspended at the time of request. The remote processor is left in suspended state on a system resume if it has been auto-suspended before, and will be woken up only when a usecase needs to run. The OMAP remoteproc driver currently uses a default value of 10 seconds for all OMAP remoteprocs, and a different value can be chosen either by choosing a positive value for the 'ti,autosuspend-delay-ms' under DT or by updating the 'autosuspend_delay_ms' field at runtime through the sysfs interface. A negative value is equivalent to disabling the runtime suspend. Eg: To use 25 seconds for IPU2 on DRA7xx, echo 25000 > /sys/bus/platform/devices/55020000.ipu/power/autosuspend_delay_ms The runtime suspend feature can also be similarly enabled or disabled by writing 'auto' or 'on' to the device's 'control' power field. The default is enabled. Eg: To disable auto-suspend for IPU2 on DRA7xx SoC, echo on > /sys/bus/platform/devices/55020000.ipu/power/control 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 9077ac1 commit 5f31b23

File tree

1 file changed

+203
-9
lines changed

1 file changed

+203
-9
lines changed

drivers/remoteproc/omap_remoteproc.c

Lines changed: 203 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/of_device.h>
2323
#include <linux/of_reserved_mem.h>
2424
#include <linux/platform_device.h>
25+
#include <linux/pm_runtime.h>
2526
#include <linux/dma-mapping.h>
2627
#include <linux/remoteproc.h>
2728
#include <linux/mailbox_client.h>
@@ -37,6 +38,9 @@
3738
#include "omap_remoteproc.h"
3839
#include "remoteproc_internal.h"
3940

41+
/* default auto-suspend delay (ms) */
42+
#define DEFAULT_AUTOSUSPEND_DELAY 10000
43+
4044
/**
4145
* struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs
4246
* @syscon: regmap handle for the system control configuration module
@@ -83,6 +87,8 @@ struct omap_rproc_timer {
8387
* @num_mems: number of internal memory regions
8488
* @num_timers: number of rproc timer(s)
8589
* @timers: timer(s) info used by rproc
90+
* @autosuspend_delay: auto-suspend delay value to be used for runtime pm
91+
* @need_resume: if true a resume is needed in the system resume callback
8692
* @rproc: rproc handle
8793
* @reset: reset handle
8894
* @pm_comp: completion primitive to sync for suspend response
@@ -97,6 +103,8 @@ struct omap_rproc {
97103
int num_mems;
98104
int num_timers;
99105
struct omap_rproc_timer *timers;
106+
int autosuspend_delay;
107+
bool need_resume;
100108
struct rproc *rproc;
101109
struct reset_control *reset;
102110
struct completion pm_comp;
@@ -407,11 +415,23 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
407415
struct device *dev = rproc->dev.parent;
408416
int ret;
409417

418+
/* wake up the rproc before kicking it */
419+
ret = pm_runtime_get_sync(dev);
420+
if (WARN_ON(ret < 0)) {
421+
dev_err(dev, "pm_runtime_get_sync() failed during kick, ret = %d\n",
422+
ret);
423+
pm_runtime_put_noidle(dev);
424+
return;
425+
}
426+
410427
/* send the index of the triggered virtqueue in the mailbox payload */
411428
ret = mbox_send_message(oproc->mbox, (void *)vqid);
412429
if (ret < 0)
413430
dev_err(dev, "failed to send mailbox message, status = %d\n",
414431
ret);
432+
433+
pm_runtime_mark_last_busy(dev);
434+
pm_runtime_put_autosuspend(dev);
415435
}
416436

417437
/**
@@ -502,6 +522,18 @@ static int omap_rproc_start(struct rproc *rproc)
502522
goto disable_timers;
503523
}
504524

525+
/*
526+
* remote processor is up, so update the runtime pm status and
527+
* enable the auto-suspend. The device usage count is incremented
528+
* manually for balancing it for auto-suspend
529+
*/
530+
pm_runtime_set_active(dev);
531+
pm_runtime_use_autosuspend(dev);
532+
pm_runtime_get_noresume(dev);
533+
pm_runtime_enable(dev);
534+
pm_runtime_mark_last_busy(dev);
535+
pm_runtime_put_autosuspend(dev);
536+
505537
return 0;
506538

507539
disable_timers:
@@ -514,20 +546,52 @@ static int omap_rproc_start(struct rproc *rproc)
514546
/* power off the remote processor */
515547
static int omap_rproc_stop(struct rproc *rproc)
516548
{
549+
struct device *dev = rproc->dev.parent;
517550
struct omap_rproc *oproc = rproc->priv;
518551
int ret;
519552

553+
/*
554+
* cancel any possible scheduled runtime suspend by incrementing
555+
* the device usage count, and resuming the device. The remoteproc
556+
* also needs to be woken up if suspended, to avoid the remoteproc
557+
* OS to continue to remember any context that it has saved, and
558+
* avoid potential issues in misindentifying a subsequent device
559+
* reboot as a power restore boot
560+
*/
561+
ret = pm_runtime_get_sync(dev);
562+
if (ret < 0) {
563+
pm_runtime_put_noidle(dev);
564+
return ret;
565+
}
566+
520567
ret = reset_control_assert(oproc->reset);
521568
if (ret)
522-
return ret;
569+
goto out;
523570

524571
ret = omap_rproc_disable_timers(rproc, true);
525572
if (ret)
526-
return ret;
573+
goto enable_device;
527574

528575
mbox_free_channel(oproc->mbox);
529576

577+
/*
578+
* update the runtime pm states and status now that the remoteproc
579+
* has stopped
580+
*/
581+
pm_runtime_disable(dev);
582+
pm_runtime_dont_use_autosuspend(dev);
583+
pm_runtime_put_noidle(dev);
584+
pm_runtime_set_suspended(dev);
585+
530586
return 0;
587+
588+
enable_device:
589+
reset_control_deassert(oproc->reset);
590+
out:
591+
/* schedule the next auto-suspend */
592+
pm_runtime_mark_last_busy(dev);
593+
pm_runtime_put_autosuspend(dev);
594+
return ret;
531595
}
532596

533597
/**
@@ -584,17 +648,19 @@ static bool _is_rproc_in_standby(struct omap_rproc *oproc)
584648

585649
/* 1 sec is long enough time to let the remoteproc side suspend the device */
586650
#define DEF_SUSPEND_TIMEOUT 1000
587-
static int _omap_rproc_suspend(struct rproc *rproc)
651+
static int _omap_rproc_suspend(struct rproc *rproc, bool auto_suspend)
588652
{
589653
struct device *dev = rproc->dev.parent;
590654
struct omap_rproc *oproc = rproc->priv;
591655
unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT);
592656
unsigned long ta = jiffies + to;
657+
u32 suspend_msg = auto_suspend ?
658+
RP_MBOX_SUSPEND_AUTO : RP_MBOX_SUSPEND_SYSTEM;
593659
int ret;
594660

595661
reinit_completion(&oproc->pm_comp);
596662
oproc->suspend_acked = false;
597-
ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_SUSPEND_SYSTEM);
663+
ret = mbox_send_message(oproc->mbox, (void *)suspend_msg);
598664
if (ret < 0) {
599665
dev_err(dev, "PM mbox_send_message failed: %d\n", ret);
600666
return ret;
@@ -638,32 +704,62 @@ static int _omap_rproc_suspend(struct rproc *rproc)
638704
goto enable_device;
639705
}
640706

707+
/*
708+
* IOMMUs would have to be disabled specifically for runtime suspend.
709+
* They are handled automatically through System PM callbacks for
710+
* regular system suspend
711+
*/
712+
if (auto_suspend) {
713+
ret = omap_iommu_domain_deactivate(rproc->domain);
714+
if (ret) {
715+
dev_err(dev, "iommu domain deactivate failed %d\n",
716+
ret);
717+
goto enable_timers;
718+
}
719+
}
720+
641721
return 0;
642722

723+
enable_timers:
724+
/* ignore errors on re-enabling code */
725+
omap_rproc_enable_timers(rproc, false);
643726
enable_device:
644727
reset_control_deassert(oproc->reset);
645728
return ret;
646729
}
647730

648-
static int _omap_rproc_resume(struct rproc *rproc)
731+
static int _omap_rproc_resume(struct rproc *rproc, bool auto_suspend)
649732
{
650733
struct device *dev = rproc->dev.parent;
651734
struct omap_rproc *oproc = rproc->priv;
652735
int ret;
653736

737+
/*
738+
* IOMMUs would have to be enabled specifically for runtime resume.
739+
* They would have been already enabled automatically through System
740+
* PM callbacks for regular system resume
741+
*/
742+
if (auto_suspend) {
743+
ret = omap_iommu_domain_activate(rproc->domain);
744+
if (ret) {
745+
dev_err(dev, "omap_iommu activate failed %d\n", ret);
746+
goto out;
747+
}
748+
}
749+
654750
/* boot address could be lost after suspend, so restore it */
655751
if (oproc->boot_data) {
656752
ret = omap_rproc_write_dsp_boot_addr(rproc);
657753
if (ret) {
658754
dev_err(dev, "boot address restore failed %d\n", ret);
659-
goto out;
755+
goto suspend_iommu;
660756
}
661757
}
662758

663759
ret = omap_rproc_enable_timers(rproc, false);
664760
if (ret) {
665761
dev_err(dev, "enabling timers during resume failed %d\n", ret);
666-
goto out;
762+
goto suspend_iommu;
667763
}
668764

669765
ret = reset_control_deassert(oproc->reset);
@@ -676,6 +772,9 @@ static int _omap_rproc_resume(struct rproc *rproc)
676772

677773
disable_timers:
678774
omap_rproc_disable_timers(rproc, false);
775+
suspend_iommu:
776+
if (auto_suspend)
777+
omap_iommu_domain_deactivate(rproc->domain);
679778
out:
680779
return ret;
681780
}
@@ -684,6 +783,7 @@ static int __maybe_unused omap_rproc_suspend(struct device *dev)
684783
{
685784
struct platform_device *pdev = to_platform_device(dev);
686785
struct rproc *rproc = platform_get_drvdata(pdev);
786+
struct omap_rproc *oproc = rproc->priv;
687787
int ret = 0;
688788

689789
mutex_lock(&rproc->lock);
@@ -698,13 +798,19 @@ static int __maybe_unused omap_rproc_suspend(struct device *dev)
698798
goto out;
699799
}
700800

701-
ret = _omap_rproc_suspend(rproc);
801+
ret = _omap_rproc_suspend(rproc, false);
702802
if (ret) {
703803
dev_err(dev, "suspend failed %d\n", ret);
704804
goto out;
705805
}
706806

807+
/*
808+
* remoteproc is running at the time of system suspend, so remember
809+
* it so as to wake it up during system resume
810+
*/
811+
oproc->need_resume = true;
707812
rproc->state = RPROC_SUSPENDED;
813+
708814
out:
709815
mutex_unlock(&rproc->lock);
710816
return ret;
@@ -714,6 +820,7 @@ static int __maybe_unused omap_rproc_resume(struct device *dev)
714820
{
715821
struct platform_device *pdev = to_platform_device(dev);
716822
struct rproc *rproc = platform_get_drvdata(pdev);
823+
struct omap_rproc *oproc = rproc->priv;
717824
int ret = 0;
718825

719826
mutex_lock(&rproc->lock);
@@ -725,12 +832,91 @@ static int __maybe_unused omap_rproc_resume(struct device *dev)
725832
goto out;
726833
}
727834

728-
ret = _omap_rproc_resume(rproc);
835+
/*
836+
* remoteproc was auto-suspended at the time of system suspend,
837+
* so no need to wake-up the processor (leave it in suspended
838+
* state, will be woken up during a subsequent runtime_resume)
839+
*/
840+
if (!oproc->need_resume)
841+
goto out;
842+
843+
ret = _omap_rproc_resume(rproc, false);
729844
if (ret) {
730845
dev_err(dev, "resume failed %d\n", ret);
731846
goto out;
732847
}
733848

849+
oproc->need_resume = false;
850+
rproc->state = RPROC_RUNNING;
851+
852+
pm_runtime_mark_last_busy(dev);
853+
out:
854+
mutex_unlock(&rproc->lock);
855+
return ret;
856+
}
857+
858+
static int omap_rproc_runtime_suspend(struct device *dev)
859+
{
860+
struct rproc *rproc = dev_get_drvdata(dev);
861+
struct omap_rproc *oproc = rproc->priv;
862+
int ret;
863+
864+
mutex_lock(&rproc->lock);
865+
if (rproc->state == RPROC_CRASHED) {
866+
dev_dbg(dev, "rproc cannot be runtime suspended when crashed!\n");
867+
ret = -EBUSY;
868+
goto out;
869+
}
870+
871+
if (WARN_ON(rproc->state != RPROC_RUNNING)) {
872+
dev_err(dev, "rproc cannot be runtime suspended when not running!\n");
873+
ret = -EBUSY;
874+
goto out;
875+
}
876+
877+
/*
878+
* do not even attempt suspend if the remote processor is not
879+
* idled for runtime auto-suspend
880+
*/
881+
if (!_is_rproc_in_standby(oproc)) {
882+
ret = -EBUSY;
883+
goto abort;
884+
}
885+
886+
ret = _omap_rproc_suspend(rproc, true);
887+
if (ret)
888+
goto abort;
889+
890+
rproc->state = RPROC_SUSPENDED;
891+
mutex_unlock(&rproc->lock);
892+
return 0;
893+
894+
abort:
895+
pm_runtime_mark_last_busy(dev);
896+
out:
897+
mutex_unlock(&rproc->lock);
898+
return ret;
899+
}
900+
901+
static int omap_rproc_runtime_resume(struct device *dev)
902+
{
903+
struct rproc *rproc = dev_get_drvdata(dev);
904+
int ret;
905+
906+
mutex_lock(&rproc->lock);
907+
if (WARN_ON(rproc->state != RPROC_SUSPENDED)) {
908+
dev_err(dev, "rproc cannot be runtime resumed if not suspended! state=%d\n",
909+
rproc->state);
910+
ret = -EBUSY;
911+
goto out;
912+
}
913+
914+
ret = _omap_rproc_resume(rproc, true);
915+
if (ret) {
916+
dev_err(dev, "runtime resume failed %d\n", ret);
917+
goto out;
918+
}
919+
734920
rproc->state = RPROC_RUNNING;
735921
out:
736922
mutex_unlock(&rproc->lock);
@@ -997,6 +1183,12 @@ static int omap_rproc_probe(struct platform_device *pdev)
9971183
goto free_rproc;
9981184

9991185
init_completion(&oproc->pm_comp);
1186+
oproc->autosuspend_delay = DEFAULT_AUTOSUSPEND_DELAY;
1187+
1188+
of_property_read_u32(pdev->dev.of_node, "ti,autosuspend-delay-ms",
1189+
&oproc->autosuspend_delay);
1190+
1191+
pm_runtime_set_autosuspend_delay(&pdev->dev, oproc->autosuspend_delay);
10001192

10011193
oproc->fck = devm_clk_get(&pdev->dev, 0);
10021194
if (IS_ERR(oproc->fck)) {
@@ -1039,6 +1231,8 @@ static int omap_rproc_remove(struct platform_device *pdev)
10391231

10401232
static const struct dev_pm_ops omap_rproc_pm_ops = {
10411233
SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume)
1234+
SET_RUNTIME_PM_OPS(omap_rproc_runtime_suspend,
1235+
omap_rproc_runtime_resume, NULL)
10421236
};
10431237

10441238
static struct platform_driver omap_rproc_driver = {

0 commit comments

Comments
 (0)