5
5
* Joel Stanley <[email protected] >
6
6
*/
7
7
8
+ #include <linux/bits.h>
8
9
#include <linux/delay.h>
10
+ #include <linux/interrupt.h>
9
11
#include <linux/io.h>
10
12
#include <linux/kernel.h>
11
13
#include <linux/module.h>
12
14
#include <linux/of.h>
15
+ #include <linux/of_irq.h>
13
16
#include <linux/platform_device.h>
14
17
#include <linux/watchdog.h>
15
18
@@ -18,28 +21,41 @@ module_param(nowayout, bool, 0);
18
21
MODULE_PARM_DESC (nowayout , "Watchdog cannot be stopped once started (default="
19
22
__MODULE_STRING (WATCHDOG_NOWAYOUT ) ")" );
20
23
24
+ struct aspeed_wdt_config {
25
+ u32 ext_pulse_width_mask ;
26
+ u32 irq_shift ;
27
+ u32 irq_mask ;
28
+ };
29
+
21
30
struct aspeed_wdt {
22
31
struct watchdog_device wdd ;
23
32
void __iomem * base ;
24
33
u32 ctrl ;
25
- };
26
-
27
- struct aspeed_wdt_config {
28
- u32 ext_pulse_width_mask ;
34
+ const struct aspeed_wdt_config * cfg ;
29
35
};
30
36
31
37
static const struct aspeed_wdt_config ast2400_config = {
32
38
.ext_pulse_width_mask = 0xff ,
39
+ .irq_shift = 0 ,
40
+ .irq_mask = 0 ,
33
41
};
34
42
35
43
static const struct aspeed_wdt_config ast2500_config = {
36
44
.ext_pulse_width_mask = 0xfffff ,
45
+ .irq_shift = 12 ,
46
+ .irq_mask = GENMASK (31 , 12 ),
47
+ };
48
+
49
+ static const struct aspeed_wdt_config ast2600_config = {
50
+ .ext_pulse_width_mask = 0xfffff ,
51
+ .irq_shift = 0 ,
52
+ .irq_mask = GENMASK (31 , 10 ),
37
53
};
38
54
39
55
static const struct of_device_id aspeed_wdt_of_table [] = {
40
56
{ .compatible = "aspeed,ast2400-wdt" , .data = & ast2400_config },
41
57
{ .compatible = "aspeed,ast2500-wdt" , .data = & ast2500_config },
42
- { .compatible = "aspeed,ast2600-wdt" , .data = & ast2500_config },
58
+ { .compatible = "aspeed,ast2600-wdt" , .data = & ast2600_config },
43
59
{ },
44
60
};
45
61
MODULE_DEVICE_TABLE (of , aspeed_wdt_of_table );
@@ -58,6 +74,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
58
74
#define WDT_CTRL_RESET_SYSTEM BIT(1)
59
75
#define WDT_CTRL_ENABLE BIT(0)
60
76
#define WDT_TIMEOUT_STATUS 0x10
77
+ #define WDT_TIMEOUT_STATUS_IRQ BIT(2)
61
78
#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
62
79
#define WDT_CLEAR_TIMEOUT_STATUS 0x14
63
80
#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
@@ -160,6 +177,26 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
160
177
return 0 ;
161
178
}
162
179
180
+ static int aspeed_wdt_set_pretimeout (struct watchdog_device * wdd ,
181
+ unsigned int pretimeout )
182
+ {
183
+ struct aspeed_wdt * wdt = to_aspeed_wdt (wdd );
184
+ u32 actual = pretimeout * WDT_RATE_1MHZ ;
185
+ u32 s = wdt -> cfg -> irq_shift ;
186
+ u32 m = wdt -> cfg -> irq_mask ;
187
+
188
+ wdd -> pretimeout = pretimeout ;
189
+ wdt -> ctrl &= ~m ;
190
+ if (pretimeout )
191
+ wdt -> ctrl |= ((actual << s ) & m ) | WDT_CTRL_WDT_INTR ;
192
+ else
193
+ wdt -> ctrl &= ~WDT_CTRL_WDT_INTR ;
194
+
195
+ writel (wdt -> ctrl , wdt -> base + WDT_CTRL );
196
+
197
+ return 0 ;
198
+ }
199
+
163
200
static int aspeed_wdt_restart (struct watchdog_device * wdd ,
164
201
unsigned long action , void * data )
165
202
{
@@ -232,6 +269,7 @@ static const struct watchdog_ops aspeed_wdt_ops = {
232
269
.stop = aspeed_wdt_stop ,
233
270
.ping = aspeed_wdt_ping ,
234
271
.set_timeout = aspeed_wdt_set_timeout ,
272
+ .set_pretimeout = aspeed_wdt_set_pretimeout ,
235
273
.restart = aspeed_wdt_restart ,
236
274
.owner = THIS_MODULE ,
237
275
};
@@ -243,10 +281,29 @@ static const struct watchdog_info aspeed_wdt_info = {
243
281
.identity = KBUILD_MODNAME ,
244
282
};
245
283
284
+ static const struct watchdog_info aspeed_wdt_pretimeout_info = {
285
+ .options = WDIOF_KEEPALIVEPING
286
+ | WDIOF_PRETIMEOUT
287
+ | WDIOF_MAGICCLOSE
288
+ | WDIOF_SETTIMEOUT ,
289
+ .identity = KBUILD_MODNAME ,
290
+ };
291
+
292
+ static irqreturn_t aspeed_wdt_irq (int irq , void * arg )
293
+ {
294
+ struct watchdog_device * wdd = arg ;
295
+ struct aspeed_wdt * wdt = to_aspeed_wdt (wdd );
296
+ u32 status = readl (wdt -> base + WDT_TIMEOUT_STATUS );
297
+
298
+ if (status & WDT_TIMEOUT_STATUS_IRQ )
299
+ watchdog_notify_pretimeout (wdd );
300
+
301
+ return IRQ_HANDLED ;
302
+ }
303
+
246
304
static int aspeed_wdt_probe (struct platform_device * pdev )
247
305
{
248
306
struct device * dev = & pdev -> dev ;
249
- const struct aspeed_wdt_config * config ;
250
307
const struct of_device_id * ofdid ;
251
308
struct aspeed_wdt * wdt ;
252
309
struct device_node * np ;
@@ -259,11 +316,33 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
259
316
if (!wdt )
260
317
return - ENOMEM ;
261
318
319
+ np = dev -> of_node ;
320
+
321
+ ofdid = of_match_node (aspeed_wdt_of_table , np );
322
+ if (!ofdid )
323
+ return - EINVAL ;
324
+ wdt -> cfg = ofdid -> data ;
325
+
262
326
wdt -> base = devm_platform_ioremap_resource (pdev , 0 );
263
327
if (IS_ERR (wdt -> base ))
264
328
return PTR_ERR (wdt -> base );
265
329
266
330
wdt -> wdd .info = & aspeed_wdt_info ;
331
+
332
+ if (wdt -> cfg -> irq_mask ) {
333
+ int irq = platform_get_irq_optional (pdev , 0 );
334
+
335
+ if (irq > 0 ) {
336
+ ret = devm_request_irq (dev , irq , aspeed_wdt_irq ,
337
+ IRQF_SHARED , dev_name (dev ),
338
+ wdt );
339
+ if (ret )
340
+ return ret ;
341
+
342
+ wdt -> wdd .info = & aspeed_wdt_pretimeout_info ;
343
+ }
344
+ }
345
+
267
346
wdt -> wdd .ops = & aspeed_wdt_ops ;
268
347
wdt -> wdd .max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS ;
269
348
wdt -> wdd .parent = dev ;
@@ -273,13 +352,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
273
352
274
353
watchdog_set_nowayout (& wdt -> wdd , nowayout );
275
354
276
- np = dev -> of_node ;
277
-
278
- ofdid = of_match_node (aspeed_wdt_of_table , np );
279
- if (!ofdid )
280
- return - EINVAL ;
281
- config = ofdid -> data ;
282
-
283
355
/*
284
356
* On clock rates:
285
357
* - ast2400 wdt can run at PCLK, or 1MHz
@@ -331,15 +403,15 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
331
403
(of_device_is_compatible (np , "aspeed,ast2600-wdt" ))) {
332
404
u32 reg = readl (wdt -> base + WDT_RESET_WIDTH );
333
405
334
- reg &= config -> ext_pulse_width_mask ;
406
+ reg &= wdt -> cfg -> ext_pulse_width_mask ;
335
407
if (of_property_read_bool (np , "aspeed,ext-active-high" ))
336
408
reg |= WDT_ACTIVE_HIGH_MAGIC ;
337
409
else
338
410
reg |= WDT_ACTIVE_LOW_MAGIC ;
339
411
340
412
writel (reg , wdt -> base + WDT_RESET_WIDTH );
341
413
342
- reg &= config -> ext_pulse_width_mask ;
414
+ reg &= wdt -> cfg -> ext_pulse_width_mask ;
343
415
if (of_property_read_bool (np , "aspeed,ext-push-pull" ))
344
416
reg |= WDT_PUSH_PULL_MAGIC ;
345
417
else
@@ -349,7 +421,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
349
421
}
350
422
351
423
if (!of_property_read_u32 (np , "aspeed,ext-pulse-duration" , & duration )) {
352
- u32 max_duration = config -> ext_pulse_width_mask + 1 ;
424
+ u32 max_duration = wdt -> cfg -> ext_pulse_width_mask + 1 ;
353
425
354
426
if (duration == 0 || duration > max_duration ) {
355
427
dev_err (dev , "Invalid pulse duration: %uus\n" ,
0 commit comments