Skip to content

Commit 69591e4

Browse files
sumanannaandersson
authored andcommitted
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 for the corresponding remote processor. The remote processor may also get interrupted to be able to print a back trace. A menuconfig option has also been added to enable/disable the Watchdog functionality, with the default as disabled. Acked-by: Mathieu Poirier <[email protected]> Signed-off-by: Suman Anna <[email protected]> Signed-off-by: Tero Kristo <[email protected]> Reviewed-by: Andrew F. Davis <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 232ba6c commit 69591e4

File tree

2 files changed

+167
-11
lines changed

2 files changed

+167
-11
lines changed

drivers/remoteproc/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ config OMAP_REMOTEPROC
5252
It's safe to say N here if you're not interested in multimedia
5353
offloading or just want a bare minimum kernel.
5454

55+
config OMAP_REMOTEPROC_WATCHDOG
56+
bool "OMAP remoteproc watchdog timer"
57+
depends on OMAP_REMOTEPROC
58+
default n
59+
help
60+
Say Y here to enable watchdog timer for remote processors.
61+
62+
This option controls the watchdog functionality for the remote
63+
processors in OMAP. Dedicated OMAP DMTimers are used by the remote
64+
processors and triggers the timer interrupt upon a watchdog
65+
detection.
66+
5567
config WKUP_M3_RPROC
5668
tristate "AMx3xx Wakeup M3 remoteproc support"
5769
depends on SOC_AM33XX || SOC_AM43XX

drivers/remoteproc/omap_remoteproc.c

