24
24
#include <linux/platform_device.h>
25
25
#include <linux/pm_runtime.h>
26
26
#include <linux/dma-mapping.h>
27
+ #include <linux/interrupt.h>
27
28
#include <linux/remoteproc.h>
28
29
#include <linux/mailbox_client.h>
29
30
#include <linux/omap-iommu.h>
@@ -72,10 +73,12 @@ struct omap_rproc_mem {
72
73
* struct omap_rproc_timer - data structure for a timer used by a omap rproc
73
74
* @odt: timer pointer
74
75
* @timer_ops: OMAP dmtimer ops for @odt timer
76
+ * @irq: timer irq
75
77
*/
76
78
struct omap_rproc_timer {
77
79
struct omap_dm_timer * odt ;
78
80
const struct omap_dm_timer_ops * timer_ops ;
81
+ int irq ;
79
82
};
80
83
81
84
/**
@@ -86,6 +89,7 @@ struct omap_rproc_timer {
86
89
* @mem: internal memory regions data
87
90
* @num_mems: number of internal memory regions
88
91
* @num_timers: number of rproc timer(s)
92
+ * @num_wd_timers: number of rproc watchdog timers
89
93
* @timers: timer(s) info used by rproc
90
94
* @autosuspend_delay: auto-suspend delay value to be used for runtime pm
91
95
* @need_resume: if true a resume is needed in the system resume callback
@@ -102,6 +106,7 @@ struct omap_rproc {
102
106
struct omap_rproc_mem * mem ;
103
107
int num_mems ;
104
108
int num_timers ;
109
+ int num_wd_timers ;
105
110
struct omap_rproc_timer * timers ;
106
111
int autosuspend_delay ;
107
112
bool need_resume ;
@@ -219,6 +224,79 @@ static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer)
219
224
return timer -> timer_ops -> free (timer -> odt );
220
225
}
221
226
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
+
222
300
/**
223
301
* omap_rproc_enable_timers() - enable the timers for a remoteproc
224
302
* @rproc: handle of a remote processor
@@ -242,19 +320,26 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
242
320
struct omap_rproc_timer * timers = oproc -> timers ;
243
321
struct device * dev = rproc -> dev .parent ;
244
322
struct device_node * np = NULL ;
323
+ int num_timers = oproc -> num_timers + oproc -> num_wd_timers ;
245
324
246
- if (!oproc -> num_timers )
325
+ if (!num_timers )
247
326
return 0 ;
248
327
249
328
if (!configure )
250
329
goto start_timers ;
251
330
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 ));
254
338
if (!np ) {
255
339
ret = - ENXIO ;
256
340
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 );
258
343
goto free_timers ;
259
344
}
260
345
@@ -277,12 +362,14 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
277
362
if (!timer_ops || !timer_ops -> request_by_node ||
278
363
!timer_ops -> set_source || !timer_ops -> set_load ||
279
364
!timer_ops -> free || !timer_ops -> start ||
280
- !timer_ops -> stop ) {
365
+ !timer_ops -> stop || !timer_ops -> get_irq ||
366
+ !timer_ops -> write_status ) {
281
367
ret = - EINVAL ;
282
368
dev_err (dev , "device does not have required timer ops\n" );
283
369
goto put_node ;
284
370
}
285
371
372
+ timers [i ].irq = -1 ;
286
373
timers [i ].timer_ops = timer_ops ;
287
374
ret = omap_rproc_request_timer (dev , np , & timers [i ]);
288
375
if (ret ) {
@@ -291,10 +378,33 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
291
378
goto put_node ;
292
379
}
293
380
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
+ }
294
404
}
295
405
296
406
start_timers :
297
- for (i = 0 ; i < oproc -> num_timers ; i ++ ) {
407
+ for (i = 0 ; i < num_timers ; i ++ ) {
298
408
ret = omap_rproc_start_timer (& timers [i ]);
299
409
if (ret ) {
300
410
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)
316
426
of_node_put (np );
317
427
free_timers :
318
428
while (i -- ) {
429
+ if (i >= oproc -> num_timers )
430
+ free_irq (timers [i ].irq , rproc );
319
431
omap_rproc_release_timer (& timers [i ]);
320
432
timers [i ].odt = NULL ;
321
433
timers [i ].timer_ops = NULL ;
434
+ timers [i ].irq = -1 ;
322
435
}
323
436
324
437
return ret ;
@@ -341,16 +454,20 @@ static int omap_rproc_disable_timers(struct rproc *rproc, bool configure)
341
454
int i ;
342
455
struct omap_rproc * oproc = rproc -> priv ;
343
456
struct omap_rproc_timer * timers = oproc -> timers ;
457
+ int num_timers = oproc -> num_timers + oproc -> num_wd_timers ;
344
458
345
- if (!oproc -> num_timers )
459
+ if (!num_timers )
346
460
return 0 ;
347
461
348
- for (i = 0 ; i < oproc -> num_timers ; i ++ ) {
462
+ for (i = 0 ; i < num_timers ; i ++ ) {
349
463
omap_rproc_stop_timer (& timers [i ]);
350
464
if (configure ) {
465
+ if (i >= oproc -> num_timers )
466
+ free_irq (timers [i ].irq , rproc );
351
467
omap_rproc_release_timer (& timers [i ]);
352
468
timers [i ].odt = NULL ;
353
469
timers [i ].timer_ops = NULL ;
470
+ timers [i ].irq = -1 ;
354
471
}
355
472
}
356
473
@@ -1104,12 +1221,35 @@ static int omap_rproc_of_get_internal_memories(struct platform_device *pdev,
1104
1221
return 0 ;
1105
1222
}
1106
1223
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
+
1107
1246
static int omap_rproc_of_get_timers (struct platform_device * pdev ,
1108
1247
struct rproc * rproc )
1109
1248
{
1110
1249
struct device_node * np = pdev -> dev .of_node ;
1111
1250
struct omap_rproc * oproc = rproc -> priv ;
1112
1251
struct device * dev = & pdev -> dev ;
1252
+ int num_timers ;
1113
1253
1114
1254
/*
1115
1255
* 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,
1122
1262
oproc -> num_timers = 0 ;
1123
1263
}
1124
1264
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 ,
1127
1270
sizeof (* oproc -> timers ),
1128
1271
GFP_KERNEL );
1129
1272
if (!oproc -> timers )
1130
1273
return - ENOMEM ;
1131
1274
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 );
1133
1277
}
1134
1278
1135
1279
return 0 ;
0 commit comments