aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuman Anna2018-02-22 17:57:17 -0600
committerSuman Anna2019-03-04 10:17:26 -0600
commit7c282f0c9667adba595780eb9f154f4a65d48346 (patch)
tree1672a8b05b6602d9a14b14bedd7528c6494bb368
parent8d21dcdccb453638c24aa39d8458766838de71bb (diff)
downloadremoteproc-7c282f0c9667adba595780eb9f154f4a65d48346.tar.gz
remoteproc-7c282f0c9667adba595780eb9f154f4a65d48346.tar.xz
remoteproc-7c282f0c9667adba595780eb9f154f4a65d48346.zip
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 <s-anna@ti.com>
-rw-r--r--drivers/remoteproc/omap_remoteproc.c199
-rw-r--r--drivers/remoteproc/omap_remoteproc.h18
2 files changed, 215 insertions, 2 deletions
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index 3e34778c2ceb..0d35efb96cc1 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -16,6 +16,7 @@
16#include <linux/kernel.h> 16#include <linux/kernel.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/err.h> 18#include <linux/err.h>
19#include <linux/io.h>
19#include <linux/of_device.h> 20#include <linux/of_device.h>
20#include <linux/of_address.h> 21#include <linux/of_address.h>
21#include <linux/of_reserved_mem.h> 22#include <linux/of_reserved_mem.h>
@@ -24,6 +25,7 @@
24#include <linux/remoteproc.h> 25#include <linux/remoteproc.h>
25#include <linux/mailbox_client.h> 26#include <linux/mailbox_client.h>
26#include <linux/omap-mailbox.h> 27#include <linux/omap-mailbox.h>
28#include <linux/omap-iommu.h>
27#include <linux/regmap.h> 29#include <linux/regmap.h>
28#include <linux/mfd/syscon.h> 30#include <linux/mfd/syscon.h>
29#include <clocksource/timer-ti-dm.h> 31#include <clocksource/timer-ti-dm.h>
@@ -84,6 +86,9 @@ struct omap_rproc_timer {
84 * @num_timers: number of rproc timer(s) 86 * @num_timers: number of rproc timer(s)
85 * @timers: timer(s) info used by rproc 87 * @timers: timer(s) info used by rproc
86 * @rproc: rproc handle 88 * @rproc: rproc handle
89 * @pm_comp: completion primitive to sync for suspend response
90 * @standby_addr: kernel address of the register having module standby status
91 * @suspend_acked: state machine flag to store the suspend request ack
87 */ 92 */
88struct omap_rproc { 93struct omap_rproc {
89 struct mbox_chan *mbox; 94 struct mbox_chan *mbox;
@@ -94,6 +99,9 @@ struct omap_rproc {
94 int num_timers; 99 int num_timers;
95 struct omap_rproc_timer *timers; 100 struct omap_rproc_timer *timers;
96 struct rproc *rproc; 101 struct rproc *rproc;
102 struct completion pm_comp;
103 void __iomem *standby_addr;
104 bool suspend_acked;
97}; 105};
98 106
99/** 107/**
@@ -344,6 +352,11 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data)
344 case RP_MBOX_ECHO_REPLY: 352 case RP_MBOX_ECHO_REPLY:
345 dev_info(dev, "received echo reply from %s\n", name); 353 dev_info(dev, "received echo reply from %s\n", name);
346 break; 354 break;
355 case RP_MBOX_SUSPEND_ACK:
356 case RP_MBOX_SUSPEND_CANCEL:
357 oproc->suspend_acked = msg == RP_MBOX_SUSPEND_ACK;
358 complete(&oproc->pm_comp);
359 break;
347 default: 360 default:
348 if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) 361 if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG)
349 return; 362 return;
@@ -534,6 +547,171 @@ static const struct rproc_ops omap_rproc_ops = {
534 .da_to_va = omap_rproc_da_to_va, 547 .da_to_va = omap_rproc_da_to_va,
535}; 548};
536 549
550#ifdef CONFIG_PM
551static bool _is_rproc_in_standby(struct omap_rproc *oproc)
552{
553 static int standby_mask = (1 << 18);
554
555 return readl(oproc->standby_addr) & standby_mask;
556}
557
558/* 1 sec is long enough time to let the remoteproc side suspend the device */
559#define DEF_SUSPEND_TIMEOUT 1000
560static int _omap_rproc_suspend(struct rproc *rproc)
561{
562 struct device *dev = rproc->dev.parent;
563 struct platform_device *pdev = to_platform_device(dev);
564 struct omap_rproc_pdata *pdata = dev_get_platdata(dev);
565 struct omap_rproc *oproc = rproc->priv;
566 unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT);
567 unsigned long ta = jiffies + to;
568 int ret;
569
570 reinit_completion(&oproc->pm_comp);
571 oproc->suspend_acked = false;
572 ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_SUSPEND_SYSTEM);
573 if (ret < 0) {
574 dev_err(dev, "PM mbox_send_message failed: %d\n", ret);
575 return ret;
576 }
577
578 ret = wait_for_completion_timeout(&oproc->pm_comp, to);
579 if (!oproc->suspend_acked)
580 return -EBUSY;
581
582 /*
583 * The remoteproc side is returning the ACK message before saving the
584 * context, because the context saving is performed within a SYS/BIOS
585 * function, and it cannot have any inter-dependencies against the IPC
586 * layer. Also, as the SYS/BIOS needs to preserve properly the processor
587 * register set, sending this ACK or signalling the completion of the
588 * context save through a shared memory variable can never be the
589 * absolute last thing to be executed on the remoteproc side, and the
590 * MPU cannot use the ACK message as a sync point to put the remoteproc
591 * into reset. The only way to ensure that the remote processor has
592 * completed saving the context is to check that the module has reached
593 * STANDBY state (after saving the context, the SYS/BIOS executes the
594 * appropriate target-specific WFI instruction causing the module to
595 * enter STANDBY).
596 */
597 while (!_is_rproc_in_standby(oproc)) {
598 if (time_after(jiffies, ta))
599 return -ETIME;
600 schedule();
601 }
602
603 ret = pdata->device_shutdown(pdev);
604 if (ret)
605 return ret;
606
607 ret = omap_rproc_disable_timers(rproc, false);
608 if (ret) {
609 dev_err(dev, "disabling timers during suspend failed %d\n",
610 ret);
611 goto enable_device;
612 }
613
614 return 0;
615
616enable_device:
617 pdata->device_enable(pdev);
618 return ret;
619}
620
621static int _omap_rproc_resume(struct rproc *rproc)
622{
623 struct device *dev = rproc->dev.parent;
624 struct platform_device *pdev = to_platform_device(dev);
625 struct omap_rproc_pdata *pdata = dev_get_platdata(dev);
626 struct omap_rproc *oproc = rproc->priv;
627 int ret;
628
629 /* boot address could be lost after suspend, so restore it */
630 if (oproc->boot_data) {
631 ret = omap_rproc_write_dsp_boot_addr(rproc);
632 if (ret) {
633 dev_err(dev, "boot address restore failed %d\n", ret);
634 goto out;
635 }
636 }
637
638 ret = omap_rproc_enable_timers(rproc, false);
639 if (ret) {
640 dev_err(dev, "enabling timers during resume failed %d\n",
641 ret);
642 goto out;
643 }
644
645 ret = pdata->device_enable(pdev);
646 if (ret)
647 goto disable_timers;
648
649 return 0;
650
651disable_timers:
652 omap_rproc_disable_timers(rproc, false);
653out:
654 return ret;
655}
656
657static int __maybe_unused omap_rproc_suspend(struct device *dev)
658{
659 struct platform_device *pdev = to_platform_device(dev);
660 struct rproc *rproc = platform_get_drvdata(pdev);
661 int ret = 0;
662
663 mutex_lock(&rproc->lock);
664 if (rproc->state == RPROC_OFFLINE)
665 goto out;
666
667 if (rproc->state == RPROC_SUSPENDED)
668 goto out;
669
670 if (rproc->state != RPROC_RUNNING) {
671 ret = -EBUSY;
672 goto out;
673 }
674
675 ret = _omap_rproc_suspend(rproc);
676 if (ret) {
677 dev_err(dev, "suspend failed %d\n", ret);
678 goto out;
679 }
680
681 rproc->state = RPROC_SUSPENDED;
682out:
683 mutex_unlock(&rproc->lock);
684 return ret;
685}
686
687static int __maybe_unused omap_rproc_resume(struct device *dev)
688{
689 struct platform_device *pdev = to_platform_device(dev);
690 struct rproc *rproc = platform_get_drvdata(pdev);
691 int ret = 0;
692
693 mutex_lock(&rproc->lock);
694 if (rproc->state == RPROC_OFFLINE)
695 goto out;
696
697 if (rproc->state != RPROC_SUSPENDED) {
698 ret = -EBUSY;
699 goto out;
700 }
701
702 ret = _omap_rproc_resume(rproc);
703 if (ret) {
704 dev_err(dev, "resume failed %d\n", ret);
705 goto out;
706 }
707
708 rproc->state = RPROC_RUNNING;
709out:
710 mutex_unlock(&rproc->lock);
711 return ret;
712}
713#endif /* CONFIG_PM */
714
537static const struct omap_rproc_dev_data omap4_dsp_dev_data = { 715static const struct omap_rproc_dev_data omap4_dsp_dev_data = {
538 .device_name = "dsp", 716 .device_name = "dsp",
539 .fw_name = "omap4-dsp-fw.xe64T", 717 .fw_name = "omap4-dsp-fw.xe64T",
@@ -754,6 +932,7 @@ static int omap_rproc_probe(struct platform_device *pdev)
754 struct omap_rproc *oproc; 932 struct omap_rproc *oproc;
755 struct rproc *rproc; 933 struct rproc *rproc;
756 const char *firmware; 934 const char *firmware;
935 u32 standby_addr = 0;
757 int ret; 936 int ret;
758 937
759 if (!np) { 938 if (!np) {
@@ -818,6 +997,21 @@ static int omap_rproc_probe(struct platform_device *pdev)
818 oproc->num_timers); 997 oproc->num_timers);
819 } 998 }
820 999
1000 init_completion(&oproc->pm_comp);
1001
1002 ret = of_property_read_u32(np, "ti,rproc-standby-info", &standby_addr);
1003 if (ret || !standby_addr) {
1004 ret = !standby_addr ? -EINVAL : ret;
1005 goto free_rproc;
1006 }
1007
1008 oproc->standby_addr = devm_ioremap(&pdev->dev, standby_addr,
1009 sizeof(u32));
1010 if (!oproc->standby_addr) {
1011 ret = -ENOMEM;
1012 goto free_rproc;
1013 }
1014
821 ret = of_reserved_mem_device_init(&pdev->dev); 1015 ret = of_reserved_mem_device_init(&pdev->dev);
822 if (ret) { 1016 if (ret) {
823 dev_err(&pdev->dev, "device does not have specific CMA pool\n"); 1017 dev_err(&pdev->dev, "device does not have specific CMA pool\n");
@@ -853,11 +1047,16 @@ static int omap_rproc_remove(struct platform_device *pdev)
853 return 0; 1047 return 0;
854} 1048}
855 1049
1050static const struct dev_pm_ops omap_rproc_pm_ops = {
1051 SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume)
1052};
1053
856static struct platform_driver omap_rproc_driver = { 1054static struct platform_driver omap_rproc_driver = {
857 .probe = omap_rproc_probe, 1055 .probe = omap_rproc_probe,
858 .remove = omap_rproc_remove, 1056 .remove = omap_rproc_remove,
859 .driver = { 1057 .driver = {
860 .name = "omap-rproc", 1058 .name = "omap-rproc",
1059 .pm = &omap_rproc_pm_ops,
861 .of_match_table = omap_rproc_of_match, 1060 .of_match_table = omap_rproc_of_match,
862 }, 1061 },
863}; 1062};
diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h
index 18f522617683..b74d140cbbb6 100644
--- a/drivers/remoteproc/omap_remoteproc.h
+++ b/drivers/remoteproc/omap_remoteproc.h
@@ -2,7 +2,7 @@
2/* 2/*
3 * Remote processor messaging 3 * Remote processor messaging
4 * 4 *
5 * Copyright (C) 2011 Texas Instruments, Inc. 5 * Copyright (C) 2011-2019 Texas Instruments, Inc.
6 * Copyright (C) 2011 Google, Inc. 6 * Copyright (C) 2011 Google, Inc.
7 * All rights reserved. 7 * All rights reserved.
8 */ 8 */
@@ -32,6 +32,16 @@
32 * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the 32 * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the
33 * recovery mechanism (to some extent). 33 * recovery mechanism (to some extent).
34 * 34 *
35 * @RP_MBOX_SUSPEND_AUTO: auto suspend request for the remote processor
36 *
37 * @RP_MBOX_SUSPEND_SYSTEM: system suspend request for the remote processor
38 *
39 * @RP_MBOX_SUSPEND_ACK: successful response from remote processor for a
40 * suspend request
41 *
42 * @RP_MBOX_SUSPEND_CANCEL: a cancel suspend response from a remote processor
43 * on a suspend request
44 *
35 * Introduce new message definitions if any here. 45 * Introduce new message definitions if any here.
36 * 46 *
37 * @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core 47 * @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core
@@ -45,7 +55,11 @@ enum omap_rp_mbox_messages {
45 RP_MBOX_ECHO_REQUEST = 0xFFFFFF03, 55 RP_MBOX_ECHO_REQUEST = 0xFFFFFF03,
46 RP_MBOX_ECHO_REPLY = 0xFFFFFF04, 56 RP_MBOX_ECHO_REPLY = 0xFFFFFF04,
47 RP_MBOX_ABORT_REQUEST = 0xFFFFFF05, 57 RP_MBOX_ABORT_REQUEST = 0xFFFFFF05,
48 RP_MBOX_END_MSG = 0xFFFFFF06, 58 RP_MBOX_SUSPEND_AUTO = 0xFFFFFF10,
59 RP_MBOX_SUSPEND_SYSTEM = 0xFFFFFF11,
60 RP_MBOX_SUSPEND_ACK = 0xFFFFFF12,
61 RP_MBOX_SUSPEND_CANCEL = 0xFFFFFF13,
62 RP_MBOX_END_MSG = 0xFFFFFF14,
49}; 63};
50 64
51#endif /* _OMAP_RPMSG_H */ 65#endif /* _OMAP_RPMSG_H */