diff options
author | Subramaniam Chanderashekarapuram <subramaniam.ca@ti.com> | 2012-06-07 18:40:48 -0500 |
---|---|---|
committer | Andy Green <andy.green@linaro.org> | 2012-09-07 13:05:51 +0800 |
commit | e59d076a77e700513a6f26a12ff41071e202c227 (patch) | |
tree | b42e7825688e7b62408b3d7895d4cf4082d9ed43 /drivers | |
parent | 5462ca5e1e3b618b16090974cd75d5ed88065697 (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/Kconfig | 12 | ||||
-rw-r--r-- | drivers/remoteproc/omap_remoteproc.c | 51 |
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; } |