Root/target/linux/rdc/files-2.6.30/drivers/watchdog/rdc321x_wdt.c

1/*
2 * RDC321x watchdog driver
3 *
4 * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22#include <linux/miscdevice.h>
23#include <linux/platform_device.h>
24#include <linux/fs.h>
25#include <linux/init.h>
26#include <linux/ioport.h>
27#include <linux/timer.h>
28#include <linux/watchdog.h>
29#include <linux/uaccess.h>
30#include <linux/pci.h>
31#include <linux/delay.h>
32
33#include <asm/rdc321x_defs.h>
34
35extern int rdc321x_pci_write(int reg, u32 val);
36extern int rdc321x_pci_read(int reg, u32 *val);
37
38#define RDC321X_WDT_REG 0x00000044
39
40#define RDC_WDT_EN 0x00800000 /* Enable bit */
41#define RDC_WDT_WDTIRQ 0x00400000 /* Create WDT IRQ before CPU reset */
42#define RDC_WDT_NMIIRQ 0x00200000 /* Create NMI IRQ before CPU reset */
43#define RDC_WDT_RST 0x00100000 /* Reset wdt */
44#define RDC_WDT_NIF 0x00080000 /* NMI interrupt occured */
45#define RDC_WDT_WIF 0x00040000 /* WDT interrupt occured */
46#define RDC_WDT_IRT 0x00000700 /* IRQ Routing table */
47#define RDC_WDT_CNT 0x0000007F /* WDT count */
48
49/* default counter value (2.34 s) */
50#define RDC_WDT_DFLT_CNT 0x00000040
51
52#define RDC_WDT_SETUP (RDC_WDT_EN | RDC_WDT_NMIIRQ | RDC_WDT_RST | RDC_WDT_DFLT_CNT)
53
54/* some device data */
55static struct {
56    struct timer_list timer;
57    int seconds_left;
58    int total_seconds;
59    bool inuse;
60    bool running;
61    bool close_expected;
62} rdc321x_wdt_dev;
63
64static struct watchdog_info ident = {
65    .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
66    .identity = "RDC321x WDT",
67};
68
69/* generic helper functions */
70static void rdc321x_wdt_timer(unsigned long unused)
71{
72    if (!rdc321x_wdt_dev.running) {
73        rdc321x_pci_write(RDC321X_WDT_REG, 0);
74        return;
75    }
76
77    rdc321x_wdt_dev.seconds_left--;
78
79    if (rdc321x_wdt_dev.seconds_left < 1)
80        return;
81
82    rdc321x_pci_write(RDC321X_WDT_REG, RDC_WDT_SETUP);
83
84    mod_timer(&rdc321x_wdt_dev.timer, HZ * 2 + jiffies);
85}
86
87static void rdc321x_wdt_reset(void)
88{
89    rdc321x_wdt_dev.seconds_left = rdc321x_wdt_dev.total_seconds;
90}
91
92static void rdc321x_wdt_start(void)
93{
94    if (rdc321x_wdt_dev.running)
95        return;
96
97    rdc321x_wdt_dev.seconds_left = rdc321x_wdt_dev.total_seconds;
98
99    rdc321x_wdt_dev.running = true;
100
101    rdc321x_wdt_timer(0);
102
103    return;
104}
105
106static int rdc321x_wdt_stop(void)
107{
108    if (WATCHDOG_NOWAYOUT)
109        return -ENOSYS;
110
111    rdc321x_wdt_dev.running = false;
112
113    return 0;
114}
115
116/* filesystem operations */
117static int rdc321x_wdt_open(struct inode *inode, struct file *file)
118{
119    if (xchg(&rdc321x_wdt_dev.inuse, true))
120        return -EBUSY;
121
122    return nonseekable_open(inode, file);
123}
124
125static int rdc321x_wdt_release(struct inode *inode, struct file *file)
126{
127    int res;
128    if (rdc321x_wdt_dev.close_expected) {
129        res = rdc321x_wdt_stop();
130        if (res)
131            return res;
132    }
133
134    rdc321x_wdt_dev.inuse = false;
135
136    return 0;
137}
138
139static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
140                unsigned long arg)
141{
142    void __user *argp = (void __user *)arg;
143    int value;
144
145    switch (cmd) {
146        case WDIOC_KEEPALIVE:
147            rdc321x_wdt_reset();
148            break;
149        case WDIOC_GETSUPPORT:
150            if (copy_to_user(argp, &ident, sizeof(ident)))
151                return -EFAULT;
152            break;
153        case WDIOC_SETTIMEOUT:
154            if (copy_from_user(&rdc321x_wdt_dev.total_seconds, argp, sizeof(int)))
155                return -EFAULT;
156            rdc321x_wdt_dev.seconds_left = rdc321x_wdt_dev.total_seconds;
157            break;
158        case WDIOC_GETTIMEOUT:
159            if (copy_to_user(argp, &rdc321x_wdt_dev.total_seconds, sizeof(int)))
160                return -EFAULT;
161            break;
162        case WDIOC_GETTIMELEFT:
163            if (copy_to_user(argp, &rdc321x_wdt_dev.seconds_left, sizeof(int)))
164                return -EFAULT;
165            break;
166        case WDIOC_SETOPTIONS:
167            if (copy_from_user(&value, argp, sizeof(int)))
168                return -EFAULT;
169            switch (value) {
170                case WDIOS_ENABLECARD:
171                    rdc321x_wdt_start();
172                    break;
173                case WDIOS_DISABLECARD:
174                    return rdc321x_wdt_stop();
175            default:
176                return -EINVAL;
177        }
178        break;
179    default:
180        return -EINVAL;
181    }
182    return 0;
183}
184
185static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
186                size_t count, loff_t *ppos)
187{
188    size_t i;
189
190    if (!count)
191        return -EIO;
192
193    rdc321x_wdt_dev.close_expected = false;
194
195    for (i = 0; i != count; i++) {
196        char c;
197
198        if (get_user(c, buf + i))
199            return -EFAULT;
200
201        if (c == 'V') {
202            rdc321x_wdt_dev.close_expected = true;
203            break;
204        }
205    }
206
207    rdc321x_wdt_reset();
208
209    return count;
210}
211
212static const struct file_operations rdc321x_wdt_fops = {
213    .llseek = no_llseek,
214    .unlocked_ioctl = rdc321x_wdt_ioctl,
215    .open = rdc321x_wdt_open,
216    .write = rdc321x_wdt_write,
217    .release = rdc321x_wdt_release,
218};
219
220static struct miscdevice rdc321x_wdt_misc = {
221    .minor = WATCHDOG_MINOR,
222    .name = "watchdog",
223    .fops = &rdc321x_wdt_fops,
224};
225
226static int __init rdc321x_wdt_probe(struct platform_device *pdev)
227{
228    int err;
229
230    err = rdc321x_pci_write(RDC321X_WDT_REG, 0);
231    if (err)
232        return err;
233
234    rdc321x_wdt_dev.running = false;
235    rdc321x_wdt_dev.close_expected = false;
236    rdc321x_wdt_dev.inuse = 0;
237    setup_timer(&rdc321x_wdt_dev.timer, rdc321x_wdt_timer, 0);
238    rdc321x_wdt_dev.total_seconds = 100;
239
240    err = misc_register(&rdc321x_wdt_misc);
241    if (err < 0) {
242        printk(KERN_ERR PFX "watchdog: misc_register failed\n");
243        return err;
244    }
245
246    dev_info(&pdev->dev, "watchdog init success\n");
247
248    return 0;
249}
250
251static int __devexit rdc321x_wdt_remove(struct platform_device *pdev)
252{
253    if (rdc321x_wdt_dev.inuse)
254        rdc321x_wdt_dev.inuse = 0;
255
256    while (timer_pending(&rdc321x_wdt_dev.timer))
257        msleep(100);
258
259    misc_deregister(&rdc321x_wdt_misc);
260    return 0;
261}
262
263static struct platform_driver rdc321x_wdt_driver = {
264    .driver.name = "rdc321x-wdt",
265    .driver.owner = THIS_MODULE,
266    .probe = rdc321x_wdt_probe,
267    .remove = __devexit_p(rdc321x_wdt_remove),
268};
269
270static int __init rdc321x_wdt_init(void)
271{
272    return platform_driver_register(&rdc321x_wdt_driver);
273}
274
275static void __exit rdc321x_wdt_exit(void)
276{
277    platform_driver_unregister(&rdc321x_wdt_driver);
278}
279
280module_init(rdc321x_wdt_init);
281module_exit(rdc321x_wdt_exit);
282
283MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
284MODULE_DESCRIPTION("RDC321x Watchdog driver");
285MODULE_LICENSE("GPL");
286MODULE_ALIAS("platform:rdc321x-wdt");
287

Archive Download this file



interactive