Blame view

drivers/watchdog/s3c2410_wdt.c 12.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /* linux/drivers/char/watchdog/s3c2410_wdt.c
   *
   * Copyright (c) 2004 Simtec Electronics
   *	Ben Dooks <ben@simtec.co.uk>
   *
   * S3C2410 Watchdog Timer Support
   *
   * Based on, softdog.c by Alan Cox,
29fa0586d   Alan Cox   [PATCH] Switch al...
9
   *     (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
  */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
  #include <linux/types.h>
  #include <linux/timer.h>
25dc46e38   Wolfram Sang   watchdog: s3c2410...
30
  #include <linux/miscdevice.h> /* for MODULE_ALIAS_MISCDEV */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  #include <linux/watchdog.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  #include <linux/init.h>
d052d1bef   Russell King   Create platform_d...
33
  #include <linux/platform_device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  #include <linux/interrupt.h>
f8ce25476   Russell King   [ARM] Move asm/ha...
35
  #include <linux/clk.h>
41dc8b72e   Alan Cox   s3c2410_wdt watch...
36
37
  #include <linux/uaccess.h>
  #include <linux/io.h>
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
38
  #include <linux/cpufreq.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
39
  #include <linux/slab.h>
25dc46e38   Wolfram Sang   watchdog: s3c2410...
40
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41

a09e64fbc   Russell King   [ARM] Move includ...
42
  #include <mach/map.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

b430708ad   Ben Dooks   [WATCHDOG] s3c241...
44
45
  #undef S3C_VA_WATCHDOG
  #define S3C_VA_WATCHDOG (0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

180ee700d   Ben Dooks   [ARM] S3C: Move r...
47
  #include <plat/regs-watchdog.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
  
  #define PFX "s3c2410-wdt: "
  
  #define CONFIG_S3C2410_WATCHDOG_ATBOOT		(0)
  #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME	(15)
25ff3780d   Ben Dooks   [WATCHDOG] s3c24X...
53
  static int nowayout	= WATCHDOG_NOWAYOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
  static int tmr_margin	= CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
  static int tmr_atboot	= CONFIG_S3C2410_WATCHDOG_ATBOOT;
41dc8b72e   Alan Cox   s3c2410_wdt watch...
56
57
  static int soft_noboot;
  static int debug;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
  
  module_param(tmr_margin,  int, 0);
  module_param(tmr_atboot,  int, 0);
  module_param(nowayout,    int, 0);
  module_param(soft_noboot, int, 0);
  module_param(debug,	  int, 0);
76550d329   Randy Dunlap   watchdog: fix sev...
64
  MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
41dc8b72e   Alan Cox   s3c2410_wdt watch...
65
66
67
68
69
70
  		__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
  MODULE_PARM_DESC(tmr_atboot,
  		"Watchdog is started at boot time if set to 1, default="
  			__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
71
  MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
76550d329   Randy Dunlap   watchdog: fix sev...
72
73
  			"0 to reboot (default 0)");
  MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74

e8ef92b8d   Ben Dooks   [WATCHDOG] change...
75
  static struct device    *wdt_dev;	/* platform device attached to */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
  static struct resource	*wdt_mem;
  static struct resource	*wdt_irq;
  static struct clk	*wdt_clock;
  static void __iomem	*wdt_base;
  static unsigned int	 wdt_count;
