Blame view

drivers/watchdog/geodewdt.c 6.45 KB
9b0fd1149   Andres Salomon   watchdog: update ...
1
  /* Watchdog timer for machines with the CS5535/CS5536 companion chip
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
2
3
   *
   * Copyright (C) 2006-2007, Advanced Micro Devices, Inc.
9b0fd1149   Andres Salomon   watchdog: update ...
4
   * Copyright (C) 2009  Andres Salomon <dilinger@collabora.co.uk>
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
5
6
7
8
9
10
   *
   * 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.
   */
27c766aaa   Joe Perches   watchdog: Use pr_...
11
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
12
13
14
15
16
17
18
19
20
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/miscdevice.h>
  #include <linux/watchdog.h>
  #include <linux/fs.h>
  #include <linux/platform_device.h>
  #include <linux/reboot.h>
089ab0791   Wim Van Sebroeck   [WATCHDOG] Clean-...
21
  #include <linux/uaccess.h>
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
22

9b0fd1149   Andres Salomon   watchdog: update ...
23
  #include <linux/cs5535.h>
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  
  #define GEODEWDT_HZ 500
  #define GEODEWDT_SCALE 6
  #define GEODEWDT_MAX_SECONDS 131
  
  #define WDT_FLAGS_OPEN 1
  #define WDT_FLAGS_ORPHAN 2
  
  #define DRV_NAME "geodewdt"
  #define WATCHDOG_NAME "Geode GX/LX WDT"
  #define WATCHDOG_TIMEOUT 60
  
  static int timeout = WATCHDOG_TIMEOUT;
  module_param(timeout, int, 0);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
38
39
40
  MODULE_PARM_DESC(timeout,
  	"Watchdog timeout in seconds. 1<= timeout <=131, default="
  				__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
41

86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
42
43
  static bool nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, bool, 0);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
44
45
46
  MODULE_PARM_DESC(nowayout,
  	"Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
47
48
49
  
  static struct platform_device *geodewdt_platform_device;
  static unsigned long wdt_flags;
9b0fd1149   Andres Salomon   watchdog: update ...
50
  static struct cs5535_mfgpt_timer *wdt_timer;
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
51
52
53
54
55
  static int safe_close;
  
  static void geodewdt_ping(void)
  {
  	/* Stop the counter */
9b0fd1149   Andres Salomon   watchdog: update ...
56
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
57
58
  
  	/* Reset the counter */
9b0fd1149   Andres Salomon   watchdog: update ...
59
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
60
61
  
  	/* Enable the counter */
9b0fd1149   Andres Salomon   watchdog: update ...
62
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
63
64
65
66
  }
  
  static void geodewdt_disable(void)
  {
9b0fd1149   Andres Salomon   watchdog: update ...
67
68
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
69
70
71
72
73
74
  }
  
  static int geodewdt_set_heartbeat(int val)
  {
  	if (val < 1 || val > GEODEWDT_MAX_SECONDS)
  		return -EINVAL;
9b0fd1149   Andres Salomon   watchdog: update ...
75
76
77
78
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ);
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
79
80
81
82
  
  	timeout = val;
  	return 0;
  }
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
83
  static int geodewdt_open(struct inode *inode, struct file *file)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
84
  {
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
85
86
  	if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags))
  		return -EBUSY;
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
87

7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
88
89
  	if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags))
  		__module_get(THIS_MODULE);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
90
91
  
  	geodewdt_ping();
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
92
  	return nonseekable_open(inode, file);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
93
  }
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
94
  static int geodewdt_release(struct inode *inode, struct file *file)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
95
96
97
98
  {
  	if (safe_close) {
  		geodewdt_disable();
  		module_put(THIS_MODULE);
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
99
  	} else {
27c766aaa   Joe Perches   watchdog: Use pr_...
100
101
  		pr_crit("Unexpected close - watchdog is not stopping
  ");
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
102
103
104
105
106
107
108
109
110
  		geodewdt_ping();
  
  		set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);
  	}
  
  	clear_bit(WDT_FLAGS_OPEN, &wdt_flags);
  	safe_close = 0;
  	return 0;
  }
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
111
112
  static ssize_t geodewdt_write(struct file *file, const char __user *data,
  				size_t len, loff_t *ppos)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
113
  {
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
114
  	if (len) {
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  		if (!nowayout) {
  			size_t i;
  			safe_close = 0;
  
  			for (i = 0; i != len; i++) {
  				char c;
  
  				if (get_user(c, data + i))
  					return -EFAULT;
  
  				if (c == 'V')
  					safe_close = 1;
  			}
  		}
  
  		geodewdt_ping();
  	}
  	return len;
  }
7275fc8c3   Wim Van Sebroeck   [WATCHDOG] unlock...
134
135
  static long geodewdt_ioctl(struct file *file, unsigned int cmd,
  				unsigned long arg)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
