Blame view

drivers/watchdog/orion_wdt.c 6.6 KB
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
1
  /*
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
2
   * drivers/watchdog/orion_wdt.c
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
3
   *
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
4
   * Watchdog driver for Orion/Kirkwood processors
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   *
   * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
   *
   * This file is licensed under  the terms of the GNU General Public
   * License version 2. This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/fs.h>
  #include <linux/miscdevice.h>
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
19
  #include <linux/platform_device.h>
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
20
21
22
23
  #include <linux/watchdog.h>
  #include <linux/init.h>
  #include <linux/uaccess.h>
  #include <linux/io.h>
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
24
  #include <linux/spinlock.h>
fdd8b079e   Nicolas Pitre   [ARM] 5460/1: Ori...
25
  #include <mach/bridge-regs.h>
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
26
  #include <plat/orion_wdt.h>
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
27
28
29
30
31
32
33
  
  /*
   * Watchdog timer block registers.
   */
  #define TIMER_CTRL		(TIMER_VIRT_BASE + 0x0000)
  #define  WDT_EN			0x0010
  #define WDT_VAL			(TIMER_VIRT_BASE + 0x0024)
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
34
  #define WDT_MAX_CYCLE_COUNT	0xffffffff
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
35
36
37
38
  #define WDT_IN_USE		0
  #define WDT_OK_TO_CLOSE		1
  
  static int nowayout = WATCHDOG_NOWAYOUT;
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
39
40
41
  static int heartbeat = -1;		/* module parameter (seconds) */
  static unsigned int wdt_max_duration;	/* (seconds) */
  static unsigned int wdt_tclk;
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
42
  static unsigned long wdt_status;
1334f3293   Axel Lin   watchdog: Use DEF...
43
  static DEFINE_SPINLOCK(wdt_lock);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
44

