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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *      Watchdog for the 7101 PMU version found in the ALi M1535 chipsets
3
 *
4
 *      This program is free software; you can redistribute it and/or
5
 *      modify it under the terms of the GNU General Public License
6
 *      as published by the Free Software Foundation; either version
7
 *      2 of the License, or (at your option) any later version.
8
 */
9
 
10
#include <linux/module.h>
11
#include <linux/moduleparam.h>
12
#include <linux/types.h>
13
#include <linux/miscdevice.h>
14
#include <linux/watchdog.h>
15
#include <linux/ioport.h>
16
#include <linux/notifier.h>
17
#include <linux/reboot.h>
18
#include <linux/init.h>
19
#include <linux/fs.h>
20
#include <linux/pci.h>
21
 
22
#include <asm/uaccess.h>
23
#include <asm/io.h>
24
 
25
#define WATCHDOG_NAME "ALi_M1535"
26
#define PFX WATCHDOG_NAME ": "
27
#define WATCHDOG_TIMEOUT 60     /* 60 sec default timeout */
28
 
29
/* internal variables */
30
static unsigned long ali_is_open;
31
static char ali_expect_release;
32
static struct pci_dev *ali_pci;
33
static u32 ali_timeout_bits;    /* stores the computed timeout */
34
static DEFINE_SPINLOCK(ali_lock);       /* Guards the hardware */
35
 