Lines changed: 155 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/platform_device.h>
2525
#include <linux/pm_runtime.h>
2626
#include <linux/dma-mapping.h>
27+
#include <linux/interrupt.h>
2728
#include <linux/remoteproc.h>
2829
#include <linux/mailbox_client.h>
2930
#include <linux/omap-iommu.h>
@@ -72,10 +73,12 @@ struct omap_rproc_mem {
7273
* struct omap_rproc_timer - data structure for a timer used by a omap rproc
7374
* @odt: timer pointer
7475
* @timer_ops: OMAP dmtimer ops for @odt timer
76+
* @irq: timer irq
7577
*/
7678
struct omap_rproc_timer {
7779
struct omap_dm_timer *odt;
7880
const struct omap_dm_timer_ops *timer_ops;
81+
int irq;
7982
};
8083

8184
/**
@@ -86,6 +89,7 @@ struct omap_rproc_timer {
8689
* @mem: internal memory regions data
8790
* @num_mems: number of internal memory regions
8891
* @num_timers: number of rproc timer(s)
92+
* @num_wd_timers: number of rproc watchdog timers
8993
* @timers: timer(s) info used by rproc
9094
* @autosuspend_delay: auto-suspend delay value to be used for runtime pm
9195
* @need_resume: if true a resume is needed in the system resume callback
@@ -102,6 +106,7 @@ struct omap_rproc {
102106
struct omap_rproc_mem *mem;
103107
int num_mems;
104108
int num_timers;
109+
int num_wd_timers;
105110
struct omap_rproc_timer *timers;
106111
int autosuspend_delay;
107112
bool need_resume;
@@ -219,6 +224,79 @@ static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer)
219224
return timer->timer_ops->free(timer->odt);
220225
}
221226

227+
/**
228+
* omap_rproc_get_timer_irq() - get the irq for a timer
229+
* @timer: handle to a OMAP rproc timer
230+
*
231+
* This function is used to get the irq associated with a watchdog timer. The
232+
* function is called by the OMAP remoteproc driver to register a interrupt
233+
* handler to handle watchdog events on the remote processor.
234+
*
235+
* Return: irq id on success, otherwise a failure as returned by DMTimer ops
236+
*/
237+
static inline int omap_rproc_get_timer_irq(struct omap_rproc_timer *timer)
238+
{
239+
return timer->timer_ops->get_irq(timer->odt);
240+
}
241+
242+
/**
243+
* omap_rproc_ack_timer_irq() - acknowledge a timer irq
244+
* @timer: handle to a OMAP rproc timer
245+
*
246+
* This function is used to clear the irq associated with a watchdog timer. The
247+
* The function is called by the OMAP remoteproc upon a watchdog event on the
248+
* remote processor to clear the interrupt status of the watchdog timer.
249+
*/
250+
static inline void omap_rproc_ack_timer_irq(struct omap_rproc_timer *timer)
251+
{
252+
timer->timer_ops->write_status(timer->odt, OMAP_TIMER_INT_OVERFLOW);
253+
}
254+
255+
/**
256+
* omap_rproc_watchdog_isr() - Watchdog ISR handler for remoteproc device
257+
* @irq: IRQ number associated with a watchdog timer
258+
* @data: IRQ handler data
259+
*
260+
* This ISR routine executes the required necessary low-level code to
261+
* acknowledge a watchdog timer interrupt. There can be multiple watchdog
262+
* timers associated with a rproc (like IPUs which have 2 watchdog timers,
263+
* one per Cortex M3/M4 core), so a lookup has to be performed to identify
264+
* the timer to acknowledge its interrupt.
265+
*
266+
* The function also invokes rproc_report_crash to report the watchdog event
267+
* to the remoteproc driver core, to trigger a recovery.
268+
*
269+
* Return: IRQ_HANDLED on success, otherwise IRQ_NONE
270+
*/
271+
static irqreturn_t omap_rproc_watchdog_isr(int irq, void *data)
272+
{
273+
struct rproc *rproc = data;
274+
struct omap_rproc *oproc = rproc->priv;
275+
struct device *dev = rproc->dev.parent;
276+
struct omap_rproc_timer *timers = oproc->timers;
277+
struct omap_rproc_timer *wd_timer = NULL;
278+
int num_timers = oproc->num_timers + oproc->num_wd_timers;
279+
int i;
280+
281+
for (i = oproc->num_timers; i < num_timers; i++) {
282+
if (timers[i].irq > 0 && irq == timers[i].irq) {
283+
wd_timer = &timers[i];
284+
break;
285+
}
286+
}
287+
288+
if (!wd_timer) {
289+
dev_err(dev, "invalid timer\n");
290+
return IRQ_NONE;
291+
}
292+
293+
omap_rproc_ack_timer_irq(wd_timer);
294+
295+
rproc_report_crash(rproc, RPROC_WATCHDOG);
296+
297+
return IRQ_HANDLED;
298+
}
299+
222300
/**
223301
* omap_rproc_enable_timers() - enable the timers for a remoteproc
224302
* @rproc: handle of a remote processor
@@ -242,19 +320,26 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
242320
struct omap_rproc_timer *timers = oproc->timers;
243321
struct device *dev = rproc->dev.parent;
244322
struct device_node *np = NULL;
323+
int num_timers = oproc->num_timers + oproc->num_wd_timers;
245324

246-
if (!oproc->num_timers)
325+
if (!num_timers)
247326
return 0;
248327

249328
if (!configure)
250329
goto start_timers;
251330

252-
for (i = 0; i < oproc->num_timers; i++) {
253-
np = of_parse_phandle(dev->of_node, "ti,timers", i);
331+
for (i = 0; i < num_timers; i++) {
332+
if (i < oproc->num_timers)
333+
np = of_parse_phandle(dev->of_node, "ti,timers", i);
334+
else
335+
np = of_parse_phandle(dev->of_node,
336+
"ti,watchdog-timers",
337+
(i - oproc->num_timers));
254338
if (!np) {
255339
ret = -ENXIO;
256340
dev_err(dev, "device node lookup for timer at index %d failed: %d\n",
257-
i, ret);
341+
i < oproc->num_timers ? i :
342+
i - oproc->num_timers, ret);
258343
goto free_timers;
259344
}
260345

@@ -277,12 +362,14 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
277362
if (!timer_ops || !timer_ops->request_by_node ||
278363
!timer_ops->set_source || !timer_ops->set_load ||
279364
!timer_ops->free || !timer_ops->start ||
280-
!timer_ops->stop) {
365+
!timer_ops->stop || !timer_ops->get_irq ||
366+
!timer_ops->write_status) {
281367
ret = -EINVAL;
282368
dev_err(dev, "device does not have required timer ops\n");
283369
goto put_node;
284370
}
285371

372+
timers[i].irq = -1;
286373
timers[i].timer_ops = timer_ops;
287374
ret = omap_rproc_request_timer(dev, np, &timers[i]);
288375
if (ret) {
@@ -291,10 +378,33 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
291378
goto put_node;
292379
}
293380
of_node_put(np);
381+
382+
if (i >= oproc->num_timers) {
383+
timers[i].irq = omap_rproc_get_timer_irq(&timers[i]);
384+
if (timers[i].irq < 0) {
385+
dev_err(dev, "get_irq for timer %p failed: %d\n",
386+
np, timers[i].irq);
387+
ret = -EBUSY;
388+
goto free_timers;
389+
}
390+
391+
ret = request_irq(timers[i].irq,
392+
omap_rproc_watchdog_isr, IRQF_SHARED,
393+
"rproc-wdt", rproc);
394+
if (ret) {
395+
dev_err(dev, "error requesting irq for timer %p\n",
396+
np);
397+
omap_rproc_release_timer(&timers[i]);
398+
timers[i].odt = NULL;
399+
timers[i].timer_ops = NULL;
400+
timers[i].irq = -1;
401+
goto free_timers;
402+
}
403+
}
294404
}
295405

296406
start_timers:
297-
for (i = 0; i < oproc->num_timers; i++) {
407+
for (i = 0; i < num_timers; i++) {
298408
ret = omap_rproc_start_timer(&timers[i]);
299409
if (ret) {
300410
dev_err(dev, "start timer %p failed failed: %d\n", np,
@@ -316,9 +426,12 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
316426
of_node_put(np);
317427
free_timers:
318428
while (i--) {
429+
if (i >= oproc->num_timers)
430+
free_irq(timers[i].irq, rproc);
319431
omap_rproc_release_timer(&timers[i]);
320432
timers[i].odt = NULL;
321433
timers[i].timer_ops = NULL;
434+
timers[i].irq = -1;
322435
}
323436

324437
return ret;
@@ -341,16 +454,20 @@ static int omap_rproc_disable_timers(struct rproc *rproc, bool configure)
341454
int i;
342455
struct omap_rproc *oproc = rproc->priv;
343456
struct omap_rproc_timer *timers = oproc->timers;
457+
int num_timers = oproc->num_timers + oproc->num_wd_timers;
344458

345-
if (!oproc->num_timers)
459+
if (!num_timers)
346460
return 0;
347461

348-
for (i = 0; i < oproc->num_timers; i++) {
462+
for (i = 0; i < num_timers; i++) {
349463
omap_rproc_stop_timer(&timers[i]);
350464
if (configure) {
465+
if (i >= oproc->num_timers)
466+
free_irq(timers[i].irq, rproc);
351467
omap_rproc_release_timer(&timers[i]);
352468
timers[i].odt = NULL;
353469
timers[i].timer_ops = NULL;
470+
timers[i].irq = -1;
354471
}
355472
}
356473

@@ -1104,12 +1221,35 @@ static int omap_rproc_of_get_internal_memories(struct platform_device *pdev,
11041221
return 0;
11051222
}
11061223

1224+
#ifdef CONFIG_OMAP_REMOTEPROC_WATCHDOG
1225+
static int omap_rproc_count_wdog_timers(struct device *dev)
1226+
{
1227+
struct device_node *np = dev->of_node;
1228+
int ret;
1229+
1230+
ret = of_count_phandle_with_args(np, "ti,watchdog-timers", NULL);
1231+
if (ret <= 0) {
1232+
dev_dbg(dev, "device does not have watchdog timers, status = %d\n",
1233+
ret);
1234+
ret = 0;
1235+
}
1236+
1237+
return ret;
1238+
}
1239+
#else
1240+
static int omap_rproc_count_wdog_timers(struct device *dev)
1241+
{
1242+
return 0;
1243+
}
1244+
#endif
1245+
11071246
static int omap_rproc_of_get_timers(struct platform_device *pdev,
11081247
struct rproc *rproc)
11091248
{
11101249
struct device_node *np = pdev->dev.of_node;
11111250
struct omap_rproc *oproc = rproc->priv;
11121251
struct device *dev = &pdev->dev;
1252+
int num_timers;
11131253

11141254
/*
11151255
* Timer nodes are directly used in client nodes as phandles, so
@@ -1122,14 +1262,18 @@ static int omap_rproc_of_get_timers(struct platform_device *pdev,
11221262
oproc->num_timers = 0;
11231263
}
11241264

1125-
if (oproc->num_timers) {
1126-
oproc->timers = devm_kcalloc(dev, oproc->num_timers,
1265+
oproc->num_wd_timers = omap_rproc_count_wdog_timers(dev);
1266+
1267+
num_timers = oproc->num_timers + oproc->num_wd_timers;
1268+
if (num_timers) {
1269+
oproc->timers = devm_kcalloc(dev, num_timers,
11271270
sizeof(*oproc->timers),
11281271
GFP_KERNEL);
11291272
if (!oproc->timers)
11301273
return -ENOMEM;
11311274

1132-
dev_dbg(dev, "device has %d tick timers\n", oproc->num_timers);
1275+
dev_dbg(dev, "device has %d tick timers and %d watchdog timers\n",
1276+
oproc->num_timers, oproc->num_wd_timers);
11331277
}
11341278

11351279
return 0;

0 commit comments

Comments
 (0)