xref: /openbmc/linux/drivers/watchdog/retu_wdt.c (revision 3d3a6d18abc66ba38e554fd5cb5991dfa805cd23)
1*3d3a6d18SAaro Koskinen /*
2*3d3a6d18SAaro Koskinen  * Retu watchdog driver
3*3d3a6d18SAaro Koskinen  *
4*3d3a6d18SAaro Koskinen  * Copyright (C) 2004, 2005 Nokia Corporation
5*3d3a6d18SAaro Koskinen  *
6*3d3a6d18SAaro Koskinen  * Based on code written by Amit Kucheria and Michael Buesch.
7*3d3a6d18SAaro Koskinen  * Rewritten by Aaro Koskinen.
8*3d3a6d18SAaro Koskinen  *
9*3d3a6d18SAaro Koskinen  * This file is subject to the terms and conditions of the GNU General
10*3d3a6d18SAaro Koskinen  * Public License. See the file "COPYING" in the main directory of this
11*3d3a6d18SAaro Koskinen  * archive for more details.
12*3d3a6d18SAaro Koskinen  *
13*3d3a6d18SAaro Koskinen  * This program is distributed in the hope that it will be useful,
14*3d3a6d18SAaro Koskinen  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15*3d3a6d18SAaro Koskinen  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16*3d3a6d18SAaro Koskinen  * GNU General Public License for more details.
17*3d3a6d18SAaro Koskinen  */
18*3d3a6d18SAaro Koskinen 
19*3d3a6d18SAaro Koskinen #include <linux/init.h>
20*3d3a6d18SAaro Koskinen #include <linux/slab.h>
21*3d3a6d18SAaro Koskinen #include <linux/errno.h>
22*3d3a6d18SAaro Koskinen #include <linux/device.h>
23*3d3a6d18SAaro Koskinen #include <linux/kernel.h>
24*3d3a6d18SAaro Koskinen #include <linux/module.h>
25*3d3a6d18SAaro Koskinen #include <linux/mfd/retu.h>
26*3d3a6d18SAaro Koskinen #include <linux/watchdog.h>
27*3d3a6d18SAaro Koskinen #include <linux/platform_device.h>
28*3d3a6d18SAaro Koskinen 
29*3d3a6d18SAaro Koskinen /* Watchdog timer values in seconds */
30*3d3a6d18SAaro Koskinen #define RETU_WDT_MAX_TIMER	63
31*3d3a6d18SAaro Koskinen 
32*3d3a6d18SAaro Koskinen struct retu_wdt_dev {
33*3d3a6d18SAaro Koskinen 	struct retu_dev		*rdev;
34*3d3a6d18SAaro Koskinen 	struct device		*dev;
35*3d3a6d18SAaro Koskinen 	struct delayed_work	ping_work;
36*3d3a6d18SAaro Koskinen };
37*3d3a6d18SAaro Koskinen 
38*3d3a6d18SAaro Koskinen /*
39*3d3a6d18SAaro Koskinen  * Since Retu watchdog cannot be disabled in hardware, we must kick it
40*3d3a6d18SAaro Koskinen  * with a timer until userspace watchdog software takes over. If
41*3d3a6d18SAaro Koskinen  * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
42*3d3a6d18SAaro Koskinen  */
43*3d3a6d18SAaro Koskinen static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
44*3d3a6d18SAaro Koskinen {
45*3d3a6d18SAaro Koskinen 	retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
46*3d3a6d18SAaro Koskinen 	schedule_delayed_work(&wdev->ping_work,
47*3d3a6d18SAaro Koskinen 			round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
48*3d3a6d18SAaro Koskinen }
49*3d3a6d18SAaro Koskinen 
50*3d3a6d18SAaro Koskinen static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
51*3d3a6d18SAaro Koskinen {
52*3d3a6d18SAaro Koskinen 	retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
53*3d3a6d18SAaro Koskinen 	cancel_delayed_work_sync(&wdev->ping_work);
54*3d3a6d18SAaro Koskinen }
55*3d3a6d18SAaro Koskinen 
56*3d3a6d18SAaro Koskinen static void retu_wdt_ping_work(struct work_struct *work)
57*3d3a6d18SAaro Koskinen {
58*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
59*3d3a6d18SAaro Koskinen 						struct retu_wdt_dev, ping_work);
60*3d3a6d18SAaro Koskinen 	retu_wdt_ping_enable(wdev);
61*3d3a6d18SAaro Koskinen }
62*3d3a6d18SAaro Koskinen 
63*3d3a6d18SAaro Koskinen static int retu_wdt_start(struct watchdog_device *wdog)
64*3d3a6d18SAaro Koskinen {
65*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
66*3d3a6d18SAaro Koskinen 
67*3d3a6d18SAaro Koskinen 	retu_wdt_ping_disable(wdev);
68*3d3a6d18SAaro Koskinen 
69*3d3a6d18SAaro Koskinen 	return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
70*3d3a6d18SAaro Koskinen }
71*3d3a6d18SAaro Koskinen 
72*3d3a6d18SAaro Koskinen static int retu_wdt_stop(struct watchdog_device *wdog)
73*3d3a6d18SAaro Koskinen {
74*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
75*3d3a6d18SAaro Koskinen 
76*3d3a6d18SAaro Koskinen 	retu_wdt_ping_enable(wdev);
77*3d3a6d18SAaro Koskinen 
78*3d3a6d18SAaro Koskinen 	return 0;
79*3d3a6d18SAaro Koskinen }
80*3d3a6d18SAaro Koskinen 
81*3d3a6d18SAaro Koskinen static int retu_wdt_ping(struct watchdog_device *wdog)
82*3d3a6d18SAaro Koskinen {
83*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
84*3d3a6d18SAaro Koskinen 
85*3d3a6d18SAaro Koskinen 	return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
86*3d3a6d18SAaro Koskinen }
87*3d3a6d18SAaro Koskinen 
88*3d3a6d18SAaro Koskinen static int retu_wdt_set_timeout(struct watchdog_device *wdog,
89*3d3a6d18SAaro Koskinen 				unsigned int timeout)
90*3d3a6d18SAaro Koskinen {
91*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
92*3d3a6d18SAaro Koskinen 
93*3d3a6d18SAaro Koskinen 	wdog->timeout = timeout;
94*3d3a6d18SAaro Koskinen 	return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
95*3d3a6d18SAaro Koskinen }
96*3d3a6d18SAaro Koskinen 
97*3d3a6d18SAaro Koskinen static const struct watchdog_info retu_wdt_info = {
98*3d3a6d18SAaro Koskinen 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
99*3d3a6d18SAaro Koskinen 	.identity = "Retu watchdog",
100*3d3a6d18SAaro Koskinen };
101*3d3a6d18SAaro Koskinen 
102*3d3a6d18SAaro Koskinen static const struct watchdog_ops retu_wdt_ops = {
103*3d3a6d18SAaro Koskinen 	.owner		= THIS_MODULE,
104*3d3a6d18SAaro Koskinen 	.start		= retu_wdt_start,
105*3d3a6d18SAaro Koskinen 	.stop		= retu_wdt_stop,
106*3d3a6d18SAaro Koskinen 	.ping		= retu_wdt_ping,
107*3d3a6d18SAaro Koskinen 	.set_timeout	= retu_wdt_set_timeout,
108*3d3a6d18SAaro Koskinen };
109*3d3a6d18SAaro Koskinen 
110*3d3a6d18SAaro Koskinen static int retu_wdt_probe(struct platform_device *pdev)
111*3d3a6d18SAaro Koskinen {
112*3d3a6d18SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
113*3d3a6d18SAaro Koskinen 	bool nowayout = WATCHDOG_NOWAYOUT;
114*3d3a6d18SAaro Koskinen 	struct watchdog_device *retu_wdt;
115*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev;
116*3d3a6d18SAaro Koskinen 	int ret;
117*3d3a6d18SAaro Koskinen 
118*3d3a6d18SAaro Koskinen 	retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
119*3d3a6d18SAaro Koskinen 	if (!retu_wdt)
120*3d3a6d18SAaro Koskinen 		return -ENOMEM;
121*3d3a6d18SAaro Koskinen 
122*3d3a6d18SAaro Koskinen 	wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
123*3d3a6d18SAaro Koskinen 	if (!wdev)
124*3d3a6d18SAaro Koskinen 		return -ENOMEM;
125*3d3a6d18SAaro Koskinen 
126*3d3a6d18SAaro Koskinen 	retu_wdt->info		= &retu_wdt_info;
127*3d3a6d18SAaro Koskinen 	retu_wdt->ops		= &retu_wdt_ops;
128*3d3a6d18SAaro Koskinen 	retu_wdt->timeout	= RETU_WDT_MAX_TIMER;
129*3d3a6d18SAaro Koskinen 	retu_wdt->min_timeout	= 0;
130*3d3a6d18SAaro Koskinen 	retu_wdt->max_timeout	= RETU_WDT_MAX_TIMER;
131*3d3a6d18SAaro Koskinen 
132*3d3a6d18SAaro Koskinen 	watchdog_set_drvdata(retu_wdt, wdev);
133*3d3a6d18SAaro Koskinen 	watchdog_set_nowayout(retu_wdt, nowayout);
134*3d3a6d18SAaro Koskinen 
135*3d3a6d18SAaro Koskinen 	wdev->rdev		= rdev;
136*3d3a6d18SAaro Koskinen 	wdev->dev		= &pdev->dev;
137*3d3a6d18SAaro Koskinen 
138*3d3a6d18SAaro Koskinen 	INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
139*3d3a6d18SAaro Koskinen 
140*3d3a6d18SAaro Koskinen 	ret = watchdog_register_device(retu_wdt);
141*3d3a6d18SAaro Koskinen 	if (ret < 0)
142*3d3a6d18SAaro Koskinen 		return ret;
143*3d3a6d18SAaro Koskinen 
144*3d3a6d18SAaro Koskinen 	if (nowayout)
145*3d3a6d18SAaro Koskinen 		retu_wdt_ping(retu_wdt);
146*3d3a6d18SAaro Koskinen 	else
147*3d3a6d18SAaro Koskinen 		retu_wdt_ping_enable(wdev);
148*3d3a6d18SAaro Koskinen 
149*3d3a6d18SAaro Koskinen 	platform_set_drvdata(pdev, retu_wdt);
150*3d3a6d18SAaro Koskinen 
151*3d3a6d18SAaro Koskinen 	return 0;
152*3d3a6d18SAaro Koskinen }
153*3d3a6d18SAaro Koskinen 
154*3d3a6d18SAaro Koskinen static int retu_wdt_remove(struct platform_device *pdev)
155*3d3a6d18SAaro Koskinen {
156*3d3a6d18SAaro Koskinen 	struct watchdog_device *wdog = platform_get_drvdata(pdev);
157*3d3a6d18SAaro Koskinen 	struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
158*3d3a6d18SAaro Koskinen 
159*3d3a6d18SAaro Koskinen 	watchdog_unregister_device(wdog);
160*3d3a6d18SAaro Koskinen 	cancel_delayed_work_sync(&wdev->ping_work);
161*3d3a6d18SAaro Koskinen 
162*3d3a6d18SAaro Koskinen 	return 0;
163*3d3a6d18SAaro Koskinen }
164*3d3a6d18SAaro Koskinen 
165*3d3a6d18SAaro Koskinen static struct platform_driver retu_wdt_driver = {
166*3d3a6d18SAaro Koskinen 	.probe		= retu_wdt_probe,
167*3d3a6d18SAaro Koskinen 	.remove		= retu_wdt_remove,
168*3d3a6d18SAaro Koskinen 	.driver		= {
169*3d3a6d18SAaro Koskinen 		.name	= "retu-wdt",
170*3d3a6d18SAaro Koskinen 	},
171*3d3a6d18SAaro Koskinen };
172*3d3a6d18SAaro Koskinen module_platform_driver(retu_wdt_driver);
173*3d3a6d18SAaro Koskinen 
174*3d3a6d18SAaro Koskinen MODULE_ALIAS("platform:retu-wdt");
175*3d3a6d18SAaro Koskinen MODULE_DESCRIPTION("Retu watchdog");
176*3d3a6d18SAaro Koskinen MODULE_AUTHOR("Amit Kucheria");
177*3d3a6d18SAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
178*3d3a6d18SAaro Koskinen MODULE_LICENSE("GPL");
179