41dc8b72e   Alan Cox   s3c2410_wdt watch...
81
  static DEFINE_SPINLOCK(wdt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
  
  /* watchdog control routines */
  
  #define DBG(msg...) do { \
  	if (debug) \
  		printk(KERN_INFO msg); \
41dc8b72e   Alan Cox   s3c2410_wdt watch...
88
  	} while (0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
  
  /* functions */
25dc46e38   Wolfram Sang   watchdog: s3c2410...
91
  static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  {
41dc8b72e   Alan Cox   s3c2410_wdt watch...
93
  	spin_lock(&wdt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  	writel(wdt_count, wdt_base + S3C2410_WTCNT);
41dc8b72e   Alan Cox   s3c2410_wdt watch...
95
  	spin_unlock(&wdt_lock);
25dc46e38   Wolfram Sang   watchdog: s3c2410...
96
97
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  }
41dc8b72e   Alan Cox   s3c2410_wdt watch...
99
  static void __s3c2410wdt_stop(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
  {
  	unsigned long wtcon;
41dc8b72e   Alan Cox   s3c2410_wdt watch...
102
103
104
105
  	wtcon = readl(wdt_base + S3C2410_WTCON);
  	wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
  	writel(wtcon, wdt_base + S3C2410_WTCON);
  }
25dc46e38   Wolfram Sang   watchdog: s3c2410...
106
  static int s3c2410wdt_stop(struct watchdog_device *wdd)
41dc8b72e   Alan Cox   s3c2410_wdt watch...
107
108
109
110
  {
  	spin_lock(&wdt_lock);
  	__s3c2410wdt_stop();
  	spin_unlock(&wdt_lock);
25dc46e38   Wolfram Sang   watchdog: s3c2410...
111
112
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  }
25dc46e38   Wolfram Sang   watchdog: s3c2410...
114
  static int s3c2410wdt_start(struct watchdog_device *wdd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  {
  	unsigned long wtcon;
41dc8b72e   Alan Cox   s3c2410_wdt watch...
117
118
119
  	spin_lock(&wdt_lock);
  
  	__s3c2410wdt_stop();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  
  	wtcon = readl(wdt_base + S3C2410_WTCON);
  	wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
  
  	if (soft_noboot) {
  		wtcon |= S3C2410_WTCON_INTEN;
  		wtcon &= ~S3C2410_WTCON_RSTEN;
  	} else {
  		wtcon &= ~S3C2410_WTCON_INTEN;
  		wtcon |= S3C2410_WTCON_RSTEN;
  	}
  
  	DBG("%s: wdt_count=0x%08x, wtcon=%08lx
  ",
fa9363c5f   Harvey Harrison   [WATCHDOG] replac...
134
  	    __func__, wdt_count, wtcon);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
  
  	writel(wdt_count, wdt_base + S3C2410_WTDAT);
  	writel(wdt_count, wdt_base + S3C2410_WTCNT);
  	writel(wtcon, wdt_base + S3C2410_WTCON);
41dc8b72e   Alan Cox   s3c2410_wdt watch...
139
  	spin_unlock(&wdt_lock);
25dc46e38   Wolfram Sang   watchdog: s3c2410...
140
141
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  }
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
143
144
145
146
  static inline int s3c2410wdt_is_running(void)
  {
  	return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
  }
25dc46e38   Wolfram Sang   watchdog: s3c2410...
147
  static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
  {
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
149
  	unsigned long freq = clk_get_rate(wdt_clock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
154
155
156
157
158
  	unsigned int count;
  	unsigned int divisor = 1;
  	unsigned long wtcon;
  
  	if (timeout < 1)
  		return -EINVAL;
  
  	freq /= 128;
  	count = timeout * freq;
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
159
160
  	DBG("%s: count=%d, timeout=%d, freq=%lu
  ",
fa9363c5f   Harvey Harrison   [WATCHDOG] replac...
161
  	    __func__, count, timeout, freq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
168
169
170
171
172
173
174
  
  	/* if the count is bigger than the watchdog register,
  	   then work out what we need to do (and if) we can
  	   actually make this value
  	*/
  
  	if (count >= 0x10000) {
  		for (divisor = 1; divisor <= 0x100; divisor++) {
  			if ((count / divisor) < 0x10000)
  				break;
  		}
  
  		if ((count / divisor) >= 0x10000) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
175
176
  			dev_err(wdt_dev, "timeout %d too big
  ", timeout);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
  			return -EINVAL;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  	DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)
  ",
fa9363c5f   Harvey Harrison   [WATCHDOG] replac...
182
  	    __func__, timeout, divisor, count, count/divisor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  
  	count /= divisor;
  	wdt_count = count;
  
  	/* update the pre-scaler */
  	wtcon = readl(wdt_base + S3C2410_WTCON);
  	wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
  	wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
  
  	writel(count, wdt_base + S3C2410_WTDAT);
  	writel(wtcon, wdt_base + S3C2410_WTCON);
  
  	return 0;
  }
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
197
  #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198

41dc8b72e   Alan Cox   s3c2410_wdt watch...
199
  static const struct watchdog_info s3c2410_wdt_ident = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
  	.options          =     OPTIONS,
  	.firmware_version =	0,
  	.identity         =	"S3C2410 Watchdog",
  };
25dc46e38   Wolfram Sang   watchdog: s3c2410...
204
205
206
207
208
209
  static struct watchdog_ops s3c2410wdt_ops = {
  	.owner = THIS_MODULE,
  	.start = s3c2410wdt_start,
  	.stop = s3c2410wdt_stop,
  	.ping = s3c2410wdt_keepalive,
  	.set_timeout = s3c2410wdt_set_heartbeat,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  };
25dc46e38   Wolfram Sang   watchdog: s3c2410...
211
212
213
  static struct watchdog_device s3c2410_wdd = {
  	.info = &s3c2410_wdt_ident,
  	.ops = &s3c2410wdt_ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  /* interrupt handler code */
7d12e780e   David Howells   IRQ: Maintain reg...
216
  static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
218
219
  	dev_info(wdt_dev, "watchdog timer expired (irq)
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220

25dc46e38   Wolfram Sang   watchdog: s3c2410...
221
  	s3c2410wdt_keepalive(&s3c2410_wdd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
  	return IRQ_HANDLED;
  }
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  
  
  #ifdef CONFIG_CPU_FREQ
  
  static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
  					  unsigned long val, void *data)
  {
  	int ret;
  
  	if (!s3c2410wdt_is_running())
  		goto done;
  
  	if (val == CPUFREQ_PRECHANGE) {
  		/* To ensure that over the change we don't cause the
  		 * watchdog to trigger, we perform an keep-alive if
  		 * the watchdog is running.
  		 */
25dc46e38   Wolfram Sang   watchdog: s3c2410...
241
  		s3c2410wdt_keepalive(&s3c2410_wdd);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
242
  	} else if (val == CPUFREQ_POSTCHANGE) {
25dc46e38   Wolfram Sang   watchdog: s3c2410...
243
  		s3c2410wdt_stop(&s3c2410_wdd);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
244

25dc46e38   Wolfram Sang   watchdog: s3c2410...
245
  		ret = s3c2410wdt_set_heartbeat(&s3c2410_wdd, s3c2410_wdd.timeout);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
246
247
  
  		if (ret >= 0)
25dc46e38   Wolfram Sang   watchdog: s3c2410...
248
  			s3c2410wdt_start(&s3c2410_wdd);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
249
250
251
252
253
254
255
256
  		else
  			goto err;
  	}
  
  done:
  	return 0;
  
   err:
25dc46e38   Wolfram Sang   watchdog: s3c2410...
257
258
259
  	dev_err(wdt_dev, "cannot set new value for timeout %d
  ",
  				s3c2410_wdd.timeout);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  	return ret;
  }
  
  static struct notifier_block s3c2410wdt_cpufreq_transition_nb = {
  	.notifier_call	= s3c2410wdt_cpufreq_transition,
  };
  
  static inline int s3c2410wdt_cpufreq_register(void)
  {
  	return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb,
  					 CPUFREQ_TRANSITION_NOTIFIER);
  }
  
  static inline void s3c2410wdt_cpufreq_deregister(void)
  {
  	cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb,
  				    CPUFREQ_TRANSITION_NOTIFIER);
  }
  
  #else
  static inline int s3c2410wdt_cpufreq_register(void)
  {
  	return 0;
  }
  
  static inline void s3c2410wdt_cpufreq_deregister(void)
  {
  }
  #endif
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
289
  static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
291
  	struct device *dev;
46b814d6e   Ben Dooks   [WATCHDOG] s3c241...
292
  	unsigned int wtcon;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
  	int started = 0;
  	int ret;
  	int size;
fa9363c5f   Harvey Harrison   [WATCHDOG] replac...
296
297
  	DBG("%s: probe=%p
  ", __func__, pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298

e8ef92b8d   Ben Dooks   [WATCHDOG] change...
299
300
  	dev = &pdev->dev;
  	wdt_dev = &pdev->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
  	/* get the memory region for the watchdog timer */
f72401e94   Julia Lawall   watchdog: s3c2410...
302
303
  	wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (wdt_mem == NULL) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
304
305
  		dev_err(dev, "no memory resource specified
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
  		return -ENOENT;
  	}
f72401e94   Julia Lawall   watchdog: s3c2410...
308
309
  	size = resource_size(wdt_mem);
  	if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
310
311
  		dev_err(dev, "failed to get memory region
  ");
100fb76f0   Banajit Goswami   watchdog: s3c2410...
312
  		return -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  	}
f72401e94   Julia Lawall   watchdog: s3c2410...
314
  	wdt_base = ioremap(wdt_mem->start, size);
b4253f8fc   Ben Dooks   [WATCHDOG] Fix NU...
315
  	if (wdt_base == NULL) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
316
317
  		dev_err(dev, "failed to ioremap() region
  ");
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
318
319
  		ret = -EINVAL;
  		goto err_req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
  	}
  
  	DBG("probe: mapped wdt_base=%p
  ", wdt_base);
62be07414   Arnaud Patard   [WATCHDOG] s3c241...
324
325
  	wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  	if (wdt_irq == NULL) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
326
327
  		dev_err(dev, "no irq resource specified
  ");
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
328
329
  		ret = -ENOENT;
  		goto err_map;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  	}
62be07414   Arnaud Patard   [WATCHDOG] s3c241...
331
  	ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  	if (ret != 0) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
333
334
  		dev_err(dev, "failed to install irq (%d)
  ", ret);
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
335
  		goto err_map;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  	}
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
337
  	wdt_clock = clk_get(&pdev->dev, "watchdog");
9cd446198   Akinobu Mita   [WATCHDOG] fix cl...
338
  	if (IS_ERR(wdt_clock)) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
339
340
  		dev_err(dev, "failed to find watchdog clock source
  ");
9cd446198   Akinobu Mita   [WATCHDOG] fix cl...
341
  		ret = PTR_ERR(wdt_clock);
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
342
  		goto err_irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
  	clk_enable(wdt_clock);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
345
346
347
348
349
  	if (s3c2410wdt_cpufreq_register() < 0) {
  		printk(KERN_ERR PFX "failed to register cpufreq
  ");
  		goto err_clk;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
  	/* see if we can actually set the requested timer margin, and if
  	 * not, try the default value */
25dc46e38   Wolfram Sang   watchdog: s3c2410...
352
353
  	if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) {
  		started = s3c2410wdt_set_heartbeat(&s3c2410_wdd,
41dc8b72e   Alan Cox   s3c2410_wdt watch...
354
  					CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355

41dc8b72e   Alan Cox   s3c2410_wdt watch...
356
357
358
359
  		if (started == 0)
  			dev_info(dev,
  			   "tmr_margin value out of range, default %d used
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  			       CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
41dc8b72e   Alan Cox   s3c2410_wdt watch...
361
  		else
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
362
363
364
  			dev_info(dev, "default timer value is out of range, "
  							"cannot start
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  	}
ff0b3cd4a   Wim Van Sebroeck   watchdog: add now...
366
  	watchdog_set_nowayout(&s3c2410_wdd, nowayout);
25dc46e38   Wolfram Sang   watchdog: s3c2410...
367
  	ret = watchdog_register_device(&s3c2410_wdd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  	if (ret) {
25dc46e38   Wolfram Sang   watchdog: s3c2410...
369
370
  		dev_err(dev, "cannot register watchdog (%d)
  ", ret);
e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
371
  		goto err_cpufreq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
  	}
  
  	if (tmr_atboot && started == 0) {
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
375
376
  		dev_info(dev, "starting watchdog timer
  ");
25dc46e38   Wolfram Sang   watchdog: s3c2410...
377
  		s3c2410wdt_start(&s3c2410_wdd);
655516c80   Ben Dooks   [WATCHDOG] s3c241...
378
379
380
381
  	} else if (!tmr_atboot) {
  		/* if we're not enabling the watchdog, then ensure it is
  		 * disabled if it has been left running from the bootloader
  		 * or other source */
25dc46e38   Wolfram Sang   watchdog: s3c2410...
382
  		s3c2410wdt_stop(&s3c2410_wdd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  	}
46b814d6e   Ben Dooks   [WATCHDOG] s3c241...
384
385
386
  	/* print out a statement of readiness */
  
  	wtcon = readl(wdt_base + S3C2410_WTCON);
e8ef92b8d   Ben Dooks   [WATCHDOG] change...
387
388
  	dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled
  ",
46b814d6e   Ben Dooks   [WATCHDOG] s3c241...
389
  		 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",
20403e845   Dmitry Artamonow   watchdog: fix ini...
390
391
  		 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
  		 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
41dc8b72e   Alan Cox   s3c2410_wdt watch...
392

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
  	return 0;
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
394

e02f838ee   Ben Dooks   [WATCHDOG] CPUFRE...
395
396
   err_cpufreq:
  	s3c2410wdt_cpufreq_deregister();
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
397
398
399
400
401
402
403
404
405
406
407
   err_clk:
  	clk_disable(wdt_clock);
  	clk_put(wdt_clock);
  
   err_irq:
  	free_irq(wdt_irq->start, pdev);
  
   err_map:
  	iounmap(wdt_base);
  
   err_req:
f72401e94   Julia Lawall   watchdog: s3c2410...
408
409
  	release_mem_region(wdt_mem->start, size);
  	wdt_mem = NULL;
0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
410
411
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  }
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
413
  static int __devexit s3c2410wdt_remove(struct platform_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  {
25dc46e38   Wolfram Sang   watchdog: s3c2410...
415
  	watchdog_unregister_device(&s3c2410_wdd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416

9a3725631   Wim Van Sebroeck   watchdog: s3c2410...
417
  	s3c2410wdt_cpufreq_deregister();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

0b6dd8a64   Ben Dooks   [WATCHDOG] s3c241...
419
420
421
  	clk_disable(wdt_clock);
  	clk_put(wdt_clock);
  	wdt_clock = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422

9a3725631   Wim Van Sebroeck   watchdog: s3c2410...
423
424
  	free_irq(wdt_irq->start, dev);
  	wdt_irq = NULL;
e34477e99   Amol Lad   [WATCHDOG] iorema...
425
  	iounmap(wdt_base);
9a3725631   Wim Van Sebroeck   watchdog: s3c2410...
426

f72401e94   Julia Lawall   watchdog: s3c2410...
427
  	release_mem_region(wdt_mem->start, resource_size(wdt_mem));
9a3725631   Wim Van Sebroeck   watchdog: s3c2410...
428
  	wdt_mem = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
  	return 0;
  }
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
431
  static void s3c2410wdt_shutdown(struct platform_device *dev)
94f1e9f31   Ben Dooks   [WATCHDOG] s3c241...
432
  {
25dc46e38   Wolfram Sang   watchdog: s3c2410...
433
  	s3c2410wdt_stop(&s3c2410_wdd);
94f1e9f31   Ben Dooks   [WATCHDOG] s3c241...
434
  }
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
435
436
437
438
  #ifdef CONFIG_PM
  
  static unsigned long wtcon_save;
  static unsigned long wtdat_save;
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
439
  static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
440
  {
9480e307c   Russell King   [PATCH] DRIVER MO...
441
442
443
  	/* Save watchdog state, and turn it off. */
  	wtcon_save = readl(wdt_base + S3C2410_WTCON);
  	wtdat_save = readl(wdt_base + S3C2410_WTDAT);
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
444

9480e307c   Russell King   [PATCH] DRIVER MO...
445
  	/* Note that WTCNT doesn't need to be saved. */
25dc46e38   Wolfram Sang   watchdog: s3c2410...
446
  	s3c2410wdt_stop(&s3c2410_wdd);
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
447
448
449
  
  	return 0;
  }
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
450
  static int s3c2410wdt_resume(struct platform_device *dev)
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
451
  {
9480e307c   Russell King   [PATCH] DRIVER MO...
452
  	/* Restore watchdog state. */
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
453

9480e307c   Russell King   [PATCH] DRIVER MO...
454
455
456
  	writel(wtdat_save, wdt_base + S3C2410_WTDAT);
  	writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
  	writel(wtcon_save, wdt_base + S3C2410_WTCON);
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
457

9480e307c   Russell King   [PATCH] DRIVER MO...
458
459
460
  	printk(KERN_INFO PFX "watchdog %sabled
  ",
  	       (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
461
462
463
464
465
466
467
468
  
  	return 0;
  }
  
  #else
  #define s3c2410wdt_suspend NULL
  #define s3c2410wdt_resume  NULL
  #endif /* CONFIG_PM */
9487a9cc7   Thomas Abraham   watchdog: s3c2410...
469
470
471
472
473
474
475
476
477
  #ifdef CONFIG_OF
  static const struct of_device_id s3c2410_wdt_match[] = {
  	{ .compatible = "samsung,s3c2410-wdt" },
  	{},
  };
  MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
  #else
  #define s3c2410_wdt_match NULL
  #endif
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
478

3ae5eaec1   Russell King   [DRIVER MODEL] Co...
479
  static struct platform_driver s3c2410wdt_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	.probe		= s3c2410wdt_probe,
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
481
  	.remove		= __devexit_p(s3c2410wdt_remove),
94f1e9f31   Ben Dooks   [WATCHDOG] s3c241...
482
  	.shutdown	= s3c2410wdt_shutdown,
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
483
484
  	.suspend	= s3c2410wdt_suspend,
  	.resume		= s3c2410wdt_resume,
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
485
486
487
  	.driver		= {
  		.owner	= THIS_MODULE,
  		.name	= "s3c2410-wdt",
9487a9cc7   Thomas Abraham   watchdog: s3c2410...
488
  		.of_match_table	= s3c2410_wdt_match,
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
489
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  };
41dc8b72e   Alan Cox   s3c2410_wdt watch...
491
492
493
  static char banner[] __initdata =
  	KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics
  ";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
  
  static int __init watchdog_init(void)
  {
  	printk(banner);
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
498
  	return platform_driver_register(&s3c2410wdt_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
  }
  
  static void __exit watchdog_exit(void)
  {
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
503
  	platform_driver_unregister(&s3c2410wdt_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
506
507
  }
  
  module_init(watchdog_init);
  module_exit(watchdog_exit);
af4bb822b   Ben Dooks   [WATCHDOG] s3c241...
508
509
  MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
  	      "Dimitry Andric <dimitry.andric@tomtom.com>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
512
  MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
f37d193c7   Kay Sievers   watchdog: fix pla...
513
  MODULE_ALIAS("platform:s3c2410-wdt");