Blame view

drivers/watchdog/booke_wdt.c 7.33 KB
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
1
  /*
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
2
3
4
   * Watchdog timer for PowerPC Book-E systems
   *
   * Author: Matthew McClintock
4c8d3d997   Kumar Gala   [PATCH] Update em...
5
   * Maintainer: Kumar Gala <galak@kernel.crashing.org>
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
6
   *
112e75466   Timur Tabi   watchdog: booke_w...
7
   * Copyright 2005, 2008, 2010-2011 Freescale Semiconductor Inc.
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
8
9
10
11
12
13
   *
   * 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.
   */
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
14
15
  #include <linux/module.h>
  #include <linux/fs.h>
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
16
  #include <linux/smp.h>
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
17
18
19
  #include <linux/miscdevice.h>
  #include <linux/notifier.h>
  #include <linux/watchdog.h>
00e9c2059   Alan Cox   [WATCHDOG 20/57] ...
20
  #include <linux/uaccess.h>
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
21
22
  
  #include <asm/reg_booke.h>
39cdc4bfb   Kumar Gala   [PATCH] ppc32: Cl...
23
  #include <asm/system.h>
dcfb74842   Chris Friesen   [WATCHDOG] fix bo...
24
25
  #include <asm/time.h>
  #include <asm/div64.h>
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
26

40ebbcbf2   Dave Jiang   [POWERPC] Fix com...
27
  /* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
28
29
30
31
32
33
   * Also, the wdt_period sets the watchdog timer period timeout.
   * For E500 cpus the wdt_period sets which bit changing from 0->1 will
   * trigger a watchog timeout. This watchdog timeout will occur 3 times, the
   * first time nothing will happen, the second time a watchdog exception will
   * occur, and the final time the board will reset.
   */
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
34
  u32 booke_wdt_enabled;
e0dc09ff9   Timur Tabi   powerpc/watchdog:...
35
  u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
36
37
  
  #ifdef	CONFIG_FSL_BOOKE
dcfb74842   Chris Friesen   [WATCHDOG] fix bo...
38
  #define WDTP(x)		((((x)&0x3)<<30)|(((x)&0x3c)<<15))
0fb06571b   Luuk Paulussen   [WATCHDOG] fixed ...
39
  #define WDTP_MASK	(WDTP(0x3f))
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
40
41
  #else
  #define WDTP(x)		(TCR_WP(x))
0a0e9e0cb   Matthias Fuchs   powerpc: Fix Book...
42
  #define WDTP_MASK	(TCR_WP_MASK)
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
43
  #endif
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
44
  static DEFINE_SPINLOCK(booke_wdt_lock);
dcfb74842   Chris Friesen   [WATCHDOG] fix bo...
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  /* For the specified period, determine the number of seconds
   * corresponding to the reset time.  There will be a watchdog
   * exception at approximately 3/5 of this time.
   *
   * The formula to calculate this is given by:
   * 2.5 * (2^(63-period+1)) / timebase_freq
   *
   * In order to simplify things, we assume that period is
   * at least 1.  This will still result in a very long timeout.
   */
  static unsigned long long period_to_sec(unsigned int period)
  {
  	unsigned long long tmp = 1ULL << (64 - period);
  	unsigned long tmp2 = ppc_tb_freq;
  
  	/* tmp may be a very large number and we don't want to overflow,
  	 * so divide the timebase freq instead of multiplying tmp
  	 */
  	tmp2 = tmp2 / 5 * 2;
  
  	do_div(tmp, tmp2);
  	return tmp;
  }
  
  /*
   * This procedure will find the highest period which will give a timeout
   * greater than the one required. e.g. for a bus speed of 66666666 and
   * and a parameter of 2 secs, then this procedure will return a value of 38.
   */
  static unsigned int sec_to_period(unsigned int secs)
  {
  	unsigned int period;
  	for (period = 63; period > 0; period--) {
  		if (period_to_sec(period) >= secs)
  			return period;
  	}
  	return 0;
  }
6ae98ed18   Randy Vinson   watchdog: Propaga...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  static void __booke_wdt_set(void *data)
  {
  	u32 val;
  
  	val = mfspr(SPRN_TCR);
  	val &= ~WDTP_MASK;
  	val |= WDTP(booke_wdt_period);
  
  	mtspr(SPRN_TCR, val);
  }
  
  static void booke_wdt_set(void)
  {
  	on_each_cpu(__booke_wdt_set, NULL, 0);
  }
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
98
  static void __booke_wdt_ping(void *data)
