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

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [char/] [pcwd.c] - Blame information for rev 199

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 199 simons
/*
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
 * 970819       Fixed use of is_open flag so pcwd_write does not always return
19
 *              -EIO; this prevented tickling. Enabled board on open and
20
 *              disabled on close.
21
 */
22
 
23
#include <linux/module.h>
24
 
25
#include <linux/types.h>
26
#include <linux/errno.h>
27
#include <linux/sched.h>
28
#include <linux/tty.h>
29
#include <linux/timer.h>
30
#include <linux/kernel.h>
31
#include <linux/wait.h>
32
#include <linux/string.h>
33
#include <linux/malloc.h>
34
#include <linux/ioport.h>
35
#include <linux/delay.h>
36
#include <linux/miscdevice.h>
37
#include <linux/fs.h>
38
#include <linux/mm.h>
39
#include <linux/pcwd.h>
40
 
41
#include <asm/io.h>
42
 
43
#define WD_VER                  "0.50 (08/21/96)"
44
#define WD_MINOR                130     /* Minor device number */
45
 
46
#define WD_TIMEOUT              3       /* 1 1/2 seconds for a timeout */
47
 
48
#define WD_TIMERRESET_PORT1     0x270   /* Reset port - first choice */
49
#define WD_TIMERRESET_PORT2     0x370   /* Reset port - second choice */
50
#define WD_CTLSTAT_PORT1        0x271   /* Control port - first choice */
51
#define WD_CTLSTAT_PORT2        0x371   /* Control port - second choice */
52
#define WD_CTLSTAT2_PORT1       0x272   /* Control port#2 - first choice */
53
#define WD_CTLSTAT2_PORT2       0x372   /* Control port#2 - second choice */
54
#define WD_DISABLE_PORT1        0x273   /* Disable port - first choice  */
55
#define WD_DISABLE_PORT2        0x373   /* Disable port - second choice  */
56
#define WD_PORT_EXTENT          4       /* Takes up four addresses */
57
 
58
#define WD_WDRST                0x01    /* Previously reset state */
59
#define WD_T110                 0x02    /* Temperature overheat sense */
60
#define WD_HRTBT                0x04    /* Heartbeat sense */
61
#define WD_RLY2                 0x08    /* External relay triggered */
62
#define WD_SRLY2                0x80    /* Software external relay triggered */
63
 
64
 
65
static int current_ctlport, current_readport;
66
static int current_ctlport2, current_disport;
67
static int is_open, is_eof;
68
 
69
int pcwd_checkcard(void)
70
{
71
        int card_dat, prev_card_dat, found = 0, count = 0, done = 0;
72
 
73
        /* As suggested by Alan Cox */
74
        if (check_region(current_ctlport, WD_PORT_EXTENT)) {
75
                printk("pcwd: Port 0x%x unavailable.\n", current_ctlport);
76
                return 0;
77
        }
78
 
79
        card_dat = 0x00;
80
        prev_card_dat = 0x00;
81
 
82
        prev_card_dat = inb(current_readport);
83
 
84
        while(count < WD_TIMEOUT) {
85
#ifdef  DEBUG
86
                printk("pcwd: Run #%d on port 0x%03x\n", count, current_readport);
87
#endif
88
 
89
        /* Read the raw card data from the port, and strip off the
90
           first 4 bits */
91
 
92
                card_dat = inb_p(current_readport);
93
                card_dat &= 0x000F;
94
 
95
        /* Sleep 1/2 second (or 500000 microseconds :) */
96
 
97
                udelay(500000L);
98
                done = 0;
99
 
100
        /* 0x0F usually means that no card data is present, or the card
101
           is not installed on this port.  If 0x0F is present here, it's
102
           normally safe to assume there's no card at that base address. */
103
 
104
                if (card_dat == 0x0F) {
105
                        count++;
106
                        done = 1;
107
 
108
#ifdef  DEBUG
109
                        printk("pcwd: I show nothing on this port.\n");
110
#endif
111
                }
112
 
113
        /* If there's a heart beat in both instances, then this means we
114
           found our card.  This also means that either the card was
115
           previously reset, or the computer was power-cycled. */
116
 
117
                if ((card_dat & WD_HRTBT) && (prev_card_dat & WD_HRTBT) &&
118
                        (!done)) {
119
                        found = 1;
120
                        done = 1;
121
#ifdef  DEBUG
122
                        printk("pcwd: I show alternate heart beats.  Card detected.\n");
123
#endif
124
                        break;
125
                }
126
 
127
        /* If the card data is exactly the same as the previous card data,
128
           it's safe to assume that we should check again.  The manual says
129
           that the heart beat will change every second (or the bit will
130
           toggle), and this can be used to see if the card is there.  If
131
           the card was powered up with a cold boot, then the card will
132
           not start blinking until 2.5 minutes after a reboot, so this
133
           bit will stay at 1. */
134
 
135
                if ((card_dat == prev_card_dat) && (!done)) {
136
                        count++;
137
#ifdef  DEBUG
138
                        printk("pcwd: The card data is exactly the same (possibility).\n");
139
#endif
140
                        done = 1;
141
                }
142
 
143
        /* If the card data is toggling any bits, this means that the heart
144
           beat was detected, or something else about the card is set. */
145
 
146
                if ((card_dat != prev_card_dat) && (!done)) {
147
                        done = 1;
148
                        found = 1;
149
#ifdef  DEBUG
150
                        printk("pcwd: I show alternate heart beats.  Card detected.\n");
151
#endif
152
                        break;
153
                }
154
 
155
        /* Otherwise something else strange happened. */
156
 
157
                if (!done)
158
                        count++;
159
        }
160
 
161
        return((found) ? 1 : 0);
162
}
163
 
