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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [sc1200wdt.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
 *      National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
3
 *      (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,
4
 *                      All Rights Reserved.
5
 *      Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.
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
 *      The author(s) of this software shall not be held liable for damages
13
 *      of any nature resulting due to the use of this software. This
14
 *      software is provided AS-IS with no warranties.
15
 *
16
 *      Changelog:
17
 *      20020220 Zwane Mwaikambo        Code based on datasheet, no hardware.
18
 *      20020221 Zwane Mwaikambo        Cleanups as suggested by Jeff Garzik and Alan Cox.
19
 *      20020222 Zwane Mwaikambo        Added probing.
20
 *      20020225 Zwane Mwaikambo        Added ISAPNP support.
21
 *      20020412 Rob Radez              Broke out start/stop functions
22
 *               <rob@osinvestor.com>   Return proper status instead of temperature warning
23
 *                                      Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls
24
 *                                      Fix CONFIG_WATCHDOG_NOWAYOUT
25
 *      20020530 Joel Becker            Add Matt Domsch's nowayout
26
 *                                      module option
27
 */
28
 
29
#include <linux/config.h>
30
#include <linux/module.h>
31
#include <linux/kernel.h>
32
#include <linux/miscdevice.h>
33
#include <linux/watchdog.h>
34
#include <linux/ioport.h>
35
#include <linux/spinlock.h>
36
#include <linux/ioport.h>
37
#include <asm/semaphore.h>
38
#include <asm/io.h>
39
#include <asm/uaccess.h>
40
#include <linux/notifier.h>
41
#include <linux/reboot.h>
42
#include <linux/init.h>
43
#include <linux/isapnp.h>
44
 
45
#define SC1200_MODULE_VER       "build 20020303"
46
#define SC1200_MODULE_NAME      "sc1200wdt"
47
#define PFX                     SC1200_MODULE_NAME ": "
48
 
49
#define MAX_TIMEOUT     255     /* 255 minutes */
50
#define PMIR            (io)    /* Power Management Index Register */
51
#define PMDR            (io+1)  /* Power Management Data Register */
52
 
53
/* Data Register indexes */
54
#define FER1            0x00    /* Function enable register 1 */
55
#define FER2            0x01    /* Function enable register 2 */
56
#define PMC1            0x02    /* Power Management Ctrl 1 */
57
#define PMC2            0x03    /* Power Management Ctrl 2 */
58
#define PMC3            0x04    /* Power Management Ctrl 3 */
59
#define WDTO            0x05    /* Watchdog timeout register */
60
#define WDCF            0x06    /* Watchdog config register */
61
#define WDST            0x07    /* Watchdog status register */
62
 
63
/* WDCF bitfields - which devices assert WDO */
64
#define KBC_IRQ         0x01    /* Keyboard Controller */
65
#define MSE_IRQ         0x02    /* Mouse */
66
#define UART1_IRQ       0x03    /* Serial0 */
67
#define UART2_IRQ       0x04    /* Serial1 */
68
/* 5 -7 are reserved */
69
 
70
static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER;
71
static int timeout = 1;
72
static int io = -1;
73
static int io_len = 2;          /* for non plug and play */
74
struct semaphore open_sem;
75
static char expect_close;
76
spinlock_t sc1200wdt_lock;      /* io port access serialisation */
77
 
78
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
79
static int isapnp = 1;
80
static struct pci_dev *wdt_dev;
81
 
82
MODULE_PARM(isapnp, "i");
83
MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled");
84
#endif
85
 
86
MODULE_PARM(io, "i");
87
MODULE_PARM_DESC(io, "io port");
88
MODULE_PARM(timeout, "i");
89
MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
90
 
91
#ifdef CONFIG_WATCHDOG_NOWAYOUT
92
static int nowayout = 1;
93
#else
94
static int nowayout = 0;
95
#endif
96
 
97
MODULE_PARM(nowayout,"i");
98
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
99
 
100
 
101
/* Read from Data Register */
102
static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data)
103
{
104
        spin_lock(&sc1200wdt_lock);
105
        outb_p(index, PMIR);
106
        *data = inb(PMDR);
107
        spin_unlock(&sc1200wdt_lock);
108
}
109
 
110
 
111
/* Write to Data Register */
112
static inline void sc1200wdt_write_data(unsigned char index, unsigned char data)
113
{
114
        spin_lock(&sc1200wdt_lock);
115
        outb_p(index, PMIR);
116
        outb(data, PMDR);
117
        spin_unlock(&sc1200wdt_lock);
118
}
119
 
120
 
121
static void sc1200wdt_start(void)
122
{
123
        unsigned char reg;
124
 
125
        sc1200wdt_read_data(WDCF, &reg);
126
        /* assert WDO when any of the following interrupts are triggered too */
127
        reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
128
        sc1200wdt_write_data(WDCF, reg);
129
        /* set the timeout and get the ball rolling */
130
        sc1200wdt_write_data(WDTO, timeout);
131
}
132
 
133
 
134
static void sc1200wdt_stop(void)
135
{
136
        sc1200wdt_write_data(WDTO, 0);
137
}
138
 
139
 
140
/* This returns the status of the WDO signal, inactive high. */
141
static inline int sc1200wdt_status(void)
142
{
143
        unsigned char ret;
144
 
145
        sc1200wdt_read_data(WDST, &ret);
146
        /* If the bit is inactive, the watchdog is enabled, so return
147
         * KEEPALIVEPING which is a bit of a kludge because there's nothing
148
         * else for enabled/disabled status
149
         */
150
        return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;   /* bits 1 - 7 are undefined */
151
}
152
 
153
 
154
static int sc1200wdt_open(struct inode *inode, struct file *file)
155
{
156
        /* allow one at a time */
157
        if (down_trylock(&open_sem))
158
                return -EBUSY;
159
 
160
        if (timeout > MAX_TIMEOUT)
161
                timeout = MAX_TIMEOUT;
162
 
163
        sc1200wdt_start();
164
        printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout);
165
 
166
        return 0;
167
}
168
 