f31909c00   Stefan Roese   [POWERPC] ppc: Fi...
99
100
101
  {
  	mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
  }
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
102
103
  static void booke_wdt_ping(void)
  {
f6f88e9bf   Ingo Molnar   generic-ipi: more...
104
  	on_each_cpu(__booke_wdt_ping, NULL, 0);
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
105
106
107
  }
  
  static void __booke_wdt_enable(void *data)
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
108
109
  {
  	u32 val;
f31909c00   Stefan Roese   [POWERPC] ppc: Fi...
110
  	/* clear status before enabling watchdog */
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
111
  	__booke_wdt_ping(NULL);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
112
  	val = mfspr(SPRN_TCR);
0a0e9e0cb   Matthias Fuchs   powerpc: Fix Book...
113
  	val &= ~WDTP_MASK;
39cdc4bfb   Kumar Gala   [PATCH] ppc32: Cl...
114
  	val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period));
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
115
116
117
  
  	mtspr(SPRN_TCR, val);
  }
fbdd7144c   Timur Tabi   powerpc/watchdog:...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  /**
   * booke_wdt_disable - disable the watchdog on the given CPU
   *
   * This function is called on each CPU.  It disables the watchdog on that CPU.
   *
   * TCR[WRC] cannot be changed once it has been set to non-zero, but we can
   * effectively disable the watchdog by setting its period to the maximum value.
   */
  static void __booke_wdt_disable(void *data)
  {
  	u32 val;
  
  	val = mfspr(SPRN_TCR);
  	val &= ~(TCR_WIE | WDTP_MASK);
  	mtspr(SPRN_TCR, val);
  
  	/* clear status to make sure nothing is pending */
  	__booke_wdt_ping(NULL);
  
  }
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
138
  static ssize_t booke_wdt_write(struct file *file, const char __user *buf,
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
139
140
141
142
143
  				size_t count, loff_t *ppos)
  {
  	booke_wdt_ping();
  	return count;
  }
d8d8b63b6   Anton Vorontsov   watchdog: booke_w...
144
  static struct watchdog_info ident = {
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
145
146
  	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  	.identity = "PowerPC Book-E Watchdog",
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
147
  };
00e9c2059   Alan Cox   [WATCHDOG 20/57] ...
148
149
  static long booke_wdt_ioctl(struct file *file,
  				unsigned int cmd, unsigned long arg)
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
150
151
  {
  	u32 tmp = 0;
538bacf8a   Al Viro   [PATCH] __user an...
152
  	u32 __user *p = (u32 __user *)arg;
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
153
154
155
  
  	switch (cmd) {
  	case WDIOC_GETSUPPORT:
dcfb74842   Chris Friesen   [WATCHDOG] fix bo...
156
  		if (copy_to_user((void *)arg, &ident, sizeof(ident)))
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
157
158
  			return -EFAULT;
  	case WDIOC_GETSTATUS:
8b18085a9   Wim Van Sebroeck   watchdog: booke_w...
159
  		return put_user(0, p);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
160
161
162
  	case WDIOC_GETBOOTSTATUS:
  		/* XXX: something is clearing TSR */
  		tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
8b18085a9   Wim Van Sebroeck   watchdog: booke_w...
163
164
  		/* returns CARDRESET if last reset was caused by the WDT */
  		return (tmp ? WDIOF_CARDRESET : 0);
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
165
166
167
168
169
170
171
172
173
  	case WDIOC_SETOPTIONS:
  		if (get_user(tmp, p))
  			return -EINVAL;
  		if (tmp == WDIOS_ENABLECARD) {
  			booke_wdt_ping();
  			break;
  		} else
  			return -EINVAL;
  		return 0;
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
174
175
176
177
  	case WDIOC_KEEPALIVE:
  		booke_wdt_ping();
  		return 0;
  	case WDIOC_SETTIMEOUT:
dcfb74842   Chris Friesen   [WATCHDOG] fix bo...
178
  		if (get_user(tmp, p))
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
179
  			return -EFAULT;
dcfb74842   Chris Friesen   [WATCHDOG] fix bo...
180
181
182
183
184
185
186
187
  #ifdef	CONFIG_FSL_BOOKE
  		/* period of 1 gives the largest possible timeout */
  		if (tmp > period_to_sec(1))
  			return -EINVAL;
  		booke_wdt_period = sec_to_period(tmp);
  #else
  		booke_wdt_period = tmp;
  #endif
6ae98ed18   Randy Vinson   watchdog: Propaga...
188
  		booke_wdt_set();
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
189
190
  		return 0;
  	case WDIOC_GETTIMEOUT:
538bacf8a   Al Viro   [PATCH] __user an...
191
  		return put_user(booke_wdt_period, p);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
192
  	default:
795b89d20   Samuel Tardieu   [WATCHDOG] use EN...
193
  		return -ENOTTY;
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
194
195
196
197
  	}
  
  	return 0;
  }
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
198

