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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *      IT8712F "Smart Guardian" Watchdog support
3
 *
4
 *      Copyright (c) 2006-2007 Jorge Boncompte - DTI2 <jorge@dti2.net>
5
 *
6
 *      Based on info and code taken from:
7
 *
8
 *      drivers/char/watchdog/scx200_wdt.c
9
 *      drivers/hwmon/it87.c
10
 *      IT8712F EC-LPC I/O Preliminary Specification 0.9.2.pdf
11
 *
12
 *      This program is free software; you can redistribute it and/or
13
 *      modify it under the terms of the GNU General Public License as
14
 *      published by the Free Software Foundation; either version 2 of the
15
 *      License, or (at your option) any later version.
16
 *
17
 *      The author(s) of this software shall not be held liable for damages
18
 *      of any nature resulting due to the use of this software. This
19
 *      software is provided AS-IS with no warranties.
20
 */
21
 
22
#include <linux/module.h>
23
#include <linux/moduleparam.h>
24
#include <linux/init.h>
25
#include <linux/miscdevice.h>
26
#include <linux/watchdog.h>
27
#include <linux/notifier.h>
28
#include <linux/reboot.h>
29
#include <linux/fs.h>
30
#include <linux/pci.h>
31
#include <linux/spinlock.h>
32
 
33
#include <asm/uaccess.h>
34
#include <asm/io.h>
35
 
36
#define NAME "it8712f_wdt"
37
 
38
MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");
39
MODULE_DESCRIPTION("IT8712F Watchdog Driver");
40
MODULE_LICENSE("GPL");
41
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
42
 
43
static int margin = 60;         /* in seconds */
44
module_param(margin, int, 0);
45
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
46
 
47
static int nowayout = WATCHDOG_NOWAYOUT;
48
module_param(nowayout, int, 0);
49
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
50
 
51
static struct semaphore it8712f_wdt_sem;
52
static unsigned expect_close;
53
static spinlock_t io_lock;
54
 
55
/* Dog Food address - We use the game port address */
56
static unsigned short address;
57
 
58
#define REG             0x2e    /* The register to read/write */
59
#define VAL             0x2f    /* The value to read/write */
60
 
61
#define LDN             0x07    /* Register: Logical device select */
62
#define DEVID           0x20    /* Register: Device ID */
63
#define DEVREV          0x22    /* Register: Device Revision */
64
#define ACT_REG         0x30    /* LDN Register: Activation */
65
#define BASE_REG        0x60    /* LDN Register: Base address */
66
 
67
#define IT8712F_DEVID   0x8712
68
 
69
#define LDN_GPIO        0x07    /* GPIO and Watch Dog Timer */
70
#define LDN_GAME        0x09    /* Game Port */
71
 
72
#define WDT_CONTROL     0x71    /* WDT Register: Control */
73
#define WDT_CONFIG      0x72    /* WDT Register: Configuration */
74
#define WDT_TIMEOUT     0x73    /* WDT Register: Timeout Value */
75
 
76
#define WDT_RESET_GAME  0x10
77
#define WDT_RESET_KBD   0x20
78
#define WDT_RESET_MOUSE 0x40
79
#define WDT_RESET_CIR   0x80
80
 
81
#define WDT_UNIT_SEC    0x80    /* If 0 in MINUTES */
82
 
83
#define WDT_OUT_PWROK   0x10
84
#define WDT_OUT_KRST    0x40
85
 
86
static int
87
superio_inb(int reg)
88
{
89
        outb(reg, REG);
90
        return inb(VAL);
91
}
92
 
93
static void
94
superio_outb(int val, int reg)
95
{
96
        outb(reg, REG);
97
        outb(val, VAL);
98
}
99
 
100
static int
101
superio_inw(int reg)
102
{
103
        int val;
104
        outb(reg++, REG);
105
        val = inb(VAL) << 8;
106
        outb(reg, REG);
107
        val |= inb(VAL);
108
        return val;
109
}
110
 
111
static inline void
112
superio_select(int ldn)
113
{
114
        outb(LDN, REG);
115
        outb(ldn, VAL);
116
}
117
 
118
static inline void
119
superio_enter(void)
120
{
121
        spin_lock(&io_lock);
122
        outb(0x87, REG);
123
        outb(0x01, REG);
124
        outb(0x55, REG);
125
        outb(0x55, REG);
126
}
127
 
