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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [i810-tco.c] - Blame information for rev 1774

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

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *      i810-tco 0.05:  TCO timer driver for i8xx chipsets
3
 *
4
 *      (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
5
 *                              http://www.kernelconcepts.de
6
 *
7
 *      This program is free software; you can redistribute it and/or
8
 *      modify it under the terms of the GNU General Public License
9
 *      as published by the Free Software Foundation; either version
10
 *      2 of the License, or (at your option) any later version.
11
 *
12
 *      Neither kernel concepts nor Nils Faerber admit liability nor provide
13
 *      warranty for any of this software. This material is provided
14
 *      "AS-IS" and at no charge.
15
 *
16
 *      (c) Copyright 2000      kernel concepts <nils@kernelconcepts.de>
17
 *                              developed for
18
 *                              Jentro AG, Haar/Munich (Germany)
19
 *
20
 *      TCO timer driver for i8xx chipsets
21
 *      based on softdog.c by Alan Cox <alan@redhat.com>
22
 *
23
 *      The TCO timer is implemented in the following I/O controller hubs:
24
 *      (See the intel documentation on http://developer.intel.com.)
25
 *      82801AA & 82801AB  chip : document number 290655-003, 290677-004,
26
 *      82801BA & 82801BAM chip : document number 290687-002, 298242-005,
27
 *      82801CA & 82801CAM chip : document number 290716-001, 290718-001,
28
 *      82801DB & 82801E   chip : document number 290744-001, 273599-001,
29
 *      82801EB & 82801ER  chip : document number 252516-001
30
 *
31
 *  20000710 Nils Faerber
32
 *      Initial Version 0.01
33
 *  20000728 Nils Faerber
34
 *      0.02 Fix for SMI_EN->TCO_EN bit, some cleanups
35
 *  20011214 Matt Domsch <Matt_Domsch@dell.com>
36
 *      0.03 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
37
 *           Didn't add timeout option as i810_margin already exists.
38
 *  20020224 Joel Becker, Wim Van Sebroeck
39
 *      0.04 Support for 82801CA(M) chipset, timer margin needs to be > 3,
40
 *           add support for WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT.
41
 *  20020412 Rob Radez <rob@osinvestor.com>, Wim Van Sebroeck
42
 *      0.05 Fix possible timer_alive race, add expect close support,
43
 *           clean up ioctls (WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS and
44
 *           WDIOC_SETOPTIONS), made i810tco_getdevice __init,
45
 *           removed boot_status, removed tco_timer_read,
46
 *           added support for 82801DB and 82801E chipset,
47
 *           added support for 82801EB and 8280ER chipset,
48
 *           general cleanup.
49
 */
50
 
51
#include <linux/module.h>
52
#include <linux/types.h>
53
#include <linux/kernel.h>
54
#include <linux/fs.h>
55
#include <linux/mm.h>
56
#include <linux/miscdevice.h>
57
#include <linux/watchdog.h>
58
#include <linux/reboot.h>
59
#include <linux/init.h>
60
#include <linux/pci.h>
61
#include <linux/ioport.h>
62
#include <asm/uaccess.h>
63
#include <asm/io.h>
64
#include "i810-tco.h"
65
 
66
 
67
/* Module and version information */
68
#define TCO_VERSION "0.05"
69
#define TCO_MODULE_NAME "i810 TCO timer"
70
#define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION
71
 
72
/* Default expire timeout */
73
#define TIMER_MARGIN    50      /* steps of 0.6sec, 3<n<64. Default is 30 seconds */
74
 
75
static unsigned int ACPIBASE;
76
static spinlock_t tco_lock;     /* Guards the hardware */
77
 
78
static int i810_margin = TIMER_MARGIN;  /* steps of 0.6sec */
79
 
80
MODULE_PARM(i810_margin, "i");
81
MODULE_PARM_DESC(i810_margin, "i810-tco timeout in steps of 0.6sec, 3<n<64. Default = 50 (30 seconds)");
82
 
83
#ifdef CONFIG_WATCHDOG_NOWAYOUT
84
static int nowayout = 1;
85
#else
86
static int nowayout = 0;
87
#endif
88
 
89
MODULE_PARM(nowayout,"i");
90
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
91
 
92
 
93
/*
94
 *      Timer active flag
95
 */
96
 
97
static unsigned long timer_alive;
98
static char tco_expect_close;
99
 
100
/*
101
 * Some TCO specific functions
102
 */
103
 
104
 
105
/*
106
 * Start the timer countdown
107
 */
108
static int tco_timer_start (void)
109
{
110
        unsigned char val;
111
 
112
        spin_lock(&tco_lock);
113
        val = inb (TCO1_CNT + 1);
114
        val &= 0xf7;
115
        outb (val, TCO1_CNT + 1);
116
        val = inb (TCO1_CNT + 1);
117
        spin_unlock(&tco_lock);
118
 
119
        if (val & 0x08)
120
                return -1;
121
        return 0;
122
}
123
 
124
/*
125
 * Stop the timer countdown
126
 */
127
static int tco_timer_stop (void)
128
{
129
        unsigned char val;
130
 
131
        spin_lock(&tco_lock);
132
        val = inb (TCO1_CNT + 1);
133
        val |= 0x08;
134
        outb (val, TCO1_CNT + 1);
135
        val = inb (TCO1_CNT + 1);
136
        spin_unlock(&tco_lock);
137
 
138
        if ((val & 0x08) == 0)
139
                return -1;
140
        return 0;
141
}
142
 
143
/*
144
 * Set the timer reload value
145
 */
146
static int tco_timer_settimer (unsigned char tmrval)
147
{
148
        unsigned char val;
149
 
150
        /* from the specs: */
151
        /* "Values of 0h-3h are ignored and should not be attempted" */
152
        if (tmrval > 0x3f || tmrval < 0x04)
153
                return -1;
154
 
155
        spin_lock(&tco_lock);
156
        val = inb (TCO1_TMR);
157
        val &= 0xc0;
158
        val |= tmrval;
159
        outb (val, TCO1_TMR);
160
        val = inb (TCO1_TMR);
161
        spin_unlock(&tco_lock);
162
 
163
        if ((val & 0x3f) != tmrval)
164
                return -1;
165
 
166
        return 0;
167
}
168
 
169
/*
170
 * Reload (trigger) the timer. Lock is needed so we dont reload it during
171
 * a reprogramming event
172
 */
173
 
174
static void tco_timer_reload (void)
175
{
176
        spin_lock(&tco_lock);
177
        outb (0x01, TCO1_RLD);
178
        spin_unlock(&tco_lock);
179
}
180
 
181
/*
182
 *      Allow only one person to hold it open
183
 */
184
 
185
static int i810tco_open (struct inode *inode, struct file *file)
186
{
187
        if (test_and_set_bit(0, &timer_alive))
188
                return -EBUSY;
189
 
190
        /*
191
         *      Reload and activate timer
192
         */
193
        tco_timer_reload ();
194
        tco_timer_start ();
195
        return 0;
196
}
197
 
198
static int i810tco_release (struct inode *inode, struct file *file)
199
{
200
        /*
201
         *      Shut off the timer.
202
         */
203
        if (tco_expect_close == 42 && !nowayout) {
204
                tco_timer_stop ();
205
        } else {
206
                tco_timer_reload ();
207
                printk(KERN_CRIT TCO_MODULE_NAME ": Unexpected close, not stopping watchdog!\n");
208
        }
209
        clear_bit(0, &timer_alive);
210
        tco_expect_close = 0;
211
        return 0;
212
}
213
 
214
static ssize_t i810tco_write (struct file *file, const char *data,
215
                              size_t len, loff_t * ppos)
216
{
217
        /*  Can't seek (pwrite) on this device  */
218
        if (ppos != &file->f_pos)
219
                return -ESPIPE;
220
 
221
        /* See if we got the magic character 'V' and reload the timer */
222
        if (len) {
223
                size_t i;
224
 
225
                tco_expect_close = 0;
226
 
227
                /* scan to see wether or not we got the magic character */
228
                for (i = 0; i != len; i++) {
229
                        u8 c;
230
                        if(get_user(c, data+i))
231
                                return -EFAULT;
232
                        if (c == 'V')
233
                                tco_expect_close = 42;
234
                }
235
 
236
                /* someone wrote to us, we should reload the timer */
237
                tco_timer_reload ();
238
                return 1;
239
        }
240
        return 0;
241
}
242
 
243
static int i810tco_ioctl (struct inode *inode, struct file *file,
244
                          unsigned int cmd, unsigned long arg)
245
{
246
        int new_margin, u_margin;
247
        int options, retval = -EINVAL;
248
 
249
        static struct watchdog_info ident = {
250
                options:                WDIOF_SETTIMEOUT |
251
                                        WDIOF_KEEPALIVEPING |
252
                                        WDIOF_MAGICCLOSE,
253
                firmware_version:       0,
254
                identity:               "i810 TCO timer",
255
        };
256
        switch (cmd) {
257
                default:
258
                        return -ENOTTY;
259
                case WDIOC_GETSUPPORT:
260
                        if (copy_to_user
261
                            ((struct watchdog_info *) arg, &ident, sizeof (ident)))
262
                                return -EFAULT;
263
                        return 0;
264
                case WDIOC_GETSTATUS:
265
                case WDIOC_GETBOOTSTATUS:
266
                        return put_user (0, (int *) arg);
267
                case WDIOC_SETOPTIONS:
268
                        if (get_user (options, (int *) arg))
269
                                return -EFAULT;
270
                        if (options & WDIOS_DISABLECARD) {
271
                                tco_timer_stop ();
272
                                retval = 0;
273
                        }
274
                        if (options & WDIOS_ENABLECARD) {
275
                                tco_timer_reload ();
276
                                tco_timer_start ();
277
                                retval = 0;
278
                        }
279
                        return retval;
280
                case WDIOC_KEEPALIVE:
281
                        tco_timer_reload ();
282
                        return 0;
283
                case WDIOC_SETTIMEOUT:
284
                        if (get_user (u_margin, (int *) arg))
285
                                return -EFAULT;
286
                        new_margin = (u_margin * 10 + 5) / 6;
287
                        if ((new_margin < 4) || (new_margin > 63))
288
                                return -EINVAL;
289
                        if (tco_timer_settimer ((unsigned char) new_margin))
290
                            return -EINVAL;
291
                        i810_margin = new_margin;
292
                        tco_timer_reload ();
293
                        /* Fall */
294
                case WDIOC_GETTIMEOUT:
295
                        return put_user ((int)(i810_margin * 6 / 10), (int *) arg);
296
        }
297
}
298
 
299
/*
300
 * Data for PCI driver interface
301
 *
302
 * This data only exists for exporting the supported
303
 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
304
 * register a pci_driver, because someone else might one day
305
 * want to register another driver on the same PCI id.
306
 */
307
static struct pci_device_id i810tco_pci_tbl[] __initdata = {
308
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0,   PCI_ANY_ID, PCI_ANY_ID, },
309
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0,   PCI_ANY_ID, PCI_ANY_ID, },
310
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,   PCI_ANY_ID, PCI_ANY_ID, },
311
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10,  PCI_ANY_ID, PCI_ANY_ID, },
312
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,   PCI_ANY_ID, PCI_ANY_ID, },
313
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12,  PCI_ANY_ID, PCI_ANY_ID, },
314
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,   PCI_ANY_ID, PCI_ANY_ID, },
315
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0,    PCI_ANY_ID, PCI_ANY_ID, },
316
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,   PCI_ANY_ID, PCI_ANY_ID, },
317
        { 0, },
