diff options
-rw-r--r-- | drivers/watchdog/smsc37b787_wdt.c | 442 |
1 files changed, 222 insertions, 220 deletions
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 5d2b5ba61414..b7c6394b7d70 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c | |||
@@ -18,7 +18,7 @@ | |||
18 | * History: | 18 | * History: |
19 | * 2003 - Created version 1.0 for Linux 2.4.x. | 19 | * 2003 - Created version 1.0 for Linux 2.4.x. |
20 | * 2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE | 20 | * 2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE |
21 | * features. Released version 1.1 | 21 | * features. Released version 1.1 |
22 | * | 22 | * |
23 | * Theory of operation: | 23 | * Theory of operation: |
24 | * | 24 | * |
@@ -55,9 +55,9 @@ | |||
55 | #include <linux/reboot.h> | 55 | #include <linux/reboot.h> |
56 | #include <linux/init.h> | 56 | #include <linux/init.h> |
57 | #include <linux/spinlock.h> | 57 | #include <linux/spinlock.h> |
58 | #include <linux/io.h> | ||
59 | #include <linux/uaccess.h> | ||
58 | 60 | ||
59 | #include <asm/io.h> | ||
60 | #include <asm/uaccess.h> | ||
61 | #include <asm/system.h> | 61 | #include <asm/system.h> |
62 | 62 | ||
63 | /* enable support for minutes as units? */ | 63 | /* enable support for minutes as units? */ |
@@ -71,15 +71,15 @@ | |||
71 | #define UNIT_MINUTE 1 | 71 | #define UNIT_MINUTE 1 |
72 | 72 | ||
73 | #define MODNAME "smsc37b787_wdt: " | 73 | #define MODNAME "smsc37b787_wdt: " |
74 | #define VERSION "1.1" | 74 | #define VERSION "1.1" |
75 | 75 | ||
76 | #define IOPORT 0x3F0 | 76 | #define IOPORT 0x3F0 |
77 | #define IOPORT_SIZE 2 | 77 | #define IOPORT_SIZE 2 |
78 | #define IODEV_NO 8 | 78 | #define IODEV_NO 8 |
79 | 79 | ||
80 | static int unit = UNIT_SECOND; /* timer's unit */ | 80 | static int unit = UNIT_SECOND; /* timer's unit */ |
81 | static int timeout = 60; /* timeout value: default is 60 "units" */ | 81 | static int timeout = 60; /* timeout value: default is 60 "units" */ |
82 | static unsigned long timer_enabled = 0; /* is the timer enabled? */ | 82 | static unsigned long timer_enabled; /* is the timer enabled? */ |
83 | 83 | ||
84 | static char expect_close; /* is the close expected? */ | 84 | static char expect_close; /* is the close expected? */ |
85 | 85 | ||
@@ -93,114 +93,121 @@ static int nowayout = WATCHDOG_NOWAYOUT; | |||
93 | 93 | ||
94 | static inline void open_io_config(void) | 94 | static inline void open_io_config(void) |
95 | { | 95 | { |
96 | outb(0x55, IOPORT); | 96 | outb(0x55, IOPORT); |
97 | mdelay(1); | 97 | mdelay(1); |
98 | outb(0x55, IOPORT); | 98 | outb(0x55, IOPORT); |
99 | } | 99 | } |
100 | 100 | ||
101 | /* lock the IO chip */ | 101 | /* lock the IO chip */ |
102 | static inline void close_io_config(void) | 102 | static inline void close_io_config(void) |
103 | { | 103 | { |
104 | outb(0xAA, IOPORT); | 104 | outb(0xAA, IOPORT); |
105 | } | 105 | } |
106 | 106 | ||
107 | /* select the IO device */ | 107 | /* select the IO device */ |
108 | static inline void select_io_device(unsigned char devno) | 108 | static inline void select_io_device(unsigned char devno) |
109 | { | 109 | { |
110 | outb(0x07, IOPORT); | 110 | outb(0x07, IOPORT); |
111 | outb(devno, IOPORT+1); | 111 | outb(devno, IOPORT+1); |
112 | } | 112 | } |
113 | 113 | ||
114 | /* write to the control register */ | 114 | /* write to the control register */ |
115 | static inline void write_io_cr(unsigned char reg, unsigned char data) | 115 | static inline void write_io_cr(unsigned char reg, unsigned char data) |
116 | { | 116 | { |
117 | outb(reg, IOPORT); | 117 | outb(reg, IOPORT); |
118 | outb(data, IOPORT+1); | 118 | outb(data, IOPORT+1); |
119 | } | 119 | } |
120 | 120 | ||
121 | /* read from the control register */ | 121 | /* read from the control register */ |
122 | static inline char read_io_cr(unsigned char reg) | 122 | static inline char read_io_cr(unsigned char reg) |
123 | { | 123 | { |
124 | outb(reg, IOPORT); | 124 | outb(reg, IOPORT); |
125 | return inb(IOPORT+1); | 125 | return inb(IOPORT+1); |
126 | } | 126 | } |
127 | 127 | ||
128 | /* -- Medium level functions ------------------------------------*/ | 128 | /* -- Medium level functions ------------------------------------*/ |
129 | 129 | ||
130 | static inline void gpio_bit12(unsigned char reg) | 130 | static inline void gpio_bit12(unsigned char reg) |
131 | { | 131 | { |
132 | // -- General Purpose I/O Bit 1.2 -- | 132 | /* -- General Purpose I/O Bit 1.2 -- |
133 | // Bit 0, In/Out: 0 = Output, 1 = Input | 133 | * Bit 0, In/Out: 0 = Output, 1 = Input |
134 | // Bit 1, Polarity: 0 = No Invert, 1 = Invert | 134 | * Bit 1, Polarity: 0 = No Invert, 1 = Invert |
135 | // Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable | 135 | * Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable |
136 | // Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17, | 136 | * Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17, |
137 | // 11 = Either Edge Triggered Intr. 2 | 137 | * 11 = Either Edge Triggered Intr. 2 |
138 | // Bit 5/6 (Reserved) | 138 | * Bit 5/6 (Reserved) |
139 | // Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain | 139 | * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain |
140 | write_io_cr(0xE2, reg); | 140 | */ |
141 | write_io_cr(0xE2, reg); | ||
141 | } | 142 | } |
142 | 143 | ||
143 | static inline void gpio_bit13(unsigned char reg) | 144 | static inline void gpio_bit13(unsigned char reg) |
144 | { | 145 | { |
145 | // -- General Purpose I/O Bit 1.3 -- | 146 | /* -- General Purpose I/O Bit 1.3 -- |
146 | // Bit 0, In/Out: 0 = Output, 1 = Input | 147 | * Bit 0, In/Out: 0 = Output, 1 = Input |
147 | // Bit 1, Polarity: 0 = No Invert, 1 = Invert | 148 | * Bit 1, Polarity: 0 = No Invert, 1 = Invert |
148 | // Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable | 149 | * Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable |
149 | // Bit 3, Function select: 0 = GPI/O, 1 = LED | 150 | * Bit 3, Function select: 0 = GPI/O, 1 = LED |
150 | // Bit 4-6 (Reserved) | 151 | * Bit 4-6 (Reserved) |
151 | // Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain | 152 | * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain |
152 | write_io_cr(0xE3, reg); | 153 | */ |
154 | write_io_cr(0xE3, reg); | ||
153 | } | 155 | } |
154 | 156 | ||
155 | static inline void wdt_timer_units(unsigned char new_units) | 157 | static inline void wdt_timer_units(unsigned char new_units) |
156 | { | 158 | { |
157 | // -- Watchdog timer units -- | 159 | /* -- Watchdog timer units -- |
158 | // Bit 0-6 (Reserved) | 160 | * Bit 0-6 (Reserved) |
159 | // Bit 7, WDT Time-out Value Units Select | 161 | * Bit 7, WDT Time-out Value Units Select |
160 | // (0 = Minutes, 1 = Seconds) | 162 | * (0 = Minutes, 1 = Seconds) |
161 | write_io_cr(0xF1, new_units); | 163 | */ |
164 | write_io_cr(0xF1, new_units); | ||
162 | } | 165 | } |
163 | 166 | ||
164 | static inline void wdt_timeout_value(unsigned char new_timeout) | 167 | static inline void wdt_timeout_value(unsigned char new_timeout) |
165 | { | 168 | { |
166 | // -- Watchdog Timer Time-out Value -- | 169 | /* -- Watchdog Timer Time-out Value -- |
167 | // Bit 0-7 Binary coded units (0=Disabled, 1..255) | 170 | * Bit 0-7 Binary coded units (0=Disabled, 1..255) |
168 | write_io_cr(0xF2, new_timeout); | 171 | */ |
172 | write_io_cr(0xF2, new_timeout); | ||
169 | } | 173 | } |
170 | 174 | ||
171 | static inline void wdt_timer_conf(unsigned char conf) | 175 | static inline void wdt_timer_conf(unsigned char conf) |
172 | { | 176 | { |
173 | // -- Watchdog timer configuration -- | 177 | /* -- Watchdog timer configuration -- |
174 | // Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon Gameport I/O | 178 | * Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon |
175 | // Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr. | 179 | * Gameport I/O |
176 | // Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr. | 180 | * Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr. |
177 | // Bit 3 Reset the timer | 181 | * Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr |
178 | // (Wrong in SMsC documentation? Given as: PowerLED Timout Enabled) | 182 | * Bit 3 Reset the timer |
179 | // Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled, | 183 | * (Wrong in SMsC documentation? Given as: PowerLED Timout |
180 | // 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15) | 184 | * Enabled) |
181 | write_io_cr(0xF3, conf); | 185 | * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled, |
186 | * 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15) | ||
187 | */ | ||
188 | write_io_cr(0xF3, conf); | ||
182 | } | 189 | } |
183 | 190 | ||
184 | static inline void wdt_timer_ctrl(unsigned char reg) | 191 | static inline void wdt_timer_ctrl(unsigned char reg) |
185 | { | 192 | { |
186 | // -- Watchdog timer control -- | 193 | /* -- Watchdog timer control -- |
187 | // Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured | 194 | * Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured |
188 | // Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz | 195 | * Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz |
189 | // Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning) | 196 | * Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning) |
190 | // Bit 3 P20 Force Timeout enabled: | 197 | * Bit 3 P20 Force Timeout enabled: |
191 | // 0 = P20 activity does not generate the WD timeout event | 198 | * 0 = P20 activity does not generate the WD timeout event |
192 | // 1 = P20 Allows rising edge of P20, from the keyboard | 199 | * 1 = P20 Allows rising edge of P20, from the keyboard |
193 | // controller, to force the WD timeout event. | 200 | * controller, to force the WD timeout event. |
194 | // Bit 4 (Reserved) | 201 | * Bit 4 (Reserved) |
195 | // -- Soft power management -- | 202 | * -- Soft power management -- |
196 | // Bit 5 Stop Counter: 1 = Stop software power down counter | 203 | * Bit 5 Stop Counter: 1 = Stop software power down counter |
197 | // set via register 0xB8, (self-cleaning) | 204 | * set via register 0xB8, (self-cleaning) |
198 | // (Upon read: 0 = Counter running, 1 = Counter stopped) | 205 | * (Upon read: 0 = Counter running, 1 = Counter stopped) |
199 | // Bit 6 Restart Counter: 1 = Restart software power down counter | 206 | * Bit 6 Restart Counter: 1 = Restart software power down counter |
200 | // set via register 0xB8, (self-cleaning) | 207 | * set via register 0xB8, (self-cleaning) |
201 | // Bit 7 SPOFF: 1 = Force software power down (self-cleaning) | 208 | * Bit 7 SPOFF: 1 = Force software power down (self-cleaning) |
202 | 209 | */ | |
203 | write_io_cr(0xF4, reg); | 210 | write_io_cr(0xF4, reg); |
204 | } | 211 | } |
205 | 212 | ||
206 | /* -- Higher level functions ------------------------------------*/ | 213 | /* -- Higher level functions ------------------------------------*/ |
@@ -209,33 +216,34 @@ static inline void wdt_timer_ctrl(unsigned char reg) | |||
209 | 216 | ||
210 | static void wb_smsc_wdt_initialize(void) | 217 | static void wb_smsc_wdt_initialize(void) |
211 | { | 218 | { |
212 | unsigned char old; | 219 | unsigned char old; |
213 | 220 | ||
214 | spin_lock(&io_lock); | 221 | spin_lock(&io_lock); |
215 | open_io_config(); | 222 | open_io_config(); |
216 | select_io_device(IODEV_NO); | 223 | select_io_device(IODEV_NO); |
217 | 224 | ||
218 | // enable the watchdog | 225 | /* enable the watchdog */ |
219 | gpio_bit13(0x08); // Select pin 80 = LED not GPIO | 226 | gpio_bit13(0x08); /* Select pin 80 = LED not GPIO */ |
220 | gpio_bit12(0x0A); // Set pin 79 = WDT not GPIO/Output/Polarity=Invert | 227 | gpio_bit12(0x0A); /* Set pin 79 = WDT not |
228 | GPIO/Output/Polarity=Invert */ | ||
229 | /* disable the timeout */ | ||
230 | wdt_timeout_value(0); | ||
221 | 231 | ||
222 | // disable the timeout | 232 | /* reset control register */ |
223 | wdt_timeout_value(0); | 233 | wdt_timer_ctrl(0x00); |
224 | 234 | ||
225 | // reset control register | 235 | /* reset configuration register */ |
226 | wdt_timer_ctrl(0x00); | ||
227 | |||
228 | // reset configuration register | ||
229 | wdt_timer_conf(0x00); | 236 | wdt_timer_conf(0x00); |
230 | 237 | ||
231 | // read old (timer units) register | 238 | /* read old (timer units) register */ |
232 | old = read_io_cr(0xF1) & 0x7F; | 239 | old = read_io_cr(0xF1) & 0x7F; |
233 | if (unit == UNIT_SECOND) old |= 0x80; // set to seconds | 240 | if (unit == UNIT_SECOND) |
241 | old |= 0x80; /* set to seconds */ | ||
234 | 242 | ||
235 | // set the watchdog timer units | 243 | /* set the watchdog timer units */ |
236 | wdt_timer_units(old); | 244 | wdt_timer_units(old); |
237 | 245 | ||
238 | close_io_config(); | 246 | close_io_config(); |
239 | spin_unlock(&io_lock); | 247 | spin_unlock(&io_lock); |
240 | } | 248 | } |
241 | 249 | ||
@@ -244,23 +252,23 @@ static void wb_smsc_wdt_initialize(void) | |||
244 | static void wb_smsc_wdt_shutdown(void) | 252 | static void wb_smsc_wdt_shutdown(void) |
245 | { | 253 | { |
246 | spin_lock(&io_lock); | 254 | spin_lock(&io_lock); |
247 | open_io_config(); | 255 | open_io_config(); |
248 | select_io_device(IODEV_NO); | 256 | select_io_device(IODEV_NO); |
249 | 257 | ||
250 | // disable the watchdog | 258 | /* disable the watchdog */ |
251 | gpio_bit13(0x09); | 259 | gpio_bit13(0x09); |
252 | gpio_bit12(0x09); | 260 | gpio_bit12(0x09); |
253 | 261 | ||
254 | // reset watchdog config register | 262 | /* reset watchdog config register */ |
255 | wdt_timer_conf(0x00); | 263 | wdt_timer_conf(0x00); |
256 | 264 | ||
257 | // reset watchdog control register | 265 | /* reset watchdog control register */ |
258 | wdt_timer_ctrl(0x00); | 266 | wdt_timer_ctrl(0x00); |
259 | 267 | ||
260 | // disable timeout | 268 | /* disable timeout */ |
261 | wdt_timeout_value(0x00); | 269 | wdt_timeout_value(0x00); |
262 | 270 | ||
263 | close_io_config(); | 271 | close_io_config(); |
264 | spin_unlock(&io_lock); | 272 | spin_unlock(&io_lock); |
265 | } | 273 | } |
266 | 274 | ||
@@ -269,16 +277,16 @@ static void wb_smsc_wdt_shutdown(void) | |||
269 | static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) | 277 | static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) |
270 | { | 278 | { |
271 | spin_lock(&io_lock); | 279 | spin_lock(&io_lock); |
272 | open_io_config(); | 280 | open_io_config(); |
273 | select_io_device(IODEV_NO); | 281 | select_io_device(IODEV_NO); |
274 | 282 | ||
275 | // set Power LED to blink, if we enable the timeout | 283 | /* set Power LED to blink, if we enable the timeout */ |
276 | wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02); | 284 | wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02); |
277 | 285 | ||
278 | // set timeout value | 286 | /* set timeout value */ |
279 | wdt_timeout_value(new_timeout); | 287 | wdt_timeout_value(new_timeout); |
280 | 288 | ||
281 | close_io_config(); | 289 | close_io_config(); |
282 | spin_unlock(&io_lock); | 290 | spin_unlock(&io_lock); |
283 | } | 291 | } |
284 | 292 | ||
@@ -286,32 +294,32 @@ static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) | |||
286 | 294 | ||
287 | static unsigned char wb_smsc_wdt_get_timeout(void) | 295 | static unsigned char wb_smsc_wdt_get_timeout(void) |
288 | { | 296 | { |
289 | unsigned char set_timeout; | 297 | unsigned char set_timeout; |
290 | 298 | ||
291 | spin_lock(&io_lock); | 299 | spin_lock(&io_lock); |
292 | open_io_config(); | 300 | open_io_config(); |
293 | select_io_device(IODEV_NO); | 301 | select_io_device(IODEV_NO); |
294 | set_timeout = read_io_cr(0xF2); | 302 | set_timeout = read_io_cr(0xF2); |
295 | close_io_config(); | 303 | close_io_config(); |
296 | spin_unlock(&io_lock); | 304 | spin_unlock(&io_lock); |
297 | 305 | ||
298 | return set_timeout; | 306 | return set_timeout; |
299 | } | 307 | } |
300 | 308 | ||
301 | /* disable watchdog */ | 309 | /* disable watchdog */ |
302 | 310 | ||
303 | static void wb_smsc_wdt_disable(void) | 311 | static void wb_smsc_wdt_disable(void) |
304 | { | 312 | { |
305 | // set the timeout to 0 to disable the watchdog | 313 | /* set the timeout to 0 to disable the watchdog */ |
306 | wb_smsc_wdt_set_timeout(0); | 314 | wb_smsc_wdt_set_timeout(0); |
307 | } | 315 | } |
308 | 316 | ||
309 | /* enable watchdog by setting the current timeout */ | 317 | /* enable watchdog by setting the current timeout */ |
310 | 318 | ||
311 | static void wb_smsc_wdt_enable(void) | 319 | static void wb_smsc_wdt_enable(void) |
312 | { | 320 | { |
313 | // set the current timeout... | 321 | /* set the current timeout... */ |
314 | wb_smsc_wdt_set_timeout(timeout); | 322 | wb_smsc_wdt_set_timeout(timeout); |
315 | } | 323 | } |
316 | 324 | ||
317 | /* reset the timer */ | 325 | /* reset the timer */ |
@@ -319,14 +327,14 @@ static void wb_smsc_wdt_enable(void) | |||
319 | static void wb_smsc_wdt_reset_timer(void) | 327 | static void wb_smsc_wdt_reset_timer(void) |
320 | { | 328 | { |
321 | spin_lock(&io_lock); | 329 | spin_lock(&io_lock); |
322 | open_io_config(); | 330 | open_io_config(); |
323 | select_io_device(IODEV_NO); | 331 | select_io_device(IODEV_NO); |
324 | 332 | ||
325 | // reset the timer | 333 | /* reset the timer */ |
326 | wdt_timeout_value(timeout); | 334 | wdt_timeout_value(timeout); |
327 | wdt_timer_conf(0x08); | 335 | wdt_timer_conf(0x08); |
328 | 336 | ||
329 | close_io_config(); | 337 | close_io_config(); |
330 | spin_unlock(&io_lock); | 338 | spin_unlock(&io_lock); |
331 | } | 339 | } |
332 | 340 | ||
@@ -355,7 +363,9 @@ static int wb_smsc_wdt_open(struct inode *inode, struct file *file) | |||
355 | /* Reload and activate timer */ | 363 | /* Reload and activate timer */ |
356 | wb_smsc_wdt_enable(); | 364 | wb_smsc_wdt_enable(); |
357 | 365 | ||
358 | printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); | 366 | printk(KERN_INFO MODNAME |
367 | "Watchdog enabled. Timeout set to %d %s.\n", | ||
368 | timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); | ||
359 | 369 | ||
360 | return nonseekable_open(inode, file); | 370 | return nonseekable_open(inode, file); |
361 | } | 371 | } |
@@ -367,10 +377,12 @@ static int wb_smsc_wdt_release(struct inode *inode, struct file *file) | |||
367 | /* Shut off the timer. */ | 377 | /* Shut off the timer. */ |
368 | 378 | ||
369 | if (expect_close == 42) { | 379 | if (expect_close == 42) { |
370 | wb_smsc_wdt_disable(); | 380 | wb_smsc_wdt_disable(); |
371 | printk(KERN_INFO MODNAME "Watchdog disabled, sleeping again...\n"); | 381 | printk(KERN_INFO MODNAME |
382 | "Watchdog disabled, sleeping again...\n"); | ||
372 | } else { | 383 | } else { |
373 | printk(KERN_CRIT MODNAME "Unexpected close, not stopping watchdog!\n"); | 384 | printk(KERN_CRIT MODNAME |
385 | "Unexpected close, not stopping watchdog!\n"); | ||
374 | wb_smsc_wdt_reset_timer(); | 386 | wb_smsc_wdt_reset_timer(); |
375 | } | 387 | } |
376 | 388 | ||
@@ -392,7 +404,8 @@ static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data, | |||
392 | /* reset expect flag */ | 404 | /* reset expect flag */ |
393 | expect_close = 0; | 405 | expect_close = 0; |
394 | 406 | ||
395 | /* scan to see whether or not we got the magic character */ | 407 | /* scan to see whether or not we got the |
408 | magic character */ | ||
396 | for (i = 0; i != len; i++) { | 409 | for (i = 0; i != len; i++) { |
397 | char c; | 410 | char c; |
398 | if (get_user(c, data+i)) | 411 | if (get_user(c, data+i)) |
@@ -410,8 +423,8 @@ static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data, | |||
410 | 423 | ||
411 | /* ioctl => control interface */ | 424 | /* ioctl => control interface */ |
412 | 425 | ||
413 | static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file, | 426 | static long wb_smsc_wdt_ioctl(struct file *file, |
414 | unsigned int cmd, unsigned long arg) | 427 | unsigned int cmd, unsigned long arg) |
415 | { | 428 | { |
416 | int new_timeout; | 429 | int new_timeout; |
417 | 430 | ||
@@ -420,9 +433,9 @@ static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file, | |||
420 | int __user *i; | 433 | int __user *i; |
421 | } uarg; | 434 | } uarg; |
422 | 435 | ||
423 | static struct watchdog_info ident = { | 436 | static const struct watchdog_info ident = { |
424 | .options = WDIOF_KEEPALIVEPING | | 437 | .options = WDIOF_KEEPALIVEPING | |
425 | WDIOF_SETTIMEOUT | | 438 | WDIOF_SETTIMEOUT | |
426 | WDIOF_MAGICCLOSE, | 439 | WDIOF_MAGICCLOSE, |
427 | .firmware_version = 0, | 440 | .firmware_version = 0, |
428 | .identity = "SMsC 37B787 Watchdog" | 441 | .identity = "SMsC 37B787 Watchdog" |
@@ -431,78 +444,62 @@ static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file, | |||
431 | uarg.i = (int __user *)arg; | 444 | uarg.i = (int __user *)arg; |
432 | 445 | ||
433 | switch (cmd) { | 446 | switch (cmd) { |
434 | default: | 447 | case WDIOC_GETSUPPORT: |
435 | return -ENOTTY; | 448 | return copy_to_user(uarg.ident, &ident, sizeof(ident)) |
436 | 449 | ? -EFAULT : 0; | |
437 | case WDIOC_GETSUPPORT: | 450 | case WDIOC_GETSTATUS: |
438 | return copy_to_user(uarg.ident, &ident, | 451 | return put_user(wb_smsc_wdt_status(), uarg.i); |
439 | sizeof(ident)) ? -EFAULT : 0; | 452 | case WDIOC_GETBOOTSTATUS: |
440 | 453 | return put_user(0, uarg.i); | |
441 | case WDIOC_GETSTATUS: | 454 | case WDIOC_KEEPALIVE: |
442 | return put_user(wb_smsc_wdt_status(), uarg.i); | 455 | wb_smsc_wdt_reset_timer(); |
443 | 456 | return 0; | |
444 | case WDIOC_GETBOOTSTATUS: | 457 | case WDIOC_SETTIMEOUT: |
445 | return put_user(0, uarg.i); | 458 | if (get_user(new_timeout, uarg.i)) |
446 | 459 | return -EFAULT; | |
447 | case WDIOC_KEEPALIVE: | 460 | /* the API states this is given in secs */ |
448 | wb_smsc_wdt_reset_timer(); | 461 | if (unit == UNIT_MINUTE) |
449 | return 0; | 462 | new_timeout /= 60; |
450 | 463 | if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) | |
451 | case WDIOC_SETTIMEOUT: | 464 | return -EINVAL; |
452 | if (get_user(new_timeout, uarg.i)) | 465 | timeout = new_timeout; |
453 | return -EFAULT; | 466 | wb_smsc_wdt_set_timeout(timeout); |
454 | 467 | /* fall through and return the new timeout... */ | |
455 | // the API states this is given in secs | 468 | case WDIOC_GETTIMEOUT: |
456 | if (unit == UNIT_MINUTE) | 469 | new_timeout = timeout; |
457 | new_timeout /= 60; | 470 | if (unit == UNIT_MINUTE) |
458 | |||
459 | if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) | ||
460 | return -EINVAL; | ||
461 | |||
462 | timeout = new_timeout; | ||
463 | wb_smsc_wdt_set_timeout(timeout); | ||
464 | |||
465 | // fall through and return the new timeout... | ||
466 | |||
467 | case WDIOC_GETTIMEOUT: | ||
468 | |||
469 | new_timeout = timeout; | ||
470 | |||
471 | if (unit == UNIT_MINUTE) | ||
472 | new_timeout *= 60; | 471 | new_timeout *= 60; |
472 | return put_user(new_timeout, uarg.i); | ||
473 | case WDIOC_SETOPTIONS: | ||
474 | { | ||
475 | int options, retval = -EINVAL; | ||
473 | 476 | ||
474 | return put_user(new_timeout, uarg.i); | 477 | if (get_user(options, uarg.i)) |
475 | 478 | return -EFAULT; | |
476 | case WDIOC_SETOPTIONS: | ||
477 | { | ||
478 | int options, retval = -EINVAL; | ||
479 | |||
480 | if (get_user(options, uarg.i)) | ||
481 | return -EFAULT; | ||
482 | |||
483 | if (options & WDIOS_DISABLECARD) { | ||
484 | wb_smsc_wdt_disable(); | ||
485 | retval = 0; | ||
486 | } | ||
487 | |||
488 | if (options & WDIOS_ENABLECARD) { | ||
489 | wb_smsc_wdt_enable(); | ||
490 | retval = 0; | ||
491 | } | ||
492 | 479 | ||
493 | return retval; | 480 | if (options & WDIOS_DISABLECARD) { |
481 | wb_smsc_wdt_disable(); | ||
482 | retval = 0; | ||
494 | } | 483 | } |
484 | if (options & WDIOS_ENABLECARD) { | ||
485 | wb_smsc_wdt_enable(); | ||
486 | retval = 0; | ||
487 | } | ||
488 | return retval; | ||
489 | } | ||
490 | default: | ||
491 | return -ENOTTY; | ||
495 | } | 492 | } |
496 | } | 493 | } |
497 | 494 | ||
498 | /* -- Notifier funtions -----------------------------------------*/ | 495 | /* -- Notifier funtions -----------------------------------------*/ |
499 | 496 | ||
500 | static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) | 497 | static int wb_smsc_wdt_notify_sys(struct notifier_block *this, |
498 | unsigned long code, void *unused) | ||
501 | { | 499 | { |
502 | if (code == SYS_DOWN || code == SYS_HALT) | 500 | if (code == SYS_DOWN || code == SYS_HALT) { |
503 | { | 501 | /* set timeout to 0, to avoid possible race-condition */ |
504 | // set timeout to 0, to avoid possible race-condition | 502 | timeout = 0; |
505 | timeout = 0; | ||
506 | wb_smsc_wdt_disable(); | 503 | wb_smsc_wdt_disable(); |
507 | } | 504 | } |
508 | return NOTIFY_DONE; | 505 | return NOTIFY_DONE; |
@@ -510,23 +507,20 @@ static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long cod | |||
510 | 507 | ||
511 | /* -- Module's structures ---------------------------------------*/ | 508 | /* -- Module's structures ---------------------------------------*/ |
512 | 509 | ||
513 | static const struct file_operations wb_smsc_wdt_fops = | 510 | static const struct file_operations wb_smsc_wdt_fops = { |
514 | { | 511 | .owner = THIS_MODULE, |
515 | .owner = THIS_MODULE, | ||
516 | .llseek = no_llseek, | 512 | .llseek = no_llseek, |
517 | .write = wb_smsc_wdt_write, | 513 | .write = wb_smsc_wdt_write, |
518 | .ioctl = wb_smsc_wdt_ioctl, | 514 | .unlocked_ioctl = wb_smsc_wdt_ioctl, |
519 | .open = wb_smsc_wdt_open, | 515 | .open = wb_smsc_wdt_open, |
520 | .release = wb_smsc_wdt_release, | 516 | .release = wb_smsc_wdt_release, |
521 | }; | 517 | }; |
522 | 518 | ||
523 | static struct notifier_block wb_smsc_wdt_notifier = | 519 | static struct notifier_block wb_smsc_wdt_notifier = { |
524 | { | ||
525 | .notifier_call = wb_smsc_wdt_notify_sys, | 520 | .notifier_call = wb_smsc_wdt_notify_sys, |
526 | }; | 521 | }; |
527 | 522 | ||
528 | static struct miscdevice wb_smsc_wdt_miscdev = | 523 | static struct miscdevice wb_smsc_wdt_miscdev = { |
529 | { | ||
530 | .minor = WATCHDOG_MINOR, | 524 | .minor = WATCHDOG_MINOR, |
531 | .name = "watchdog", | 525 | .name = "watchdog", |
532 | .fops = &wb_smsc_wdt_fops, | 526 | .fops = &wb_smsc_wdt_fops, |
@@ -540,39 +534,44 @@ static int __init wb_smsc_wdt_init(void) | |||
540 | { | 534 | { |
541 | int ret; | 535 | int ret; |
542 | 536 | ||
543 | printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n"); | 537 | printk(KERN_INFO "SMsC 37B787 watchdog component driver " |
538 | VERSION " initialising...\n"); | ||
544 | 539 | ||
545 | if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) { | 540 | if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) { |
546 | printk(KERN_ERR MODNAME "Unable to register IO port %#x\n", IOPORT); | 541 | printk(KERN_ERR MODNAME "Unable to register IO port %#x\n", |
542 | IOPORT); | ||
547 | ret = -EBUSY; | 543 | ret = -EBUSY; |
548 | goto out_pnp; | 544 | goto out_pnp; |
549 | } | 545 | } |
550 | 546 | ||
551 | // set new maximum, if it's too big | 547 | /* set new maximum, if it's too big */ |
552 | if (timeout > MAX_TIMEOUT) | 548 | if (timeout > MAX_TIMEOUT) |
553 | timeout = MAX_TIMEOUT; | 549 | timeout = MAX_TIMEOUT; |
554 | 550 | ||
555 | // init the watchdog timer | 551 | /* init the watchdog timer */ |
556 | wb_smsc_wdt_initialize(); | 552 | wb_smsc_wdt_initialize(); |
557 | 553 | ||
558 | ret = register_reboot_notifier(&wb_smsc_wdt_notifier); | 554 | ret = register_reboot_notifier(&wb_smsc_wdt_notifier); |
559 | if (ret) { | 555 | if (ret) { |
560 | printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret); | 556 | printk(KERN_ERR MODNAME |
557 | "Unable to register reboot notifier err = %d\n", ret); | ||
561 | goto out_io; | 558 | goto out_io; |
562 | } | 559 | } |
563 | 560 | ||
564 | ret = misc_register(&wb_smsc_wdt_miscdev); | 561 | ret = misc_register(&wb_smsc_wdt_miscdev); |
565 | if (ret) { | 562 | if (ret) { |
566 | printk(KERN_ERR MODNAME "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); | 563 | printk(KERN_ERR MODNAME |
564 | "Unable to register miscdev on minor %d\n", | ||
565 | WATCHDOG_MINOR); | ||
567 | goto out_rbt; | 566 | goto out_rbt; |
568 | } | 567 | } |
569 | 568 | ||
570 | // output info | 569 | /* output info */ |
571 | printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); | 570 | printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", |
572 | printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout); | 571 | timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); |
573 | 572 | printk(KERN_INFO MODNAME | |
574 | // ret = 0 | 573 | "Watchdog initialized and sleeping (nowayout=%d)...\n", |
575 | 574 | nowayout); | |
576 | out_clean: | 575 | out_clean: |
577 | return ret; | 576 | return ret; |
578 | 577 | ||
@@ -591,8 +590,7 @@ out_pnp: | |||
591 | static void __exit wb_smsc_wdt_exit(void) | 590 | static void __exit wb_smsc_wdt_exit(void) |
592 | { | 591 | { |
593 | /* Stop the timer before we leave */ | 592 | /* Stop the timer before we leave */ |
594 | if (!nowayout) | 593 | if (!nowayout) { |
595 | { | ||
596 | wb_smsc_wdt_shutdown(); | 594 | wb_smsc_wdt_shutdown(); |
597 | printk(KERN_INFO MODNAME "Watchdog disabled.\n"); | 595 | printk(KERN_INFO MODNAME "Watchdog disabled.\n"); |
598 | } | 596 | } |
@@ -601,25 +599,29 @@ static void __exit wb_smsc_wdt_exit(void) | |||
601 | unregister_reboot_notifier(&wb_smsc_wdt_notifier); | 599 | unregister_reboot_notifier(&wb_smsc_wdt_notifier); |
602 | release_region(IOPORT, IOPORT_SIZE); | 600 | release_region(IOPORT, IOPORT_SIZE); |
603 | 601 | ||
604 | printk("SMsC 37B787 watchdog component driver removed.\n"); | 602 | printk(KERN_INFO "SMsC 37B787 watchdog component driver removed.\n"); |
605 | } | 603 | } |
606 | 604 | ||
607 | module_init(wb_smsc_wdt_init); | 605 | module_init(wb_smsc_wdt_init); |
608 | module_exit(wb_smsc_wdt_exit); | 606 | module_exit(wb_smsc_wdt_exit); |
609 | 607 | ||
610 | MODULE_AUTHOR("Sven Anders <anders@anduras.de>"); | 608 | MODULE_AUTHOR("Sven Anders <anders@anduras.de>"); |
611 | MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version " VERSION ")"); | 609 | MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version " |
610 | VERSION ")"); | ||
612 | MODULE_LICENSE("GPL"); | 611 | MODULE_LICENSE("GPL"); |
613 | 612 | ||
614 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 613 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
615 | 614 | ||
616 | #ifdef SMSC_SUPPORT_MINUTES | 615 | #ifdef SMSC_SUPPORT_MINUTES |
617 | module_param(unit, int, 0); | 616 | module_param(unit, int, 0); |
618 | MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0"); | 617 | MODULE_PARM_DESC(unit, |
618 | "set unit to use, 0=seconds or 1=minutes, default is 0"); | ||
619 | #endif | 619 | #endif |
620 | 620 | ||
621 | module_param(timeout, int, 0); | 621 | module_param(timeout, int, 0); |
622 | MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60"); | 622 | MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60"); |
623 | 623 | ||
624 | module_param(nowayout, int, 0); | 624 | module_param(nowayout, int, 0); |
625 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 625 | MODULE_PARM_DESC(nowayout, |
626 | "Watchdog cannot be stopped once started (default=" | ||
627 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||