128
static inline void
129
superio_exit(void)
130
{
131
        outb(0x02, REG);
132
        outb(0x02, VAL);
133
        spin_unlock(&io_lock);
134
}
135
 
136
static inline void
137
it8712f_wdt_ping(void)
138
{
139
        inb(address);
140
}
141
 
142
static void
143
it8712f_wdt_update_margin(void)
144
{
145
        int config = WDT_OUT_KRST | WDT_OUT_PWROK;
146
 
147
        printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
148
 
149
        /* The timeout register only has 8bits wide */
150
        if (margin < 256)
151
                config |= WDT_UNIT_SEC; /* else UNIT are MINUTES */
152
        superio_outb(config, WDT_CONFIG);
153
 
154
        superio_outb((margin > 255) ? (margin / 60) : margin, WDT_TIMEOUT);
155
}
156
 
157
static void
158
it8712f_wdt_enable(void)
159
{
160
        printk(KERN_DEBUG NAME ": enabling watchdog timer\n");
161
        superio_enter();
162
        superio_select(LDN_GPIO);
163
 
164
        superio_outb(WDT_RESET_GAME, WDT_CONTROL);
165
 
166
        it8712f_wdt_update_margin();
167
 
168
        superio_exit();
169
 
170
        it8712f_wdt_ping();
171
}
172
 
173
static void
174
it8712f_wdt_disable(void)
175
{
176
        printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
177
 
178
        superio_enter();
179
        superio_select(LDN_GPIO);
180
 
181
        superio_outb(0, WDT_CONFIG);
182
        superio_outb(0, WDT_CONTROL);
183
        superio_outb(0, WDT_TIMEOUT);
184
 
185
        superio_exit();
186
}
187
 
188
static int
189
it8712f_wdt_notify(struct notifier_block *this,
190
                    unsigned long code, void *unused)
191
{
192
        if (code == SYS_HALT || code == SYS_POWER_OFF)
193
                if (!nowayout)
194
                        it8712f_wdt_disable();
195
 
196
        return NOTIFY_DONE;
197
}
198
 
199
static struct notifier_block it8712f_wdt_notifier = {
200
        .notifier_call = it8712f_wdt_notify,
201
};
202
 
203
static ssize_t
204
it8712f_wdt_write(struct file *file, const char __user *data,
205
        size_t len, loff_t *ppos)
206
{
207
        /* check for a magic close character */
208
        if (len) {
209
                size_t i;
210
 
211
                it8712f_wdt_ping();
212
 
213
                expect_close = 0;
214
                for (i = 0; i < len; ++i) {
215
                        char c;
216
                        if (get_user(c, data+i))
217
                                return -EFAULT;
218
                        if (c == 'V')
219
                                expect_close = 42;
220
                }
221
        }
222
 
223
        return len;
224
}
225
 
226
static int
227
it8712f_wdt_ioctl(struct inode *inode, struct file *file,
228
        unsigned int cmd, unsigned long arg)
229
{
230
        void __user *argp = (void __user *)arg;
231
        int __user *p = argp;
232
        static struct watchdog_info ident = {
233
                .identity = "IT8712F Watchdog",
234
                .firmware_version = 1,
235
                .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
236
        };
237
        int new_margin;
238
 
239
        switch (cmd) {
240
        default:
241
                return -ENOTTY;
242
        case WDIOC_GETSUPPORT:
243
                if (copy_to_user(argp, &ident, sizeof(ident)))
244
                        return -EFAULT;
245
                return 0;
246
        case WDIOC_GETSTATUS:
247
        case WDIOC_GETBOOTSTATUS:
248
                return put_user(0, p);
249
        case WDIOC_KEEPALIVE:
250
                it8712f_wdt_ping();
251
                return 0;
252
        case WDIOC_SETTIMEOUT:
253
                if (get_user(new_margin, p))
254
                        return -EFAULT;
255
                if (new_margin < 1)
256
                        return -EINVAL;
257
                margin = new_margin;
258
                superio_enter();
259
                superio_select(LDN_GPIO);
260
 
261
                it8712f_wdt_update_margin();
262
 
263
                superio_exit();
264
                it8712f_wdt_ping();
265
        case WDIOC_GETTIMEOUT:
266
                if (put_user(margin, p))
267
                        return -EFAULT;
268
                return 0;
269
        }
270
}
271
 
