1 // SPDX-License-Identifier: GPL-2.0+
5 * Copyright (C) 2004, 2005 Nokia Corporation
7 * Based on code written by Amit Kucheria and Michael Buesch.
8 * Rewritten by Aaro Koskinen.
11 #include <linux/slab.h>
12 #include <linux/errno.h>
13 #include <linux/device.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/mfd/retu.h>
17 #include <linux/watchdog.h>
18 #include <linux/platform_device.h>
20 /* Watchdog timer values in seconds */
21 #define RETU_WDT_MAX_TIMER 63
24 struct retu_dev
*rdev
;
26 struct delayed_work ping_work
;
30 * Since Retu watchdog cannot be disabled in hardware, we must kick it
31 * with a timer until userspace watchdog software takes over. If
32 * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
34 static void retu_wdt_ping_enable(struct retu_wdt_dev
*wdev
)
36 retu_write(wdev
->rdev
, RETU_REG_WATCHDOG
, RETU_WDT_MAX_TIMER
);
37 schedule_delayed_work(&wdev
->ping_work
,
38 round_jiffies_relative(RETU_WDT_MAX_TIMER
* HZ
/ 2));
41 static void retu_wdt_ping_disable(struct retu_wdt_dev
*wdev
)
43 retu_write(wdev
->rdev
, RETU_REG_WATCHDOG
, RETU_WDT_MAX_TIMER
);
44 cancel_delayed_work_sync(&wdev
->ping_work
);
47 static void retu_wdt_ping_work(struct work_struct
*work
)
49 struct retu_wdt_dev
*wdev
= container_of(to_delayed_work(work
),
50 struct retu_wdt_dev
, ping_work
);
51 retu_wdt_ping_enable(wdev
);
54 static int retu_wdt_start(struct watchdog_device
*wdog
)
56 struct retu_wdt_dev
*wdev
= watchdog_get_drvdata(wdog
);
58 retu_wdt_ping_disable(wdev
);
60 return retu_write(wdev
->rdev
, RETU_REG_WATCHDOG
, wdog
->timeout
);
63 static int retu_wdt_stop(struct watchdog_device
*wdog
)
65 struct retu_wdt_dev
*wdev
= watchdog_get_drvdata(wdog
);
67 retu_wdt_ping_enable(wdev
);
72 static int retu_wdt_ping(struct watchdog_device
*wdog
)
74 struct retu_wdt_dev
*wdev
= watchdog_get_drvdata(wdog
);
76 return retu_write(wdev
->rdev
, RETU_REG_WATCHDOG
, wdog
->timeout
);
79 static int retu_wdt_set_timeout(struct watchdog_device
*wdog
,
82 struct retu_wdt_dev
*wdev
= watchdog_get_drvdata(wdog
);
84 wdog
->timeout
= timeout
;
85 return retu_write(wdev
->rdev
, RETU_REG_WATCHDOG
, wdog
->timeout
);
88 static const struct watchdog_info retu_wdt_info
= {
89 .options
= WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
,
90 .identity
= "Retu watchdog",
93 static const struct watchdog_ops retu_wdt_ops
= {
95 .start
= retu_wdt_start
,
96 .stop
= retu_wdt_stop
,
97 .ping
= retu_wdt_ping
,
98 .set_timeout
= retu_wdt_set_timeout
,
101 static int retu_wdt_probe(struct platform_device
*pdev
)
103 struct retu_dev
*rdev
= dev_get_drvdata(pdev
->dev
.parent
);
104 bool nowayout
= WATCHDOG_NOWAYOUT
;
105 struct watchdog_device
*retu_wdt
;
106 struct retu_wdt_dev
*wdev
;
109 retu_wdt
= devm_kzalloc(&pdev
->dev
, sizeof(*retu_wdt
), GFP_KERNEL
);
113 wdev
= devm_kzalloc(&pdev
->dev
, sizeof(*wdev
), GFP_KERNEL
);
117 retu_wdt
->info
= &retu_wdt_info
;
118 retu_wdt
->ops
= &retu_wdt_ops
;
119 retu_wdt
->timeout
= RETU_WDT_MAX_TIMER
;
120 retu_wdt
->min_timeout
= 0;
121 retu_wdt
->max_timeout
= RETU_WDT_MAX_TIMER
;
122 retu_wdt
->parent
= &pdev
->dev
;
124 watchdog_set_drvdata(retu_wdt
, wdev
);
125 watchdog_set_nowayout(retu_wdt
, nowayout
);
128 wdev
->dev
= &pdev
->dev
;
130 INIT_DELAYED_WORK(&wdev
->ping_work
, retu_wdt_ping_work
);
132 ret
= watchdog_register_device(retu_wdt
);
137 retu_wdt_ping(retu_wdt
);
139 retu_wdt_ping_enable(wdev
);
141 platform_set_drvdata(pdev
, retu_wdt
);
146 static int retu_wdt_remove(struct platform_device
*pdev
)
148 struct watchdog_device
*wdog
= platform_get_drvdata(pdev
);
149 struct retu_wdt_dev
*wdev
= watchdog_get_drvdata(wdog
);
151 watchdog_unregister_device(wdog
);
152 cancel_delayed_work_sync(&wdev
->ping_work
);
157 static struct platform_driver retu_wdt_driver
= {
158 .probe
= retu_wdt_probe
,
159 .remove
= retu_wdt_remove
,
164 module_platform_driver(retu_wdt_driver
);
166 MODULE_ALIAS("platform:retu-wdt");
167 MODULE_DESCRIPTION("Retu watchdog");
168 MODULE_AUTHOR("Amit Kucheria");
169 MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
170 MODULE_LICENSE("GPL");