aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sbus
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-08-29 20:05:51 -0400
committerDavid S. Miller <davem@davemloft.net>2008-08-29 20:05:51 -0400
commitc5f8556cb5b8ab020f234191a6071cbeeeabd638 (patch)
tree19ef631808de2f6b2625879fd69eb6162f7e4522 /drivers/sbus
parente25ecd08c43e5111148dc4d847ac7e139ef76888 (diff)
cpwatchdog: Cleanup and convert to pure OF driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/sbus')
-rw-r--r--drivers/sbus/char/cpwatchdog.c1035
1 files changed, 436 insertions, 599 deletions
diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c
index 23abfdfb44f1..c88df62e7055 100644
--- a/drivers/sbus/char/cpwatchdog.c
+++ b/drivers/sbus/char/cpwatchdog.c
@@ -11,6 +11,7 @@
11 * reset 'stopped' watchdogs on affected platforms. 11 * reset 'stopped' watchdogs on affected platforms.
12 * 12 *
13 * Copyright (c) 2000 Eric Brower (ebrower@usa.net) 13 * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
14 * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
14 */ 15 */
15 16
16#include <linux/kernel.h> 17#include <linux/kernel.h>
@@ -25,36 +26,34 @@
25#include <linux/timer.h> 26#include <linux/timer.h>
26#include <linux/smp_lock.h> 27#include <linux/smp_lock.h>
27#include <linux/io.h> 28#include <linux/io.h>
29#include <linux/of.h>
30#include <linux/of_device.h>
31
28#include <asm/irq.h> 32#include <asm/irq.h>
29#include <asm/ebus.h>
30#include <asm/oplib.h>
31#include <asm/uaccess.h> 33#include <asm/uaccess.h>
32 34
33#include <asm/watchdog.h> 35#include <asm/watchdog.h>
34 36
37#define DRIVER_NAME "cpwd"
38#define PFX DRIVER_NAME ": "
39
35#define WD_OBPNAME "watchdog" 40#define WD_OBPNAME "watchdog"
36#define WD_BADMODEL "SUNW,501-5336" 41#define WD_BADMODEL "SUNW,501-5336"
37#define WD_BTIMEOUT (jiffies + (HZ * 1000)) 42#define WD_BTIMEOUT (jiffies + (HZ * 1000))
38#define WD_BLIMIT 0xFFFF 43#define WD_BLIMIT 0xFFFF
39 44
40#define WD0_DEVNAME "watchdog0"
41#define WD1_DEVNAME "watchdog1"
42#define WD2_DEVNAME "watchdog2"
43
44#define WD0_MINOR 212 45#define WD0_MINOR 212
45#define WD1_MINOR 213 46#define WD1_MINOR 213
46#define WD2_MINOR 214 47#define WD2_MINOR 214
47 48
49/* Internal driver definitions. */
50#define WD0_ID 0
51#define WD1_ID 1
52#define WD2_ID 2
53#define WD_NUMDEVS 3
48 54
49/* Internal driver definitions 55#define WD_INTR_OFF 0
50 */ 56#define WD_INTR_ON 1
51#define WD0_ID 0 /* Watchdog0 */
52#define WD1_ID 1 /* Watchdog1 */
53#define WD2_ID 2 /* Watchdog2 */
54#define WD_NUMDEVS 3 /* Device contains 3 timers */
55
56#define WD_INTR_OFF 0 /* Interrupt disable value */
57#define WD_INTR_ON 1 /* Interrupt enable value */
58 57
59#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */ 58#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
60#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */ 59#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
@@ -69,6 +68,29 @@
69#define WD_S_RUNNING 0x01 /* Watchdog device status running */ 68#define WD_S_RUNNING 0x01 /* Watchdog device status running */
70#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */ 69#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
71 70
71struct cpwd {
72 void __iomem *regs;
73 spinlock_t lock;
74
75 unsigned int irq;
76
77 unsigned long timeout;
78 bool enabled;
79 bool reboot;
80 bool broken;
81 bool initialized;
82
83 struct {
84 struct miscdevice misc;
85 void __iomem *regs;
86 u8 intr_mask;
87 u8 runstatus;
88 u16 timeout;
89 } devs[WD_NUMDEVS];
90};
91
92static struct cpwd *cpwd_device;
93
72/* Sun uses Altera PLD EPF8820ATC144-4 94/* Sun uses Altera PLD EPF8820ATC144-4
73 * providing three hardware watchdogs: 95 * providing three hardware watchdogs:
74 * 96 *
@@ -130,40 +152,12 @@
130#define PLD_IMASK (PLD_OFF + 0x00) 152#define PLD_IMASK (PLD_OFF + 0x00)
131#define PLD_STATUS (PLD_OFF + 0x04) 153#define PLD_STATUS (PLD_OFF + 0x04)
132 154
133/* Individual timer structure 155static struct timer_list cpwd_timer;
134 */
135struct wd_timer {
136 __u16 timeout;
137 __u8 intr_mask;
138 unsigned char runstatus;
139 void __iomem *regs;
140};
141
142/* Device structure
143 */
144struct wd_device {
145 int irq;
146 spinlock_t lock;
147 unsigned char isbaddoggie; /* defective PLD */
148 unsigned char opt_enable;
149 unsigned char opt_reboot;
150 unsigned short opt_timeout;
151 unsigned char initialized;
152 struct wd_timer watchdog[WD_NUMDEVS];
153 void __iomem *regs;
154};
155
156static struct wd_device wd_dev = {
157 0, __SPIN_LOCK_UNLOCKED(wd_dev.lock), 0, 0, 0, 0,
158};
159
160static struct timer_list wd_timer;
161 156
162static int wd0_timeout = 0; 157static int wd0_timeout = 0;
163static int wd1_timeout = 0; 158static int wd1_timeout = 0;
164static int wd2_timeout = 0; 159static int wd2_timeout = 0;
165 160
166#ifdef MODULE
167module_param (wd0_timeout, int, 0); 161module_param (wd0_timeout, int, 0);
168MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); 162MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
169module_param (wd1_timeout, int, 0); 163module_param (wd1_timeout, int, 0);
@@ -171,234 +165,313 @@ MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
171module_param (wd2_timeout, int, 0); 165module_param (wd2_timeout, int, 0);
172MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); 166MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
173 167
174MODULE_AUTHOR 168MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
175 ("Eric Brower <ebrower@usa.net>"); 169MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
176MODULE_DESCRIPTION
177 ("Hardware watchdog driver for Sun Microsystems CP1400/1500");
178MODULE_LICENSE("GPL"); 170MODULE_LICENSE("GPL");
179MODULE_SUPPORTED_DEVICE 171MODULE_SUPPORTED_DEVICE("watchdog");
180 ("watchdog"); 172
181#endif /* ifdef MODULE */ 173static void cpwd_writew(u16 val, void __iomem *addr)
174{
175 writew(cpu_to_le16(val), addr);
176}
177static u16 cpwd_readw(void __iomem *addr)
178{
179 u16 val = readw(addr);
180
181 return le16_to_cpu(val);
182}
183
184static void cpwd_writeb(u8 val, void __iomem *addr)
185{
186 writeb(val, addr);
187}
188
189static u8 cpwd_readb(void __iomem *addr)
190{
191 return readb(addr);
192}
182 193
183/* Forward declarations of internal methods 194/* Enable or disable watchdog interrupts
195 * Because of the CP1400 defect this should only be
196 * called during initialzation or by wd_[start|stop]timer()
197 *
198 * index - sub-device index, or -1 for 'all'
199 * enable - non-zero to enable interrupts, zero to disable
184 */ 200 */
185#ifdef WD_DEBUG 201static void cpwd_toggleintr(struct cpwd *p, int index, int enable)
186static void wd_dumpregs(void); 202{
187#endif 203 unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);
188static irqreturn_t wd_interrupt(int irq, void *dev_id); 204 unsigned char setregs =
189static void wd_toggleintr(struct wd_timer* pTimer, int enable); 205 (index == -1) ?
190static void wd_pingtimer(struct wd_timer* pTimer); 206 (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
191static void wd_starttimer(struct wd_timer* pTimer); 207 (p->devs[index].intr_mask);
192static void wd_resetbrokentimer(struct wd_timer* pTimer); 208
193static void wd_stoptimer(struct wd_timer* pTimer); 209 if (enable == WD_INTR_ON)
194static void wd_brokentimer(unsigned long data); 210 curregs &= ~setregs;
195static int wd_getstatus(struct wd_timer* pTimer); 211 else
196 212 curregs |= setregs;
197/* PLD expects words to be written in LSB format, 213
198 * so we must flip all words prior to writing them to regs 214 cpwd_writeb(curregs, p->regs + PLD_IMASK);
215}
216
217/* Restarts timer with maximum limit value and
218 * does not unset 'brokenstop' value.
199 */ 219 */
200static inline unsigned short flip_word(unsigned short word) 220static void cpwd_resetbrokentimer(struct cpwd *p, int index)
201{ 221{
202 return ((word & 0xff) << 8) | ((word >> 8) & 0xff); 222 cpwd_toggleintr(p, index, WD_INTR_ON);
223 cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
203} 224}
204 225
205#define wd_writew(val, addr) (writew(flip_word(val), addr)) 226/* Timer method called to reset stopped watchdogs--
206#define wd_readw(addr) (flip_word(readw(addr))) 227 * because of the PLD bug on CP1400, we cannot mask
207#define wd_writeb(val, addr) (writeb(val, addr)) 228 * interrupts within the PLD so me must continually
208#define wd_readb(addr) (readb(addr)) 229 * reset the timers ad infinitum.
230 */
231static void cpwd_brokentimer(unsigned long data)
232{
233 struct cpwd *p = (struct cpwd *) data;
234 int id, tripped = 0;
235
236 /* kill a running timer instance, in case we
237 * were called directly instead of by kernel timer
238 */
239 if (timer_pending(&cpwd_timer))
240 del_timer(&cpwd_timer);
209 241
242 for (id = 0; id < WD_NUMDEVS; id++) {
243 if (p->devs[id].runstatus & WD_STAT_BSTOP) {
244 ++tripped;
245 cpwd_resetbrokentimer(p, id);
246 }
247 }
210 248
211/* CP1400s seem to have broken PLD implementations-- 249 if (tripped) {
212 * the interrupt_mask register cannot be written, so 250 /* there is at least one timer brokenstopped-- reschedule */
213 * no timer interrupts can be masked within the PLD. 251 cpwd_timer.expires = WD_BTIMEOUT;
252 add_timer(&cpwd_timer);
253 }
254}
255
256/* Reset countdown timer with 'limit' value and continue countdown.
257 * This will not start a stopped timer.
214 */ 258 */
215static inline int wd_isbroken(void) 259static void cpwd_pingtimer(struct cpwd *p, int index)
216{ 260{
217 /* we could test this by read/write/read/restore 261 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
218 * on the interrupt mask register only if OBP 262 cpwd_readw(p->devs[index].regs + WD_DCNTR);
219 * 'watchdog-enable?' == FALSE, but it seems
220 * ubiquitous on CP1400s
221 */
222 char val[32];
223 prom_getproperty(prom_root_node, "model", val, sizeof(val));
224 return((!strcmp(val, WD_BADMODEL)) ? 1 : 0);
225} 263}
226 264
227/* Retrieve watchdog-enable? option from OBP 265/* Stop a running watchdog timer-- the timer actually keeps
228 * Returns 0 if false, 1 if true 266 * running, but the interrupt is masked so that no action is
267 * taken upon expiration.
229 */ 268 */
230static inline int wd_opt_enable(void) 269static void cpwd_stoptimer(struct cpwd *p, int index)
231{ 270{
232 int opt_node; 271 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
272 cpwd_toggleintr(p, index, WD_INTR_OFF);
233 273
234 opt_node = prom_getchild(prom_root_node); 274 if (p->broken) {
235 opt_node = prom_searchsiblings(opt_node, "options"); 275 p->devs[index].runstatus |= WD_STAT_BSTOP;
236 return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1); 276 cpwd_brokentimer((unsigned long) p);
277 }
278 }
237} 279}
238 280
239/* Retrieve watchdog-reboot? option from OBP 281/* Start a watchdog timer with the specified limit value
240 * Returns 0 if false, 1 if true 282 * If the watchdog is running, it will be restarted with
283 * the provided limit value.
284 *
285 * This function will enable interrupts on the specified
286 * watchdog.
241 */ 287 */
242static inline int wd_opt_reboot(void) 288static void cpwd_starttimer(struct cpwd *p, int index)
243{ 289{
244 int opt_node; 290 if (p->broken)
291 p->devs[index].runstatus &= ~WD_STAT_BSTOP;
292
293 p->devs[index].runstatus &= ~WD_STAT_SVCD;
245 294
246 opt_node = prom_getchild(prom_root_node); 295 cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);
247 opt_node = prom_searchsiblings(opt_node, "options"); 296 cpwd_toggleintr(p, index, WD_INTR_ON);
248 return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1);
249} 297}
250 298
251/* Retrieve watchdog-timeout option from OBP 299static int cpwd_getstatus(struct cpwd *p, int index)
252 * Returns OBP value, or 0 if not located
253 */
254static inline int wd_opt_timeout(void)
255{ 300{
256 int opt_node; 301 unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);
257 char value[32]; 302 unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);
258 char *p = value; 303 unsigned char ret = WD_STOPPED;
259 304
260 opt_node = prom_getchild(prom_root_node); 305 /* determine STOPPED */
261 opt_node = prom_searchsiblings(opt_node, "options"); 306 if (!stat)
262 opt_node = prom_getproperty(opt_node, 307 return ret;
263 "watchdog-timeout", 308
264 value, 309 /* determine EXPIRED vs FREERUN vs RUNNING */
265 sizeof(value)); 310 else if (WD_S_EXPIRED & stat) {
266 if(-1 != opt_node) { 311 ret = WD_EXPIRED;
267 /* atoi implementation */ 312 } else if(WD_S_RUNNING & stat) {
268 for(opt_node = 0; /* nop */; p++) { 313 if (intr & p->devs[index].intr_mask) {
269 if(*p >= '0' && *p <= '9') { 314 ret = WD_FREERUN;
270 opt_node = (10*opt_node)+(*p-'0'); 315 } else {
271 } 316 /* Fudge WD_EXPIRED status for defective CP1400--
272 else { 317 * IF timer is running
273 break; 318 * AND brokenstop is set
319 * AND an interrupt has been serviced
320 * we are WD_EXPIRED.
321 *
322 * IF timer is running
323 * AND brokenstop is set
324 * AND no interrupt has been serviced
325 * we are WD_FREERUN.
326 */
327 if (p->broken &&
328 (p->devs[index].runstatus & WD_STAT_BSTOP)) {
329 if (p->devs[index].runstatus & WD_STAT_SVCD) {
330 ret = WD_EXPIRED;
331 } else {
332 /* we could as well pretend we are expired */
333 ret = WD_FREERUN;
334 }
335 } else {
336 ret = WD_RUNNING;
274 } 337 }
275 } 338 }
276 } 339 }
277 return((-1 == opt_node) ? (0) : (opt_node)); 340
341 /* determine SERVICED */
342 if (p->devs[index].runstatus & WD_STAT_SVCD)
343 ret |= WD_SERVICED;
344
345 return(ret);
346}
347
348static irqreturn_t cpwd_interrupt(int irq, void *dev_id)
349{
350 struct cpwd *p = dev_id;
351
352 /* Only WD0 will interrupt-- others are NMI and we won't
353 * see them here....
354 */
355 spin_lock_irq(&p->lock);
356
357 cpwd_stoptimer(p, WD0_ID);
358 p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;
359
360 spin_unlock_irq(&p->lock);
361
362 return IRQ_HANDLED;
278} 363}
279 364
280static int wd_open(struct inode *inode, struct file *f) 365static int cpwd_open(struct inode *inode, struct file *f)
281{ 366{
367 struct cpwd *p = cpwd_device;
368
282 lock_kernel(); 369 lock_kernel();
283 switch(iminor(inode)) 370 switch(iminor(inode)) {
284 {
285 case WD0_MINOR: 371 case WD0_MINOR:
286 f->private_data = &wd_dev.watchdog[WD0_ID];
287 break;
288 case WD1_MINOR: 372 case WD1_MINOR:
289 f->private_data = &wd_dev.watchdog[WD1_ID];
290 break;
291 case WD2_MINOR: 373 case WD2_MINOR:
292 f->private_data = &wd_dev.watchdog[WD2_ID];
293 break; 374 break;
375
294 default: 376 default:
295 unlock_kernel(); 377 unlock_kernel();
296 return(-ENODEV); 378 return -ENODEV;
297 } 379 }
298 380
299 /* Register IRQ on first open of device */ 381 /* Register IRQ on first open of device */
300 if(0 == wd_dev.initialized) 382 if (!p->initialized) {
301 { 383 if (request_irq(p->irq, &cpwd_interrupt,
302 if (request_irq(wd_dev.irq, 384 IRQF_SHARED, DRIVER_NAME, p)) {
303 &wd_interrupt, 385 printk(KERN_ERR PFX "Cannot register IRQ %d\n",
304 IRQF_SHARED, 386 p->irq);
305 WD_OBPNAME,
306 (void *)wd_dev.regs)) {
307 printk("%s: Cannot register IRQ %d\n",
308 WD_OBPNAME, wd_dev.irq);
309 unlock_kernel(); 387 unlock_kernel();
310 return(-EBUSY); 388 return -EBUSY;
311 } 389 }
312 wd_dev.initialized = 1; 390 p->initialized = true;
313 } 391 }
314 392
315 unlock_kernel(); 393 unlock_kernel();
316 return(nonseekable_open(inode, f)); 394
395 return nonseekable_open(inode, f);
317} 396}
318 397
319static int wd_release(struct inode *inode, struct file *file) 398static int cpwd_release(struct inode *inode, struct file *file)
320{ 399{
321 return 0; 400 return 0;
322} 401}
323 402
324static int wd_ioctl(struct inode *inode, struct file *file, 403static int cpwd_ioctl(struct inode *inode, struct file *file,
325 unsigned int cmd, unsigned long arg) 404 unsigned int cmd, unsigned long arg)
326{ 405{
327 int setopt = 0; 406 static struct watchdog_info info = {
328 struct wd_timer* pTimer = (struct wd_timer*)file->private_data; 407 .options = WDIOF_SETTIMEOUT,
329 void __user *argp = (void __user *)arg; 408 .firmware_version = 1,
330 struct watchdog_info info = { 409 .identity = DRIVER_NAME,
331 0,
332 0,
333 "Altera EPF8820ATC144-4"
334 }; 410 };
411 void __user *argp = (void __user *)arg;
412 int index = iminor(inode) - WD0_MINOR;
413 struct cpwd *p = cpwd_device;
414 int setopt = 0;
335 415
336 if(NULL == pTimer) { 416 switch (cmd) {
337 return(-EINVAL); 417 /* Generic Linux IOCTLs */
338 } 418 case WDIOC_GETSUPPORT:
419 if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))
420 return -EFAULT;
421 break;
339 422
340 switch(cmd) 423 case WDIOC_GETSTATUS:
341 { 424 case WDIOC_GETBOOTSTATUS:
342 /* Generic Linux IOCTLs */ 425 if (put_user(0, (int __user *)argp))
343 case WDIOC_GETSUPPORT: 426 return -EFAULT;
344 if(copy_to_user(argp, &info, sizeof(struct watchdog_info))) { 427 break;
345 return(-EFAULT); 428
346 } 429 case WDIOC_KEEPALIVE:
347 break; 430 cpwd_pingtimer(p, index);
348 case WDIOC_GETSTATUS: 431 break;
349 case WDIOC_GETBOOTSTATUS: 432
350 if (put_user(0, (int __user *)argp)) 433 case WDIOC_SETOPTIONS:
351 return -EFAULT; 434 if (copy_from_user(&setopt, argp, sizeof(unsigned int)))
352 break; 435 return -EFAULT;
353 case WDIOC_KEEPALIVE: 436
354 wd_pingtimer(pTimer); 437 if (setopt & WDIOS_DISABLECARD) {
355 break; 438 if (p->enabled)
356 case WDIOC_SETOPTIONS: 439 return -EINVAL;
357 if(copy_from_user(&setopt, argp, sizeof(unsigned int))) { 440 cpwd_stoptimer(p, index);
358 return -EFAULT; 441 } else if (setopt & WDIOS_ENABLECARD) {
359 } 442 cpwd_starttimer(p, index);
360 if(setopt & WDIOS_DISABLECARD) { 443 } else {
361 if(wd_dev.opt_enable) { 444 return -EINVAL;
362 printk( 445 }
363 "%s: cannot disable watchdog in ENABLED mode\n", 446 break;
364 WD_OBPNAME); 447
365 return(-EINVAL); 448 /* Solaris-compatible IOCTLs */
366 } 449 case WIOCGSTAT:
367 wd_stoptimer(pTimer); 450 setopt = cpwd_getstatus(p, index);
368 } 451 if (copy_to_user(argp, &setopt, sizeof(unsigned int)))
369 else if(setopt & WDIOS_ENABLECARD) { 452 return -EFAULT;
370 wd_starttimer(pTimer); 453 break;
371 } 454
372 else { 455 case WIOCSTART:
373 return(-EINVAL); 456 cpwd_starttimer(p, index);
374 } 457 break;
375 break; 458
376 /* Solaris-compatible IOCTLs */ 459 case WIOCSTOP:
377 case WIOCGSTAT: 460 if (p->enabled)
378 setopt = wd_getstatus(pTimer);
379 if(copy_to_user(argp, &setopt, sizeof(unsigned int))) {
380 return(-EFAULT);
381 }
382 break;
383 case WIOCSTART:
384 wd_starttimer(pTimer);
385 break;
386 case WIOCSTOP:
387 if(wd_dev.opt_enable) {
388 printk("%s: cannot disable watchdog in ENABLED mode\n",
389 WD_OBPNAME);
390 return(-EINVAL);
391 }
392 wd_stoptimer(pTimer);
393 break;
394 default:
395 return(-EINVAL); 461 return(-EINVAL);
462
463 cpwd_stoptimer(p, index);
464 break;
465
466 default:
467 return -EINVAL;
396 } 468 }
397 return(0); 469
470 return 0;
398} 471}
399 472
400static long wd_compat_ioctl(struct file *file, unsigned int cmd, 473static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
401 unsigned long arg) 474 unsigned long arg)
402{ 475{
403 int rval = -ENOIOCTLCMD; 476 int rval = -ENOIOCTLCMD;
404 477
@@ -408,9 +481,10 @@ static long wd_compat_ioctl(struct file *file, unsigned int cmd,
408 case WIOCSTOP: 481 case WIOCSTOP:
409 case WIOCGSTAT: 482 case WIOCGSTAT:
410 lock_kernel(); 483 lock_kernel();
411 rval = wd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); 484 rval = cpwd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
412 unlock_kernel(); 485 unlock_kernel();
413 break; 486 break;
487
414 /* everything else is handled by the generic compat layer */ 488 /* everything else is handled by the generic compat layer */
415 default: 489 default:
416 break; 490 break;
@@ -419,440 +493,203 @@ static long wd_compat_ioctl(struct file *file, unsigned int cmd,
419 return rval; 493 return rval;
420} 494}
421 495
422static ssize_t wd_write(struct file *file, 496static ssize_t cpwd_write(struct file *file, const char __user *buf,
423 const char __user *buf, 497 size_t count, loff_t *ppos)
424 size_t count,
425 loff_t *ppos)
426{ 498{
427 struct wd_timer* pTimer = (struct wd_timer*)file->private_data; 499 struct inode *inode = file->f_path.dentry->d_inode;
428 500 struct cpwd *p = cpwd_device;
429 if(NULL == pTimer) { 501 int index = iminor(inode);
430 return(-EINVAL);
431 }
432 502
433 if (count) { 503 if (count) {
434 wd_pingtimer(pTimer); 504 cpwd_pingtimer(p, index);
435 return 1; 505 return 1;
436 } 506 }
437 return 0;
438}
439 507
440static ssize_t wd_read(struct file * file, char __user *buffer, 508 return 0;
441 size_t count, loff_t *ppos)
442{
443#ifdef WD_DEBUG
444 wd_dumpregs();
445 return(0);
446#else
447 return(-EINVAL);
448#endif /* ifdef WD_DEBUG */
449} 509}
450 510
451static irqreturn_t wd_interrupt(int irq, void *dev_id) 511static ssize_t cpwd_read(struct file * file, char __user *buffer,
512 size_t count, loff_t *ppos)
452{ 513{
453 /* Only WD0 will interrupt-- others are NMI and we won't 514 return -EINVAL;
454 * see them here....
455 */
456 spin_lock_irq(&wd_dev.lock);
457 if((unsigned long)wd_dev.regs == (unsigned long)dev_id)
458 {
459 wd_stoptimer(&wd_dev.watchdog[WD0_ID]);
460 wd_dev.watchdog[WD0_ID].runstatus |= WD_STAT_SVCD;
461 }
462 spin_unlock_irq(&wd_dev.lock);
463 return IRQ_HANDLED;
464} 515}
465 516
466static const struct file_operations wd_fops = { 517static const struct file_operations cpwd_fops = {
467 .owner = THIS_MODULE, 518 .owner = THIS_MODULE,
468 .ioctl = wd_ioctl, 519 .ioctl = cpwd_ioctl,
469 .compat_ioctl = wd_compat_ioctl, 520 .compat_ioctl = cpwd_compat_ioctl,
470 .open = wd_open, 521 .open = cpwd_open,
471 .write = wd_write, 522 .write = cpwd_write,
472 .read = wd_read, 523 .read = cpwd_read,
473 .release = wd_release, 524 .release = cpwd_release,
474}; 525};
475 526
476static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops }; 527static int __devinit cpwd_probe(struct of_device *op,
477static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops }; 528 const struct of_device_id *match)
478static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops };
479
480#ifdef WD_DEBUG
481static void wd_dumpregs(void)
482{ 529{
483 /* Reading from downcounters initiates watchdog countdown-- 530 struct device_node *options;
484 * Example is included below for illustration purposes. 531 const char *str_prop;
485 */ 532 const void *prop_val;
486 int i; 533 int i, err = -EINVAL;
487 printk("%s: dumping register values\n", WD_OBPNAME); 534 struct cpwd *p;
488 for(i = WD0_ID; i < WD_NUMDEVS; ++i) {
489 /* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n",
490 * WD_OBPNAME,
491 * i,
492 * (unsigned long)(&wd_dev.watchdog[i].regs->dcntr),
493 * readw(&wd_dev.watchdog[i].regs->dcntr));
494 */
495 printk("\t%s%i: limit at 0x%lx: 0x%x\n",
496 WD_OBPNAME,
497 i,
498 (unsigned long)(&wd_dev.watchdog[i].regs->limit),
499 readw(&wd_dev.watchdog[i].regs->limit));
500 printk("\t%s%i: status at 0x%lx: 0x%x\n",
501 WD_OBPNAME,
502 i,
503 (unsigned long)(&wd_dev.watchdog[i].regs->status),
504 readb(&wd_dev.watchdog[i].regs->status));
505 printk("\t%s%i: driver status: 0x%x\n",
506 WD_OBPNAME,
507 i,
508 wd_getstatus(&wd_dev.watchdog[i]));
509 }
510 printk("\tintr_mask at %p: 0x%x\n",
511 wd_dev.regs + PLD_IMASK,
512 readb(wd_dev.regs + PLD_IMASK));
513 printk("\tpld_status at %p: 0x%x\n",
514 wd_dev.regs + PLD_STATUS,
515 readb(wd_dev.regs + PLD_STATUS));
516}
517#endif
518
519/* Enable or disable watchdog interrupts
520 * Because of the CP1400 defect this should only be
521 * called during initialzation or by wd_[start|stop]timer()
522 *
523 * pTimer - pointer to timer device, or NULL to indicate all timers
524 * enable - non-zero to enable interrupts, zero to disable
525 */
526static void wd_toggleintr(struct wd_timer* pTimer, int enable)
527{
528 unsigned char curregs = wd_readb(wd_dev.regs + PLD_IMASK);
529 unsigned char setregs =
530 (NULL == pTimer) ?
531 (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
532 (pTimer->intr_mask);
533
534 (WD_INTR_ON == enable) ?
535 (curregs &= ~setregs):
536 (curregs |= setregs);
537 535
538 wd_writeb(curregs, wd_dev.regs + PLD_IMASK); 536 if (cpwd_device)
539 return; 537 return -EINVAL;
540}
541 538
542/* Reset countdown timer with 'limit' value and continue countdown. 539 p = kzalloc(sizeof(*p), GFP_KERNEL);
543 * This will not start a stopped timer. 540 err = -ENOMEM;
544 * 541 if (!p) {
545 * pTimer - pointer to timer device 542 printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n");
546 */ 543 goto out;
547static void wd_pingtimer(struct wd_timer* pTimer)
548{
549 if (wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
550 wd_readw(pTimer->regs + WD_DCNTR);
551 } 544 }
552}
553 545
554/* Stop a running watchdog timer-- the timer actually keeps 546 p->irq = op->irqs[0];
555 * running, but the interrupt is masked so that no action is
556 * taken upon expiration.
557 *
558 * pTimer - pointer to timer device
559 */
560static void wd_stoptimer(struct wd_timer* pTimer)
561{
562 if(wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
563 wd_toggleintr(pTimer, WD_INTR_OFF);
564 547
565 if(wd_dev.isbaddoggie) { 548 spin_lock_init(&p->lock);
566 pTimer->runstatus |= WD_STAT_BSTOP;
567 wd_brokentimer((unsigned long)&wd_dev);
568 }
569 }
570}
571 549
572/* Start a watchdog timer with the specified limit value 550 p->regs = of_ioremap(&op->resource[0], 0,
573 * If the watchdog is running, it will be restarted with 551 4 * WD_TIMER_REGSZ, DRIVER_NAME);
574 * the provided limit value. 552 if (!p->regs) {
575 * 553 printk(KERN_ERR PFX "Unable to map registers.\n");
576 * This function will enable interrupts on the specified 554 goto out_free;
577 * watchdog.
578 *
579 * pTimer - pointer to timer device
580 * limit - limit (countdown) value in 1/10th seconds
581 */
582static void wd_starttimer(struct wd_timer* pTimer)
583{
584 if(wd_dev.isbaddoggie) {
585 pTimer->runstatus &= ~WD_STAT_BSTOP;
586 } 555 }
587 pTimer->runstatus &= ~WD_STAT_SVCD;
588 556
589 wd_writew(pTimer->timeout, pTimer->regs + WD_LIMIT); 557 options = of_find_node_by_path("/options");
590 wd_toggleintr(pTimer, WD_INTR_ON); 558 err = -ENODEV;
591} 559 if (!options) {
560 printk(KERN_ERR PFX "Unable to find /options node.\n");
561 goto out_iounmap;
562 }
592 563
593/* Restarts timer with maximum limit value and 564 prop_val = of_get_property(options, "watchdog-enable?", NULL);
594 * does not unset 'brokenstop' value. 565 p->enabled = (prop_val ? true : false);
595 */
596static void wd_resetbrokentimer(struct wd_timer* pTimer)
597{
598 wd_toggleintr(pTimer, WD_INTR_ON);
599 wd_writew(WD_BLIMIT, pTimer->regs + WD_LIMIT);
600}
601 566
602/* Timer device initialization helper. 567 prop_val = of_get_property(options, "watchdog-reboot?", NULL);
603 * Returns 0 on success, other on failure 568 p->reboot = (prop_val ? true : false);
604 */
605static int wd_inittimer(int whichdog)
606{
607 struct miscdevice *whichmisc;
608 void __iomem *whichregs;
609 char whichident[8];
610 int whichmask;
611 __u16 whichlimit;
612 569
613 switch(whichdog) 570 str_prop = of_get_property(options, "watchdog-timeout", NULL);
614 { 571 if (str_prop)
615 case WD0_ID: 572 p->timeout = simple_strtoul(str_prop, NULL, 10);
616 whichmisc = &wd0_miscdev;
617 strcpy(whichident, "RIC");
618 whichregs = wd_dev.regs + WD0_OFF;
619 whichmask = WD0_INTR_MASK;
620 whichlimit= (0 == wd0_timeout) ?
621 (wd_dev.opt_timeout):
622 (wd0_timeout);
623 break;
624 case WD1_ID:
625 whichmisc = &wd1_miscdev;
626 strcpy(whichident, "XIR");
627 whichregs = wd_dev.regs + WD1_OFF;
628 whichmask = WD1_INTR_MASK;
629 whichlimit= (0 == wd1_timeout) ?
630 (wd_dev.opt_timeout):
631 (wd1_timeout);
632 break;
633 case WD2_ID:
634 whichmisc = &wd2_miscdev;
635 strcpy(whichident, "POR");
636 whichregs = wd_dev.regs + WD2_OFF;
637 whichmask = WD2_INTR_MASK;
638 whichlimit= (0 == wd2_timeout) ?
639 (wd_dev.opt_timeout):
640 (wd2_timeout);
641 break;
642 default:
643 printk("%s: %s: invalid watchdog id: %i\n",
644 WD_OBPNAME, __func__, whichdog);
645 return(1);
646 }
647 if(0 != misc_register(whichmisc))
648 {
649 return(1);
650 }
651 wd_dev.watchdog[whichdog].regs = whichregs;
652 wd_dev.watchdog[whichdog].timeout = whichlimit;
653 wd_dev.watchdog[whichdog].intr_mask = whichmask;
654 wd_dev.watchdog[whichdog].runstatus &= ~WD_STAT_BSTOP;
655 wd_dev.watchdog[whichdog].runstatus |= WD_STAT_INIT;
656
657 printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n",
658 WD_OBPNAME,
659 whichdog,
660 whichident,
661 wd_dev.watchdog[whichdog].timeout / 10,
662 wd_dev.watchdog[whichdog].timeout % 10,
663 (0 != wd_dev.opt_enable) ? "in ENABLED mode" : "");
664 return(0);
665}
666 573
667/* Timer method called to reset stopped watchdogs-- 574 /* CP1400s seem to have broken PLD implementations-- the
668 * because of the PLD bug on CP1400, we cannot mask 575 * interrupt_mask register cannot be written, so no timer
669 * interrupts within the PLD so me must continually 576 * interrupts can be masked within the PLD.
670 * reset the timers ad infinitum.
671 */
672static void wd_brokentimer(unsigned long data)
673{
674 struct wd_device* pDev = (struct wd_device*)data;
675 int id, tripped = 0;
676
677 /* kill a running timer instance, in case we
678 * were called directly instead of by kernel timer
679 */ 577 */
680 if(timer_pending(&wd_timer)) { 578 str_prop = of_get_property(op->node, "model", NULL);
681 del_timer(&wd_timer); 579 p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
682 } 580
683 581 if (!p->enabled)
684 for(id = WD0_ID; id < WD_NUMDEVS; ++id) { 582 cpwd_toggleintr(p, -1, WD_INTR_OFF);
685 if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) { 583
686 ++tripped; 584 for (i = 0; i < WD_NUMDEVS; i++) {
687 wd_resetbrokentimer(&pDev->watchdog[id]); 585 static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
586 static int *parms[] = { &wd0_timeout,
587 &wd1_timeout,
588 &wd2_timeout };
589 struct miscdevice *mp = &p->devs[i].misc;
590
591 mp->minor = WD0_MINOR + i;
592 mp->name = cpwd_names[i];
593 mp->fops = &cpwd_fops;
594
595 p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
596 p->devs[i].intr_mask = (WD0_INTR_MASK << i);
597 p->devs[i].runstatus &= ~WD_STAT_BSTOP;
598 p->devs[i].runstatus |= WD_STAT_INIT;
599 p->devs[i].timeout = p->timeout;
600 if (*parms[i])
601 p->devs[i].timeout = *parms[i];
602
603 err = misc_register(&p->devs[i].misc);
604 if (err) {
605 printk(KERN_ERR "Could not register misc device for "
606 "dev %d\n", i);
607 goto out_unregister;
688 } 608 }
689 } 609 }
690 610
691 if(tripped) { 611 if (p->broken) {
692 /* there is at least one timer brokenstopped-- reschedule */ 612 init_timer(&cpwd_timer);
693 init_timer(&wd_timer); 613 cpwd_timer.function = cpwd_brokentimer;
694 wd_timer.expires = WD_BTIMEOUT; 614 cpwd_timer.data = (unsigned long) p;
695 add_timer(&wd_timer); 615 cpwd_timer.expires = WD_BTIMEOUT;
616
617 printk(KERN_INFO PFX "PLD defect workaround enabled for "
618 "model " WD_BADMODEL ".\n");
696 } 619 }
697}
698 620
699static int wd_getstatus(struct wd_timer* pTimer) 621 dev_set_drvdata(&op->dev, p);
700{ 622 cpwd_device = p;
701 unsigned char stat = wd_readb(pTimer->regs + WD_STATUS); 623 err = 0;
702 unsigned char intr = wd_readb(wd_dev.regs + PLD_IMASK);
703 unsigned char ret = WD_STOPPED;
704 624
705 /* determine STOPPED */ 625out:
706 if(0 == stat ) { 626 return err;
707 return(ret);
708 }
709 /* determine EXPIRED vs FREERUN vs RUNNING */
710 else if(WD_S_EXPIRED & stat) {
711 ret = WD_EXPIRED;
712 }
713 else if(WD_S_RUNNING & stat) {
714 if(intr & pTimer->intr_mask) {
715 ret = WD_FREERUN;
716 }
717 else {
718 /* Fudge WD_EXPIRED status for defective CP1400--
719 * IF timer is running
720 * AND brokenstop is set
721 * AND an interrupt has been serviced
722 * we are WD_EXPIRED.
723 *
724 * IF timer is running
725 * AND brokenstop is set
726 * AND no interrupt has been serviced
727 * we are WD_FREERUN.
728 */
729 if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) {
730 if(pTimer->runstatus & WD_STAT_SVCD) {
731 ret = WD_EXPIRED;
732 }
733 else {
734 /* we could as well pretend we are expired */
735 ret = WD_FREERUN;
736 }
737 }
738 else {
739 ret = WD_RUNNING;
740 }
741 }
742 }
743 627
744 /* determine SERVICED */ 628out_unregister:
745 if(pTimer->runstatus & WD_STAT_SVCD) { 629 for (i--; i >= 0; i--)
746 ret |= WD_SERVICED; 630 misc_deregister(&p->devs[i].misc);
747 }
748 631
749 return(ret); 632out_iounmap:
633 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
634
635out_free:
636 kfree(p);
637 goto out;
750} 638}
751 639
752static int __init wd_init(void) 640static int __devexit cpwd_remove(struct of_device *op)
753{ 641{
754 int id; 642 struct cpwd *p = dev_get_drvdata(&op->dev);
755 struct linux_ebus *ebus = NULL; 643 int i;
756 struct linux_ebus_device *edev = NULL; 644
757 645 for (i = 0; i < 4; i++) {
758 for_each_ebus(ebus) { 646 misc_deregister(&p->devs[i].misc);
759 for_each_ebusdev(edev, ebus) { 647
760 if (!strcmp(edev->ofdev.node->name, WD_OBPNAME)) 648 if (!p->enabled) {
761 goto ebus_done; 649 cpwd_stoptimer(p, i);
650 if (p->devs[i].runstatus & WD_STAT_BSTOP)
651 cpwd_resetbrokentimer(p, i);
762 } 652 }
763 } 653 }
764 654
765ebus_done: 655 if (p->broken)
766 if(!edev) { 656 del_timer_sync(&cpwd_timer);
767 printk("%s: unable to locate device\n", WD_OBPNAME);
768 return -ENODEV;
769 }
770 657
771 wd_dev.regs = 658 if (p->initialized)
772 ioremap(edev->resource[0].start, 4 * WD_TIMER_REGSZ); /* ? */ 659 free_irq(p->irq, p);
773 660
774 if(NULL == wd_dev.regs) { 661 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
775 printk("%s: unable to map registers\n", WD_OBPNAME); 662 kfree(p);
776 return(-ENODEV);
777 }
778 663
779 /* initialize device structure from OBP parameters */ 664 cpwd_device = NULL;
780 wd_dev.irq = edev->irqs[0];
781 wd_dev.opt_enable = wd_opt_enable();
782 wd_dev.opt_reboot = wd_opt_reboot();
783 wd_dev.opt_timeout = wd_opt_timeout();
784 wd_dev.isbaddoggie = wd_isbroken();
785 665
786 /* disable all interrupts unless watchdog-enabled? == true */ 666 return 0;
787 if(! wd_dev.opt_enable) { 667}
788 wd_toggleintr(NULL, WD_INTR_OFF);
789 }
790 668
791 /* register miscellaneous devices */ 669static struct of_device_id cpwd_match[] = {
792 for(id = WD0_ID; id < WD_NUMDEVS; ++id) { 670 {
793 if(0 != wd_inittimer(id)) { 671 .name = "watchdog",
794 printk("%s%i: unable to initialize\n", WD_OBPNAME, id); 672 },
795 } 673 {},
796 } 674};
675MODULE_DEVICE_TABLE(of, cpwd_match);
797 676
798 /* warn about possible defective PLD */ 677static struct of_platform_driver cpwd_driver = {
799 if(wd_dev.isbaddoggie) { 678 .name = DRIVER_NAME,
800 init_timer(&wd_timer); 679 .match_table = cpwd_match,
801 wd_timer.function = wd_brokentimer; 680 .probe = cpwd_probe,
802 wd_timer.data = (unsigned long)&wd_dev; 681 .remove = __devexit_p(cpwd_remove),
803 wd_timer.expires = WD_BTIMEOUT; 682};
804 683
805 printk("%s: PLD defect workaround enabled for model %s\n", 684static int __init cpwd_init(void)
806 WD_OBPNAME, WD_BADMODEL); 685{
807 } 686 return of_register_driver(&cpwd_driver, &of_bus_type);
808 return(0);
809} 687}
810 688
811static void __exit wd_cleanup(void) 689static void __exit cpwd_exit(void)
812{ 690{
813 int id; 691 of_unregister_driver(&cpwd_driver);
814
815 /* if 'watchdog-enable?' == TRUE, timers are not stopped
816 * when module is unloaded. All brokenstopped timers will
817 * also now eventually trip.
818 */
819 for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
820 if(WD_S_RUNNING == wd_readb(wd_dev.watchdog[id].regs + WD_STATUS)) {
821 if(wd_dev.opt_enable) {
822 printk(KERN_WARNING "%s%i: timer not stopped at release\n",
823 WD_OBPNAME, id);
824 }
825 else {
826 wd_stoptimer(&wd_dev.watchdog[id]);
827 if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) {
828 wd_resetbrokentimer(&wd_dev.watchdog[id]);
829 printk(KERN_WARNING
830 "%s%i: defect workaround disabled at release, "\
831 "timer expires in ~%01i sec\n",
832 WD_OBPNAME, id,
833 wd_readw(wd_dev.watchdog[id].regs + WD_LIMIT) / 10);
834 }
835 }
836 }
837 }
838
839 if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) {
840 del_timer(&wd_timer);
841 }
842 if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) {
843 misc_deregister(&wd0_miscdev);
844 }
845 if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) {
846 misc_deregister(&wd1_miscdev);
847 }
848 if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) {
849 misc_deregister(&wd2_miscdev);
850 }
851 if(0 != wd_dev.initialized) {
852 free_irq(wd_dev.irq, (void *)wd_dev.regs);
853 }
854 iounmap(wd_dev.regs);
855} 692}
856 693
857module_init(wd_init); 694module_init(cpwd_init);
858module_exit(wd_cleanup); 695module_exit(cpwd_exit);