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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [watchdog/] [sc1200wdt.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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 module option
26
 *      20030116 Adam Belay             Updated to the latest pnp code
27
 *
28
 */
29
 
30
#include <linux/module.h>
31
#include <linux/moduleparam.h>
32
#include <linux/miscdevice.h>
33
#include <linux/watchdog.h>
34
#include <linux/ioport.h>
35
#include <linux/spinlock.h>
36
#include <linux/notifier.h>
37
#include <linux/reboot.h>
38
#include <linux/init.h>
39
#include <linux/pnp.h>
40
#include <linux/fs.h>
41
 
42
#include <asm/semaphore.h>
43
#include <asm/io.h>
44
#include <asm/uaccess.h>
45
 
46
#define SC1200_MODULE_VER       "build 20020303"
47
#define SC1200_MODULE_NAME      "sc1200wdt"
48
#define PFX                     SC1200_MODULE_NAME ": "
49
 
50
#define MAX_TIMEOUT     255     /* 255 minutes */
51
#define PMIR            (io)    /* Power Management Index Register */
52
#define PMDR            (io+1)  /* Power Management Data Register */
53
 
54
/* Data Register indexes */
55
#define FER1            0x00    /* Function enable register 1 */
56
#define FER2            0x01    /* Function enable register 2 */
57
#define PMC1            0x02    /* Power Management Ctrl 1 */
58
#define PMC2            0x03    /* Power Management Ctrl 2 */
59
#define PMC3            0x04    /* Power Management Ctrl 3 */
60
#define WDTO            0x05    /* Watchdog timeout register */
61
#define WDCF            0x06    /* Watchdog config register */
62
#define WDST            0x07    /* Watchdog status register */
63
 
64
/* WDCF bitfields - which devices assert WDO */
65
#define KBC_IRQ         0x01    /* Keyboard Controller */
66
#define MSE_IRQ         0x02    /* Mouse */
67
#define UART1_IRQ       0x03    /* Serial0 */
68
#define UART2_IRQ       0x04    /* Serial1 */
69
/* 5 -7 are reserved */
70
 
71
static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER;
72
static int timeout = 1;
73
static int io = -1;
74
static int io_len = 2;          /* for non plug and play */
75
static struct semaphore open_sem;
76
static char expect_close;
77
static DEFINE_SPINLOCK(sc1200wdt_lock); /* io port access serialisation */
78
 
79
#if defined CONFIG_PNP
80
static int isapnp = 1;
81
static struct pnp_dev *wdt_dev;
82
 
83
module_param(isapnp, int, 0);
84
MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled");
85
#endif
86
 
87
module_param(io, int, 0);
88
MODULE_PARM_DESC(io, "io port");
89
module_param(timeout, int, 0);
90
MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
91
 
92
static int nowayout = WATCHDOG_NOWAYOUT;
93
module_param(nowayout, int, 0);
94
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
95
 
96
 
97
 
98
/* Read from Data Register */
99
static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data)
100
{
101
        spin_lock(&sc1200wdt_lock);
102
        outb_p(index, PMIR);
103
        *data = inb(PMDR);
104
        spin_unlock(&sc1200wdt_lock);
105
}
106
 
107
 
108
/* Write to Data Register */
109
static inline void sc1200wdt_write_data(unsigned char index, unsigned char data)
110
{
111
        spin_lock(&sc1200wdt_lock);
112
        outb_p(index, PMIR);
113
        outb(data, PMDR);
114
        spin_unlock(&sc1200wdt_lock);
115
}
116
 
117
 
118
static void sc1200wdt_start(void)
119
{
120
        unsigned char reg;
121
 
122
        sc1200wdt_read_data(WDCF, &reg);
123
        /* assert WDO when any of the following interrupts are triggered too */
124
        reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
125
        sc1200wdt_write_data(WDCF, reg);
126
        /* set the timeout and get the ball rolling */
127
        sc1200wdt_write_data(WDTO, timeout);
128
}
129
 
130
 
131
static void sc1200wdt_stop(void)
132
{
133
        sc1200wdt_write_data(WDTO, 0);
134
}
135
 
136
 
137
/* This returns the status of the WDO signal, inactive high. */
138
static inline int sc1200wdt_status(void)
139
{
140
        unsigned char ret;
141
 
142
        sc1200wdt_read_data(WDST, &ret);
143
        /* If the bit is inactive, the watchdog is enabled, so return
144
         * KEEPALIVEPING which is a bit of a kludge because there's nothing
145
         * else for enabled/disabled status
146
         */
147
        return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;   /* bits 1 - 7 are undefined */
148
}
149
 
150
 
