summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSubramaniam Chanderashekarapuram <subramaniam.ca@ti.com>2012-06-07 18:40:48 -0500
committerAndy Green <andy.green@linaro.org>2012-09-07 13:05:51 +0800
commite59d076a77e700513a6f26a12ff41071e202c227 (patch)
treeb42e7825688e7b62408b3d7895d4cf4082d9ed43 /drivers
parent5462ca5e1e3b618b16090974cd75d5ed88065697 (diff)
remoteproc: omap: add watchdog functionality for remote processors
Remote processors can be stuck in a loop, and may not be recoverable if they do not have a built-in watchdog. The watchdog implementation for OMAP remote processors uses external gptimers that can be used to interrupt both the Linux host as well as the remote processor. Each remote processor is responsible for refreshing the timer during normal behavior - during OS task scheduling or entering the idle loop properly. During a watchdog condition (executing a tight loop causing no scheduling), the host processor gets interrupts and schedules a recovery. The remote processor also gets interrupted to be able to print a back trace A menuconfig option has also been added to disable the Watchdog functionality. This is useful for any JTAG debugging. Change-Id: Ide8c137ec3df020e90b7b73b370aa2a1d65023ad Signed-off-by: Subramaniam Chanderashekarapuram <subramaniam.ca@ti.com> Signed-off-by: Shahid Akhtar <sakhtar@ti.com> Signed-off-by: Fernando Guzman Lugo <fernando.lugo@ti.com> Signed-off-by: Suman Anna <s-anna@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/remoteproc/Kconfig12
-rw-r--r--drivers/remoteproc/omap_remoteproc.c51
2 files changed, 62 insertions, 1 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 0cf2881c57e0..5301557533a7 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -51,6 +51,18 @@ config OMAP_REMOTEPROC_DSP
It's safe to say n here if you're not interested in offloading
audio or any other algorithm (by DSP) or just want a bare minimum kernel.
+config OMAP_REMOTEPROC_WATCHDOG
+ bool "OMAP remoteproc watchdog timer"
+ depends on OMAP_REMOTEPROC
+ default y
+ help
+ Say Y here to enable watchdog timer for remote processors.
+
+ This option controls the watchdog functionality for the remote
+ processors in OMAP. Dedicated timers are used by the remote
+ processors and triggers the timer interrupt upon a watchdog
+ detection.
+
# Amount of CMA memory to reserve for OMAP's remoteproc sub-system.
# We need quite much. Fortunately, CMA makes sure this memory isn't
# wasted in case we're not loading the remote processors.
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index b9091ed38bc4..6a4d1b65eca3 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -216,6 +216,33 @@ omap_rproc_set_frequency(struct device *dev, struct rproc *rproc, long val)
return 0;
}
+static irqreturn_t omap_rproc_watchdog_isr(int irq, void *p)
+{
+ struct rproc *rproc = p;
+ struct device *dev = rproc->dev.parent;
+ struct omap_rproc_pdata *pdata = dev->platform_data;
+ struct omap_rproc_timers_info *timers = pdata->timers;
+ struct omap_dm_timer *timer = NULL;
+ int i;
+
+ for (i = 0; i < pdata->timers_cnt; i++) {
+ if (irq == omap_dm_timer_get_irq(timers[i].odt)) {
+ timer = timers[i].odt;
+ break;
+ }
+ }
+
+ if (!timer) {
+ dev_err(dev, "invalid timer\n");
+ return IRQ_NONE;
+ }
+ omap_dm_timer_write_status(timer, OMAP_TIMER_INT_OVERFLOW);
+
+ rproc_error_reporter(rproc, RPROC_ERR_WATCHDOG);
+
+ return IRQ_HANDLED;
+}
+
/*
* Power up the remote processor.
*
@@ -228,7 +255,7 @@ static int omap_rproc_start(struct rproc *rproc)
struct omap_rproc *oproc = rproc->priv;
struct device *dev = rproc->dev.parent;
struct platform_device *pdev = to_platform_device(dev);
- struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+ struct omap_rproc_pdata *pdata = dev->platform_data;
struct omap_rproc_timers_info *timers = pdata->timers;
int ret, i;
@@ -270,6 +297,22 @@ static int omap_rproc_start(struct rproc *rproc)
goto err_timers;
}
omap_dm_timer_set_source(timers[i].odt, OMAP_TIMER_SRC_SYS_CLK);
+
+ if (timers[i].is_wdt) {
+ ret = request_irq(omap_dm_timer_get_irq(timers[i].odt),
+ omap_rproc_watchdog_isr, IRQF_SHARED,
+ "rproc-wdt", rproc);
+ if (ret) {
+ dev_err(dev,
+ "error requesting irq for timer %d\n",
+ timers[i].id);
+ omap_dm_timer_free(timers[i].odt);
+ timers[i].odt = NULL;
+ goto err_timers;
+ }
+ /* clean counter, remoteproc proc will set the value */
+ omap_dm_timer_set_load(timers[i].odt, 0, 0);
+ }
omap_dm_timer_start(timers[i].odt);
}
@@ -284,6 +327,9 @@ static int omap_rproc_start(struct rproc *rproc)
err_timers:
while (i--) {
omap_dm_timer_stop(timers[i].odt);
+ if (timers[i].is_wdt)
+ free_irq(omap_dm_timer_get_irq(timers[i].odt), rproc);
+
omap_dm_timer_free(timers[i].odt);
timers[i].odt = NULL;
}
@@ -309,6 +355,9 @@ static int omap_rproc_stop(struct rproc *rproc)
for (i = 0; i < pdata->timers_cnt; i++) {
omap_dm_timer_stop(timers[i].odt);
+ if (timers[i].is_wdt)
+ free_irq(omap_dm_timer_get_irq(timers[i].odt), rproc);
+
omap_dm_timer_free(timers[i].odt);
timers[i].odt = NULL;
}