aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuman Anna2018-02-21 21:11:42 -0600
committerSuman Anna2019-03-11 12:02:06 -0500
commitbda92cdb45d62b399b2e0854b4c77a09f601c3b8 (patch)
treeabf10c99c45da12ec20418488fad6a3eb2a503c1
parent9582a7a2a16e4096b96b370e180e4f137053a7b4 (diff)
downloadremoteproc-bda92cdb45d62b399b2e0854b4c77a09f601c3b8.tar.gz
remoteproc-bda92cdb45d62b399b2e0854b4c77a09f601c3b8.tar.xz
remoteproc-bda92cdb45d62b399b2e0854b4c77a09f601c3b8.zip
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. Signed-off-by: Suman Anna <s-anna@ti.com>
-rw-r--r--drivers/remoteproc/Kconfig11
-rw-r--r--drivers/remoteproc/omap_remoteproc.c154
2 files changed, 153 insertions, 12 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 722aa4ee8c16..6bdeada238ac 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -41,6 +41,17 @@ config OMAP_REMOTEPROC
41 It's safe to say N here if you're not interested in multimedia 41 It's safe to say N here if you're not interested in multimedia
42 offloading or just want a bare minimum kernel. 42 offloading or just want a bare minimum kernel.
43 43
44config OMAP_REMOTEPROC_WATCHDOG
45 bool "OMAP remoteproc watchdog timer"
46 depends on OMAP_REMOTEPROC
47 help
48 Say Y here to enable watchdog timer for remote processors.
49
50 This option controls the watchdog functionality for the remote
51 processors in OMAP. Dedicated OMAP DMTimers are used by the remote
52 processors and triggers the timer interrupt upon a watchdog
53 detection.
54
44config WKUP_M3_RPROC 55config WKUP_M3_RPROC
45 tristate "AMx3xx Wakeup M3 remoteproc support" 56 tristate "AMx3xx Wakeup M3 remoteproc support"
46 depends on SOC_AM33XX || SOC_AM43XX 57 depends on SOC_AM33XX || SOC_AM43XX
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index 5142d01f1cd6..aa325c8c5fae 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -23,6 +23,7 @@
23#include <linux/platform_device.h> 23#include <linux/platform_device.h>
24#include <linux/pm_runtime.h> 24#include <linux/pm_runtime.h>
25#include <linux/dma-mapping.h> 25#include <linux/dma-mapping.h>
26#include <linux/interrupt.h>
26#include <linux/remoteproc.h> 27#include <linux/remoteproc.h>
27#include <linux/mailbox_client.h> 28#include <linux/mailbox_client.h>
28#include <linux/omap-mailbox.h> 29#include <linux/omap-mailbox.h>
@@ -74,10 +75,12 @@ struct omap_rproc_mem {
74 * struct omap_rproc_timer - data structure for a timer used by a omap rproc 75 * struct omap_rproc_timer - data structure for a timer used by a omap rproc
75 * @odt: timer pointer 76 * @odt: timer pointer
76 * @timer_ops: OMAP dmtimer ops for @odt timer 77 * @timer_ops: OMAP dmtimer ops for @odt timer
78 * @irq: timer irq
77 */ 79 */
78struct omap_rproc_timer { 80struct omap_rproc_timer {
79 struct omap_dm_timer *odt; 81 struct omap_dm_timer *odt;
80 const struct omap_dm_timer_ops *timer_ops; 82 const struct omap_dm_timer_ops *timer_ops;
83 int irq;
81}; 84};
82 85
83/** 86/**
@@ -88,6 +91,7 @@ struct omap_rproc_timer {
88 * @mem: internal memory regions data 91 * @mem: internal memory regions data
89 * @num_mems: number of internal memory regions 92 * @num_mems: number of internal memory regions
90 * @num_timers: number of rproc timer(s) 93 * @num_timers: number of rproc timer(s)
94 * @num_wd_timers: number of rproc watchdog timers
91 * @timers: timer(s) info used by rproc 95 * @timers: timer(s) info used by rproc
92 * @autosuspend_delay: auto-suspend delay value to be used for runtime pm 96 * @autosuspend_delay: auto-suspend delay value to be used for runtime pm
93 * @need_resume: if true a resume is needed in the system resume callback 97 * @need_resume: if true a resume is needed in the system resume callback
@@ -103,6 +107,7 @@ struct omap_rproc {
103 struct omap_rproc_mem *mem; 107 struct omap_rproc_mem *mem;
104 int num_mems; 108 int num_mems;
105 int num_timers; 109 int num_timers;
110 int num_wd_timers;
106 struct omap_rproc_timer *timers; 111 struct omap_rproc_timer *timers;
107 int autosuspend_delay; 112 int autosuspend_delay;
108 bool need_resume; 113 bool need_resume;
@@ -211,6 +216,81 @@ static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer)
211} 216}
212 217
213/** 218/**
219 * omap_rproc_get_timer_irq - get the irq for a timer
220 * @timer - handle to a OMAP rproc timer
221 *
222 * This function is used to get the irq associated with a watchdog timer. The
223 * function is called by the OMAP remoteproc driver to register a interrupt
224 * handler to handle watchdog events on the remote processor.
225 *
226 * Returns the irq id on success, otherwise a failure as returned by DMTimer ops
227 */
228static inline int omap_rproc_get_timer_irq(struct omap_rproc_timer *timer)
229{
230 return timer->timer_ops->get_irq(timer->odt);
231}
232
233/**
234 * omap_rproc_ack_timer_irq - acknowledge a timer irq
235 * @timer: handle to a OMAP rproc timer
236 *
237 * This function is used to clear the irq associated with a watchdog timer. The
238 * The function is called by the OMAP remoteproc upon a watchdog event on the
239 * remote processor to clear the interrupt status of the watchdog timer.
240 *
241 * Returns the irq id on success, otherwise a failure as returned by DMTimer ops
242 */
243static inline void omap_rproc_ack_timer_irq(struct omap_rproc_timer *timer)
244{
245 timer->timer_ops->write_status(timer->odt, OMAP_TIMER_INT_OVERFLOW);
246}
247
248/**
249 * omap_rproc_watchdog_isr - Watchdog ISR handler for remoteproc device
250 * @irq: IRQ number associated with a watchdog timer
251 * @data: IRQ handler data
252 *
253 * This ISR routine executes the required necessary low-level code to
254 * acknowledge a watchdog timer interrupt. There can be multiple watchdog
255 * timers associated with a rproc (like IPUs which have 2 watchdog timers,
256 * one per Cortex M3/M4 core), so a lookup has to be performed to identify
257 * the timer to acknowledge its interrupt.
258 *
259 * The function also invokes rproc_report_crash to report the watchdog event
260 * to the remoteproc driver core, to trigger a recovery.
261 *
262 * Return: IRQ_HANDLED or IRQ_NONE
263 */
264static irqreturn_t omap_rproc_watchdog_isr(int irq, void *data)
265{
266 struct rproc *rproc = data;
267 struct omap_rproc *oproc = rproc->priv;
268 struct device *dev = rproc->dev.parent;
269 struct omap_rproc_timer *timers = oproc->timers;
270 struct omap_rproc_timer *wd_timer = NULL;
271 int num_timers = oproc->num_timers + oproc->num_wd_timers;
272 int i;
273
274 for (i = oproc->num_timers; i < num_timers; i++) {
275 if (timers[i].irq > 0 && irq == timers[i].irq) {
276 wd_timer = &timers[i];
277 break;
278 }
279 }
280
281 if (!wd_timer) {
282 dev_err(dev, "invalid timer\n");
283 return IRQ_NONE;
284 }
285
286 omap_rproc_ack_timer_irq(wd_timer);
287
288 rproc_report_crash(rproc, RPROC_WATCHDOG);
289
290 return IRQ_HANDLED;
291}
292
293/**
214 * omap_rproc_enable_timers - enable the timers for a remoteproc 294 * omap_rproc_enable_timers - enable the timers for a remoteproc
215 * @rproc: handle of a remote processor 295 * @rproc: handle of a remote processor
216 * @configure: boolean flag used to acquire and configure the timer handle 296 * @configure: boolean flag used to acquire and configure the timer handle
@@ -231,19 +311,25 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
231 struct omap_rproc_timer *timers = oproc->timers; 311 struct omap_rproc_timer *timers = oproc->timers;
232 struct device *dev = rproc->dev.parent; 312 struct device *dev = rproc->dev.parent;
233 struct device_node *np = NULL; 313 struct device_node *np = NULL;
314 int num_timers = oproc->num_timers + oproc->num_wd_timers;
234 315
235 if (oproc->num_timers <= 0) 316 if (num_timers <= 0)
236 return 0; 317 return 0;
237 318
238 if (!configure) 319 if (!configure)
239 goto start_timers; 320 goto start_timers;
240 321
241 for (i = 0; i < oproc->num_timers; i++) { 322 for (i = 0; i < num_timers; i++) {
242 np = of_parse_phandle(dev->of_node, "timers", i); 323 if (i < oproc->num_timers)
324 np = of_parse_phandle(dev->of_node, "timers", i);
325 else
326 np = of_parse_phandle(dev->of_node, "watchdog-timers",
327 (i - oproc->num_timers));
243 if (!np) { 328 if (!np) {
244 ret = -ENXIO; 329 ret = -ENXIO;
245 dev_err(dev, "device node lookup for timer at index %d failed: %d\n", 330 dev_err(dev, "device node lookup for timer at index %d failed: %d\n",
246 i, ret); 331 i < oproc->num_timers ? i :
332 i - oproc->num_timers, ret);
247 goto free_timers; 333 goto free_timers;
248 } 334 }
249 335
@@ -266,12 +352,14 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
266 if (!timer_ops || !timer_ops->request_by_node || 352 if (!timer_ops || !timer_ops->request_by_node ||
267 !timer_ops->set_source || !timer_ops->set_load || 353 !timer_ops->set_source || !timer_ops->set_load ||
268 !timer_ops->free || !timer_ops->start || 354 !timer_ops->free || !timer_ops->start ||
269 !timer_ops->stop) { 355 !timer_ops->stop || !timer_ops->get_irq ||
356 !timer_ops->write_status) {
270 ret = -EINVAL; 357 ret = -EINVAL;
271 dev_err(dev, "device does not have required timer ops\n"); 358 dev_err(dev, "device does not have required timer ops\n");
272 goto put_node; 359 goto put_node;
273 } 360 }
274 361
362 timers[i].irq = -1;
275 timers[i].timer_ops = timer_ops; 363 timers[i].timer_ops = timer_ops;
276 ret = omap_rproc_request_timer(np, &timers[i]); 364 ret = omap_rproc_request_timer(np, &timers[i]);
277 if (ret) { 365 if (ret) {
@@ -280,10 +368,33 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
280 goto put_node; 368 goto put_node;
281 } 369 }
282 of_node_put(np); 370 of_node_put(np);
371
372 if (i >= oproc->num_timers) {
373 timers[i].irq = omap_rproc_get_timer_irq(&timers[i]);
374 if (timers[i].irq < 0) {
375 dev_err(dev, "get_irq for timer %p failed: %d\n",
376 np, timers[i].irq);
377 ret = -EBUSY;
378 goto free_timers;
379 }
380
381 ret = request_irq(timers[i].irq,
382 omap_rproc_watchdog_isr, IRQF_SHARED,
383 "rproc-wdt", rproc);
384 if (ret) {
385 dev_err(dev, "error requesting irq for timer %p\n",
386 np);
387 omap_rproc_release_timer(&timers[i]);
388 timers[i].odt = NULL;
389 timers[i].timer_ops = NULL;
390 timers[i].irq = -1;
391 goto free_timers;
392 }
393 }
283 } 394 }
284 395
285start_timers: 396start_timers:
286 for (i = 0; i < oproc->num_timers; i++) 397 for (i = 0; i < num_timers; i++)
287 omap_rproc_start_timer(&timers[i]); 398 omap_rproc_start_timer(&timers[i]);
288 return 0; 399 return 0;
289 400
@@ -291,9 +402,12 @@ put_node:
291 of_node_put(np); 402 of_node_put(np);
292free_timers: 403free_timers:
293 while (i--) { 404 while (i--) {
405 if (i >= oproc->num_timers)
406 free_irq(timers[i].irq, rproc);
294 omap_rproc_release_timer(&timers[i]); 407 omap_rproc_release_timer(&timers[i]);
295 timers[i].odt = NULL; 408 timers[i].odt = NULL;
296 timers[i].timer_ops = NULL; 409 timers[i].timer_ops = NULL;
410 timers[i].irq = -1;
297 } 411 }
298 412
299 return ret; 413 return ret;
@@ -314,16 +428,20 @@ static int omap_rproc_disable_timers(struct rproc *rproc, bool configure)
314 int i; 428 int i;
315 struct omap_rproc *oproc = rproc->priv; 429 struct omap_rproc *oproc = rproc->priv;
316 struct omap_rproc_timer *timers = oproc->timers; 430 struct omap_rproc_timer *timers = oproc->timers;
431 int num_timers = oproc->num_timers + oproc->num_wd_timers;
317 432
318 if (oproc->num_timers <= 0) 433 if (num_timers <= 0)
319 return 0; 434 return 0;
320 435
321 for (i = 0; i < oproc->num_timers; i++) { 436 for (i = 0; i < num_timers; i++) {
322 omap_rproc_stop_timer(&timers[i]); 437 omap_rproc_stop_timer(&timers[i]);
323 if (configure) { 438 if (configure) {
439 if (i >= oproc->num_timers)
440 free_irq(timers[i].irq, rproc);
324 omap_rproc_release_timer(&timers[i]); 441 omap_rproc_release_timer(&timers[i]);
325 timers[i].odt = NULL; 442 timers[i].odt = NULL;
326 timers[i].timer_ops = NULL; 443 timers[i].timer_ops = NULL;
444 timers[i].irq = -1;
327 } 445 }
328 } 446 }
329 447
@@ -1154,6 +1272,7 @@ static int omap_rproc_probe(struct platform_device *pdev)
1154 struct rproc *rproc; 1272 struct rproc *rproc;
1155 const char *firmware; 1273 const char *firmware;
1156 u32 standby_addr = 0; 1274 u32 standby_addr = 0;
1275 int num_timers;
1157 int ret; 1276 int ret;
1158 1277
1159 if (!np) { 1278 if (!np) {
@@ -1216,16 +1335,27 @@ static int omap_rproc_probe(struct platform_device *pdev)
1216 oproc->num_timers = 0; 1335 oproc->num_timers = 0;
1217 } 1336 }
1218 1337
1219 if (oproc->num_timers) { 1338#ifdef CONFIG_OMAP_REMOTEPROC_WATCHDOG
1339 oproc->num_wd_timers = of_count_phandle_with_args(np, "watchdog-timers",
1340 NULL);
1341 if (oproc->num_wd_timers <= 0) {
1342 dev_dbg(&pdev->dev, "device does not have watchdog timers, status = %d\n",
1343 oproc->num_wd_timers);
1344 oproc->num_wd_timers = 0;
1345 }
1346#endif
1347
1348 if (oproc->num_timers || oproc->num_wd_timers) {
1349 num_timers = oproc->num_timers + oproc->num_wd_timers;
1220 oproc->timers = devm_kzalloc(&pdev->dev, sizeof(*oproc->timers) 1350 oproc->timers = devm_kzalloc(&pdev->dev, sizeof(*oproc->timers)
1221 * oproc->num_timers, GFP_KERNEL); 1351 * num_timers, GFP_KERNEL);
1222 if (!oproc->timers) { 1352 if (!oproc->timers) {
1223 ret = -ENOMEM; 1353 ret = -ENOMEM;
1224 goto free_rproc; 1354 goto free_rproc;
1225 } 1355 }
1226 1356
1227 dev_dbg(&pdev->dev, "device has %d tick timers\n", 1357 dev_dbg(&pdev->dev, "device has %d tick timers and %d watchdog timers\n",
1228 oproc->num_timers); 1358 oproc->num_timers, oproc->num_wd_timers);
1229 } 1359 }
1230 1360
1231 init_completion(&oproc->pm_comp); 1361 init_completion(&oproc->pm_comp);