164
void pcwd_showprevstate(void)
165
{
166
        int card_status = 0x0000;
167
 
168
        card_status = inb(current_readport);
169
 
170
        if (card_status & WD_WDRST)
171
                printk("pcwd: Previous reboot was caused by the card.\n");
172
 
173
        if (card_status & WD_T110)
174
                printk("pcwd: CPU overheat sense.\n");
175
 
176
        if ((!(card_status & WD_WDRST)) &&
177
            (!(card_status & WD_T110)))
178
                printk("pcwd: Cold boot sense.\n");
179
}
180
 
181
static int pcwd_return_data(void)
182
{
183
        return(inb(current_readport));
184
}
185
 
186
static int pcwd_write(struct inode *inode, struct file *file, const char *data,
187
        int len)
188
{
189
        int wdrst_stat;
190
 
191
        if (!is_open)
192
                return -EIO;
193
 
194
#ifdef  DEBUG
195
        printk("pcwd: write request\n");
196
#endif
197
 
198
        wdrst_stat = inb_p(current_readport);
199
        wdrst_stat &= 0x0F;
200
 
201
        wdrst_stat |= WD_WDRST;
202
 
203
        outb_p(wdrst_stat, current_ctlport);
204
 
205
        return(len);
206
}
207
 
208
static int pcwd_ioctl(struct inode *inode, struct file *file,
209
        unsigned int cmd, unsigned long arg)
210
{
211
        int i, cdat, rv;
212
 
213
        switch(cmd) {
214
        default:
215
                return -ENOIOCTLCMD;
216
 
217
        case PCWD_GETSTAT:
218
                i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
219
                if (i)
220
                        return i;
221
                else {
222
                        cdat = pcwd_return_data();
223
                        rv = 0;
224
 
225
                        if (cdat & WD_WDRST)
226
                                rv |= 0x01;
227
 
228
                        if (cdat & WD_T110)
229
                                rv |= 0x02;
230
 
231
                        put_user(rv, (int *) arg);
232
                        return 0;
233
                }
234
                break;
235
 
236
        case PCWD_PING:
237
                pcwd_write(NULL, NULL, NULL, 1);        /* Is this legal? */
238
                break;
239
        }
240
 
241
        return 0;
242
}
243
 
244
static int pcwd_open(struct inode *ino, struct file *filep)
245
{
246
#ifdef  DEBUG
247
        int before, after;
248
 
249
        before = inb_p (current_ctlport2);
250
#endif
251
        if (is_open)
252
                return -EIO;
253
 
254
        /*  Enable the port  */
255
        outb_p (0, current_disport);
256
#ifdef  DEBUG
257
        after = inb_p (current_ctlport2);
258
        printk ("pcwd: open: control status #2 (b,a): %x, %x\n", before,after);
259
#endif
260
 
261
        MOD_INC_USE_COUNT;
262
        is_open = 1;
263
        is_eof = 0;
264
        return(0);
265
}
266
 
