blob: 18d17c6952a2bcd947114c36c767b7640f3599c5 [file] [log] [blame]
yuezonghe824eb0c2024-06-27 02:32:26 -07001/*
2 * drivers/watchdog/zx29_wdt.c
3 *
4 * Copyright (C) 2015 ZTE-TSP
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/module.h>
18#include <linux/moduleparam.h>
19#include <linux/types.h>
20#include <linux/timer.h>
21#include <linux/miscdevice.h>
22#include <linux/watchdog.h>
23#include <linux/init.h>
24#include <linux/platform_device.h>
25#include <linux/interrupt.h>
26#include <linux/clk.h>
27#include <linux/uaccess.h>
28#include <linux/io.h>
29#include <linux/cpufreq.h>
30#include <linux/slab.h>
31#include <linux/err.h>
32
33#include <mach/board.h>
34#include "zx29_wdt.h"
35
36/*****************************************************************************
37 ** | --> | --> |
38 ** 15s(value load) 5s(feed in irq) 0s(reset if no feed)
39 *****************************************************************************
40 **/
41
42#define CONFIG_ZX29_WATCHDOG_ATBOOT (0)
43#define CONFIG_ZX29_WATCHDOG_DEFAULT_TIME (15)
44#define CONFIG_ZX29_WATCHDOG_DEADLINE_TIME (5)
45
46static bool nowayout = WATCHDOG_NOWAYOUT; /* 1-disagree to close wdt 0-agree to close wdt */
47static int tmr_margin = CONFIG_ZX29_WATCHDOG_DEFAULT_TIME; /* max during to feed dog, unit is second */
48static int tmr_atboot = CONFIG_ZX29_WATCHDOG_ATBOOT; /* 0 - no enable wdt when boot */
49static int debug;
50
51module_param(tmr_margin, int, 0);
52module_param(tmr_atboot, int, 0);
53module_param(nowayout, bool, 0);
54module_param(debug, int, 0);
55
56MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
57 __MODULE_STRING(CONFIG_ZX29_WATCHDOG_DEFAULT_TIME) ")");
58MODULE_PARM_DESC(tmr_atboot,
59 "Watchdog is started at boot time if set to 1, default="
60 __MODULE_STRING(CONFIG_ZX29_WATCHDOG_ATBOOT));
61MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
62 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
63MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
64
65static struct device *wdt_dev; /* platform device attached to */
66static struct clk *wdt_clock;
67static struct clk *wdt_apb_clock;
68static void __iomem *wdt_base;
69static unsigned int wdt_count;
70static DEFINE_SPINLOCK(wdt_lock);
71static struct resource *wdt_irq;
72
73/* watchdog control routines */
74
75#define DBG(fmt, ...) \
76do { \
77 if (debug) \
78 pr_info(fmt, ##__VA_ARGS__); \
79} while (0)
80
81/* functions */
82static unsigned int feed_count = 0;
83static int zx29_wdt_keepalive(struct watchdog_device *wdd)
84{
85 spin_lock(&wdt_lock);
86 __wdt_set_load(wdt_base, wdt_count);
87 feed_count ++;
88 spin_unlock(&wdt_lock);
89
90 return 0;
91}
92
93static int zx29_wdt_stop(struct watchdog_device *wdd)
94{
95 spin_lock(&wdt_lock);
96 __wdt_stop(wdt_base);
97 spin_unlock(&wdt_lock);
98
99 return 0;
100}
101
102static int zx29_wdt_start(struct watchdog_device *wdd)
103{
104 spin_lock(&wdt_lock);
105 __wdt_start(wdt_base);
106 spin_unlock(&wdt_lock);
107
108 return 0;
109}
110
111/*
112 * timeout -- unit(s)
113 *
114 */
115static int zx29_wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
116{
117 wdt_count = timeout*WDT_TIME_1S;
118 zx29_wdt_keepalive(wdd);
119
120 spin_lock(&wdt_lock);
121 __wdt_set_int_value(wdt_base, CONFIG_ZX29_WATCHDOG_DEADLINE_TIME*WDT_TIME_1S);
122 spin_unlock(&wdt_lock);
123
124 return 0;
125}
126
127#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
128
129static const struct watchdog_info zx29_wdt_ident = {
130 .options = OPTIONS,
131 .firmware_version = 0,
132 .identity = "ZX29 Watchdog",
133};
134
135static struct watchdog_ops zx29_wdt_ops = {
136 .owner = THIS_MODULE,
137 .start = zx29_wdt_start,
138 .stop = zx29_wdt_stop,
139 .ping = zx29_wdt_keepalive,
140 .set_timeout = zx29_wdt_set_heartbeat,
141};
142
143static struct watchdog_device zx29_wdd = {
144 .info = &zx29_wdt_ident,
145 .ops = &zx29_wdt_ops,
146};
147
148/* interrupt handler code */
149static irqreturn_t zx29_wdt_irq(int irqno, void *param)
150{
151 dev_info(wdt_dev, "watchdog timer expired (irq)\n");
152
153 zx29_wdt_keepalive(&zx29_wdd);
154 return IRQ_HANDLED;
155}
156
157static int __devinit wdt_init_clk(struct platform_device *pdev)
158{
159 int ret;
160
161 wdt_apb_clock = clk_get(&pdev->dev, "apb_clk");
162 if (IS_ERR(wdt_apb_clock)) {
163 dev_err(&pdev->dev, "failed to find watchdog apb clock source\n");
164 ret = PTR_ERR(wdt_apb_clock);
165 return ret;
166 }
167 clk_enable(wdt_apb_clock);
168
169 wdt_clock = clk_get(&pdev->dev, "work_clk");
170 if (IS_ERR(wdt_clock)) {
171 dev_err(&pdev->dev, "failed to find watchdog clock source\n");
172 ret = PTR_ERR(wdt_clock);
173 goto err;
174 }
175 clk_enable(wdt_clock);
176 clk_set_rate(wdt_clock, WDT_SOURCE_CLOCK_RATE);
177
178 /* 32768/32 = 1kHz */
179 __wdt_set_prescale(wdt_base, 31);
180
181 return 0;
182
183err:
184 clk_disable(wdt_apb_clock);
185 clk_put(wdt_apb_clock);
186 wdt_apb_clock = NULL;
187
188 return ret;
189}
190
191static int __devinit zx29_wdt_probe(struct platform_device *pdev)
192{
193 struct device *dev;
194 int started = 0;
195 int ret;
196 struct resource *wdt_mem;
197
198 DBG("[WDT]%s: probe=%p\n", __func__, pdev);
199
200 dev = &pdev->dev;
201 wdt_dev = &pdev->dev;
202
203 wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
204 if (wdt_mem == NULL) {
205 dev_err(dev, "no memory resource specified\n");
206 return -ENOENT;
207 }
208 wdt_base = (void __iomem *)wdt_mem->start;
209 DBG("[WDT]probe: wdt_base=%p\n", wdt_base);
210
211 wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
212 if (wdt_irq == NULL) {
213 dev_err(dev, "no irq resource specified\n");
214 return -ENOENT;
215 }
216
217 ret = wdt_init_clk(pdev);
218 if(ret)
219 return ret;
220
221 started = zx29_wdt_set_heartbeat(&zx29_wdd, tmr_margin);
222 if (started) {
223 dev_info(dev,
224 "tmr_margin value out of range, ( %d ) used\n",
225 tmr_margin);
226 }
227
228 ret = request_irq(wdt_irq->start, zx29_wdt_irq, 0, pdev->name, pdev);
229 if (ret != 0) {
230 dev_err(dev, "failed to install irq (%d)\n", ret);
231 goto err_clk;
232 }
233
234 watchdog_set_nowayout(&zx29_wdd, nowayout);
235
236 ret = watchdog_register_device(&zx29_wdd);
237 if (ret) {
238 dev_err(dev, "cannot register watchdog (%d)\n", ret);
239 goto err_irq;
240 }
241
242 if (tmr_atboot && started == 0) {
243 dev_info(dev, "starting watchdog timer\n");
244 zx29_wdt_start(&zx29_wdd);
245 __wdt_enable_reset();
246 } else if (!tmr_atboot) {
247 zx29_wdt_stop(&zx29_wdd);
248 }
249
250 pr_info("[WDT]Watchdog Timer init OK.\n");
251
252 return 0;
253
254 err_irq:
255 free_irq(wdt_irq->start, pdev);
256
257 err_clk:
258 clk_disable(wdt_clock);
259 clk_put(wdt_clock);
260 wdt_clock = NULL;
261
262 return ret;
263}
264
265static int __devexit zx29_wdt_remove(struct platform_device *dev)
266{
267 watchdog_unregister_device(&zx29_wdd);
268
269 free_irq(wdt_irq->start, dev);
270
271 clk_disable(wdt_clock);
272 clk_put(wdt_clock);
273 wdt_clock = NULL;
274
275 clk_disable(wdt_apb_clock);
276 clk_put(wdt_apb_clock);
277 wdt_apb_clock = NULL;
278
279 return 0;
280}
281
282static void zx29_wdt_shutdown(struct platform_device *dev)
283{
284 zx29_wdt_stop(&zx29_wdd);
285 __wdt_disable_reset();
286}
287
288#ifdef CONFIG_PM
289static zx29_wdt_context wdt_context;
290static int zx29_wdt_suspend(struct platform_device *dev, pm_message_t state)
291{
292 spin_lock(&wdt_lock);
293 __wdt_save_context(wdt_base, (u32 *)&wdt_context);
294 spin_unlock(&wdt_lock);
295
296 return 0;
297}
298
299static int zx29_wdt_resume(struct platform_device *dev)
300{
301 spin_lock(&wdt_lock);
302 __wdt_restore_context(wdt_base, (u32 *)&wdt_context);
303 spin_unlock(&wdt_lock);
304
305 return 0;
306}
307
308#else
309#define zx29_wdt_suspend NULL
310#define zx29_wdt_resume NULL
311#endif /* CONFIG_PM */
312
313#ifdef CONFIG_OF
314static const struct of_device_id zx29_wdt_match[] = {
315 { .compatible = "zte, zx29_ap_wdt" },
316 {},
317};
318MODULE_DEVICE_TABLE(of, zx29_wdt_match);
319#else
320#define zx29_wdt_match NULL
321#endif
322
323static struct platform_driver zx29_wdt_driver = {
324 .probe = zx29_wdt_probe,
325 .remove = __devexit_p(zx29_wdt_remove),
326 .shutdown = zx29_wdt_shutdown,
327 .suspend = zx29_wdt_suspend,
328 .resume = zx29_wdt_resume,
329 .driver = {
330 .owner = THIS_MODULE,
331 .name = "zx29_ap_wdt",
332 .of_match_table = zx29_wdt_match,
333 },
334};
335
336
337static int __init watchdog_init(void)
338{
339 return platform_driver_register(&zx29_wdt_driver);
340}
341
342static void __exit watchdog_exit(void)
343{
344 platform_driver_unregister(&zx29_wdt_driver);
345}
346
347module_init(watchdog_init);
348module_exit(watchdog_exit);
349