318
};
319
MODULE_DEVICE_TABLE (pci, i810tco_pci_tbl);
320
 
321
static struct pci_dev *i810tco_pci;
322
 
323
static unsigned char __init i810tco_getdevice (void)
324
{
325
        struct pci_dev *dev;
326
        u8 val1, val2;
327
        u16 badr;
328
        /*
329
         *      Find the PCI device
330
         */
331
 
332
        pci_for_each_dev(dev) {
333
                if (pci_match_device(i810tco_pci_tbl, dev)) {
334
                        i810tco_pci = dev;
335
                        break;
336
                }
337
        }
338
 
339
        if (i810tco_pci) {
340
                /*
341
                 *      Find the ACPI base I/O address which is the base
342
                 *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
343
                 *      ACPIBASE is bits [15:7] from 0x40-0x43
344
                 */
345
                pci_read_config_byte (i810tco_pci, 0x40, &val1);
346
                pci_read_config_byte (i810tco_pci, 0x41, &val2);
347
                badr = ((val2 << 1) | (val1 >> 7)) << 7;
348
                ACPIBASE = badr;
349
                /* Something's wrong here, ACPIBASE has to be set */
350
                if (badr == 0x0001 || badr == 0x0000) {
351
                        printk (KERN_ERR TCO_MODULE_NAME " init: failed to get TCOBASE address\n");
352
                        return 0;
353
                }
354
                /*
355
                 * Check chipset's NO_REBOOT bit
356
                 */
357
                pci_read_config_byte (i810tco_pci, 0xd4, &val1);
358
                if (val1 & 0x02) {
359
                        val1 &= 0xfd;
360
                        pci_write_config_byte (i810tco_pci, 0xd4, val1);
361
                        pci_read_config_byte (i810tco_pci, 0xd4, &val1);
362
                        if (val1 & 0x02) {
363
                                printk (KERN_ERR TCO_MODULE_NAME " init: failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
364
                                return 0;        /* Cannot reset NO_REBOOT bit */
365
                        }
366
                }
367
                /* Set the TCO_EN bit in SMI_EN register */
368
                val1 = inb (SMI_EN + 1);
369
                val1 &= 0xdf;
370
                outb (val1, SMI_EN + 1);
371
                /* Clear out the (probably old) status */
372
                outb (0, TCO1_STS);
373
                outb (3, TCO2_STS);
374
                return 1;
375
        }
376
        return 0;
377
}
378
 
379
static struct file_operations i810tco_fops = {
380
        owner:          THIS_MODULE,
381
        write:          i810tco_write,
382
        ioctl:          i810tco_ioctl,
383
        open:           i810tco_open,
384
        release:        i810tco_release,
385
};
386
 
387
static struct miscdevice i810tco_miscdev = {
388
        minor:          WATCHDOG_MINOR,
389
        name:           "watchdog",
390
        fops:           &i810tco_fops,
391
};
392
 
393
static int __init watchdog_init (void)
394
{
395
        spin_lock_init(&tco_lock);
396
        if (!i810tco_getdevice () || i810tco_pci == NULL)
397
                return -ENODEV;
398
        if (!request_region (TCOBASE, 0x10, "i810 TCO")) {
399
                printk (KERN_ERR TCO_MODULE_NAME
400
                        ": I/O address 0x%04x already in use\n",
401
                        TCOBASE);
402
                return -EIO;
403
        }
404
        if (misc_register (&i810tco_miscdev) != 0) {
405
                release_region (TCOBASE, 0x10);
406
                printk (KERN_ERR TCO_MODULE_NAME ": cannot register miscdev\n");
407
                return -EIO;
408
        }
409
        tco_timer_settimer ((unsigned char) i810_margin);
410
        tco_timer_reload ();
411
 
412
        printk (KERN_INFO TCO_DRIVER_NAME
413
                ": timer margin: %d sec (0x%04x) (nowayout=%d)\n",
414
                (int) (i810_margin * 6 / 10), TCOBASE, nowayout);
415
        return 0;
416
}
417
 
418
static void __exit watchdog_cleanup (void)
419
{
420
        u8 val;
421
 
422
        /* Reset the timer before we leave */
423
        tco_timer_reload ();
424
        /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
425
        pci_read_config_byte (i810tco_pci, 0xd4, &val);
426
        val |= 0x02;
427
        pci_write_config_byte (i810tco_pci, 0xd4, val);
428
        release_region (TCOBASE, 0x10);
429
        misc_deregister (&i810tco_miscdev);
430
}
431
 
432
module_init(watchdog_init);
433
module_exit(watchdog_cleanup);
434
 
435
MODULE_AUTHOR("Nils Faerber");
436
MODULE_DESCRIPTION("TCO timer driver for i8xx chipsets");
437
MODULE_LICENSE("GPL");
438
EXPORT_NO_SYMBOLS;

powered by: WebSVN 2.1.0

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