36
/* module parameters */
37
static int timeout = WATCHDOG_TIMEOUT;
38
module_param(timeout, int, 0);
39
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (0<timeout<18000, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
40
 
41
static int nowayout = WATCHDOG_NOWAYOUT;
42
module_param(nowayout, int, 0);
43
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
44
 
45
/*
46
 *      ali_start       -       start watchdog countdown
47
 *
48
 *      Starts the timer running providing the timer has a counter
49
 *      configuration set.
50
 */
51
 
52
static void ali_start(void)
53
{
54
        u32 val;
55
 
56
        spin_lock(&ali_lock);
57
 
58
        pci_read_config_dword(ali_pci, 0xCC, &val);
59
        val &= ~0x3F;   /* Mask count */
60
        val |= (1<<25) | ali_timeout_bits;
61
        pci_write_config_dword(ali_pci, 0xCC, val);
62
 
63
        spin_unlock(&ali_lock);
64
}
65
 
66
/*
67
 *      ali_stop        -       stop the timer countdown
68
 *
69
 *      Stop the ALi watchdog countdown
70
 */
71
 
72
static void ali_stop(void)
73
{
74
        u32 val;
75
 
76
        spin_lock(&ali_lock);
77
 
78
        pci_read_config_dword(ali_pci, 0xCC, &val);
79
        val &= ~0x3F;   /* Mask count to zero (disabled) */
80
        val &= ~(1<<25);/* and for safety mask the reset enable */
81
        pci_write_config_dword(ali_pci, 0xCC, val);
82
 
83
        spin_unlock(&ali_lock);
84
}
85
 
86
/*
87
 *      ali_keepalive   -       send a keepalive to the watchdog
88
 *
89
 *      Send a keepalive to the timer (actually we restart the timer).
90
 */
91
 
92
static void ali_keepalive(void)
93
{
94
        ali_start();
95
}
96
 
97
/*
98
 *      ali_settimer    -       compute the timer reload value
99
 *      @t: time in seconds
100
 *
101
 *      Computes the timeout values needed
102
 */
103
 
104
static int ali_settimer(int t)
105
{
106
        if(t < 0)
107
                return -EINVAL;
108
        else if(t < 60)
109
                ali_timeout_bits = t|(1<<6);
110
        else if(t < 3600)
111
                ali_timeout_bits = (t/60)|(1<<7);
112
        else if(t < 18000)
113
                ali_timeout_bits = (t/300)|(1<<6)|(1<<7);
114
        else return -EINVAL;
115
 
116
        timeout = t;
117
        return 0;
118
}
119
 
120
/*
121
 *      /dev/watchdog handling
122
 */
123
 
124
/*
125
 *      ali_write       -       writes to ALi watchdog
126
 *      @file: file from VFS
127
 *      @data: user address of data
128
 *      @len: length of data
129
 *      @ppos: pointer to the file offset
130
 *
131
 *      Handle a write to the ALi watchdog. Writing to the file pings
132
 *      the watchdog and resets it. Writing the magic 'V' sequence allows
133
 *      the next close to turn off the watchdog.
134
 */
135
 
136
static ssize_t ali_write(struct file *file, const char __user *data,
137
                              size_t len, loff_t * ppos)
138
{
139
        /* See if we got the magic character 'V' and reload the timer */
140
        if (len) {
141
                if (!nowayout) {
142
                        size_t i;
143
 
144
                        /* note: just in case someone wrote the magic character
145
                         * five months ago... */
146
                        ali_expect_release = 0;
147
 
148
                        /* scan to see whether or not we got the magic character */
149
                        for (i = 0; i != len; i++) {
150
                                char c;
151
                                if(get_user(c, data+i))
152
                                        return -EFAULT;
153
                                if (c == 'V')
154
                                        ali_expect_release = 42;
155
                        }
156
                }
157
 
158
                /* someone wrote to us, we should reload the timer */
159
                ali_start();
160
        }
161
        return len;
162
}
163
 
164
/*
165
 *      ali_ioctl       -       handle watchdog ioctls
166
 *      @inode: VFS inode
167
 *      @file: VFS file pointer
168
 *      @cmd: ioctl number
169
 *      @arg: arguments to the ioctl
170
 *
171
 *      Handle the watchdog ioctls supported by the ALi driver. Really
172
 *      we want an extension to enable irq ack monitoring and the like
173
 */
174
 
175
static int ali_ioctl(struct inode *inode, struct file *file,
176
                          unsigned int cmd, unsigned long arg)
177
{
178
        void __user *argp = (void __user *)arg;
179
        int __user *p = argp;
180
        static struct watchdog_info ident = {
181
                .options =              WDIOF_KEEPALIVEPING |
182
                                        WDIOF_SETTIMEOUT |
183
                                        WDIOF_MAGICCLOSE,
184
                .firmware_version =     0,
185
                .identity =             "ALi M1535 WatchDog Timer",
186
        };
187
 
188
        switch (cmd) {
189
                case WDIOC_GETSUPPORT:
190
                        return copy_to_user(argp, &ident,
191
                                sizeof (ident)) ? -EFAULT : 0;
192
 
193
                case WDIOC_GETSTATUS:
194
                case WDIOC_GETBOOTSTATUS:
195
                        return put_user(0, p);
196
 
197
                case WDIOC_KEEPALIVE:
198
                        ali_keepalive();
199
                        return 0;
200
 
201
                case WDIOC_SETOPTIONS:
202
                {
203
                        int new_options, retval = -EINVAL;
204
 
205
                        if (get_user (new_options, p))
206
                                return -EFAULT;
207
 
208
                        if (new_options & WDIOS_DISABLECARD) {
209
                                ali_stop();
210
                                retval = 0;
211
                        }
212
 
213
                        if (new_options & WDIOS_ENABLECARD) {
214
                                ali_start();
215
                                retval = 0;
216
                        }
217
 
218
                        return retval;
219
                }
220
 
221
                case WDIOC_SETTIMEOUT:
222
                {
223
                        int new_timeout;
224
 
225
                        if (get_user(new_timeout, p))
226
                                return -EFAULT;
227
 
228
                        if (ali_settimer(new_timeout))
229
                            return -EINVAL;
230
 
231
                        ali_keepalive();
232
                        /* Fall */
233
                }
234
 
235
                case WDIOC_GETTIMEOUT:
236
                        return put_user(timeout, p);
237
 
238
                default:
239
                        return -ENOTTY;
240
        }
241
}
242
 
243
/*
244
 *      ali_open        -       handle open of ali watchdog
245
 *      @inode: inode from VFS
246
 *      @file: file from VFS
247
 *
248
 *      Open the ALi watchdog device. Ensure only one person opens it
249
 *      at a time. Also start the watchdog running.
250
 */
251
 
252
static int ali_open(struct inode *inode, struct file *file)
253
{
254
        /* /dev/watchdog can only be opened once */
255
        if (test_and_set_bit(0, &ali_is_open))
256
                return -EBUSY;
257
 
258
        /* Activate */
259
        ali_start();
260
        return nonseekable_open(inode, file);
261
}
262
 
263
/*
264
 *      ali_release     -       close an ALi watchdog
265
 *      @inode: inode from VFS
266
 *      @file: file from VFS
267
 *
268
 *      Close the ALi watchdog device. Actual shutdown of the timer
269
 *      only occurs if the magic sequence has been set.
270
 */
271
 
272
static int ali_release(struct inode *inode, struct file *file)
273
{
274
        /*
275
         *      Shut off the timer.
276
         */
277
        if (ali_expect_release == 42) {
278
                ali_stop();
279
        } else {
280
                printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
281
                ali_keepalive();
282
        }
283
        clear_bit(0, &ali_is_open);
284
        ali_expect_release = 0;
285
        return 0;
286
}
287
 
288
/*
289
 *      ali_notify_sys  -       System down notifier
290
 *
291
 *      Notifier for system down
292
 */
293
 
294
 
295
static int ali_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
296
{
297
        if (code==SYS_DOWN || code==SYS_HALT) {
298
                /* Turn the WDT off */
299
                ali_stop();
300
        }
301
 
302
        return NOTIFY_DONE;
303
}
304
 
305
/*
306
 *      Data for PCI driver interface
307
 *
308
 *      This data only exists for exporting the supported
309
 *      PCI ids via MODULE_DEVICE_TABLE.  We do not actually
310
 *      register a pci_driver, because someone else might one day
311
 *      want to register another driver on the same PCI id.
312
 */
313
 
314
static struct pci_device_id ali_pci_tbl[] = {
315
        { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
316
        { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
317
        { 0, },
318
};
319
MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
320
 
321
/*
322
 *      ali_find_watchdog       -       find a 1535 and 7101
323
 *
324
 *      Scans the PCI hardware for a 1535 series bridge and matching 7101
325
 *      watchdog device. This may be overtight but it is better to be safe
326
 */
327
 
328
static int __init ali_find_watchdog(void)
329
{
330
        struct pci_dev *pdev;
331
        u32 wdog;
332
 
333
        /* Check for a 1533/1535 series bridge */
334
        pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
335
        if (pdev == NULL)
336
                pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1533, NULL);
337
        if (pdev == NULL)
338
                return -ENODEV;
339
        pci_dev_put(pdev);
340
 
341
        /* Check for the a 7101 PMU */
342
        pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
343
        if(pdev == NULL)
344
                return -ENODEV;
345
 
346
        if(pci_enable_device(pdev)) {
347
                pci_dev_put(pdev);
348
                return -EIO;
349
        }
350
 
351
        ali_pci = pdev;
352
 
353
        /*
354
         *      Initialize the timer bits
355
         */
356
        pci_read_config_dword(pdev, 0xCC, &wdog);
357
 
358
        wdog &= ~0x3F;          /* Timer bits */
359
        wdog &= ~((1<<27)|(1<<26)|(1<<25)|(1<<24));     /* Issued events */
360
        wdog &= ~((1<<16)|(1<<13)|(1<<12)|(1<<11)|(1<<10)|(1<<9));      /* No monitor bits */
361
 
362
        pci_write_config_dword(pdev, 0xCC, wdog);
363
 
364
        return 0;
365
}
366
 
367
/*
368
 *      Kernel Interfaces
369
 */
370
 
371
static const struct file_operations ali_fops = {
372
        .owner =        THIS_MODULE,
373
        .llseek =       no_llseek,
374
        .write =        ali_write,
375
        .ioctl =        ali_ioctl,
376
        .open =         ali_open,
377
        .release =      ali_release,
378
};
379
 
380
static struct miscdevice ali_miscdev = {
381
        .minor =        WATCHDOG_MINOR,
382
        .name =         "watchdog",
383
        .fops =         &ali_fops,
384
};
385
 
386
static struct notifier_block ali_notifier = {
387
        .notifier_call =        ali_notify_sys,
388
};
389
 
390
/*
391
 *      watchdog_init   -       module initialiser
392
 *
393
 *      Scan for a suitable watchdog and if so initialize it. Return an error
394
 *      if we cannot, the error causes the module to unload
395
 */
396
 
397
static int __init watchdog_init(void)
398
{
399
        int ret;
400
 
401
        /* Check whether or not the hardware watchdog is there */
402
        if (ali_find_watchdog() != 0) {
403
                return -ENODEV;
404
        }
405
 
406
        /* Check that the timeout value is within it's range ; if not reset to the default */
407
        if (timeout < 1 || timeout >= 18000) {
408
                timeout = WATCHDOG_TIMEOUT;
409
                printk(KERN_INFO PFX "timeout value must be 0<timeout<18000, using %d\n",
410
                        timeout);
411
        }
412
 
413
        /* Calculate the watchdog's timeout */
414
        ali_settimer(timeout);
415
 
416
        ret = misc_register(&ali_miscdev);
417
        if (ret != 0) {
418
                printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
419
                        WATCHDOG_MINOR, ret);
420
                goto out;
421
        }
422
 
423
        ret = register_reboot_notifier(&ali_notifier);
424
        if (ret != 0) {
425
                printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
426
                        ret);
427
                goto unreg_miscdev;
428
        }
429
 
430
        printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
431
                timeout, nowayout);
432
 
433
out:
434
        return ret;
435
unreg_miscdev:
436
        misc_deregister(&ali_miscdev);
437
        goto out;
438
}
439
 
440
/*
441
 *      watchdog_exit   -       module de-initialiser
442
 *
443
 *      Called while unloading a successfully installed watchdog module.
444
 */
445
 
446
static void __exit watchdog_exit(void)
447
{
448
        /* Stop the timer before we leave */
449
        ali_stop();
450
 
451
        /* Deregister */
452
        unregister_reboot_notifier(&ali_notifier);
453
        misc_deregister(&ali_miscdev);
454
        pci_dev_put(ali_pci);
455
}
456
 
457
module_init(watchdog_init);
458
module_exit(watchdog_exit);
459
 
460
MODULE_AUTHOR("Alan Cox");
461
MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
462
MODULE_LICENSE("GPL");
463
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.