diff options
author | Guenter Roeck <linux@roeck-us.net> | 2017-12-24 16:04:14 -0500 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2018-01-21 06:56:36 -0500 |
commit | 7cd9d5fff792026a908ccf3c229e1ec84668733c (patch) | |
tree | 1202acefa73063056292a8cff77cf81ba4aeb15d | |
parent | 5bbecc5d3454d1069baf061a2cf0f04d0f0b9e7f (diff) |
watchdog: sp5100_tco: Convert to use watchdog subsystem
Convert to watchdog subsystem. As part of that rework, use devm functions
where possible, and replace almost all static variables with a dynamically
allocated data structure.
Cc: Zoltán Böszörményi <zboszor@pr.hu>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | drivers/watchdog/sp5100_tco.c | 358 |
1 files changed, 102 insertions, 256 deletions
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 1123fad38fdf..bb6c4608c1c0 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c | |||
@@ -24,37 +24,31 @@ | |||
24 | 24 | ||
25 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 25 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
26 | 26 | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/ioport.h> | ||
27 | #include <linux/module.h> | 30 | #include <linux/module.h> |
28 | #include <linux/moduleparam.h> | 31 | #include <linux/moduleparam.h> |
29 | #include <linux/types.h> | ||
30 | #include <linux/miscdevice.h> | ||
31 | #include <linux/watchdog.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/pci.h> | 32 | #include <linux/pci.h> |
35 | #include <linux/ioport.h> | ||
36 | #include <linux/platform_device.h> | 33 | #include <linux/platform_device.h> |
37 | #include <linux/uaccess.h> | 34 | #include <linux/types.h> |
38 | #include <linux/io.h> | 35 | #include <linux/watchdog.h> |
39 | 36 | ||
40 | #include "sp5100_tco.h" | 37 | #include "sp5100_tco.h" |
41 | 38 | ||
42 | /* Module and version information */ | 39 | #define TCO_DRIVER_NAME "sp5100-tco" |
43 | #define TCO_VERSION "0.05" | ||
44 | #define TCO_MODULE_NAME "SP5100 TCO timer" | ||
45 | #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION | ||
46 | 40 | ||
47 | /* internal variables */ | 41 | /* internal variables */ |
48 | static u32 tcobase_phys; | 42 | |
49 | static u32 tco_wdt_fired; | 43 | struct sp5100_tco { |
50 | static void __iomem *tcobase; | 44 | struct watchdog_device wdd; |
51 | static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ | 45 | void __iomem *tcobase; |
52 | static unsigned long timer_alive; | 46 | }; |
53 | static char tco_expect_close; | ||
54 | static struct pci_dev *sp5100_tco_pci; | ||
55 | 47 | ||
56 | /* the watchdog platform device */ | 48 | /* the watchdog platform device */ |
57 | static struct platform_device *sp5100_tco_platform_device; | 49 | static struct platform_device *sp5100_tco_platform_device; |
50 | /* the associated PCI device */ | ||
51 | static struct pci_dev *sp5100_tco_pci; | ||
58 | 52 | ||
59 | /* module parameters */ | 53 | /* module parameters */ |
60 | 54 | ||
@@ -79,55 +73,52 @@ static bool tco_has_sp5100_reg_layout(struct pci_dev *dev) | |||
79 | dev->revision < 0x40; | 73 | dev->revision < 0x40; |
80 | } | 74 | } |
81 | 75 | ||
82 | static void tco_timer_start(void) | 76 | static int tco_timer_start(struct watchdog_device *wdd) |
83 | { | 77 | { |
78 | struct sp5100_tco *tco = watchdog_get_drvdata(wdd); | ||
84 | u32 val; | 79 | u32 val; |
85 | unsigned long flags; | ||
86 | 80 | ||
87 | spin_lock_irqsave(&tco_lock, flags); | 81 | val = readl(SP5100_WDT_CONTROL(tco->tcobase)); |
88 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
89 | val |= SP5100_WDT_START_STOP_BIT; | 82 | val |= SP5100_WDT_START_STOP_BIT; |
90 | writel(val, SP5100_WDT_CONTROL(tcobase)); | 83 | writel(val, SP5100_WDT_CONTROL(tco->tcobase)); |
91 | spin_unlock_irqrestore(&tco_lock, flags); | 84 | |
85 | return 0; | ||
92 | } | 86 | } |
93 | 87 | ||
94 | static void tco_timer_stop(void) | 88 | static int tco_timer_stop(struct watchdog_device *wdd) |
95 | { | 89 | { |
90 | struct sp5100_tco *tco = watchdog_get_drvdata(wdd); | ||
96 | u32 val; | 91 | u32 val; |
97 | unsigned long flags; | ||
98 | 92 | ||
99 | spin_lock_irqsave(&tco_lock, flags); | 93 | val = readl(SP5100_WDT_CONTROL(tco->tcobase)); |
100 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
101 | val &= ~SP5100_WDT_START_STOP_BIT; | 94 | val &= ~SP5100_WDT_START_STOP_BIT; |
102 | writel(val, SP5100_WDT_CONTROL(tcobase)); | 95 | writel(val, SP5100_WDT_CONTROL(tco->tcobase)); |
103 | spin_unlock_irqrestore(&tco_lock, flags); | 96 | |
97 | return 0; | ||
104 | } | 98 | } |
105 | 99 | ||
106 | static void tco_timer_keepalive(void) | 100 | static int tco_timer_ping(struct watchdog_device *wdd) |
107 | { | 101 | { |
102 | struct sp5100_tco *tco = watchdog_get_drvdata(wdd); | ||
108 | u32 val; | 103 | u32 val; |
109 | unsigned long flags; | ||
110 | 104 | ||
111 | spin_lock_irqsave(&tco_lock, flags); | 105 | val = readl(SP5100_WDT_CONTROL(tco->tcobase)); |
112 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
113 | val |= SP5100_WDT_TRIGGER_BIT; | 106 | val |= SP5100_WDT_TRIGGER_BIT; |
114 | writel(val, SP5100_WDT_CONTROL(tcobase)); | 107 | writel(val, SP5100_WDT_CONTROL(tco->tcobase)); |
115 | spin_unlock_irqrestore(&tco_lock, flags); | 108 | |
109 | return 0; | ||
116 | } | 110 | } |
117 | 111 | ||
118 | static int tco_timer_set_heartbeat(int t) | 112 | static int tco_timer_set_timeout(struct watchdog_device *wdd, |
113 | unsigned int t) | ||
119 | { | 114 | { |
120 | unsigned long flags; | 115 | struct sp5100_tco *tco = watchdog_get_drvdata(wdd); |
121 | |||
122 | if (t < 0 || t > 0xffff) | ||
123 | return -EINVAL; | ||
124 | 116 | ||
125 | /* Write new heartbeat to watchdog */ | 117 | /* Write new heartbeat to watchdog */ |
126 | spin_lock_irqsave(&tco_lock, flags); | 118 | writel(t, SP5100_WDT_COUNT(tco->tcobase)); |
127 | writel(t, SP5100_WDT_COUNT(tcobase)); | 119 | |
128 | spin_unlock_irqrestore(&tco_lock, flags); | 120 | wdd->timeout = t; |
129 | 121 | ||
130 | heartbeat = t; | ||
131 | return 0; | 122 | return 0; |
132 | } | 123 | } |
133 | 124 | ||
@@ -182,137 +173,7 @@ static void tco_timer_enable(void) | |||
182 | } | 173 | } |
183 | } | 174 | } |
184 | 175 | ||
185 | /* | 176 | static u32 sp5100_tco_read_pm_reg32(u8 index) |
186 | * /dev/watchdog handling | ||
187 | */ | ||
188 | |||
189 | static int sp5100_tco_open(struct inode *inode, struct file *file) | ||
190 | { | ||
191 | /* /dev/watchdog can only be opened once */ | ||
192 | if (test_and_set_bit(0, &timer_alive)) | ||
193 | return -EBUSY; | ||
194 | |||
195 | /* Reload and activate timer */ | ||
196 | tco_timer_start(); | ||
197 | tco_timer_keepalive(); | ||
198 | return nonseekable_open(inode, file); | ||
199 | } | ||
200 | |||
201 | static int sp5100_tco_release(struct inode *inode, struct file *file) | ||
202 | { | ||
203 | /* Shut off the timer. */ | ||
204 | if (tco_expect_close == 42) { | ||
205 | tco_timer_stop(); | ||
206 | } else { | ||
207 | pr_crit("Unexpected close, not stopping watchdog!\n"); | ||
208 | tco_timer_keepalive(); | ||
209 | } | ||
210 | clear_bit(0, &timer_alive); | ||
211 | tco_expect_close = 0; | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static ssize_t sp5100_tco_write(struct file *file, const char __user *data, | ||
216 | size_t len, loff_t *ppos) | ||
217 | { | ||
218 | /* See if we got the magic character 'V' and reload the timer */ | ||
219 | if (len) { | ||
220 | if (!nowayout) { | ||
221 | size_t i; | ||
222 | |||
223 | /* note: just in case someone wrote the magic character | ||
224 | * five months ago... */ | ||
225 | tco_expect_close = 0; | ||
226 | |||
227 | /* scan to see whether or not we got the magic character | ||
228 | */ | ||
229 | for (i = 0; i != len; i++) { | ||
230 | char c; | ||
231 | if (get_user(c, data + i)) | ||
232 | return -EFAULT; | ||
233 | if (c == 'V') | ||
234 | tco_expect_close = 42; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /* someone wrote to us, we should reload the timer */ | ||
239 | tco_timer_keepalive(); | ||
240 | } | ||
241 | return len; | ||
242 | } | ||
243 | |||
244 | static long sp5100_tco_ioctl(struct file *file, unsigned int cmd, | ||
245 | unsigned long arg) | ||
246 | { | ||
247 | int new_options, retval = -EINVAL; | ||
248 | int new_heartbeat; | ||
249 | void __user *argp = (void __user *)arg; | ||
250 | int __user *p = argp; | ||
251 | static const struct watchdog_info ident = { | ||
252 | .options = WDIOF_SETTIMEOUT | | ||
253 | WDIOF_KEEPALIVEPING | | ||
254 | WDIOF_MAGICCLOSE, | ||
255 | .firmware_version = 0, | ||
256 | .identity = TCO_MODULE_NAME, | ||
257 | }; | ||
258 | |||
259 | switch (cmd) { | ||
260 | case WDIOC_GETSUPPORT: | ||
261 | return copy_to_user(argp, &ident, | ||
262 | sizeof(ident)) ? -EFAULT : 0; | ||
263 | case WDIOC_GETSTATUS: | ||
264 | case WDIOC_GETBOOTSTATUS: | ||
265 | return put_user(0, p); | ||
266 | case WDIOC_SETOPTIONS: | ||
267 | if (get_user(new_options, p)) | ||
268 | return -EFAULT; | ||
269 | if (new_options & WDIOS_DISABLECARD) { | ||
270 | tco_timer_stop(); | ||
271 | retval = 0; | ||
272 | } | ||
273 | if (new_options & WDIOS_ENABLECARD) { | ||
274 | tco_timer_start(); | ||
275 | tco_timer_keepalive(); | ||
276 | retval = 0; | ||
277 | } | ||
278 | return retval; | ||
279 | case WDIOC_KEEPALIVE: | ||
280 | tco_timer_keepalive(); | ||
281 | return 0; | ||
282 | case WDIOC_SETTIMEOUT: | ||
283 | if (get_user(new_heartbeat, p)) | ||
284 | return -EFAULT; | ||
285 | if (tco_timer_set_heartbeat(new_heartbeat)) | ||
286 | return -EINVAL; | ||
287 | tco_timer_keepalive(); | ||
288 | /* Fall through */ | ||
289 | case WDIOC_GETTIMEOUT: | ||
290 | return put_user(heartbeat, p); | ||
291 | default: | ||
292 | return -ENOTTY; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * Kernel Interfaces | ||
298 | */ | ||
299 | |||
300 | static const struct file_operations sp5100_tco_fops = { | ||
301 | .owner = THIS_MODULE, | ||
302 | .llseek = no_llseek, | ||
303 | .write = sp5100_tco_write, | ||
304 | .unlocked_ioctl = sp5100_tco_ioctl, | ||
305 | .open = sp5100_tco_open, | ||
306 | .release = sp5100_tco_release, | ||
307 | }; | ||
308 | |||
309 | static struct miscdevice sp5100_tco_miscdev = { | ||
310 | .minor = WATCHDOG_MINOR, | ||
311 | .name = "watchdog", | ||
312 | .fops = &sp5100_tco_fops, | ||
313 | }; | ||
314 | |||
315 | static u8 sp5100_tco_read_pm_reg32(u8 index) | ||
316 | { | 177 | { |
317 | u32 val = 0; | 178 | u32 val = 0; |
318 | int i; | 179 | int i; |
@@ -323,14 +184,13 @@ static u8 sp5100_tco_read_pm_reg32(u8 index) | |||
323 | return val; | 184 | return val; |
324 | } | 185 | } |
325 | 186 | ||
326 | /* | 187 | static int sp5100_tco_setupdevice(struct device *dev, |
327 | * Init & exit routines | 188 | struct watchdog_device *wdd) |
328 | */ | ||
329 | static int sp5100_tco_setupdevice(struct device *dev) | ||
330 | { | 189 | { |
331 | const char *dev_name = NULL; | 190 | struct sp5100_tco *tco = watchdog_get_drvdata(wdd); |
332 | u32 val; | 191 | const char *dev_name; |
333 | u8 base_addr; | 192 | u8 base_addr; |
193 | u32 val; | ||
334 | int ret; | 194 | int ret; |
335 | 195 | ||
336 | /* | 196 | /* |
@@ -361,8 +221,8 @@ static int sp5100_tco_setupdevice(struct device *dev) | |||
361 | dev_dbg(dev, "Got 0x%04x from indirect I/O\n", val); | 221 | dev_dbg(dev, "Got 0x%04x from indirect I/O\n", val); |
362 | 222 | ||
363 | /* Check MMIO address conflict */ | 223 | /* Check MMIO address conflict */ |
364 | if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, | 224 | if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, |
365 | dev_name)) { | 225 | dev_name)) { |
366 | dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); | 226 | dev_dbg(dev, "MMIO address 0x%04x already in use\n", val); |
367 | /* | 227 | /* |
368 | * Secondly, Find the watchdog timer MMIO address | 228 | * Secondly, Find the watchdog timer MMIO address |
@@ -391,8 +251,8 @@ static int sp5100_tco_setupdevice(struct device *dev) | |||
391 | /* Add the Watchdog Timer offset to base address. */ | 251 | /* Add the Watchdog Timer offset to base address. */ |
392 | val += SB800_PM_WDT_MMIO_OFFSET; | 252 | val += SB800_PM_WDT_MMIO_OFFSET; |
393 | /* Check MMIO address conflict */ | 253 | /* Check MMIO address conflict */ |
394 | if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, | 254 | if (!devm_request_mem_region(dev, val, SP5100_WDT_MEM_MAP_SIZE, |
395 | dev_name)) { | 255 | dev_name)) { |
396 | dev_dbg(dev, "MMIO address 0x%04x already in use\n", | 256 | dev_dbg(dev, "MMIO address 0x%04x already in use\n", |
397 | val); | 257 | val); |
398 | ret = -EBUSY; | 258 | ret = -EBUSY; |
@@ -401,13 +261,11 @@ static int sp5100_tco_setupdevice(struct device *dev) | |||
401 | dev_dbg(dev, "Got 0x%04x from SBResource_MMIO register\n", val); | 261 | dev_dbg(dev, "Got 0x%04x from SBResource_MMIO register\n", val); |
402 | } | 262 | } |
403 | 263 | ||
404 | tcobase_phys = val; | 264 | tco->tcobase = devm_ioremap(dev, val, SP5100_WDT_MEM_MAP_SIZE); |
405 | 265 | if (!tco->tcobase) { | |
406 | tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); | ||
407 | if (!tcobase) { | ||
408 | dev_err(dev, "failed to get tcobase address\n"); | 266 | dev_err(dev, "failed to get tcobase address\n"); |
409 | ret = -ENOMEM; | 267 | ret = -ENOMEM; |
410 | goto unreg_mem_region; | 268 | goto unreg_region; |
411 | } | 269 | } |
412 | 270 | ||
413 | dev_info(dev, "Using 0x%04x for watchdog MMIO address\n", val); | 271 | dev_info(dev, "Using 0x%04x for watchdog MMIO address\n", val); |
@@ -416,107 +274,95 @@ static int sp5100_tco_setupdevice(struct device *dev) | |||
416 | tco_timer_enable(); | 274 | tco_timer_enable(); |
417 | 275 | ||
418 | /* Check that the watchdog action is set to reset the system */ | 276 | /* Check that the watchdog action is set to reset the system */ |
419 | val = readl(SP5100_WDT_CONTROL(tcobase)); | 277 | val = readl(SP5100_WDT_CONTROL(tco->tcobase)); |
420 | /* | 278 | /* |
421 | * Save WatchDogFired status, because WatchDogFired flag is | 279 | * Save WatchDogFired status, because WatchDogFired flag is |
422 | * cleared here. | 280 | * cleared here. |
423 | */ | 281 | */ |
424 | tco_wdt_fired = val & SP5100_WDT_FIRED; | 282 | if (val & SP5100_WDT_FIRED) |
283 | wdd->bootstatus = WDIOF_CARDRESET; | ||
425 | val &= ~SP5100_WDT_ACTION_RESET; | 284 | val &= ~SP5100_WDT_ACTION_RESET; |
426 | writel(val, SP5100_WDT_CONTROL(tcobase)); | 285 | writel(val, SP5100_WDT_CONTROL(tco->tcobase)); |
427 | 286 | ||
428 | /* Set a reasonable heartbeat before we stop the timer */ | 287 | /* Set a reasonable heartbeat before we stop the timer */ |
429 | tco_timer_set_heartbeat(heartbeat); | 288 | tco_timer_set_timeout(wdd, wdd->timeout); |
430 | 289 | ||
431 | /* | 290 | /* |
432 | * Stop the TCO before we change anything so we don't race with | 291 | * Stop the TCO before we change anything so we don't race with |
433 | * a zeroed timer. | 292 | * a zeroed timer. |
434 | */ | 293 | */ |
435 | tco_timer_stop(); | 294 | tco_timer_stop(wdd); |
436 | 295 | ||
437 | release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); | 296 | release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); |
438 | 297 | ||
439 | return 0; | 298 | return 0; |
440 | 299 | ||
441 | unreg_mem_region: | ||
442 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); | ||
443 | unreg_region: | 300 | unreg_region: |
444 | release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); | 301 | release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); |
445 | return ret; | 302 | return ret; |
446 | } | 303 | } |
447 | 304 | ||
305 | static struct watchdog_info sp5100_tco_wdt_info = { | ||
306 | .identity = "SP5100 TCO timer", | ||
307 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
308 | }; | ||
309 | |||
310 | static const struct watchdog_ops sp5100_tco_wdt_ops = { | ||
311 | .owner = THIS_MODULE, | ||
312 | .start = tco_timer_start, | ||
313 | .stop = tco_timer_stop, | ||
314 | .ping = tco_timer_ping, | ||
315 | .set_timeout = tco_timer_set_timeout, | ||
316 | }; | ||
317 | |||
448 | static int sp5100_tco_probe(struct platform_device *pdev) | 318 | static int sp5100_tco_probe(struct platform_device *pdev) |
449 | { | 319 | { |
450 | struct device *dev = &pdev->dev; | 320 | struct device *dev = &pdev->dev; |
321 | struct watchdog_device *wdd; | ||
322 | struct sp5100_tco *tco; | ||
451 | int ret; | 323 | int ret; |
452 | 324 | ||
453 | /* | 325 | tco = devm_kzalloc(dev, sizeof(*tco), GFP_KERNEL); |
454 | * Check whether or not the hardware watchdog is there. If found, then | 326 | if (!tco) |
455 | * set it up. | 327 | return -ENOMEM; |
456 | */ | 328 | |
457 | ret = sp5100_tco_setupdevice(dev); | 329 | wdd = &tco->wdd; |
330 | wdd->parent = dev; | ||
331 | wdd->info = &sp5100_tco_wdt_info; | ||
332 | wdd->ops = &sp5100_tco_wdt_ops; | ||
333 | wdd->timeout = WATCHDOG_HEARTBEAT; | ||
334 | wdd->min_timeout = 1; | ||
335 | wdd->max_timeout = 0xffff; | ||
336 | |||
337 | if (watchdog_init_timeout(wdd, heartbeat, NULL)) | ||
338 | dev_info(dev, "timeout value invalid, using %d\n", | ||
339 | wdd->timeout); | ||
340 | watchdog_set_nowayout(wdd, nowayout); | ||
341 | watchdog_stop_on_reboot(wdd); | ||
342 | watchdog_stop_on_unregister(wdd); | ||
343 | watchdog_set_drvdata(wdd, tco); | ||
344 | |||
345 | ret = sp5100_tco_setupdevice(dev, wdd); | ||
458 | if (ret) | 346 | if (ret) |
459 | return ret; | 347 | return ret; |
460 | 348 | ||
461 | /* Check to see if last reboot was due to watchdog timeout */ | 349 | ret = devm_watchdog_register_device(dev, wdd); |
462 | dev_info(dev, "Last reboot was %striggered by watchdog.\n", | 350 | if (ret) { |
463 | tco_wdt_fired ? "" : "not "); | 351 | dev_err(dev, "cannot register watchdog device (err=%d)\n", ret); |
464 | 352 | return ret; | |
465 | /* | ||
466 | * Check that the heartbeat value is within it's range. | ||
467 | * If not, reset to the default. | ||
468 | */ | ||
469 | if (tco_timer_set_heartbeat(heartbeat)) { | ||
470 | heartbeat = WATCHDOG_HEARTBEAT; | ||
471 | tco_timer_set_heartbeat(heartbeat); | ||
472 | } | ||
473 | |||
474 | ret = misc_register(&sp5100_tco_miscdev); | ||
475 | if (ret != 0) { | ||
476 | dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n", | ||
477 | WATCHDOG_MINOR, ret); | ||
478 | goto exit; | ||
479 | } | 353 | } |
480 | 354 | ||
481 | clear_bit(0, &timer_alive); | ||
482 | |||
483 | /* Show module parameters */ | 355 | /* Show module parameters */ |
484 | dev_info(dev, "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", | 356 | dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n", |
485 | tcobase, heartbeat, nowayout); | 357 | wdd->timeout, nowayout); |
486 | 358 | ||
487 | return 0; | 359 | return 0; |
488 | |||
489 | exit: | ||
490 | iounmap(tcobase); | ||
491 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); | ||
492 | return ret; | ||
493 | } | ||
494 | |||
495 | static int sp5100_tco_remove(struct platform_device *pdev) | ||
496 | { | ||
497 | /* Stop the timer before we leave */ | ||
498 | if (!nowayout) | ||
499 | tco_timer_stop(); | ||
500 | |||
501 | /* Deregister */ | ||
502 | misc_deregister(&sp5100_tco_miscdev); | ||
503 | iounmap(tcobase); | ||
504 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); | ||
505 | |||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | static void sp5100_tco_shutdown(struct platform_device *pdev) | ||
510 | { | ||
511 | tco_timer_stop(); | ||
512 | } | 360 | } |
513 | 361 | ||
514 | static struct platform_driver sp5100_tco_driver = { | 362 | static struct platform_driver sp5100_tco_driver = { |
515 | .probe = sp5100_tco_probe, | 363 | .probe = sp5100_tco_probe, |
516 | .remove = sp5100_tco_remove, | ||
517 | .shutdown = sp5100_tco_shutdown, | ||
518 | .driver = { | 364 | .driver = { |
519 | .name = TCO_MODULE_NAME, | 365 | .name = TCO_DRIVER_NAME, |
520 | }, | 366 | }, |
521 | }; | 367 | }; |
522 | 368 | ||
@@ -555,14 +401,14 @@ static int __init sp5100_tco_init(void) | |||
555 | if (!sp5100_tco_pci) | 401 | if (!sp5100_tco_pci) |
556 | return -ENODEV; | 402 | return -ENODEV; |
557 | 403 | ||
558 | pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); | 404 | pr_info("SP5100/SB800 TCO WatchDog Timer Driver\n"); |
559 | 405 | ||
560 | err = platform_driver_register(&sp5100_tco_driver); | 406 | err = platform_driver_register(&sp5100_tco_driver); |
561 | if (err) | 407 | if (err) |
562 | return err; | 408 | return err; |
563 | 409 | ||
564 | sp5100_tco_platform_device = platform_device_register_simple( | 410 | sp5100_tco_platform_device = |
565 | TCO_MODULE_NAME, -1, NULL, 0); | 411 | platform_device_register_simple(TCO_DRIVER_NAME, -1, NULL, 0); |
566 | if (IS_ERR(sp5100_tco_platform_device)) { | 412 | if (IS_ERR(sp5100_tco_platform_device)) { |
567 | err = PTR_ERR(sp5100_tco_platform_device); | 413 | err = PTR_ERR(sp5100_tco_platform_device); |
568 | goto unreg_platform_driver; | 414 | goto unreg_platform_driver; |