169
 
170
static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
171
{
172
        int new_timeout;
173
        static struct watchdog_info ident = {
174
                options:                WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
175
                firmware_version:       0,
176
                identity:               "PC87307/PC97307"
177
        };
178
 
179
        switch (cmd) {
180
                default:
181
                        return -ENOTTY; /* Keep Pavel Machek amused ;) */
182
 
183
                case WDIOC_GETSUPPORT:
184
                        if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident))
185
                                return -EFAULT;
186
                        return 0;
187
 
188
                case WDIOC_GETSTATUS:
189
                        return put_user(sc1200wdt_status(), (int *)arg);
190
 
191
                case WDIOC_GETBOOTSTATUS:
192
                        return put_user(0, (int *)arg);
193
 
194
                case WDIOC_KEEPALIVE:
195
                        sc1200wdt_write_data(WDTO, timeout);
196
                        return 0;
197
 
198
                case WDIOC_SETTIMEOUT:
199
                        if (get_user(new_timeout, (int *)arg))
200
                                return -EFAULT;
201
 
202
                        /* the API states this is given in secs */
203
                        new_timeout /= 60;
204
                        if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
205
                                return -EINVAL;
206
 
207
                        timeout = new_timeout;
208
                        sc1200wdt_write_data(WDTO, timeout);
209
                        /* fall through and return the new timeout */
210
 
211
                case WDIOC_GETTIMEOUT:
212
                        return put_user(timeout * 60, (int *)arg);
213
 
214
                case WDIOC_SETOPTIONS:
215
                {
216
                        int options, retval = -EINVAL;
217
 
218
                        if (get_user(options, (int *)arg))
219
                                return -EFAULT;
220
 
221
                        if (options & WDIOS_DISABLECARD) {
222
                                sc1200wdt_stop();
223
                                retval = 0;
224
                        }
225
 
226
                        if (options & WDIOS_ENABLECARD) {
227
                                sc1200wdt_start();
228
                                retval = 0;
229
                        }
230
 
231
                        return retval;
232
                }
233
        }
234
}
235
 
236
 
237
static int sc1200wdt_release(struct inode *inode, struct file *file)
238
{
239
        if (expect_close == 42) {
240
                sc1200wdt_stop();
241
                printk(KERN_INFO PFX "Watchdog disabled\n");
242
        } else {
243
                sc1200wdt_write_data(WDTO, timeout);
244
                printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout);
245
        }
246
        up(&open_sem);
247
        expect_close = 0;
248
 
249
        return 0;
250
}
251
 
252
 
253
static ssize_t sc1200wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
254
{
255
        if (ppos != &file->f_pos)
256
                return -ESPIPE;
257
 
258
        if (len) {
259
                if (!nowayout) {
260
                        size_t i;
261
 
262
                        expect_close = 0;
263
 
264
                        for (i = 0; i != len; i++)
265
                        {
266
                                char c;
267
                                if(get_user(c, data+i))
268
                                        return -EFAULT;
269
                                if (c == 'V')
270
                                        expect_close = 42;
271
                        }
272
                }
273
                sc1200wdt_write_data(WDTO, timeout);
274
                return len;
275
        }
276
 
277
        return 0;
278
}
279
 
280
 
281
static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
282
{
283
        if (code == SYS_DOWN || code == SYS_HALT)
284
                sc1200wdt_stop();
285
 
286
        return NOTIFY_DONE;
287
}
288
 
289
 
290
static struct notifier_block sc1200wdt_notifier =
291
{
292
        notifier_call:  sc1200wdt_notify_sys
293
};
294
 
295
static struct file_operations sc1200wdt_fops =
296
{
297
        owner:          THIS_MODULE,
298
        write:          sc1200wdt_write,
299
        ioctl:          sc1200wdt_ioctl,
300
        open:           sc1200wdt_open,
301
        release:        sc1200wdt_release
302
};
303
 
304
static struct miscdevice sc1200wdt_miscdev =
305
{
306
        minor:          WATCHDOG_MINOR,
307
        name:           "watchdog",
308
        fops:           &sc1200wdt_fops,
309
};
310
 
311
 
