diff options
Diffstat (limited to 'drivers/watchdog/ibmasr.c')
-rw-r--r-- | drivers/watchdog/ibmasr.c | 149 |
1 files changed, 87 insertions, 62 deletions
diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c index 94155f6136c2..6824bf80b37e 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c | |||
@@ -19,9 +19,8 @@ | |||
19 | #include <linux/miscdevice.h> | 19 | #include <linux/miscdevice.h> |
20 | #include <linux/watchdog.h> | 20 | #include <linux/watchdog.h> |
21 | #include <linux/dmi.h> | 21 | #include <linux/dmi.h> |
22 | 22 | #include <linux/io.h> | |
23 | #include <asm/io.h> | 23 | #include <linux/uaccess.h> |
24 | #include <asm/uaccess.h> | ||
25 | 24 | ||
26 | 25 | ||
27 | enum { | 26 | enum { |
@@ -70,10 +69,13 @@ static char asr_expect_close; | |||
70 | static unsigned int asr_type, asr_base, asr_length; | 69 | static unsigned int asr_type, asr_base, asr_length; |
71 | static unsigned int asr_read_addr, asr_write_addr; | 70 | static unsigned int asr_read_addr, asr_write_addr; |
72 | static unsigned char asr_toggle_mask, asr_disable_mask; | 71 | static unsigned char asr_toggle_mask, asr_disable_mask; |
72 | static spinlock_t asr_lock; | ||
73 | 73 | ||
74 | static void asr_toggle(void) | 74 | static void __asr_toggle(void) |
75 | { | 75 | { |
76 | unsigned char reg = inb(asr_read_addr); | 76 | unsigned char reg; |
77 | |||
78 | reg = inb(asr_read_addr); | ||
77 | 79 | ||
78 | outb(reg & ~asr_toggle_mask, asr_write_addr); | 80 | outb(reg & ~asr_toggle_mask, asr_write_addr); |
79 | reg = inb(asr_read_addr); | 81 | reg = inb(asr_read_addr); |
@@ -83,12 +85,21 @@ static void asr_toggle(void) | |||
83 | 85 | ||
84 | outb(reg & ~asr_toggle_mask, asr_write_addr); | 86 | outb(reg & ~asr_toggle_mask, asr_write_addr); |
85 | reg = inb(asr_read_addr); | 87 | reg = inb(asr_read_addr); |
88 | spin_unlock(&asr_lock); | ||
89 | } | ||
90 | |||
91 | static void asr_toggle(void) | ||
92 | { | ||
93 | spin_lock(&asr_lock); | ||
94 | __asr_toggle(); | ||
95 | spin_unlock(&asr_lock); | ||
86 | } | 96 | } |
87 | 97 | ||
88 | static void asr_enable(void) | 98 | static void asr_enable(void) |
89 | { | 99 | { |
90 | unsigned char reg; | 100 | unsigned char reg; |
91 | 101 | ||
102 | spin_lock(&asr_lock); | ||
92 | if (asr_type == ASMTYPE_TOPAZ) { | 103 | if (asr_type == ASMTYPE_TOPAZ) { |
93 | /* asr_write_addr == asr_read_addr */ | 104 | /* asr_write_addr == asr_read_addr */ |
94 | reg = inb(asr_read_addr); | 105 | reg = inb(asr_read_addr); |
@@ -99,17 +110,21 @@ static void asr_enable(void) | |||
99 | * First make sure the hardware timer is reset by toggling | 110 | * First make sure the hardware timer is reset by toggling |
100 | * ASR hardware timer line. | 111 | * ASR hardware timer line. |
101 | */ | 112 | */ |
102 | asr_toggle(); | 113 | __asr_toggle(); |
103 | 114 | ||
104 | reg = inb(asr_read_addr); | 115 | reg = inb(asr_read_addr); |
105 | outb(reg & ~asr_disable_mask, asr_write_addr); | 116 | outb(reg & ~asr_disable_mask, asr_write_addr); |
106 | } | 117 | } |
107 | reg = inb(asr_read_addr); | 118 | reg = inb(asr_read_addr); |
119 | spin_unlock(&asr_lock); | ||
108 | } | 120 | } |
109 | 121 | ||
110 | static void asr_disable(void) | 122 | static void asr_disable(void) |
111 | { | 123 | { |
112 | unsigned char reg = inb(asr_read_addr); | 124 | unsigned char reg; |
125 | |||
126 | spin_lock(&asr_lock); | ||
127 | reg = inb(asr_read_addr); | ||
113 | 128 | ||
114 | if (asr_type == ASMTYPE_TOPAZ) | 129 | if (asr_type == ASMTYPE_TOPAZ) |
115 | /* asr_write_addr == asr_read_addr */ | 130 | /* asr_write_addr == asr_read_addr */ |
@@ -122,6 +137,7 @@ static void asr_disable(void) | |||
122 | outb(reg | asr_disable_mask, asr_write_addr); | 137 | outb(reg | asr_disable_mask, asr_write_addr); |
123 | } | 138 | } |
124 | reg = inb(asr_read_addr); | 139 | reg = inb(asr_read_addr); |
140 | spin_unlock(&asr_lock); | ||
125 | } | 141 | } |
126 | 142 | ||
127 | static int __init asr_get_base_address(void) | 143 | static int __init asr_get_base_address(void) |
@@ -133,7 +149,8 @@ static int __init asr_get_base_address(void) | |||
133 | 149 | ||
134 | switch (asr_type) { | 150 | switch (asr_type) { |
135 | case ASMTYPE_TOPAZ: | 151 | case ASMTYPE_TOPAZ: |
136 | /* SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F) */ | 152 | /* SELECT SuperIO CHIP FOR QUERYING |
153 | (WRITE 0x07 TO BOTH 0x2E and 0x2F) */ | ||
137 | outb(0x07, 0x2e); | 154 | outb(0x07, 0x2e); |
138 | outb(0x07, 0x2f); | 155 | outb(0x07, 0x2f); |
139 | 156 | ||
@@ -154,14 +171,26 @@ static int __init asr_get_base_address(void) | |||
154 | 171 | ||
155 | case ASMTYPE_JASPER: | 172 | case ASMTYPE_JASPER: |
156 | type = "Jaspers "; | 173 | type = "Jaspers "; |
157 | 174 | #if 0 | |
158 | /* FIXME: need to use pci_config_lock here, but it's not exported */ | 175 | u32 r; |
176 | /* Suggested fix */ | ||
177 | pdev = pci_get_bus_and_slot(0, DEVFN(0x1f, 0)); | ||
178 | if (pdev == NULL) | ||
179 | return -ENODEV; | ||
180 | pci_read_config_dword(pdev, 0x58, &r); | ||
181 | asr_base = r & 0xFFFE; | ||
182 | pci_dev_put(pdev); | ||
183 | #else | ||
184 | /* FIXME: need to use pci_config_lock here, | ||
185 | but it's not exported */ | ||
159 | 186 | ||
160 | /* spin_lock_irqsave(&pci_config_lock, flags);*/ | 187 | /* spin_lock_irqsave(&pci_config_lock, flags);*/ |
161 | 188 | ||
162 | /* Select the SuperIO chip in the PCI I/O port register */ | 189 | /* Select the SuperIO chip in the PCI I/O port register */ |
163 | outl(0x8000f858, 0xcf8); | 190 | outl(0x8000f858, 0xcf8); |
164 | 191 | ||
192 | /* BUS 0, Slot 1F, fnc 0, offset 58 */ | ||
193 | |||
165 | /* | 194 | /* |
166 | * Read the base address for the SuperIO chip. | 195 | * Read the base address for the SuperIO chip. |
167 | * Only the lower 16 bits are valid, but the address is word | 196 | * Only the lower 16 bits are valid, but the address is word |
@@ -170,7 +199,7 @@ static int __init asr_get_base_address(void) | |||
170 | asr_base = inl(0xcfc) & 0xfffe; | 199 | asr_base = inl(0xcfc) & 0xfffe; |
171 | 200 | ||
172 | /* spin_unlock_irqrestore(&pci_config_lock, flags);*/ | 201 | /* spin_unlock_irqrestore(&pci_config_lock, flags);*/ |
173 | 202 | #endif | |
174 | asr_read_addr = asr_write_addr = | 203 | asr_read_addr = asr_write_addr = |
175 | asr_base + JASPER_ASR_REG_OFFSET; | 204 | asr_base + JASPER_ASR_REG_OFFSET; |
176 | asr_toggle_mask = JASPER_ASR_TOGGLE_MASK; | 205 | asr_toggle_mask = JASPER_ASR_TOGGLE_MASK; |
@@ -241,11 +270,10 @@ static ssize_t asr_write(struct file *file, const char __user *buf, | |||
241 | return count; | 270 | return count; |
242 | } | 271 | } |
243 | 272 | ||
244 | static int asr_ioctl(struct inode *inode, struct file *file, | 273 | static long asr_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
245 | unsigned int cmd, unsigned long arg) | ||
246 | { | 274 | { |
247 | static const struct watchdog_info ident = { | 275 | static const struct watchdog_info ident = { |
248 | .options = WDIOF_KEEPALIVEPING | | 276 | .options = WDIOF_KEEPALIVEPING | |
249 | WDIOF_MAGICCLOSE, | 277 | WDIOF_MAGICCLOSE, |
250 | .identity = "IBM ASR" | 278 | .identity = "IBM ASR" |
251 | }; | 279 | }; |
@@ -254,53 +282,45 @@ static int asr_ioctl(struct inode *inode, struct file *file, | |||
254 | int heartbeat; | 282 | int heartbeat; |
255 | 283 | ||
256 | switch (cmd) { | 284 | switch (cmd) { |
257 | case WDIOC_GETSUPPORT: | 285 | case WDIOC_GETSUPPORT: |
258 | return copy_to_user(argp, &ident, sizeof(ident)) ? | 286 | return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; |
259 | -EFAULT : 0; | 287 | case WDIOC_GETSTATUS: |
260 | 288 | case WDIOC_GETBOOTSTATUS: | |
261 | case WDIOC_GETSTATUS: | 289 | return put_user(0, p); |
262 | case WDIOC_GETBOOTSTATUS: | 290 | case WDIOC_KEEPALIVE: |
263 | return put_user(0, p); | 291 | asr_toggle(); |
264 | 292 | return 0; | |
265 | case WDIOC_KEEPALIVE: | 293 | /* |
294 | * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT | ||
295 | * and WDIOC_GETTIMEOUT always returns 256. | ||
296 | */ | ||
297 | case WDIOC_GETTIMEOUT: | ||
298 | heartbeat = 256; | ||
299 | return put_user(heartbeat, p); | ||
300 | case WDIOC_SETOPTIONS: | ||
301 | { | ||
302 | int new_options, retval = -EINVAL; | ||
303 | if (get_user(new_options, p)) | ||
304 | return -EFAULT; | ||
305 | if (new_options & WDIOS_DISABLECARD) { | ||
306 | asr_disable(); | ||
307 | retval = 0; | ||
308 | } | ||
309 | if (new_options & WDIOS_ENABLECARD) { | ||
310 | asr_enable(); | ||
266 | asr_toggle(); | 311 | asr_toggle(); |
267 | return 0; | 312 | retval = 0; |
268 | |||
269 | /* | ||
270 | * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT | ||
271 | * and WDIOC_GETTIMEOUT always returns 256. | ||
272 | */ | ||
273 | case WDIOC_GETTIMEOUT: | ||
274 | heartbeat = 256; | ||
275 | return put_user(heartbeat, p); | ||
276 | |||
277 | case WDIOC_SETOPTIONS: { | ||
278 | int new_options, retval = -EINVAL; | ||
279 | |||
280 | if (get_user(new_options, p)) | ||
281 | return -EFAULT; | ||
282 | |||
283 | if (new_options & WDIOS_DISABLECARD) { | ||
284 | asr_disable(); | ||
285 | retval = 0; | ||
286 | } | ||
287 | |||
288 | if (new_options & WDIOS_ENABLECARD) { | ||
289 | asr_enable(); | ||
290 | asr_toggle(); | ||
291 | retval = 0; | ||
292 | } | ||
293 | |||
294 | return retval; | ||
295 | } | 313 | } |
314 | return retval; | ||
315 | } | ||
316 | default: | ||
317 | return -ENOTTY; | ||
296 | } | 318 | } |
297 | |||
298 | return -ENOTTY; | ||
299 | } | 319 | } |
300 | 320 | ||
301 | static int asr_open(struct inode *inode, struct file *file) | 321 | static int asr_open(struct inode *inode, struct file *file) |
302 | { | 322 | { |
303 | if(test_and_set_bit(0, &asr_is_open)) | 323 | if (test_and_set_bit(0, &asr_is_open)) |
304 | return -EBUSY; | 324 | return -EBUSY; |
305 | 325 | ||
306 | asr_toggle(); | 326 | asr_toggle(); |
@@ -314,7 +334,8 @@ static int asr_release(struct inode *inode, struct file *file) | |||
314 | if (asr_expect_close == 42) | 334 | if (asr_expect_close == 42) |
315 | asr_disable(); | 335 | asr_disable(); |
316 | else { | 336 | else { |
317 | printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); | 337 | printk(KERN_CRIT PFX |
338 | "unexpected close, not stopping watchdog!\n"); | ||
318 | asr_toggle(); | 339 | asr_toggle(); |
319 | } | 340 | } |
320 | clear_bit(0, &asr_is_open); | 341 | clear_bit(0, &asr_is_open); |
@@ -323,12 +344,12 @@ static int asr_release(struct inode *inode, struct file *file) | |||
323 | } | 344 | } |
324 | 345 | ||
325 | static const struct file_operations asr_fops = { | 346 | static const struct file_operations asr_fops = { |
326 | .owner = THIS_MODULE, | 347 | .owner = THIS_MODULE, |
327 | .llseek = no_llseek, | 348 | .llseek = no_llseek, |
328 | .write = asr_write, | 349 | .write = asr_write, |
329 | .ioctl = asr_ioctl, | 350 | .unlocked_ioctl = asr_ioctl, |
330 | .open = asr_open, | 351 | .open = asr_open, |
331 | .release = asr_release, | 352 | .release = asr_release, |
332 | }; | 353 | }; |
333 | 354 | ||
334 | static struct miscdevice asr_miscdev = { | 355 | static struct miscdevice asr_miscdev = { |
@@ -367,6 +388,8 @@ static int __init ibmasr_init(void) | |||
367 | if (!asr_type) | 388 | if (!asr_type) |
368 | return -ENODEV; | 389 | return -ENODEV; |
369 | 390 | ||
391 | spin_lock_init(&asr_lock); | ||
392 | |||
370 | rc = asr_get_base_address(); | 393 | rc = asr_get_base_address(); |
371 | if (rc) | 394 | if (rc) |
372 | return rc; | 395 | return rc; |
@@ -395,7 +418,9 @@ module_init(ibmasr_init); | |||
395 | module_exit(ibmasr_exit); | 418 | module_exit(ibmasr_exit); |
396 | 419 | ||
397 | module_param(nowayout, int, 0); | 420 | module_param(nowayout, int, 0); |
398 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 421 | MODULE_PARM_DESC(nowayout, |
422 | "Watchdog cannot be stopped once started (default=" | ||
423 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
399 | 424 | ||
400 | MODULE_DESCRIPTION("IBM Automatic Server Restart driver"); | 425 | MODULE_DESCRIPTION("IBM Automatic Server Restart driver"); |
401 | MODULE_AUTHOR("Andrey Panin"); | 426 | MODULE_AUTHOR("Andrey Panin"); |