267
static void pcwd_close(struct inode *ino, struct file *filep)
268
{
269
#ifdef  DEBUG
270
        printk("pcwd: close request\n");
271
#endif
272
 
273
        is_open = 0;
274
        /*  Disable the board  */
275
        outb_p (0xa5, current_disport);
276
        outb_p (0xa5, current_disport);
277
        MOD_DEC_USE_COUNT;
278
}
279
 
280
static struct file_operations pcwd_fops = {
281
        NULL,           /* Seek */
282
        NULL,           /* Read */
283
        pcwd_write,     /* Write */
284
        NULL,           /* Readdir */
285
        NULL,           /* Select */
286
        pcwd_ioctl,     /* IOctl */
287
        NULL,           /* MMAP */
288
        pcwd_open,      /* Open */
289
        pcwd_close      /* Close */
290
};
291
 
292
static struct miscdevice pcwd_miscdev = {
293
        WD_MINOR,
294
        "pcwatchdog",
295
        &pcwd_fops
296
};
297
 
298
#ifdef  MODULE
299
int init_module(void)
300
#else
301
int pcwatchdog_init(void)
302
#endif
303
{
304
#ifdef  DEBUG
305
        printk("pcwd: Success.\n");
306
#endif
307
        printk("pcwd: v%s Ken Hollis (khollis@bitgate.com)\n", WD_VER);
308
 
309
#ifdef  DEBUG
310
        printk("pcwd: About to perform card autosense loop.\n");
311
#endif
312
 
313
        is_eof = 0;
314
        is_open = 0;
315
 
316
        current_ctlport = WD_TIMERRESET_PORT1;
317
        current_readport = WD_CTLSTAT_PORT1;
318
        current_ctlport2 = WD_CTLSTAT2_PORT1;
319
        current_disport = WD_DISABLE_PORT1;
320
 
321
        if (!pcwd_checkcard()) {
322
#ifdef  DEBUG
323
                printk("pcwd: Trying port 0x370.\n");
324
#endif
325
 
326
                current_ctlport = WD_TIMERRESET_PORT2;
327
                current_readport = WD_CTLSTAT_PORT2;
328
                current_ctlport2 = WD_CTLSTAT2_PORT2;
329
                current_disport = WD_DISABLE_PORT2;
330
 
331
                if (!pcwd_checkcard()) {
332
                        printk("pcwd: No card detected, or wrong port assigned.\n");
333
                        return(-EIO);
334
                } else
335
                        printk("pcwd: Watchdog Rev.A detected at port 0x370\n");
336
        } else
337
                printk("pcwd: Watchdog Rev.A detected at port 0x270\n");
338
 
339
        pcwd_showprevstate();
340
 
341
#ifdef  DEBUG
342
        printk("pcwd: Requesting region entry\n");
343
#endif
344
 
345
        request_region(current_ctlport, WD_PORT_EXTENT, "PCWD Rev.A (Berkshire)");
346
 
347
#ifdef  DEBUG
348
        printk("pcwd: character device creation.\n");
349
#endif
350
 
351
        misc_register(&pcwd_miscdev);
352
 
353
        return 0;
354
}
355
 
356
#ifdef  MODULE
357
void cleanup_module(void)
358
{
359
        /*  Disable the board  */
360
        outb_p (0xa5, current_disport);
361
        outb_p (0xa5, current_disport);
362
        misc_deregister(&pcwd_miscdev);
363
        release_region(current_ctlport, WD_PORT_EXTENT);
364
#ifdef  DEBUG
365
        printk("pcwd: Cleanup successful.\n");
366
#endif
367
}
368
#endif
369
 
370
/*
371
** TODO:
372
**
373
**      Both Revisions:
374
**      o) Support for revision B of the Watchdog Card
375
**      o) Implement the rest of the IOCTLs as discussed with Alan Cox
376
**      o) Implement only card heartbeat reset via IOCTL, not via write
377
**      o) Faster card detection routines
378
**      o) /proc device creation
379
**
380
**      Revision B functions:
381
**      o) /dev/temp device creation for temperature device (possibly use
382
**         the one from the WDT drivers?)
383
**      o) Direct Motorola controller chip access via read/write routines
384
**      o) Autoprobe IO Ports for autodetection (possibly by chip detect?)
385
*/

powered by: WebSVN 2.1.0

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