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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/* drivers/char/watchdog/scx200_wdt.c
2
 
3
   National Semiconductor SCx200 Watchdog support
4
 
5
   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
6
 
7
   Some code taken from:
8
   National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
9
   (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
10
 
11
   This program is free software; you can redistribute it and/or
12
   modify it under the terms of the GNU General Public License as
13
   published by the Free Software Foundation; either version 2 of the
14
   License, or (at your option) any later version.
15
 
16
   The author(s) of this software shall not be held liable for damages
17
   of any nature resulting due to the use of this software. This
18
   software is provided AS-IS with no warranties. */
19
 
20
#include <linux/module.h>
21
#include <linux/moduleparam.h>
22
#include <linux/init.h>
23
#include <linux/miscdevice.h>
24
#include <linux/watchdog.h>
25
#include <linux/notifier.h>
26
#include <linux/reboot.h>
27
#include <linux/fs.h>
28
#include <linux/ioport.h>
29
#include <linux/scx200.h>
30
 
31
#include <asm/uaccess.h>
32
#include <asm/io.h>
33
 
34
#define NAME "scx200_wdt"
35
 
36
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
37
MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
38
MODULE_LICENSE("GPL");
39
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
40
 
41
static int margin = 60;         /* in seconds */
42
module_param(margin, int, 0);
43
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
44
 
45
static int nowayout = WATCHDOG_NOWAYOUT;
46
module_param(nowayout, int, 0);
47
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
48
 
49
static u16 wdto_restart;
50
static struct semaphore open_semaphore;
51
static char expect_close;
52
 
53
/* Bits of the WDCNFG register */
54
#define W_ENABLE 0x00fa         /* Enable watchdog */
55
#define W_DISABLE 0x0000        /* Disable watchdog */
56
 
57
/* The scaling factor for the timer, this depends on the value of W_ENABLE */
58
#define W_SCALE (32768/1024)
59
 
60
static void scx200_wdt_ping(void)
61
{
62
        outw(wdto_restart, scx200_cb_base + SCx200_WDT_WDTO);
63
}
64
 
65
static void scx200_wdt_update_margin(void)
66
{
67
        printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
68
        wdto_restart = margin * W_SCALE;
69
}
70
 
71
static void scx200_wdt_enable(void)
72
{
73
        printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n",
74
               wdto_restart);
75
 
76
        outw(0, scx200_cb_base + SCx200_WDT_WDTO);
77
        outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS);
78
        outw(W_ENABLE, scx200_cb_base + SCx200_WDT_WDCNFG);
79
 
80
        scx200_wdt_ping();
81
}
82
 
83
static void scx200_wdt_disable(void)
84
{
85
        printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
86
 
87
        outw(0, scx200_cb_base + SCx200_WDT_WDTO);
88
        outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS);
89
        outw(W_DISABLE, scx200_cb_base + SCx200_WDT_WDCNFG);
90
}
91
 
92
static int scx200_wdt_open(struct inode *inode, struct file *file)
93
{
94
        /* only allow one at a time */
95
        if (down_trylock(&open_semaphore))
96
                return -EBUSY;
97
        scx200_wdt_enable();
98
 
99
        return nonseekable_open(inode, file);
100
}
101
 
102
static int scx200_wdt_release(struct inode *inode, struct file *file)
103
{
104
        if (expect_close != 42) {
105
                printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n");
106
        } else if (!nowayout) {
107
                scx200_wdt_disable();
108
        }
109
        expect_close = 0;
110
        up(&open_semaphore);
111
 
112
        return 0;
113
}
114
 
115
static int scx200_wdt_notify_sys(struct notifier_block *this,
116
                                      unsigned long code, void *unused)
117
{
118
        if (code == SYS_HALT || code == SYS_POWER_OFF)
119
                if (!nowayout)
120
                        scx200_wdt_disable();
121
 
122
        return NOTIFY_DONE;
123
}
124
 
125
static struct notifier_block scx200_wdt_notifier =
126
{
127
        .notifier_call = scx200_wdt_notify_sys,
128
};
129
 
130
static ssize_t scx200_wdt_write(struct file *file, const char __user *data,
131
                                     size_t len, loff_t *ppos)