272
static int
273
it8712f_wdt_open(struct inode *inode, struct file *file)
274
{
275
        /* only allow one at a time */
276
        if (down_trylock(&it8712f_wdt_sem))
277
                return -EBUSY;
278
        it8712f_wdt_enable();
279
 
280
        return nonseekable_open(inode, file);
281
}
282
 
283
static int
284
it8712f_wdt_release(struct inode *inode, struct file *file)
285
{
286
        if (expect_close != 42) {
287
                printk(KERN_WARNING NAME
288
                        ": watchdog device closed unexpectedly, will not"
289
                        " disable the watchdog timer\n");
290
        } else if (!nowayout) {
291
                it8712f_wdt_disable();
292
        }
293
        expect_close = 0;
294
        up(&it8712f_wdt_sem);
295
 
296
        return 0;
297
}
298
 
299
static struct file_operations it8712f_wdt_fops = {
300
        .owner = THIS_MODULE,
301
        .llseek = no_llseek,
302
        .write = it8712f_wdt_write,
303
        .ioctl = it8712f_wdt_ioctl,
304
        .open = it8712f_wdt_open,
305
        .release = it8712f_wdt_release,
306
};
307
 
308
static struct miscdevice it8712f_wdt_miscdev = {
309
        .minor = WATCHDOG_MINOR,
310
        .name = "watchdog",
311
        .fops = &it8712f_wdt_fops,
312
};
313
 
314
static int __init
315
it8712f_wdt_find(unsigned short *address)
316
{
317
        int err = -ENODEV;
318
        int chip_type;
319
 
320
        superio_enter();
321
        chip_type = superio_inw(DEVID);
322
        if (chip_type != IT8712F_DEVID)
323
                goto exit;
324
 
325
        superio_select(LDN_GAME);
326
        superio_outb(1, ACT_REG);
327
        if (!(superio_inb(ACT_REG) & 0x01)) {
328
                printk(KERN_ERR NAME ": Device not activated, skipping\n");
329
                goto exit;
330
        }
331
 
332
        *address = superio_inw(BASE_REG);
333
        if (*address == 0) {
334
                printk(KERN_ERR NAME ": Base address not set, skipping\n");
335
                goto exit;
336
        }
337
 
338
        err = 0;
339
        printk(KERN_DEBUG NAME ": Found IT%04xF chip revision %d - "
340
                "using DogFood address 0x%x\n",
341
                chip_type, superio_inb(DEVREV) & 0x0f, *address);
342
 
343
exit:
344
        superio_exit();
345
        return err;
346
}
347
 
348
static int __init
349
it8712f_wdt_init(void)
350
{
351
        int err = 0;
352
 
353
        spin_lock_init(&io_lock);
354
 
355
        if (it8712f_wdt_find(&address))
356
                return -ENODEV;
357
 
358
        if (!request_region(address, 1, "IT8712F Watchdog")) {
359
                printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
360
                return -EBUSY;
361
        }
362
 
363
        it8712f_wdt_disable();
364
 
365
        sema_init(&it8712f_wdt_sem, 1);
366
 
367
        err = register_reboot_notifier(&it8712f_wdt_notifier);
368
        if (err) {
369
                printk(KERN_ERR NAME ": unable to register reboot notifier\n");
370
                goto out;
371
        }
372
 
373
        err = misc_register(&it8712f_wdt_miscdev);
374
        if (err) {
375
                printk(KERN_ERR NAME
376
                        ": cannot register miscdev on minor=%d (err=%d)\n",
377
                        WATCHDOG_MINOR, err);
378
                goto reboot_out;
379
        }
380
 
381
        return 0;
382
 
383
 
384
reboot_out:
385
        unregister_reboot_notifier(&it8712f_wdt_notifier);
386
out:
387
        release_region(address, 1);
388
        return err;
389
}
390
 
391
static void __exit
392
it8712f_wdt_exit(void)
393
{
394
        misc_deregister(&it8712f_wdt_miscdev);
395
        unregister_reboot_notifier(&it8712f_wdt_notifier);
396
        release_region(address, 1);
397
}
398
 
399
module_init(it8712f_wdt_init);
400
module_exit(it8712f_wdt_exit);

powered by: WebSVN 2.1.0

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