5d63c1341   Timur Tabi   watchdog: add CON...
199
200
  /* wdt_is_active stores wether or not the /dev/watchdog device is opened */
  static unsigned long wdt_is_active;
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
201
  static int booke_wdt_open(struct inode *inode, struct file *file)
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
202
  {
5d63c1341   Timur Tabi   watchdog: add CON...
203
204
205
  	/* /dev/watchdog can only be opened once */
  	if (test_and_set_bit(0, &wdt_is_active))
  		return -EBUSY;
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
206
  	spin_lock(&booke_wdt_lock);
39cdc4bfb   Kumar Gala   [PATCH] ppc32: Cl...
207
208
  	if (booke_wdt_enabled == 0) {
  		booke_wdt_enabled = 1;
f6f88e9bf   Ingo Molnar   generic-ipi: more...
209
  		on_each_cpu(__booke_wdt_enable, NULL, 0);
112e75466   Timur Tabi   watchdog: booke_w...
210
211
212
  		pr_debug("booke_wdt: watchdog enabled (timeout = %llu sec)
  ",
  			period_to_sec(booke_wdt_period));
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
213
  	}
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
214
  	spin_unlock(&booke_wdt_lock);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
215

ec9505a7e   Wim Van Sebroeck   [WATCHDOG] VFS cl...
216
  	return nonseekable_open(inode, file);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
217
  }
fbdd7144c   Timur Tabi   powerpc/watchdog:...
218
219
  static int booke_wdt_release(struct inode *inode, struct file *file)
  {
5d63c1341   Timur Tabi   watchdog: add CON...
220
221
222
223
224
225
  #ifndef CONFIG_WATCHDOG_NOWAYOUT
  	/* Normally, the watchdog is disabled when /dev/watchdog is closed, but
  	 * if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the
  	 * watchdog should remain enabled.  So we disable it only if
  	 * CONFIG_WATCHDOG_NOWAYOUT is not defined.
  	 */
fbdd7144c   Timur Tabi   powerpc/watchdog:...
226
227
  	on_each_cpu(__booke_wdt_disable, NULL, 0);
  	booke_wdt_enabled = 0;
112e75466   Timur Tabi   watchdog: booke_w...
228
229
  	pr_debug("booke_wdt: watchdog disabled
  ");
5d63c1341   Timur Tabi   watchdog: add CON...
230
231
232
  #endif
  
  	clear_bit(0, &wdt_is_active);
fbdd7144c   Timur Tabi   powerpc/watchdog:...
233
234
235
  
  	return 0;
  }
62322d255   Arjan van de Ven   [PATCH] make more...
236
  static const struct file_operations booke_wdt_fops = {
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
237
238
239
  	.owner = THIS_MODULE,
  	.llseek = no_llseek,
  	.write = booke_wdt_write,
00e9c2059   Alan Cox   [WATCHDOG 20/57] ...
240
  	.unlocked_ioctl = booke_wdt_ioctl,
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
241
  	.open = booke_wdt_open,
fbdd7144c   Timur Tabi   powerpc/watchdog:...
242
  	.release = booke_wdt_release,
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
243
244
245
  };
  
  static struct miscdevice booke_wdt_miscdev = {
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
246
247
248
  	.minor = WATCHDOG_MINOR,
  	.name = "watchdog",
  	.fops = &booke_wdt_fops,
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
249
250
251
252
253
254
  };
  
  static void __exit booke_wdt_exit(void)
  {
  	misc_deregister(&booke_wdt_miscdev);
  }
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
255
256
257
  static int __init booke_wdt_init(void)
  {
  	int ret = 0;
112e75466   Timur Tabi   watchdog: booke_w...
258
259
  	pr_info("booke_wdt: powerpc book-e watchdog driver loaded
  ");
a78719c38   Al Viro   [PATCH] ppc: book...
260
  	ident.firmware_version = cur_cpu_spec->pvr_value;
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
261
262
263
  
  	ret = misc_register(&booke_wdt_miscdev);
  	if (ret) {
112e75466   Timur Tabi   watchdog: booke_w...
264
265
266
  		pr_err("booke_wdt: cannot register device (minor=%u, ret=%i)
  ",
  		       WATCHDOG_MINOR, ret);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
267
268
  		return ret;
  	}
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
269
  	spin_lock(&booke_wdt_lock);
39cdc4bfb   Kumar Gala   [PATCH] ppc32: Cl...
270
  	if (booke_wdt_enabled == 1) {
112e75466   Timur Tabi   watchdog: booke_w...
271
272
273
  		pr_info("booke_wdt: watchdog enabled (timeout = %llu sec)
  ",
  			period_to_sec(booke_wdt_period));
f6f88e9bf   Ingo Molnar   generic-ipi: more...
274
  		on_each_cpu(__booke_wdt_enable, NULL, 0);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
275
  	}
f172ddc61   Chen Gong   [WATCHDOG] Fix bo...
276
  	spin_unlock(&booke_wdt_lock);
a2f40ccd2   Kumar Gala   [PATCH] ppc32: Ad...
277
278
279
  
  	return ret;
  }
fbdd7144c   Timur Tabi   powerpc/watchdog:...
280
281
282
283
284
285
  
  module_init(booke_wdt_init);
  module_exit(booke_wdt_exit);
  
  MODULE_DESCRIPTION("PowerPC Book-E watchdog driver");
  MODULE_LICENSE("GPL");