312
static int __init sc1200wdt_probe(void)
313
{
314
        /* The probe works by reading the PMC3 register's default value of 0x0e
315
         * there is one caveat, if the device disables the parallel port or any
316
         * of the UARTs we won't be able to detect it.
317
         * Nb. This could be done with accuracy by reading the SID registers, but
318
         * we don't have access to those io regions.
319
         */
320
 
321
        unsigned char reg;
322
 
323
        sc1200wdt_read_data(PMC3, &reg);
324
        reg &= 0x0f;                            /* we don't want the UART busy bits */
325
        return (reg == 0x0e) ? 0 : -ENODEV;
326
}
327
 
328
 
329
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
330
 
331
static int __init sc1200wdt_isapnp_probe(void)
332
{
333
        int ret;
334
 
335
        /* The WDT is logical device 8 on the main device */
336
        wdt_dev = isapnp_find_dev(NULL, ISAPNP_VENDOR('N','S','C'), ISAPNP_FUNCTION(0x08), NULL);
337
        if (!wdt_dev)
338
                return -ENODEV;
339
 
340
        if (wdt_dev->prepare(wdt_dev) < 0) {
341
                printk(KERN_ERR PFX "ISA PnP found device that could not be autoconfigured\n");
342
                return -EAGAIN;
343
        }
344
 
345
        if (!(pci_resource_flags(wdt_dev, 0) & IORESOURCE_IO)) {
346
                printk(KERN_ERR PFX "ISA PnP could not find io ports\n");
347
                return -ENODEV;
348
        }
349
 
350
        ret = wdt_dev->activate(wdt_dev);
351
        if (ret && (ret != -EBUSY))
352
                return -ENOMEM;
353
 
354
        /* io port resource overriding support? */
355
        io = pci_resource_start(wdt_dev, 0);
356
        io_len = pci_resource_len(wdt_dev, 0);
357
 
358
        printk(KERN_DEBUG PFX "ISA PnP found device at io port %#x/%d\n", io, io_len);
359
        return 0;
360
}
361
 
362
#endif /* CONFIG_ISAPNP */
363
 
364
 
365
static int __init sc1200wdt_init(void)
366
{
367
        int ret;
368
 
369
        printk(banner);
370
 
371
        spin_lock_init(&sc1200wdt_lock);
372
        sema_init(&open_sem, 1);
373
 
374
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
375
        if (isapnp) {
376
                ret = sc1200wdt_isapnp_probe();
377
                if (ret)
378
                        goto out_clean;
379
        }
380
#endif
381
 
382
        if (io == -1) {
383
                printk(KERN_ERR PFX "io parameter must be specified\n");
384
                ret = -EINVAL;
385
                goto out_clean;
386
        }
387
 
388
        if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
389
                printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
390
                ret = -EBUSY;
391
                goto out_pnp;
392
        }
393
 
394
        ret = sc1200wdt_probe();
395
        if (ret)
396
                goto out_io;
397
 
398
        ret = register_reboot_notifier(&sc1200wdt_notifier);
399
        if (ret) {
400
                printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret);
401
                goto out_io;
402
        }
403
 
404
        ret = misc_register(&sc1200wdt_miscdev);
405
        if (ret) {
406
                printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
407
                goto out_rbt;
408
        }
409
 
410
        /* ret = 0 */
411
 
412
out_clean:
413
        return ret;
414
 
415
out_rbt:
416
        unregister_reboot_notifier(&sc1200wdt_notifier);
417
 
418
out_io:
419
        release_region(io, io_len);
420
 
421
out_pnp:
422
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
423
        if (isapnp && wdt_dev)
424
                wdt_dev->deactivate(wdt_dev);
425
#endif
426
        goto out_clean;
427
}
428
 
429
 
430
static void __exit sc1200wdt_exit(void)
431
{
432
        misc_deregister(&sc1200wdt_miscdev);
433
        unregister_reboot_notifier(&sc1200wdt_notifier);
434
 
435
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
436
        if(isapnp && wdt_dev)
437
                wdt_dev->deactivate(wdt_dev);
438
#endif
439
 
440
        release_region(io, io_len);
441
}
442
 
443
 
444
#ifndef MODULE
445
static int __init sc1200wdt_setup(char *str)
446
{
447
        int ints[4];
448
 
449
        str = get_options (str, ARRAY_SIZE(ints), ints);
450
 
451
        if (ints[0] > 0) {
452
                io = ints[1];
453
                if (ints[0] > 1)
454
                        timeout = ints[2];
455
 
456
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
457
                if (ints[0] > 2)
458
                        isapnp = ints[3];
459
#endif
460
        }
461
 
462
        return 1;
463
}
464
 
465
__setup("sc1200wdt=", sc1200wdt_setup);
466
#endif /* MODULE */
467
 
468
 
469
module_init(sc1200wdt_init);
470
module_exit(sc1200wdt_exit);
471
 
472
MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
473
MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component");
474
MODULE_LICENSE("GPL");
475
EXPORT_NO_SYMBOLS;
476
 

powered by: WebSVN 2.1.0

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