Blame view
drivers/watchdog/sb_wdog.c
8.58 KB
75c752e6c [WATCHDOG] Add su... |
1 2 3 |
/* * Watchdog driver for SiByte SB1 SoCs * |
5b73a41c0 fix my email addr... |
4 |
* Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@lsi.com> |
75c752e6c [WATCHDOG] Add su... |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
* * This driver is intended to make the second of two hardware watchdogs * on the Sibyte 12XX and 11XX SoCs available to the user. There are two * such devices available on the SoC, but it seems that there isn't an * enumeration class for watchdogs in Linux like there is for RTCs. * The second is used rather than the first because it uses IRQ 1, * thereby avoiding all that IRQ 0 problematic nonsense. * * I have not tried this driver on a 1480 processor; it might work * just well enough to really screw things up. * * It is a simple timer, and there is an interrupt that is raised the * first time the timer expires. The second time it expires, the chip * is reset and there is no way to redirect that NMI. Which could * be problematic in some cases where this chip is sitting on the HT * bus and has just taken responsibility for providing a cache block. * Since the reset can't be redirected to the external reset pin, it is * possible that other HT connected processors might hang and not reset. * For Linux, a soft reset would probably be even worse than a hard reset. * There you have it. * * The timer takes 23 bits of a 64 bit register (?) as a count value, * and decrements the count every microsecond, for a max value of * 0x7fffff usec or about 8.3ish seconds. * * This watchdog borrows some user semantics from the softdog driver, * in that if you close the fd, it leaves the watchdog running, unless * you previously wrote a 'V' to the fd, in which case it disables * the watchdog when you close the fd like some other drivers. * * Based on various other watchdog drivers, which are probably all * loosely based on something Alan Cox wrote years ago. * |
29fa0586d [PATCH] Switch al... |
38 39 |
* (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, * All Rights Reserved. |
75c752e6c [WATCHDOG] Add su... |
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 1 or 2 as published by the Free Software Foundation. * */ #include <linux/module.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/fs.h> #include <linux/reboot.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/interrupt.h> #include <asm/sibyte/sb1250.h> #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> #include <asm/sibyte/sb1250_scd.h> |
df3c9de3d [WATCHDOG 41/57] ... |
59 |
static DEFINE_SPINLOCK(sbwd_lock); |
75c752e6c [WATCHDOG] Add su... |
60 61 62 63 64 65 66 67 |
/* * set the initial count value of a timer * * wdog is the iomem address of the cfg register */ void sbwdog_set(char __iomem *wdog, unsigned long t) { |
df3c9de3d [WATCHDOG 41/57] ... |
68 |
spin_lock(&sbwd_lock); |
86913315d Watchdog: sb_wdog... |
69 70 |
__raw_writeb(0, wdog); __raw_writeq(t & 0x7fffffUL, wdog - 0x10); |
df3c9de3d [WATCHDOG 41/57] ... |
71 |
spin_unlock(&sbwd_lock); |
75c752e6c [WATCHDOG] Add su... |
72 73 74 75 76 77 78 79 80 81 |
} /* * cause the timer to [re]load it's initial count and start counting * all over again * * wdog is the iomem address of the cfg register */ void sbwdog_pet(char __iomem *wdog) { |
df3c9de3d [WATCHDOG 41/57] ... |
82 |
spin_lock(&sbwd_lock); |
75c752e6c [WATCHDOG] Add su... |
83 |
__raw_writeb(__raw_readb(wdog) | 1, wdog); |
df3c9de3d [WATCHDOG 41/57] ... |
84 |
spin_unlock(&sbwd_lock); |
75c752e6c [WATCHDOG] Add su... |
85 86 87 88 89 90 91 |
} static unsigned long sbwdog_gate; /* keeps it to one thread only */ static char __iomem *kern_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_0)); static char __iomem *user_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_1)); static unsigned long timeout = 0x7fffffUL; /* useconds: 8.3ish secs. */ static int expect_close; |
df3c9de3d [WATCHDOG 41/57] ... |
92 93 |
static const struct watchdog_info ident = { .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | |
e73a78027 [WATCHDOG] Correc... |
94 |
WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
75c752e6c [WATCHDOG] Add su... |
95 96 97 98 99 100 101 102 103 |
.identity = "SiByte Watchdog", }; /* * Allow only a single thread to walk the dog */ static int sbwdog_open(struct inode *inode, struct file *file) { nonseekable_open(inode, file); |
df3c9de3d [WATCHDOG 41/57] ... |
104 |
if (test_and_set_bit(0, &sbwdog_gate)) |
75c752e6c [WATCHDOG] Add su... |
105 |
return -EBUSY; |
75c752e6c [WATCHDOG] Add su... |
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
__module_get(THIS_MODULE); /* * Activate the timer */ sbwdog_set(user_dog, timeout); __raw_writeb(1, user_dog); return 0; } /* * Put the dog back in the kennel. */ static int sbwdog_release(struct inode *inode, struct file *file) { if (expect_close == 42) { __raw_writeb(0, user_dog); module_put(THIS_MODULE); } else { |
df3c9de3d [WATCHDOG 41/57] ... |
126 127 128 129 |
printk(KERN_CRIT "%s: Unexpected close, not stopping watchdog! ", ident.identity); |
75c752e6c [WATCHDOG] Add su... |
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
sbwdog_pet(user_dog); } clear_bit(0, &sbwdog_gate); expect_close = 0; return 0; } /* * 42 - the answer */ static ssize_t sbwdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { int i; if (len) { /* * restart the timer */ expect_close = 0; for (i = 0; i != len; i++) { char c; |
df3c9de3d [WATCHDOG 41/57] ... |
154 |
if (get_user(c, data + i)) |
75c752e6c [WATCHDOG] Add su... |
155 |
return -EFAULT; |
df3c9de3d [WATCHDOG 41/57] ... |
156 |
if (c == 'V') |
75c752e6c [WATCHDOG] Add su... |
157 |
expect_close = 42; |
75c752e6c [WATCHDOG] Add su... |
158 159 160 161 162 163 |
} sbwdog_pet(user_dog); } return len; } |
df3c9de3d [WATCHDOG 41/57] ... |
164 165 |
static long sbwdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
75c752e6c [WATCHDOG] Add su... |
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
{ int ret = -ENOTTY; unsigned long time; void __user *argp = (void __user *)arg; int __user *p = argp; switch (cmd) { case WDIOC_GETSUPPORT: ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: ret = put_user(0, p); break; |
0c06090c9 [WATCHDOG] Coding... |
181 182 183 184 |
case WDIOC_KEEPALIVE: sbwdog_pet(user_dog); ret = 0; break; |
75c752e6c [WATCHDOG] Add su... |
185 186 |
case WDIOC_SETTIMEOUT: ret = get_user(time, p); |
df3c9de3d [WATCHDOG 41/57] ... |
187 |
if (ret) |
75c752e6c [WATCHDOG] Add su... |
188 |
break; |
75c752e6c [WATCHDOG] Add su... |
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
time *= 1000000; if (time > 0x7fffffUL) { ret = -EINVAL; break; } timeout = time; sbwdog_set(user_dog, timeout); sbwdog_pet(user_dog); case WDIOC_GETTIMEOUT: /* * get the remaining count from the ... count register * which is 1*8 before the config register */ ret = put_user(__raw_readq(user_dog - 8) / 1000000, p); break; |
75c752e6c [WATCHDOG] Add su... |
206 207 208 209 210 211 212 |
} return ret; } /* * Notifier for system down */ |
7944d3a5a [WATCHDOG] more c... |
213 214 |
static int sbwdog_notify_sys(struct notifier_block *this, unsigned long code, void *erf) |
75c752e6c [WATCHDOG] Add su... |
215 216 217 218 219 220 221 222 223 224 225 |
{ if (code == SYS_DOWN || code == SYS_HALT) { /* * sit and sit */ __raw_writeb(0, user_dog); __raw_writeb(0, kern_dog); } return NOTIFY_DONE; } |
df3c9de3d [WATCHDOG 41/57] ... |
226 |
static const struct file_operations sbwdog_fops = { |
75c752e6c [WATCHDOG] Add su... |
227 228 229 |
.owner = THIS_MODULE, .llseek = no_llseek, .write = sbwdog_write, |
df3c9de3d [WATCHDOG 41/57] ... |
230 |
.unlocked_ioctl = sbwdog_ioctl, |
75c752e6c [WATCHDOG] Add su... |
231 232 233 |
.open = sbwdog_open, .release = sbwdog_release, }; |
df3c9de3d [WATCHDOG 41/57] ... |
234 |
static struct miscdevice sbwdog_miscdev = { |
75c752e6c [WATCHDOG] Add su... |
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
.minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &sbwdog_fops, }; static struct notifier_block sbwdog_notifier = { .notifier_call = sbwdog_notify_sys, }; /* * interrupt handler * * doesn't do a whole lot for user, but oh so cleverly written so kernel * code can use it to re-up the watchdog, thereby saving the kernel from * having to create and maintain a timer, just to tickle another timer, * which is just so wrong. */ irqreturn_t sbwdog_interrupt(int irq, void *addr) { unsigned long wd_init; char *wd_cfg_reg = (char *)addr; u8 cfg; cfg = __raw_readb(wd_cfg_reg); wd_init = __raw_readq(wd_cfg_reg - 8) & 0x7fffff; /* * if it's the second watchdog timer, it's for those users */ |
df3c9de3d [WATCHDOG 41/57] ... |
264 |
if (wd_cfg_reg == user_dog) |
a77dba7e4 [WATCHDOG] Some m... |
265 266 267 268 269 |
printk(KERN_CRIT "%s in danger of initiating system reset " "in %ld.%01ld seconds ", ident.identity, wd_init / 1000000, (wd_init / 100000) % 10); |
df3c9de3d [WATCHDOG 41/57] ... |
270 |
else |
75c752e6c [WATCHDOG] Add su... |
271 |
cfg |= 1; |
75c752e6c [WATCHDOG] Add su... |
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
__raw_writeb(cfg, wd_cfg_reg); return IRQ_HANDLED; } static int __init sbwdog_init(void) { int ret; /* * register a reboot notifier */ ret = register_reboot_notifier(&sbwdog_notifier); if (ret) { |
df3c9de3d [WATCHDOG 41/57] ... |
287 288 289 290 |
printk(KERN_ERR "%s: cannot register reboot notifier (err=%d) ", ident.identity, ret); |
75c752e6c [WATCHDOG] Add su... |
291 292 293 294 295 296 |
return ret; } /* * get the resources */ |
75c752e6c [WATCHDOG] Add su... |
297 |
|
86b591288 watchdog: irq: Re... |
298 |
ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED, |
75c752e6c [WATCHDOG] Add su... |
299 300 |
ident.identity, (void *)user_dog); if (ret) { |
df3c9de3d [WATCHDOG 41/57] ... |
301 302 303 |
printk(KERN_ERR "%s: failed to request irq 1 - %d ", ident.identity, ret); |
ae44855ae watchdog: sb_wdog... |
304 |
goto out; |
75c752e6c [WATCHDOG] Add su... |
305 |
} |
df3c9de3d [WATCHDOG 41/57] ... |
306 307 308 309 310 311 |
ret = misc_register(&sbwdog_miscdev); if (ret == 0) { printk(KERN_INFO "%s: timeout is %ld.%ld secs ", ident.identity, timeout / 1000000, (timeout / 100000) % 10); |
ae44855ae watchdog: sb_wdog... |
312 313 314 315 316 |
return 0; } free_irq(1, (void *)user_dog); out: unregister_reboot_notifier(&sbwdog_notifier); |
75c752e6c [WATCHDOG] Add su... |
317 318 319 320 321 322 |
return ret; } static void __exit sbwdog_exit(void) { misc_deregister(&sbwdog_miscdev); |
ae44855ae watchdog: sb_wdog... |
323 324 |
free_irq(1, (void *)user_dog); unregister_reboot_notifier(&sbwdog_notifier); |
75c752e6c [WATCHDOG] Add su... |
325 326 327 328 |
} module_init(sbwdog_init); module_exit(sbwdog_exit); |
5b73a41c0 fix my email addr... |
329 |
MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>"); |
75c752e6c [WATCHDOG] Add su... |
330 331 332 333 |
MODULE_DESCRIPTION("SiByte Watchdog"); module_param(timeout, ulong, 0); MODULE_PARM_DESC(timeout, |
df3c9de3d [WATCHDOG 41/57] ... |
334 |
"Watchdog timeout in microseconds (max/default 8388607 or 8.3ish secs)"); |
75c752e6c [WATCHDOG] Add su... |
335 336 337 338 339 340 341 |
MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); /* * example code that can be put in a platform code area to utilize the * first watchdog timer for the kernels own purpose. |
df3c9de3d [WATCHDOG 41/57] ... |
342 |
void platform_wd_setup(void) |
75c752e6c [WATCHDOG] Add su... |
343 344 |
{ int ret; |
86b591288 watchdog: irq: Re... |
345 |
ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED, |
75c752e6c [WATCHDOG] Add su... |
346 347 |
"Kernel Watchdog", IOADDR(A_SCD_WDOG_CFG_0)); if (ret) { |
df3c9de3d [WATCHDOG 41/57] ... |
348 349 350 |
printk(KERN_CRIT "Watchdog IRQ zero(0) failed to be requested - %d ", ret); |
75c752e6c [WATCHDOG] Add su... |
351 352 353 354 355 |
} } */ |