OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [pcwd.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * PC Watchdog Driver
3
 * by Ken Hollis (khollis@bitgate.com)
4
 *
5
 * Permission granted from Simon Machell (73244.1270@compuserve.com)
6
 * Written for the Linux Kernel, and GPLed by Ken Hollis
7
 *
8
 * 960107       Added request_region routines, modulized the whole thing.
9
 * 960108       Fixed end-of-file pointer (Thanks to Dan Hollis), added
10
 *              WD_TIMEOUT define.
11
 * 960216       Added eof marker on the file, and changed verbose messages.
12
 * 960716       Made functional and cosmetic changes to the source for
13
 *              inclusion in Linux 2.0.x kernels, thanks to Alan Cox.
14
 * 960717       Removed read/seek routines, replaced with ioctl.  Also, added
15
 *              check_region command due to Alan's suggestion.
16
 * 960821       Made changes to compile in newer 2.0.x kernels.  Added
17
 *              "cold reboot sense" entry.
18
 * 960825       Made a few changes to code, deleted some defines and made
19
 *              typedefs to replace them.  Made heartbeat reset only available
20
 *              via ioctl, and removed the write routine.
21
 * 960828       Added new items for PC Watchdog Rev.C card.
22
 * 960829       Changed around all of the IOCTLs, added new features,
23
 *              added watchdog disable/re-enable routines.  Added firmware
24
 *              version reporting.  Added read routine for temperature.
25
 *              Removed some extra defines, added an autodetect Revision
26
 *              routine.
27
 * 961006       Revised some documentation, fixed some cosmetic bugs.  Made
28
 *              drivers to panic the system if it's overheating at bootup.
29
 * 961118       Changed some verbiage on some of the output, tidied up
30
 *              code bits, and added compatibility to 2.1.x.
31
 * 970912       Enabled board on open and disable on close.
32
 * 971107       Took account of recent VFS changes (broke read).
33
 * 971210       Disable board on initialisation in case board already ticking.
34
 * 971222       Changed open/close for temperature handling
35
 *              Michael Meskes <meskes@debian.org>.
36
 * 980112       Used minor numbers from include/linux/miscdevice.h
37
 * 990403       Clear reset status after reading control status register in
38
 *              pcwd_showprevstate(). [Marc Boucher <marc@mbsi.ca>]
39
 * 990605       Made changes to code to support Firmware 1.22a, added
40
 *              fairly useless proc entry.
41
 * 990610       removed said useless proc code for the merge <alan>
42
 * 000403       Removed last traces of proc code. <davej>
43
 * 011214       Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
44
 * 020210       Backported 2.5 open_allowed changes, and got rid of a useless
45
 *              variable <rob@osinvestor.com>
46
 *              Added timeout module option to override default
47
 * 020306       Support the PCI version [Lindsay Harris <lindsay@bluegum.com>]
48
 */
49
 
50
/*
51
 *  A bells and whistles driver is available from http://www.pcwd.de/
52
 */
53
#include <linux/module.h>
54
 
55
#include <linux/types.h>
56
#include <linux/delay.h>
57
#include <linux/miscdevice.h>
58
#include <linux/watchdog.h>
59
#include <linux/init.h>
60
 
61
#include <asm/uaccess.h>
62
#include <asm/io.h>
63
 
64
#include <linux/notifier.h>
65
#include <linux/reboot.h>
66
#include <linux/pci.h>
67
 
68
#define WD_VER                  "1.13 (03/06/2002)"
69
 
70
/*  Stuff for the PCI version  */
71
#ifndef PCI_VENDOR_ID_QUICKLOGIC
72
#define PCI_VENDOR_ID_QUICKLOGIC        0x11e3
73
#endif
74
#ifndef PCI_DEVICE_ID_BERKSHIRE
75
#define PCI_DEVICE_ID_BERKSHIRE 0x5030
76
#endif
77
 
78
/*
79
 * It should be noted that PCWD_REV_B was removed because A and B
80
 * are essentially the same types of card, with the exception that B
81
 * has temperature reporting.  Since I didn't receive a Rev.B card,
82
 * the Rev.B card is not supported.  (It's a good thing too, as they
83
 * are no longer in production.)
84
 */
85
#define PCWD_REV_A      0
86
#define PCWD_REV_C      1
87
#define PCWD_REV_PCI    2
88
 
89
static int timeout_val;
90
static int timeout = 2;
91
static int expect_close = 0;
92
 
93
MODULE_PARM (timeout, "i");
94
MODULE_PARM_DESC (timeout, "Watchdog timeout in seconds (default=2)");
95
 
96
#ifdef CONFIG_WATCHDOG_NOWAYOUT
97
static int nowayout = 1;
98
#else
99
static int nowayout = 0;
100
#endif
101
 
102
MODULE_PARM (nowayout, "i");
103
MODULE_PARM_DESC (nowayout,
104
                  "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
105
 
106
/*
107
 * These are the defines for the PC Watchdog card, revision A.
108
 */
109
#define WD_WDRST                0x01    /* Previously reset state */
110
#define WD_T110                 0x02    /* Temperature overheat sense */
111
#define WD_HRTBT                0x04    /* Heartbeat sense */
112
#define WD_RLY2                 0x08    /* External relay triggered */
113
#define WD_SRLY2                0x80    /* Software external relay triggered */
114
 
115
/*
116
 *  Differences between cards regarding how they perform some operations
117
 *  are handled by an array of structs, with per card functions for the
118
 *  incompatible operations.  It's all defined here.
119
 */
120
 
121
/*  ENABLE/DISABLE the card  */
122
typedef int (*fn_enable) (int); /* Enable/disable card */
123
 
124
static int pcwd_enable_card (int enable);       /* Actually works */
125
static int pcwd_enable_nop (int enable);        /* NOP - REV A cannot */
126
 
127
/* Obtain firmware version, if possible */
128
#define PCWD_FIRMWARE_BSZ       16      /* Version buffer size */
129
typedef void (*fn_firmware) (char *bp);
130
 
131
static void pcwd_firmware_ver_none (char *bp);  /* REV A can't do it */
132
static void pcwd_firmware_ver_revc (char *bp);  /* REV C boards can */
133
static void pcwd_firmware_ver_pci (char *bp);   /* PCI boards can too */
134
 
135
/*  Tickle the watchdog timer */
136
typedef void (*fn_tickle) (void);
137
 
138
static void pcwd_tickle_reva (void);    /* Rev A only */
139
static void pcwd_tickle (void); /* Rev C, PCI */
140
 
141
/*  Determine reboot and temperature status */
142
typedef int (*fn_status) (int reset_boot);
143
 
144
static int pcwd_get_stat_reva (int reset_boot);
145
static int pcwd_get_stat (int reset_boot);
146
 
147
/*  Per card type specifications */
148
typedef struct {
149
        fn_tickle wd_tickle;    /* Reset the watchdog */
150
        fn_enable enable_card;  /* Enable/disable card, if possible */
151
        fn_firmware firmware_ver;       /* Get firmware version, if possible */
152
        fn_status wd_status;    /* Card reset and/or over temp */
153
        int io_size;            /* I/O space used */
154
        const char *name;       /* Nice name to display */
155
} PCWD_CARD_INFO;
156
 
157
/* Per card information, indexed by card version ID */
158
static PCWD_CARD_INFO pcwd_card_info[] = {
159
        {
160
         pcwd_tickle_reva,
161
         pcwd_enable_nop,
162
         pcwd_firmware_ver_none,
163
         pcwd_get_stat_reva,
164
         2,
165
         "Berkshire Products PC Watchdog (REV A)",
166
         },
167
        {
168
         pcwd_tickle,
169
         pcwd_enable_card,
170
         pcwd_firmware_ver_revc,
171
         pcwd_get_stat,
172
         4,
173
         "Berkshire Products PC Watchdog (REV C)",
174
         },
175
        {
176
         pcwd_tickle,
177
         pcwd_enable_card,
178
         pcwd_firmware_ver_pci,
179
         pcwd_get_stat,
180
         8,
181
         "Berkshire Products PC Watchdog (PCI)",
182
         },
183
};
184
 
185
/*  Overall driver information, including per card pointer */
186
static struct {
187
        PCWD_CARD_INFO *card_info;      /* Points to one of the above */
188
        atomic_t open_allowed;  /* Watchdog is single open */
189
        int flags;              /* Defined below */
190
        int boot_status;        /* Card status at boot time */
191
        int io_addr;            /* Card's base address */
192
} pcwd_info = {
193
NULL, ATOMIC_INIT (1), 0, 0, 0};
194
 
195
/*  Bits allocated in flags above. */
196
#define PCWD_HAS_TEMP   0x0001  /* Set when thermometer available */
197
#define PCWD_PCI_REG    0x0002  /* Set if PCI register code worked */
198
#define PCWD_TEMP_PANIC 0x0004  /* Panic when over temperature */
199
 
200
static spinlock_t io_lock;
201
 
202
/* D E T E R M I N E   C A R D   S T A T U S   F U N C T I O N S  */
203
/*   Rev A cards return status information from the base register,
204
 * which is used for the temperature in other cards.  */
205
 
206
static int
207
pcwd_get_stat_reva (int reset_boot)
208
{
209
        int retval;
210
        int status;
211
 
212
        spin_lock (&io_lock);
213
        status = inb_p (pcwd_info.io_addr);
214
        spin_unlock (&io_lock);
215
 
216
        /* Transform the card register to the ioctl bits we use internally */
217
        retval = WDIOF_MAGICCLOSE;
218
        if (status & WD_WDRST)
219
                retval |= WDIOF_CARDRESET;
220
        if (status & WD_T110)
221
                retval |= WDIOF_OVERHEAT;
222
 
223
        return retval;
224
}
225
 
226
/*
227
 *  Rev C and PCI cards return card status in the base address + 1 register.
228
 *  And use different bits to indicate a card initiated reset, and
229
 *  an over-temperature condition.  And the reboot status can be reset.
230
 */
231
 
232
static int
233
pcwd_get_stat (int reset_boot)
234
{
235
        int retval;
236
        int status;
237
 
238
        spin_lock (&io_lock);
239
        status = inb_p (pcwd_info.io_addr + 1);
240
        if (reset_boot) {
241
                /*  NOTE:  the REV C card clears the "card caused reboot"
242
                 * flag when writing ANY value to this port.  However,
243
                 * the PCI card requires writing a 1 to bit 0.  */
244
                outb_p (0x01, pcwd_info.io_addr + 1);
245
        }
246
        spin_unlock (&io_lock);
247
 
248
        retval = 0;
249
        if (status & 0x01)
250
                retval |= WDIOF_CARDRESET;
251
        if (status & 0x04)
252
                retval |= WDIOF_OVERHEAT;
253
 
254
        return retval;
255
}
256
 
257
/*  W A T C H D O G   T I M E R   R E S E T   F U N C T I O N S   */
258
/*  Rev A cards are reset by setting a specific bit in register 1.  */
259
 
260
static void
261
pcwd_tickle_reva (void)
262
{
263
        int wdrst_stat;
264
 
265
        spin_lock (&io_lock);
266
        wdrst_stat = inb_p (pcwd_info.io_addr);
267
        wdrst_stat = (wdrst_stat & 0x0F) | WD_WDRST;
268
 
269
        outb_p (wdrst_stat, pcwd_info.io_addr + 1);
270
        spin_unlock (&io_lock);
271
 
272
        return;
273
}
274
 
275
/*  Other cards are reset by writing anything to the base register.  */
276
 
277
static void
278
pcwd_tickle (void)
279
{
280
        spin_lock (&io_lock);
281
        outb_p (0x42, pcwd_info.io_addr);
282
        spin_unlock (&io_lock);
283
 
284
        return;
285
}
286
 
287
static int
288
pcwd_ioctl (struct inode *inode, struct file *file,
289
            unsigned int cmd, unsigned long arg)
290
{
291
        int rv;
292
        int retval;
293
 
294
        static struct watchdog_info ident = {
295
                WDIOF_OVERHEAT | WDIOF_CARDRESET,
296
                1,
297
                "PCWD"
298
        };
299
 
300
        switch (cmd) {
301
        case WDIOC_GETSUPPORT:
302
                rv = copy_to_user ((void *) arg, &ident, sizeof (ident));
303
                return rv ? -EFAULT : 0;
304
 
305
        case WDIOC_GETSTATUS:
306
                rv = pcwd_info.card_info->wd_status (0);
307
 
308
                if (rv & WDIOF_OVERHEAT) {
309
                        if (pcwd_info.flags & PCWD_TEMP_PANIC)
310
                                panic ("pcwd: Temperature overheat trip!\n");
311
                }
312
 
313
                if (put_user (rv, (int *) arg))
314
                        return -EFAULT;
315
                return 0;
316
 
317
        case WDIOC_GETBOOTSTATUS:
318
                rv = pcwd_info.boot_status;
319
 
320
                if (put_user (rv, (int *) arg))
321
                        return -EFAULT;
322
                return 0;
323
 
324
        case WDIOC_GETTEMP:
325
                rv = 0;
326
                if (pcwd_info.flags & PCWD_HAS_TEMP) {
327
                        spin_lock (&io_lock);
328
                        rv = inb_p (pcwd_info.io_addr);
329
                        spin_unlock (&io_lock);
330
                }
331
                if (put_user (rv, (int *) arg))
332
                        return -EFAULT;
333
                return 0;
334
 
335
        case WDIOC_SETOPTIONS:
336
                if (copy_from_user (&rv, (int *) arg, sizeof (int)))
337
                        return -EFAULT;
338
 
339
                retval = -EINVAL;
340
 
341
                if (rv & WDIOS_DISABLECARD) {
342
                        if (!pcwd_info.card_info->enable_card (0)) {
343
                                printk (KERN_EMERG
344
                                        "pcwd: Could not disable card\n");
345
                                return -EIO;
346
                        }
347
 
348
                        retval = 0;
349
                }
350
 
351
                if (rv & WDIOS_ENABLECARD) {
352
                        if (!pcwd_info.card_info->enable_card (1)) {
353
                                printk (KERN_EMERG
354
                                        "pcwd: Could not enable card\n");
355
                                return -EIO;
356
                        }
357
                        retval = 0;
358
                }
359
 
360
                if (rv & WDIOS_TEMPPANIC) {
361
                        pcwd_info.flags |= PCWD_TEMP_PANIC;
362
 
363
                        retval = 0;
364
                }
365
 
366
                return retval;
367
 
368
        case WDIOC_KEEPALIVE:
369
                pcwd_info.card_info->wd_tickle ();
370
                return 0;
371
 
372
        default:
373
                return -ENOTTY;
374
        }
375
 
376
        return 0;
377
}
378
 
379
/*   Write:  only for the watchdog device (thermometer is read-only).  */
380
 
381
static ssize_t
382
pcwd_write (struct file *file, const char *buf, size_t len, loff_t * ppos)
383
{
384
        /*  Can't seek (pwrite) on this device  */
385
        if (ppos != &file->f_pos)
386
                return -ESPIPE;
387
 
388
        if (len) {
389
                if (!nowayout) {
390
                        size_t i;
391
 
392
                        /* In case it was set long ago */
393
                        expect_close = 0;
394
 
395
                        for (i = 0; i != len; i++) {
396
                                char c;
397
                                if (get_user(c, buf + i))
398
                                        return -EFAULT;
399
                                if (c == 'V')
400
                                        expect_close = 1;
401
                        }
402
                }
403
                pcwd_info.card_info->wd_tickle ();
404
                return 1;
405
        }
406
        return 0;
407
}
408
 
409
static int
410
pcwd_open (struct inode *ino, struct file *filep)
411
{
412
        switch (MINOR (ino->i_rdev)) {
413
        case WATCHDOG_MINOR:
414
                if (!atomic_dec_and_test (&pcwd_info.open_allowed)) {
415
                        atomic_inc (&pcwd_info.open_allowed);
416
                        return -EBUSY;
417
                }
418
 
419
                /*  Enable the card  */
420
                pcwd_info.card_info->enable_card (1);
421
                pcwd_info.card_info->wd_tickle ();
422
 
423
                return 0;
424
 
425
        case TEMP_MINOR:
426
                if (pcwd_info.flags & PCWD_HAS_TEMP) {
427
                        return 0;
428
                }
429
                return -ENODEV;
430
 
431
        default:
432
                return -ENODEV;
433
        }
434
}
435
 
436
/*      Read:  applies only to the thermometer (watchdog is write only).  */
437
static ssize_t
438
pcwd_read (struct file *file, char *buf, size_t count, loff_t * ppos)
439
{
440
        unsigned short c;
441
        unsigned char cp;
442
 
443
        /*  Can't seek (pread) on this device  */
444
        if (ppos != &file->f_pos)
445
                return -ESPIPE;
446
 
447
        /*
448
         * Convert celsius to fahrenheit, since this was
449
         * the decided 'standard' for this return value.
450
         */
451
 
452
        spin_lock (&io_lock);
453
        c = inb_p (pcwd_info.io_addr);
454
        spin_unlock (&io_lock);
455
 
456
        cp = (c * 9 / 5) + 32;
457
        if (copy_to_user (buf, &cp, 1))
458
                return -EFAULT;
459
 
460
        return 1;
461
}
462
 
463
static int
464
pcwd_close (struct inode *ino, struct file *filep)
465
{
466
        switch (MINOR (ino->i_rdev)) {
467
        case WATCHDOG_MINOR:
468
                if (expect_close)
469
                        pcwd_info.card_info->enable_card (0);
470
 
471
                atomic_inc (&pcwd_info.open_allowed);
472
                break;
473
 
474
        case TEMP_MINOR:
475
                break;
476
        }
477
        return 0;
478
}
479
 
480
/*
481
 *  System is shutting down, so disable the card.  Otherwise the timeout
482
 * may expire during shutdown.  Of course, this means a hang during
483
 * shutdown will not be reset, but somebody is probably nearby and will
484
 * notice.  The alternative is to have the shutdown aborted when the
485
 * watchdog expires and hits reset.
486
 */
487
 
488
static int
489
pcwd_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
490
{
491
        if (code == SYS_DOWN || code == SYS_HALT) {
492
                /*
493
                 *  If initialisation is still in progress, the device pointer
494
                 * may not be valid, so check, just to make sure.
495
                 */
496
 
497
                if (pcwd_info.card_info)
498
                        pcwd_info.card_info->enable_card (0);
499
        }
500
 
501
        return NOTIFY_DONE;
502
}
503
 
504
/*  C A R D   E N A B L E / D I S A B L E   F U N C T I O N S  */
505
/*  Enable/disable the card, not REV A.  The two writes are required by card */
506
 
507
static int
508
pcwd_enable_card (int enable)
509
{
510
        int stat_reg;
511
 
512
        spin_lock (&io_lock);
513
        if (enable) {
514
                outb_p (0x00, pcwd_info.io_addr + 3);
515
        } else {
516
                outb_p (0xA5, pcwd_info.io_addr + 3);
517
                outb_p (0xA5, pcwd_info.io_addr + 3);
518
        }
519
        stat_reg = inb_p (pcwd_info.io_addr + 2);
520
        spin_unlock (&io_lock);
521
 
522
        stat_reg &= 0x10;       /* "disabled when set" bit */
523
        if (enable) {
524
                stat_reg ^= 0x10;
525
        }
526
 
527
        return stat_reg;
528
}
529
 
530
static int
531
pcwd_enable_nop (int enable)
532
{
533
        return 0;
534
}
535
 
536
static void __init
537
set_card_type (int is_pci)
538
{
539
 
540
        if (is_pci) {
541
                pcwd_info.card_info = &pcwd_card_info[PCWD_REV_PCI];
542
                pcwd_info.flags |= PCWD_PCI_REG;
543
        } else {
544
                pcwd_info.card_info = &pcwd_card_info[PCWD_REV_C];
545
 
546
                /* REV A cards use only 2 io ports; test
547
                 * presumes a floating bus reads as 0xff.  */
548
                if ((inb (pcwd_info.io_addr + 2) == 0xFF) ||
549
                    (inb (pcwd_info.io_addr + 3) == 0xFF)) {
550
                        pcwd_info.card_info = &pcwd_card_info[PCWD_REV_A];
551
                }
552
        }
553
 
554
        return;
555
}
556
 
557
/* G E T    F I R M W A R E   V E R S I O N   F U N C T I O N S    */
558
/* REV A can't do it */
559
static void __init
560
pcwd_firmware_ver_none (char *bp)
561
{
562
        strncpy (bp, "<unavailable>", PCWD_FIRMWARE_BSZ);
563
 
564
        return;
565
}
566
 
567
/* PCI boards can too */
568
static void __init
569
pcwd_firmware_ver_pci (char *bp)
570
{
571
        int count;
572
 
573
        /* Write the 'Get Firmware Version' command to port 6 and wait */
574
        outb (0x08, pcwd_info.io_addr + 6);
575
 
576
        /* Card sets bit 0x40 (WRSP) bit in port 2.  Can take 10ms! */
577
        for (count = 0; count < 15; ++count) {
578
                mdelay (1);     /* Board responds slowly */
579
 
580
                if (inb (pcwd_info.io_addr + 2) & 0x40) {
581
                        /* Board says data now valid */
582
 
583
                        snprintf (bp, PCWD_FIRMWARE_BSZ, "%u.%u",
584
                                  inb (pcwd_info.io_addr + 5),
585
                                  inb (pcwd_info.io_addr + 4));
586
 
587
                        return;
588
                }
589
        }
590
        strncpy (bp, "<card no answer>", PCWD_FIRMWARE_BSZ);
591
 
592
        return;
593
}
594
 
595
/*
596
 *   REV C boards read diagnostic (including firmware version) data
597
 * from the register 0.  To do this, the card is put into diagnostic
598
 * mode, then the command is submitted and data read from register 0.
599
 * NOTE:  the onboard processor writes 4 bits at a time to the register,
600
 * so it's necessary to wait for the data to stabilise before
601
 * accepting it.
602
 */
603
 
604
static int __init
605
send_command (int cmd)
606
{
607
        int ii;
608
 
609
        int reg0, last_reg0;    /* Double read for stabilising */
610
 
611
        outb (cmd, pcwd_info.io_addr + 2);
612
        /*
613
         *    The following delay need only be 200 microseconds according
614
         *  to the spec I have.  But my card seems slower, as waiting
615
         *  250 microseconds returns valid data, but NOT from this
616
         *  command.  The 1000 value may be excessive, but is reliable.
617
         */
618
        mdelay (1);
619
 
620
        reg0 = inb (pcwd_info.io_addr);
621
        for (ii = 0; ii < 25; ++ii) {
622
                last_reg0 = reg0;
623
                reg0 = inb (pcwd_info.io_addr);
624
 
625
                if (reg0 == last_reg0)
626
                        break;  /* Data is stable */
627
 
628
                udelay (250);
629
        }
630
 
631
        return reg0;
632
}
633
 
634
/*  REV C board function to retrieve firmware version */
635
static void __init
636
pcwd_firmware_ver_revc (char *bp)
637
{
638
        int i, found = 0, count = 0;
639
 
640
        /*  Set the card into debug mode to find firmware version */
641
        outb_p (0x00, pcwd_info.io_addr + 2);   /* Spec says to do this */
642
        udelay (500);
643
 
644
        while ((count < 3) && (!found)) {
645
                i = send_command (0x80);
646
 
647
                if (i == 0x00) {
648
                        found = 1;
649
                        break;
650
                } else if (i == 0xF3) {
651
                        /* Card does not like what we've done to it */
652
                        outb_p (0x00, pcwd_info.io_addr + 2);
653
                        udelay (1200);  /* Spec says wait 1ms */
654
                        outb_p (0x00, pcwd_info.io_addr + 2);
655
                        udelay (500);
656
                }
657
                count++;
658
        }
659
 
660
        if (found) {
661
 
662
                *bp++ = send_command (0x81);
663
                *bp++ = '.';
664
                *bp++ = send_command (0x82);
665
                *bp++ = send_command (0x83);
666
                *bp++ = send_command (0x84);
667
                *bp++ = '\0';
668
 
669
                /* Out of debug mode */
670
                outb (0x00, pcwd_info.io_addr + 2);
671
        } else
672
                strncpy (bp, "<err - no go>", PCWD_FIRMWARE_BSZ);
673
 
674
        return;
675
}
676
 
677
/*   Initialisation function called ONLY from the PCI layer.  */
678
 
679
static int __init
680
pcwd_init_one (struct pci_dev *dev, const struct pci_device_id *ent)
681
{
682
        static int devices = 0;
683
 
684
        ++devices;
685
        if (devices > 1) {
686
                printk (KERN_ERR "pcwd: Driver supports only ONE device\n");
687
 
688
                return -ENODEV;
689
        }
690
 
691
        pcwd_info.io_addr = pci_resource_start (dev, 0);
692
 
693
        if (pcwd_info.io_addr == 0 || pci_enable_device (dev))
694
                return -ENODEV;
695
 
696
        return 0;
697
}
698
 
699
static struct pci_device_id pcwd_pci_tbl[] __initdata = {
700
        {PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_BERKSHIRE,
701
         PCI_ANY_ID, PCI_ANY_ID,},
702
        {0},                     /* End of list */
703
};
704
 
705
MODULE_DEVICE_TABLE (pci, pcwd_pci_tbl);
706
 
707
static struct pci_driver pcwd_driver = {
708
        name:"pcwd",
709
        id_table:pcwd_pci_tbl,
710
        probe:pcwd_init_one,
711
};
712
 
713
static struct file_operations pcwd_fops = {
714
        owner:THIS_MODULE,
715
        write:pcwd_write,
716
        ioctl:pcwd_ioctl,
717
        open:pcwd_open,
718
        release:pcwd_close,
719
};
720
 
721
static struct miscdevice pcwd_miscdev = {
722
        WATCHDOG_MINOR,
723
        "watchdog",
724
        &pcwd_fops
725
};
726
 
727
static struct file_operations pcwd_temp_fops = {
728
        owner:THIS_MODULE,
729
        read:pcwd_read,
730
        open:pcwd_open,
731
        release:pcwd_close,
732
};
733
 
734
static struct miscdevice temp_miscdev = {
735
        TEMP_MINOR,
736
        "temperature",
737
        &pcwd_temp_fops
738
};
739
 
740
/* Need to know about shutdown to kill the timer - may reset during shutdown! */
741
    static struct notifier_block pcwd_notifier =
742
{
743
        pcwd_notify_sys,
744
        NULL,
745
        0,
746
};
747
 
748
/*
749
 *   The ISA cards have a heartbeat bit in one of the registers, which
750
 *  register is card dependent.  The heartbeat bit is monitored, and if
751
 *  found, is considered proof that a Berkshire card has been found.
752
 *  The initial rate is once per second at board start up, then twice
753
 *  per second for normal operation.
754
 */
755
static int __init
756
check_isa_card (int base_addr)
757
{
758
        int reg0, last_reg0;    /* Reg 0, in case it's REV A */
759
        int reg1, last_reg1;    /* Register 1 for REV C cards */
760
        int ii;
761
        int retval;
762
 
763
        /* As suggested by Alan Cox - this is a safety measure. */
764
        if (!request_region (base_addr, 4, "pcwd-isa")) {
765
                printk (KERN_INFO "pcwd: Port 0x%x unavailable\n", base_addr);
766
                return 0;
767
        }
768
 
769
        retval = 0;
770
 
771
        reg0 = inb_p (base_addr);       /* For REV A boards */
772
        reg1 = inb (base_addr + 1);     /* For REV C boards */
773
        if (reg0 != 0xff || reg1 != 0xff) {
774
                /* Not an 'ff' from a floating bus, so must be a card! */
775
                for (ii = 0; ii < timeout_val; ++ii) {
776
 
777
                        set_current_state (TASK_INTERRUPTIBLE);
778
                        schedule_timeout (HZ / 2);
779
 
780
                        last_reg0 = reg0;
781
                        last_reg1 = reg1;
782
 
783
                        reg0 = inb_p (base_addr);
784
                        reg1 = inb (base_addr + 1);
785
 
786
                        /* Has either hearbeat bit changed?  */
787
                        if ((reg0 ^ last_reg0) & WD_HRTBT ||
788
                            (reg1 ^ last_reg1) & 0x02) {
789
 
790
                                retval = 1;
791
                                break;
792
                        }
793
                }
794
        }
795
        release_region (base_addr, 4);
796
 
797
        return retval;
798
}
799
 
800
static int __init
801
pcwd_card_init (void)
802
{
803
        int retval;
804
        char fvbuf[PCWD_FIRMWARE_BSZ];
805
 
806
        pcwd_info.card_info->firmware_ver (fvbuf);
807
 
808
        printk (KERN_INFO "pcwd: %s at port 0x%03x (Firmware: %s)\n",
809
                pcwd_info.card_info->name, pcwd_info.io_addr, fvbuf);
810
 
811
        /* Returns 0xf0 in temperature register if no thermometer */
812
        if (inb (pcwd_info.io_addr) != 0xF0) {
813
                pcwd_info.flags |= PCWD_HAS_TEMP;
814
                printk (KERN_INFO "pcwd: Temperature option detected\n");
815
        }
816
 
817
        if (nowayout)
818
                printk (KERN_INFO
819
                        "pcwd: Watchdog cannot be stopped once started\n");
820
 
821
        /* Record the power up status of "card did reset" and/or temp trip */
822
        pcwd_info.boot_status = pcwd_info.card_info->wd_status (1);
823
 
824
        if (pcwd_info.boot_status & WDIOF_CARDRESET)
825
                printk (KERN_INFO
826
                        "pcwd: Previous reboot was caused by the card\n");
827
 
828
        if (pcwd_info.boot_status & WDIOF_OVERHEAT) {
829
                printk (KERN_EMERG
830
                        "pcwd: Card senses a CPU Overheat.  Panicking!\n");
831
                panic ("pcwd: CPU Overheat\n");
832
        }
833
 
834
        if (pcwd_info.boot_status == 0)
835
                printk (KERN_INFO "pcwd: Cold boot sense\n");
836
 
837
        pcwd_info.card_info->enable_card (0);
838
 
839
        retval = 0;
840
        if (!request_region (pcwd_info.io_addr,
841
                             pcwd_info.card_info->io_size,
842
                             pcwd_info.card_info->name)) {
843
                printk (KERN_ERR "pcwd: I/0 %d is not free\n",
844
                        pcwd_info.io_addr);
845
 
846
                return retval;
847
        }
848
 
849
        retval = misc_register (&pcwd_miscdev);
850
        if (retval) {
851
                release_region (pcwd_info.io_addr,
852
                                pcwd_info.card_info->io_size);
853
                printk (KERN_ERR "pcwd: can't misc_register on minor %d\n",
854
                        WATCHDOG_MINOR);
855
                return retval;
856
        }
857
 
858
        if (pcwd_info.flags & PCWD_HAS_TEMP) {
859
                if (misc_register (&temp_miscdev)) {
860
                        printk (KERN_ERR
861
                                "pwcd: can't misc_register thermometer - disabling it\n");
862
                        pcwd_info.flags &= ~PCWD_HAS_TEMP;
863
                }
864
        }
865
 
866
        retval = register_reboot_notifier (&pcwd_notifier);
867
        if (retval) {
868
                if (pcwd_info.flags & PCWD_HAS_TEMP)
869
                        misc_deregister (&temp_miscdev);
870
                misc_deregister (&pcwd_miscdev);
871
                release_region (pcwd_info.io_addr,
872
                                pcwd_info.card_info->io_size);
873
        }
874
 
875
        return retval;
876
}
877
 
878
static int __init
879
pcwatchdog_init (void)
880
{
881
        int i, found = 0;
882
        /*
883
         * ISA card auto-probe addresses available.  Last one is only
884
         * available on REV C cards.
885
         */
886
        static int pcwd_ioports[] = { 0x270, 0x350, 0x370 };
887
#define PCWD_NUM_ADDR   (sizeof(pcwd_ioports)/sizeof(pcwd_ioports[0]))
888
 
889
        timeout_val = timeout * 2;
890
 
891
        spin_lock_init (&io_lock);
892
 
893
        printk (KERN_INFO "pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER);
894
 
895
        if (pci_register_driver (&pcwd_driver) > 0) {
896
                found = 1;
897
                set_card_type (1);      /* Set to PCI card model */
898
        } else {
899
                /* No PCI entry, try the ISA addresses.  */
900
                for (i = 0; i < PCWD_NUM_ADDR; i++) {
901
 
902
                        if (check_isa_card (pcwd_ioports[i])) {
903
                                found = 1;
904
 
905
                                pcwd_info.io_addr = pcwd_ioports[i];
906
 
907
                                set_card_type (0);
908
                                break;
909
                        }
910
                }
911
        }
912
 
913
        if (!found) {
914
                printk (KERN_INFO
915
                        "pcwd: No card detected, or port not available\n");
916
                return -EIO;
917
        }
918
 
919
        return pcwd_card_init ();
920
}
921
 
922
static void __exit
923
pcwatchdog_exit (void)
924
{
925
        unregister_reboot_notifier (&pcwd_notifier);
926
        misc_deregister (&pcwd_miscdev);
927
 
928
        if (!nowayout)
929
                pcwd_info.card_info->enable_card (0);
930
 
931
        if (pcwd_info.flags & PCWD_HAS_TEMP)
932
                misc_deregister (&temp_miscdev);
933
 
934
        release_region (pcwd_info.io_addr, pcwd_info.card_info->io_size);
935
 
936
        pci_unregister_driver (&pcwd_driver);
937
 
938
        return;
939
}
940
 
941
module_init (pcwatchdog_init);
942
module_exit (pcwatchdog_exit);
943
 
944
MODULE_LICENSE ("GPL");
945
 
946
EXPORT_NO_SYMBOLS;

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.