132
{
133
        /* check for a magic close character */
134
        if (len)
135
        {
136
                size_t i;
137
 
138
                scx200_wdt_ping();
139
 
140
                expect_close = 0;
141
                for (i = 0; i < len; ++i) {
142
                        char c;
143
                        if (get_user(c, data+i))
144
                                return -EFAULT;
145
                        if (c == 'V')
146
                                expect_close = 42;
147
                }
148
 
149
                return len;
150
        }
151
 
152
        return 0;
153
}
154
 
155
static int scx200_wdt_ioctl(struct inode *inode, struct file *file,
156
        unsigned int cmd, unsigned long arg)
157
{
158
        void __user *argp = (void __user *)arg;
159
        int __user *p = argp;
160
        static struct watchdog_info ident = {
161
                .identity = "NatSemi SCx200 Watchdog",
162
                .firmware_version = 1,
163
                .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
164
        };
165
        int new_margin;
166
 
167
        switch (cmd) {
168
        default:
169
                return -ENOTTY;
170
        case WDIOC_GETSUPPORT:
171
                if(copy_to_user(argp, &ident, sizeof(ident)))
172
                        return -EFAULT;
173
                return 0;
174
        case WDIOC_GETSTATUS:
175
        case WDIOC_GETBOOTSTATUS:
176
                if (put_user(0, p))
177
                        return -EFAULT;
178
                return 0;
179
        case WDIOC_KEEPALIVE:
180
                scx200_wdt_ping();
181
                return 0;
182
        case WDIOC_SETTIMEOUT:
183
                if (get_user(new_margin, p))
184
                        return -EFAULT;
185
                if (new_margin < 1)
186
                        return -EINVAL;
187
                margin = new_margin;
188
                scx200_wdt_update_margin();
189
                scx200_wdt_ping();
190
        case WDIOC_GETTIMEOUT:
191
                if (put_user(margin, p))
192
                        return -EFAULT;
193
                return 0;
194
        }
195
}
196
 
197
static const struct file_operations scx200_wdt_fops = {
198
        .owner   = THIS_MODULE,
199
        .llseek  = no_llseek,
200
        .write   = scx200_wdt_write,
201
        .ioctl   = scx200_wdt_ioctl,
202
        .open    = scx200_wdt_open,
203
        .release = scx200_wdt_release,
204
};
205
 
206
static struct miscdevice scx200_wdt_miscdev = {
207
        .minor = WATCHDOG_MINOR,
208
        .name  = "watchdog",
209
        .fops  = &scx200_wdt_fops,
210
};
211
 
212
static int __init scx200_wdt_init(void)
213
{
214
        int r;
215
 
216
        printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
217
 
218
        /* check that we have found the configuration block */
219
        if (!scx200_cb_present())
220
                return -ENODEV;
221
 
222
        if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET,
223
                            SCx200_WDT_SIZE,
224
                            "NatSemi SCx200 Watchdog")) {
225
                printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
226
                return -EBUSY;
227
        }
228
 
229
        scx200_wdt_update_margin();
230
        scx200_wdt_disable();
231
 
232
        sema_init(&open_semaphore, 1);
233
 
234
        r = misc_register(&scx200_wdt_miscdev);
235
        if (r) {
236
                release_region(scx200_cb_base + SCx200_WDT_OFFSET,
237
                                SCx200_WDT_SIZE);
238
                return r;
239
        }
240
 
241
        r = register_reboot_notifier(&scx200_wdt_notifier);
242
        if (r) {
243
                printk(KERN_ERR NAME ": unable to register reboot notifier");
244
                misc_deregister(&scx200_wdt_miscdev);
245
                release_region(scx200_cb_base + SCx200_WDT_OFFSET,
246
                                SCx200_WDT_SIZE);
247
                return r;
248
        }
249
 
250
        return 0;
251
}
252
 
253
static void __exit scx200_wdt_cleanup(void)
254
{
255
        unregister_reboot_notifier(&scx200_wdt_notifier);
256
        misc_deregister(&scx200_wdt_miscdev);
257
        release_region(scx200_cb_base + SCx200_WDT_OFFSET,
258
                       SCx200_WDT_SIZE);
259
}
260
 
261
module_init(scx200_wdt_init);
262
module_exit(scx200_wdt_cleanup);
263
 
264
/*
265
    Local variables:
266
        compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
267
        c-basic-offset: 8
268
    End:
269
*/

powered by: WebSVN 2.1.0

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