151
static int sc1200wdt_open(struct inode *inode, struct file *file)
152
{
153
        /* allow one at a time */
154
        if (down_trylock(&open_sem))
155
                return -EBUSY;
156
 
157
        if (timeout > MAX_TIMEOUT)
158
                timeout = MAX_TIMEOUT;
159
 
160
        sc1200wdt_start();
161
        printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout);
162
 
163
        return nonseekable_open(inode, file);
164
}
165
 
166
 
167
static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
168
{
169
        int new_timeout;
170
        void __user *argp = (void __user *)arg;
171
        int __user *p = argp;
172
        static struct watchdog_info ident = {
173
                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
174
                .firmware_version = 0,
175
                .identity = "PC87307/PC97307",
176
        };
177
 
178
        switch (cmd) {
179
                default:
180
                        return -ENOTTY;
181
 
182
                case WDIOC_GETSUPPORT:
183
                        if (copy_to_user(argp, &ident, sizeof ident))
184
                                return -EFAULT;
185
                        return 0;
186
 
187
                case WDIOC_GETSTATUS:
188
                        return put_user(sc1200wdt_status(), p);
189
 
190
                case WDIOC_GETBOOTSTATUS:
191
                        return put_user(0, p);
192
 
193
                case WDIOC_KEEPALIVE:
194
                        sc1200wdt_write_data(WDTO, timeout);
195
                        return 0;
196
 
197
                case WDIOC_SETTIMEOUT:
198
                        if (get_user(new_timeout, p))
199
                                return -EFAULT;
200
 
201
                        /* the API states this is given in secs */
202
                        new_timeout /= 60;
203
                        if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
204
                                return -EINVAL;
205
 
206
                        timeout = new_timeout;
207
                        sc1200wdt_write_data(WDTO, timeout);
208
                        /* fall through and return the new timeout */
209
 
210
                case WDIOC_GETTIMEOUT:
211
                        return put_user(timeout * 60, p);
212
 
213
                case WDIOC_SETOPTIONS:
214
                {
215
                        int options, retval = -EINVAL;
216
 
217
                        if (get_user(options, p))
218
                                return -EFAULT;
219
 
220
                        if (options & WDIOS_DISABLECARD) {
221
                                sc1200wdt_stop();
222
                                retval = 0;
223
                        }
224
 
225
                        if (options & WDIOS_ENABLECARD) {
226
                                sc1200wdt_start();
227
                                retval = 0;
228
                        }
229
 
230
                        return retval;
231
                }
232
        }
233
}
234
 
235
 
236
static int sc1200wdt_release(struct inode *inode, struct file *file)
237
{
238
        if (expect_close == 42) {
239
                sc1200wdt_stop();
240
                printk(KERN_INFO PFX "Watchdog disabled\n");
241
        } else {
242
                sc1200wdt_write_data(WDTO, timeout);
243
                printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout);
244
        }
245
        up(&open_sem);
246
        expect_close = 0;
247
 
248
        return 0;
249
}
250
 
251
 
252
static ssize_t sc1200wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos)
253
{
254
        if (len) {
255
                if (!nowayout) {
256
                        size_t i;
257
 
258
                        expect_close = 0;
259
 
260
                        for (i = 0; i != len; i++) {
261
                                char c;
262
 
263
                                if (get_user(c, data+i))
264
                                        return -EFAULT;
265
                                if (c == 'V')
266
                                        expect_close = 42;
267
                        }
268
                }
269
 
270
                sc1200wdt_write_data(WDTO, timeout);
271
                return len;
272
        }
273
 
274
        return 0;
275
}
276
 
277
 
278
static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
279
{
280
        if (code == SYS_DOWN || code == SYS_HALT)
281
                sc1200wdt_stop();
282
 
283
        return NOTIFY_DONE;
284
}
285
 
286
 
287
static struct notifier_block sc1200wdt_notifier =
288
{
289
        .notifier_call =        sc1200wdt_notify_sys,
290
};
291
 
292
static const struct file_operations sc1200wdt_fops =
293
{
294
        .owner          = THIS_MODULE,
295
        .llseek         = no_llseek,
296
        .write          = sc1200wdt_write,
297
        .ioctl          = sc1200wdt_ioctl,
298
        .open           = sc1200wdt_open,
299
        .release        = sc1200wdt_release,
300
};
301
 
302
static struct miscdevice sc1200wdt_miscdev =
303
{
304
        .minor          = WATCHDOG_MINOR,
305
        .name           = "watchdog",
306
        .fops           = &sc1200wdt_fops,
307
};
308
 
309
 