3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
45
  static void orion_wdt_ping(void)
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
46
47
48
49
50
51
52
53
  {
  	spin_lock(&wdt_lock);
  
  	/* Reload watchdog duration */
  	writel(wdt_tclk * heartbeat, WDT_VAL);
  
  	spin_unlock(&wdt_lock);
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
54
  static void orion_wdt_enable(void)
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
55
56
  {
  	u32 reg;
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
57
  	spin_lock(&wdt_lock);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
58
  	/* Set watchdog duration */
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
59
  	writel(wdt_tclk * heartbeat, WDT_VAL);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
60
61
62
63
64
65
66
67
68
69
70
71
  
  	/* Clear watchdog timer interrupt */
  	reg = readl(BRIDGE_CAUSE);
  	reg &= ~WDT_INT_REQ;
  	writel(reg, BRIDGE_CAUSE);
  
  	/* Enable watchdog timer */
  	reg = readl(TIMER_CTRL);
  	reg |= WDT_EN;
  	writel(reg, TIMER_CTRL);
  
  	/* Enable reset on watchdog */
6462c6160   Thomas Reitmayr   [ARM] orion5x: Ch...
72
73
74
  	reg = readl(RSTOUTn_MASK);
  	reg |= WDT_RESET_OUT_EN;
  	writel(reg, RSTOUTn_MASK);
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
75
76
  
  	spin_unlock(&wdt_lock);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
77
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
78
  static void orion_wdt_disable(void)
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
79
80
  {
  	u32 reg;
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
81
  	spin_lock(&wdt_lock);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
82
  	/* Disable reset on watchdog */
6462c6160   Thomas Reitmayr   [ARM] orion5x: Ch...
83
84
85
  	reg = readl(RSTOUTn_MASK);
  	reg &= ~WDT_RESET_OUT_EN;
  	writel(reg, RSTOUTn_MASK);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
86
87
88
89
90
  
  	/* Disable watchdog timer */
  	reg = readl(TIMER_CTRL);
  	reg &= ~WDT_EN;
  	writel(reg, TIMER_CTRL);
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
91
92
93
  
  	spin_unlock(&wdt_lock);
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
94
  static int orion_wdt_get_timeleft(int *time_left)
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
95
96
  {
  	spin_lock(&wdt_lock);
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
97
  	*time_left = readl(WDT_VAL) / wdt_tclk;
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
98
99
  	spin_unlock(&wdt_lock);
  	return 0;
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
100
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
101
  static int orion_wdt_open(struct inode *inode, struct file *file)
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
102
103
104
105
  {
  	if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  		return -EBUSY;
  	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
106
  	orion_wdt_enable();
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
107
108
  	return nonseekable_open(inode, file);
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
109
  static ssize_t orion_wdt_write(struct file *file, const char *data,
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  					size_t len, loff_t *ppos)
  {
  	if (len) {
  		if (!nowayout) {
  			size_t i;
  
  			clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  			for (i = 0; i != len; i++) {
  				char c;
  
  				if (get_user(c, data + i))
  					return -EFAULT;
  				if (c == 'V')
  					set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  			}
  		}
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
126
  		orion_wdt_ping();
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
127
128
129
  	}
  	return len;
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
130
  static int orion_wdt_settimeout(int new_time)
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
131
132
133
134
135
  {
  	if ((new_time <= 0) || (new_time > wdt_max_duration))
  		return -EINVAL;
  
  	/* Set new watchdog time to be used when
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
136
  	 * orion_wdt_enable() or orion_wdt_ping() is called. */
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
137
138
139
140
141
  	heartbeat = new_time;
  	return 0;
  }
  
  static const struct watchdog_info ident = {
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
142
143
  	.options	= WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
  			  WDIOF_KEEPALIVEPING,
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
144
  	.identity	= "Orion Watchdog",
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
145
  };
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
146
  static long orion_wdt_ioctl(struct file *file, unsigned int cmd,
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  				unsigned long arg)
  {
  	int ret = -ENOTTY;
  	int time;
  
  	switch (cmd) {
  	case WDIOC_GETSUPPORT:
  		ret = copy_to_user((struct watchdog_info *)arg, &ident,
  				   sizeof(ident)) ? -EFAULT : 0;
  		break;
  
  	case WDIOC_GETSTATUS:
  	case WDIOC_GETBOOTSTATUS:
  		ret = put_user(0, (int *)arg);
  		break;
  
  	case WDIOC_KEEPALIVE:
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
164
  		orion_wdt_ping();
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
165
166
167
168
169
170
171
  		ret = 0;
  		break;
  
  	case WDIOC_SETTIMEOUT:
  		ret = get_user(time, (int *)arg);
  		if (ret)
  			break;
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
172
  		if (orion_wdt_settimeout(time)) {
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
173
174
175
  			ret = -EINVAL;
  			break;
  		}
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
176
  		orion_wdt_ping();
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
177
178
179
180
181
182
183
  		/* Fall through */
  
  	case WDIOC_GETTIMEOUT:
  		ret = put_user(heartbeat, (int *)arg);
  		break;
  
  	case WDIOC_GETTIMELEFT:
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
184
  		if (orion_wdt_get_timeleft(&time)) {
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
185
186
187
188
189
190
191
192
  			ret = -EINVAL;
  			break;
  		}
  		ret = put_user(time, (int *)arg);
  		break;
  	}
  	return ret;
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
193
  static int orion_wdt_release(struct inode *inode, struct file *file)
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
194
195
  {
  	if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
196
  		orion_wdt_disable();
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
197
198
199
200
201
202
203
204
205
  	else
  		printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
  					"timer will not stop
  ");
  	clear_bit(WDT_IN_USE, &wdt_status);
  	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  
  	return 0;
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
206
  static const struct file_operations orion_wdt_fops = {
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
207
208
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
209
210
211
212
  	.write		= orion_wdt_write,
  	.unlocked_ioctl	= orion_wdt_ioctl,
  	.open		= orion_wdt_open,
  	.release	= orion_wdt_release,
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
213
  };
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
214
  static struct miscdevice orion_wdt_miscdev = {
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
215
216
  	.minor		= WATCHDOG_MINOR,
  	.name		= "watchdog",
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
217
  	.fops		= &orion_wdt_fops,
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
218
  };
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
219
  static int __devinit orion_wdt_probe(struct platform_device *pdev)
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
220
  {
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
221
  	struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
222
  	int ret;
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
223
224
225
  	if (pdata) {
  		wdt_tclk = pdata->tclk;
  	} else {
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
226
227
  		printk(KERN_ERR "Orion Watchdog misses platform data
  ");
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
228
229
  		return -ENODEV;
  	}
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
230
  	if (orion_wdt_miscdev.parent)
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
231
  		return -EBUSY;
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
232
  	orion_wdt_miscdev.parent = &pdev->dev;
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
233
234
  
  	wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
235
  	if (orion_wdt_settimeout(heartbeat))
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
236
  		heartbeat = wdt_max_duration;
6d0f0dfdb   Wim Van Sebroeck   [WATCHDOG] orion5...
237

3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
238
  	ret = misc_register(&orion_wdt_miscdev);
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
239
240
  	if (ret)
  		return ret;
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
241
242
  	printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s
  ",
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
243
244
245
  				heartbeat, nowayout ? ", nowayout" : "");
  	return 0;
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
246
  static int __devexit orion_wdt_remove(struct platform_device *pdev)
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
247
248
249
250
  {
  	int ret;
  
  	if (test_bit(WDT_IN_USE, &wdt_status)) {
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
251
  		orion_wdt_disable();
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
252
253
  		clear_bit(WDT_IN_USE, &wdt_status);
  	}
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
254
  	ret = misc_deregister(&orion_wdt_miscdev);
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
255
  	if (!ret)
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
256
  		orion_wdt_miscdev.parent = NULL;
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
257
258
259
  
  	return ret;
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
260
  static void orion_wdt_shutdown(struct platform_device *pdev)
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
261
262
  {
  	if (test_bit(WDT_IN_USE, &wdt_status))
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
263
  		orion_wdt_disable();
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
264
  }
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
265
266
267
268
  static struct platform_driver orion_wdt_driver = {
  	.probe		= orion_wdt_probe,
  	.remove		= __devexit_p(orion_wdt_remove),
  	.shutdown	= orion_wdt_shutdown,
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
269
270
  	.driver		= {
  		.owner	= THIS_MODULE,
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
271
  		.name	= "orion_wdt",
9e058d4f5   Thomas Reitmayr   [WATCHDOG] orion5...
272
273
  	},
  };
b8ec61189   Axel Lin   watchdog: convert...
274
  module_platform_driver(orion_wdt_driver);
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
275
276
  
  MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
3b937a7db   Nicolas Pitre   [ARM] Orion/Kirkw...
277
  MODULE_DESCRIPTION("Orion Processor Watchdog");
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
278
279
  
  module_param(heartbeat, int, 0);
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
280
  MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
281
282
  
  module_param(nowayout, int, 0);
df6707b21   Thomas Reitmayr   [WATCHDOG] orion5...
283
284
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
22ac92322   Sylver Bruneau   [WATCHDOG] Orion:...
285
286
287
  
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);