55 * Joel Stanley <joel@jms.id.au>
66 */
77
8+ #include <linux/bits.h>
89#include <linux/delay.h>
10+ #include <linux/interrupt.h>
911#include <linux/io.h>
1012#include <linux/kernel.h>
1113#include <linux/module.h>
1214#include <linux/of.h>
15+ #include <linux/of_irq.h>
1316#include <linux/platform_device.h>
1417#include <linux/watchdog.h>
1518
@@ -18,28 +21,41 @@ module_param(nowayout, bool, 0);
1821MODULE_PARM_DESC (nowayout , "Watchdog cannot be stopped once started (default="
1922 __MODULE_STRING (WATCHDOG_NOWAYOUT ) ")" );
2023
24+ struct aspeed_wdt_config {
25+ u32 ext_pulse_width_mask ;
26+ u32 irq_shift ;
27+ u32 irq_mask ;
28+ };
29+
2130struct aspeed_wdt {
2231 struct watchdog_device wdd ;
2332 void __iomem * base ;
2433 u32 ctrl ;
25- };
26-
27- struct aspeed_wdt_config {
28- u32 ext_pulse_width_mask ;
34+ const struct aspeed_wdt_config * cfg ;
2935};
3036
3137static const struct aspeed_wdt_config ast2400_config = {
3238 .ext_pulse_width_mask = 0xff ,
39+ .irq_shift = 0 ,
40+ .irq_mask = 0 ,
3341};
3442
3543static const struct aspeed_wdt_config ast2500_config = {
3644 .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 ),
3753};
3854
3955static const struct of_device_id aspeed_wdt_of_table [] = {
4056 { .compatible = "aspeed,ast2400-wdt" , .data = & ast2400_config },
4157 { .compatible = "aspeed,ast2500-wdt" , .data = & ast2500_config },
42- { .compatible = "aspeed,ast2600-wdt" , .data = & ast2500_config },
58+ { .compatible = "aspeed,ast2600-wdt" , .data = & ast2600_config },
4359 { },
4460};
4561MODULE_DEVICE_TABLE (of , aspeed_wdt_of_table );
@@ -58,6 +74,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
5874#define WDT_CTRL_RESET_SYSTEM BIT(1)
5975#define WDT_CTRL_ENABLE BIT(0)
6076#define WDT_TIMEOUT_STATUS 0x10
77+ #define WDT_TIMEOUT_STATUS_IRQ BIT(2)
6178#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
6279#define WDT_CLEAR_TIMEOUT_STATUS 0x14
6380#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
@@ -160,6 +177,26 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
160177 return 0 ;
161178}
162179
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+
163200static int aspeed_wdt_restart (struct watchdog_device * wdd ,
164201 unsigned long action , void * data )
165202{
@@ -232,6 +269,7 @@ static const struct watchdog_ops aspeed_wdt_ops = {
232269 .stop = aspeed_wdt_stop ,
233270 .ping = aspeed_wdt_ping ,
234271 .set_timeout = aspeed_wdt_set_timeout ,
272+ .set_pretimeout = aspeed_wdt_set_pretimeout ,
235273 .restart = aspeed_wdt_restart ,
236274 .owner = THIS_MODULE ,
237275};
@@ -243,10 +281,29 @@ static const struct watchdog_info aspeed_wdt_info = {
243281 .identity = KBUILD_MODNAME ,
244282};
245283
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+
246304static int aspeed_wdt_probe (struct platform_device * pdev )
247305{
248306 struct device * dev = & pdev -> dev ;
249- const struct aspeed_wdt_config * config ;
250307 const struct of_device_id * ofdid ;
251308 struct aspeed_wdt * wdt ;
252309 struct device_node * np ;
@@ -259,11 +316,33 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
259316 if (!wdt )
260317 return - ENOMEM ;
261318
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+
262326 wdt -> base = devm_platform_ioremap_resource (pdev , 0 );
263327 if (IS_ERR (wdt -> base ))
264328 return PTR_ERR (wdt -> base );
265329
266330 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+
267346 wdt -> wdd .ops = & aspeed_wdt_ops ;
268347 wdt -> wdd .max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS ;
269348 wdt -> wdd .parent = dev ;
@@ -273,13 +352,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
273352
274353 watchdog_set_nowayout (& wdt -> wdd , nowayout );
275354
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-
283355 /*
284356 * On clock rates:
285357 * - ast2400 wdt can run at PCLK, or 1MHz
@@ -331,15 +403,15 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
331403 (of_device_is_compatible (np , "aspeed,ast2600-wdt" ))) {
332404 u32 reg = readl (wdt -> base + WDT_RESET_WIDTH );
333405
334- reg &= config -> ext_pulse_width_mask ;
406+ reg &= wdt -> cfg -> ext_pulse_width_mask ;
335407 if (of_property_read_bool (np , "aspeed,ext-active-high" ))
336408 reg |= WDT_ACTIVE_HIGH_MAGIC ;
337409 else
338410 reg |= WDT_ACTIVE_LOW_MAGIC ;
339411
340412 writel (reg , wdt -> base + WDT_RESET_WIDTH );
341413
342- reg &= config -> ext_pulse_width_mask ;
414+ reg &= wdt -> cfg -> ext_pulse_width_mask ;
343415 if (of_property_read_bool (np , "aspeed,ext-push-pull" ))
344416 reg |= WDT_PUSH_PULL_MAGIC ;
345417 else
@@ -349,7 +421,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
349421 }
350422
351423 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 ;
353425
354426 if (duration == 0 || duration > max_duration ) {
355427 dev_err (dev , "Invalid pulse duration: %uus\n" ,
0 commit comments