310
static int __init sc1200wdt_probe(void)
311
{
312
        /* The probe works by reading the PMC3 register's default value of 0x0e
313
         * there is one caveat, if the device disables the parallel port or any
314
         * of the UARTs we won't be able to detect it.
315
         * Nb. This could be done with accuracy by reading the SID registers, but
316
         * we don't have access to those io regions.
317
         */
318
 
319
        unsigned char reg;
320
 
321
        sc1200wdt_read_data(PMC3, &reg);
322
        reg &= 0x0f;                            /* we don't want the UART busy bits */
323
        return (reg == 0x0e) ? 0 : -ENODEV;
324
}
325
 
326
 
327
#if defined CONFIG_PNP
328
 
329
static struct pnp_device_id scl200wdt_pnp_devices[] = {
330
        /* National Semiconductor PC87307/PC97307 watchdog component */
331
        {.id = "NSC0800", .driver_data = 0},
332
        {.id = ""},
333
};
334
 
335
static int scl200wdt_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
336
{
337
        /* this driver only supports one card at a time */
338
        if (wdt_dev || !isapnp)
339
                return -EBUSY;
340
 
341
        wdt_dev = dev;
342
        io = pnp_port_start(wdt_dev, 0);
343
        io_len = pnp_port_len(wdt_dev, 0);
344
 
345
        if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
346
                printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
347
                return -EBUSY;
348
        }
349
 
350
        printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d\n", io, io_len);
351
        return 0;
352
}
353
 
354
static void scl200wdt_pnp_remove(struct pnp_dev * dev)
355
{
356
        if (wdt_dev){
357
                release_region(io, io_len);
358
                wdt_dev = NULL;
359
        }
360
}
361
 
362
static struct pnp_driver scl200wdt_pnp_driver = {
363
        .name           = "scl200wdt",
364
        .id_table       = scl200wdt_pnp_devices,
365
        .probe          = scl200wdt_pnp_probe,
366
        .remove         = scl200wdt_pnp_remove,
367
};
368
 
369
#endif /* CONFIG_PNP */
370
 
371
 
372
static int __init sc1200wdt_init(void)
373
{
374
        int ret;
375
 
376
        printk("%s\n", banner);
377
 
378
        sema_init(&open_sem, 1);
379
 
380
#if defined CONFIG_PNP
381
        if (isapnp) {
382
                ret = pnp_register_driver(&scl200wdt_pnp_driver);
383
                if (ret)
384
                        goto out_clean;
385
        }
386
#endif
387
 
388
        if (io == -1) {
389
                printk(KERN_ERR PFX "io parameter must be specified\n");
390
                ret = -EINVAL;
391
                goto out_pnp;
392
        }
393
 
394
#if defined CONFIG_PNP
395
        /* now that the user has specified an IO port and we haven't detected
396
         * any devices, disable pnp support */
397
        isapnp = 0;
398
        pnp_unregister_driver(&scl200wdt_pnp_driver);
399
#endif
400
 
401
        if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
402
                printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
403
                ret = -EBUSY;
404
                goto out_pnp;
405
        }
406
 
407
        ret = sc1200wdt_probe();
408
        if (ret)
409
                goto out_io;
410
 
411
        ret = register_reboot_notifier(&sc1200wdt_notifier);
412
        if (ret) {
413
                printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret);
414
                goto out_io;
415
        }
416
 
417
        ret = misc_register(&sc1200wdt_miscdev);
418
        if (ret) {
419
                printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
420
                goto out_rbt;
421
        }
422
 
423
        /* ret = 0 */
424
 
425
out_clean:
426
        return ret;
427
 
428
out_rbt:
429
        unregister_reboot_notifier(&sc1200wdt_notifier);
430
 
431
out_io:
432
        release_region(io, io_len);
433
 
434
out_pnp:
435
#if defined CONFIG_PNP
436
        if (isapnp)
437
                pnp_unregister_driver(&scl200wdt_pnp_driver);
438
#endif
439
        goto out_clean;
440
}
441
 
442
 
443
static void __exit sc1200wdt_exit(void)
444
{
445
        misc_deregister(&sc1200wdt_miscdev);
446
        unregister_reboot_notifier(&sc1200wdt_notifier);
447
 
448
#if defined CONFIG_PNP
449
        if(isapnp)
450
                pnp_unregister_driver(&scl200wdt_pnp_driver);
451
        else
452
#endif
453
        release_region(io, io_len);
454
}
455
 
456
module_init(sc1200wdt_init);
457
module_exit(sc1200wdt_exit);
458
 
459
MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
460
MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component");
461
MODULE_LICENSE("GPL");
462
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

powered by: WebSVN 2.1.0

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