136
137
138
139
  {
  	void __user *argp = (void __user *)arg;
  	int __user *p = argp;
  	int interval;
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
140
  	static const struct watchdog_info ident = {
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
141
142
143
144
  		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
  		| WDIOF_MAGICCLOSE,
  		.firmware_version =     1,
  		.identity =             WATCHDOG_NAME,
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
145
  	};
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
146

5eb82498e   Wim Van Sebroeck   [WATCHDOG] Coding...
147
  	switch (cmd) {
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
148
149
150
151
152
153
154
155
  	case WDIOC_GETSUPPORT:
  		return copy_to_user(argp, &ident,
  				    sizeof(ident)) ? -EFAULT : 0;
  		break;
  
  	case WDIOC_GETSTATUS:
  	case WDIOC_GETBOOTSTATUS:
  		return put_user(0, p);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  	case WDIOC_SETOPTIONS:
  	{
  		int options, ret = -EINVAL;
  
  		if (get_user(options, p))
  			return -EFAULT;
  
  		if (options & WDIOS_DISABLECARD) {
  			geodewdt_disable();
  			ret = 0;
  		}
  
  		if (options & WDIOS_ENABLECARD) {
  			geodewdt_ping();
  			ret = 0;
  		}
  
  		return ret;
  	}
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
175
176
177
178
179
180
181
182
183
184
185
186
187
  	case WDIOC_KEEPALIVE:
  		geodewdt_ping();
  		return 0;
  
  	case WDIOC_SETTIMEOUT:
  		if (get_user(interval, p))
  			return -EFAULT;
  
  		if (geodewdt_set_heartbeat(interval))
  			return -EINVAL;
  	/* Fall through */
  	case WDIOC_GETTIMEOUT:
  		return put_user(timeout, p);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
188
189
190
191
192
193
194
195
  	default:
  		return -ENOTTY;
  	}
  
  	return 0;
  }
  
  static const struct file_operations geodewdt_fops = {
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
196
197
198
  	.owner          = THIS_MODULE,
  	.llseek         = no_llseek,
  	.write          = geodewdt_write,
7275fc8c3   Wim Van Sebroeck   [WATCHDOG] unlock...
199
  	.unlocked_ioctl = geodewdt_ioctl,
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
200
201
  	.open           = geodewdt_open,
  	.release        = geodewdt_release,
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
202
203
204
205
206
  };
  
  static struct miscdevice geodewdt_miscdev = {
  	.minor = WATCHDOG_MINOR,
  	.name = "watchdog",
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
207
  	.fops = &geodewdt_fops,
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
208
  };
78411be4c   Jean Delvare   watchdog: geodewd...
209
  static int __init geodewdt_probe(struct platform_device *dev)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
210
  {
9b0fd1149   Andres Salomon   watchdog: update ...
211
  	int ret;
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
212

9b0fd1149   Andres Salomon   watchdog: update ...
213
214
  	wdt_timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
  	if (!wdt_timer) {
27c766aaa   Joe Perches   watchdog: Use pr_...
215
216
  		pr_err("No timers were available
  ");
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
217
218
  		return -ENODEV;
  	}
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
219
  	/* Set up the timer */
9b0fd1149   Andres Salomon   watchdog: update ...
220
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP,
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
221
222
223
  			  GEODEWDT_SCALE | (3 << 8));
  
  	/* Set up comparator 2 to reset when the event fires */
9b0fd1149   Andres Salomon   watchdog: update ...
224
  	cs5535_mfgpt_toggle_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET, 1);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
225
226
  
  	/* Set up the initial timeout */
9b0fd1149   Andres Salomon   watchdog: update ...
227
  	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2,
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
228
229
230
231
232
233
  		timeout * GEODEWDT_HZ);
  
  	ret = misc_register(&geodewdt_miscdev);
  
  	return ret;
  }
4b12b896c   Bill Pemberton   watchdog: remove ...
234
  static int geodewdt_remove(struct platform_device *dev)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
235
236
237
238
  {
  	misc_deregister(&geodewdt_miscdev);
  	return 0;
  }
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
239
  static void geodewdt_shutdown(struct platform_device *dev)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
240
241
242
243
244
  {
  	geodewdt_disable();
  }
  
  static struct platform_driver geodewdt_driver = {
82268714b   Bill Pemberton   watchdog: remove ...
245
  	.remove		= geodewdt_remove,
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
246
247
  	.shutdown	= geodewdt_shutdown,
  	.driver		= {
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
248
249
250
  		.name	= DRV_NAME,
  	},
  };
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
251
  static int __init geodewdt_init(void)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
252
253
  {
  	int ret;
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
254
255
  	geodewdt_platform_device = platform_device_register_simple(DRV_NAME,
  								-1, NULL, 0);
78411be4c   Jean Delvare   watchdog: geodewd...
256
257
258
259
260
  	if (IS_ERR(geodewdt_platform_device))
  		return PTR_ERR(geodewdt_platform_device);
  
  	ret = platform_driver_probe(&geodewdt_driver, geodewdt_probe);
  	if (ret)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
261
  		goto err;
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
262
263
264
  
  	return 0;
  err:
78411be4c   Jean Delvare   watchdog: geodewd...
265
  	platform_device_unregister(geodewdt_platform_device);
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
266
267
  	return ret;
  }
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
268
  static void __exit geodewdt_exit(void)
0b36086b5   Jordan Crouse   [WATCHDOG] Add a ...
269
270
271
272
273
274
275
276
277
278
279
  {
  	platform_device_unregister(geodewdt_platform_device);
  	platform_driver_unregister(&geodewdt_driver);
  }
  
  module_init(geodewdt_init);
  module_exit(geodewdt_exit);
  
  MODULE_AUTHOR("Advanced Micro Devices, Inc");
  MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver");
  MODULE_LICENSE("GPL");