~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/watchdog/bcm47xx_wdt.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Watchdog driver for Broadcom BCM47XX
 
3
 *
 
4
 *  Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
 
5
 *  Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
 
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
 
 
13
#include <linux/bitops.h>
 
14
#include <linux/errno.h>
 
15
#include <linux/fs.h>
 
16
#include <linux/init.h>
 
17
#include <linux/kernel.h>
 
18
#include <linux/miscdevice.h>
 
19
#include <linux/module.h>
 
20
#include <linux/moduleparam.h>
 
21
#include <linux/reboot.h>
 
22
#include <linux/types.h>
 
23
#include <linux/uaccess.h>
 
24
#include <linux/watchdog.h>
 
25
#include <linux/timer.h>
 
26
#include <linux/jiffies.h>
 
27
#include <linux/ssb/ssb_embedded.h>
 
28
#include <asm/mach-bcm47xx/bcm47xx.h>
 
29
 
 
30
#define DRV_NAME                "bcm47xx_wdt"
 
31
 
 
32
#define WDT_DEFAULT_TIME        30      /* seconds */
 
33
#define WDT_MAX_TIME            255     /* seconds */
 
34
 
 
35
static int wdt_time = WDT_DEFAULT_TIME;
 
36
static int nowayout = WATCHDOG_NOWAYOUT;
 
37
 
 
38
module_param(wdt_time, int, 0);
 
39
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
 
40
                                __MODULE_STRING(WDT_DEFAULT_TIME) ")");
 
41
 
 
42
#ifdef CONFIG_WATCHDOG_NOWAYOUT
 
43
module_param(nowayout, int, 0);
 
44
MODULE_PARM_DESC(nowayout,
 
45
                "Watchdog cannot be stopped once started (default="
 
46
                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
47
#endif
 
48
 
 
49
static unsigned long bcm47xx_wdt_busy;
 
50
static char expect_release;
 
51
static struct timer_list wdt_timer;
 
52
static atomic_t ticks;
 
53
 
 
54
static inline void bcm47xx_wdt_hw_start(void)
 
55
{
 
56
        /* this is 2,5s on 100Mhz clock  and 2s on 133 Mhz */
 
57
        switch (bcm47xx_bus_type) {
 
58
#ifdef CONFIG_BCM47XX_SSB
 
59
        case BCM47XX_BUS_TYPE_SSB:
 
60
                ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff);
 
61
                break;
 
62
#endif
 
63
#ifdef CONFIG_BCM47XX_BCMA
 
64
        case BCM47XX_BUS_TYPE_BCMA:
 
65
                bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc,
 
66
                                               0xfffffff);
 
67
                break;
 
68
#endif
 
69
        }
 
70
}
 
71
 
 
72
static inline int bcm47xx_wdt_hw_stop(void)
 
73
{
 
74
        switch (bcm47xx_bus_type) {
 
75
#ifdef CONFIG_BCM47XX_SSB
 
76
        case BCM47XX_BUS_TYPE_SSB:
 
77
                return ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0);
 
78
#endif
 
79
#ifdef CONFIG_BCM47XX_BCMA
 
80
        case BCM47XX_BUS_TYPE_BCMA:
 
81
                bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0);
 
82
                return 0;
 
83
#endif
 
84
        }
 
85
        return -EINVAL;
 
86
}
 
87
 
 
88
static void bcm47xx_timer_tick(unsigned long unused)
 
89
{
 
90
        if (!atomic_dec_and_test(&ticks)) {
 
91
                bcm47xx_wdt_hw_start();
 
92
                mod_timer(&wdt_timer, jiffies + HZ);
 
93
        } else {
 
94
                printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n");
 
95
        }
 
96
}
 
97
 
 
98
static inline void bcm47xx_wdt_pet(void)
 
99
{
 
100
        atomic_set(&ticks, wdt_time);
 
101
}
 
102
 
 
103
static void bcm47xx_wdt_start(void)
 
104
{
 
105
        bcm47xx_wdt_pet();
 
106
        bcm47xx_timer_tick(0);
 
107
}
 
108
 
 
109
static void bcm47xx_wdt_pause(void)
 
110
{
 
111
        del_timer_sync(&wdt_timer);
 
112
        bcm47xx_wdt_hw_stop();
 
113
}
 
114
 
 
115
static void bcm47xx_wdt_stop(void)
 
116
{
 
117
        bcm47xx_wdt_pause();
 
118
}
 
119
 
 
120
static int bcm47xx_wdt_settimeout(int new_time)
 
121
{
 
122
        if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
 
123
                return -EINVAL;
 
124
 
 
125
        wdt_time = new_time;
 
126
        return 0;
 
127
}
 
128
 
 
129
static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
 
130
{
 
131
        if (test_and_set_bit(0, &bcm47xx_wdt_busy))
 
132
                return -EBUSY;
 
133
 
 
134
        bcm47xx_wdt_start();
 
135
        return nonseekable_open(inode, file);
 
136
}
 
137
 
 
138
static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
 
139
{
 
140
        if (expect_release == 42) {
 
141
                bcm47xx_wdt_stop();
 
142
        } else {
 
143
                printk(KERN_CRIT DRV_NAME
 
144
                        ": Unexpected close, not stopping watchdog!\n");
 
145
                bcm47xx_wdt_start();
 
146
        }
 
147
 
 
148
        clear_bit(0, &bcm47xx_wdt_busy);
 
149
        expect_release = 0;
 
150
        return 0;
 
151
}
 
152
 
 
153
static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
 
154
                                size_t len, loff_t *ppos)
 
155
{
 
156
        if (len) {
 
157
                if (!nowayout) {
 
158
                        size_t i;
 
159
 
 
160
                        expect_release = 0;
 
161
 
 
162
                        for (i = 0; i != len; i++) {
 
163
                                char c;
 
164
                                if (get_user(c, data + i))
 
165
                                        return -EFAULT;
 
166
                                if (c == 'V')
 
167
                                        expect_release = 42;
 
168
                        }
 
169
                }
 
170
                bcm47xx_wdt_pet();
 
171
        }
 
172
        return len;
 
173
}
 
174
 
 
175
static const struct watchdog_info bcm47xx_wdt_info = {
 
176
        .identity       = DRV_NAME,
 
177
        .options        = WDIOF_SETTIMEOUT |
 
178
                                WDIOF_KEEPALIVEPING |
 
179
                                WDIOF_MAGICCLOSE,
 
180
};
 
181
 
 
182
static long bcm47xx_wdt_ioctl(struct file *file,
 
183
                                        unsigned int cmd, unsigned long arg)
 
184
{
 
185
        void __user *argp = (void __user *)arg;
 
186
        int __user *p = argp;
 
187
        int new_value, retval = -EINVAL;
 
188
 
 
189
        switch (cmd) {
 
190
        case WDIOC_GETSUPPORT:
 
191
                return copy_to_user(argp, &bcm47xx_wdt_info,
 
192
                                sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0;
 
193
 
 
194
        case WDIOC_GETSTATUS:
 
195
        case WDIOC_GETBOOTSTATUS:
 
196
                return put_user(0, p);
 
197
 
 
198
        case WDIOC_SETOPTIONS:
 
199
                if (get_user(new_value, p))
 
200
                        return -EFAULT;
 
201
 
 
202
                if (new_value & WDIOS_DISABLECARD) {
 
203
                        bcm47xx_wdt_stop();
 
204
                        retval = 0;
 
205
                }
 
206
 
 
207
                if (new_value & WDIOS_ENABLECARD) {
 
208
                        bcm47xx_wdt_start();
 
209
                        retval = 0;
 
210
                }
 
211
 
 
212
                return retval;
 
213
 
 
214
        case WDIOC_KEEPALIVE:
 
215
                bcm47xx_wdt_pet();
 
216
                return 0;
 
217
 
 
218
        case WDIOC_SETTIMEOUT:
 
219
                if (get_user(new_value, p))
 
220
                        return -EFAULT;
 
221
 
 
222
                if (bcm47xx_wdt_settimeout(new_value))
 
223
                        return -EINVAL;
 
224
 
 
225
                bcm47xx_wdt_pet();
 
226
 
 
227
        case WDIOC_GETTIMEOUT:
 
228
                return put_user(wdt_time, p);
 
229
 
 
230
        default:
 
231
                return -ENOTTY;
 
232
        }
 
233
}
 
234
 
 
235
static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
 
236
        unsigned long code, void *unused)
 
237
{
 
238
        if (code == SYS_DOWN || code == SYS_HALT)
 
239
                bcm47xx_wdt_stop();
 
240
        return NOTIFY_DONE;
 
241
}
 
242
 
 
243
static const struct file_operations bcm47xx_wdt_fops = {
 
244
        .owner          = THIS_MODULE,
 
245
        .llseek         = no_llseek,
 
246
        .unlocked_ioctl = bcm47xx_wdt_ioctl,
 
247
        .open           = bcm47xx_wdt_open,
 
248
        .release        = bcm47xx_wdt_release,
 
249
        .write          = bcm47xx_wdt_write,
 
250
};
 
251
 
 
252
static struct miscdevice bcm47xx_wdt_miscdev = {
 
253
        .minor          = WATCHDOG_MINOR,
 
254
        .name           = "watchdog",
 
255
        .fops           = &bcm47xx_wdt_fops,
 
256
};
 
257
 
 
258
static struct notifier_block bcm47xx_wdt_notifier = {
 
259
        .notifier_call = bcm47xx_wdt_notify_sys,
 
260
};
 
261
 
 
262
static int __init bcm47xx_wdt_init(void)
 
263
{
 
264
        int ret;
 
265
 
 
266
        if (bcm47xx_wdt_hw_stop() < 0)
 
267
                return -ENODEV;
 
268
 
 
269
        setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
 
270
 
 
271
        if (bcm47xx_wdt_settimeout(wdt_time)) {
 
272
                bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
 
273
                printk(KERN_INFO DRV_NAME ": "
 
274
                        "wdt_time value must be 0 < wdt_time < %d, using %d\n",
 
275
                        (WDT_MAX_TIME + 1), wdt_time);
 
276
        }
 
277
 
 
278
        ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
 
279
        if (ret)
 
280
                return ret;
 
281
 
 
282
        ret = misc_register(&bcm47xx_wdt_miscdev);
 
283
        if (ret) {
 
284
                unregister_reboot_notifier(&bcm47xx_wdt_notifier);
 
285
                return ret;
 
286
        }
 
287
 
 
288
        printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
 
289
                                wdt_time, nowayout ? ", nowayout" : "");
 
290
        return 0;
 
291
}
 
292
 
 
293
static void __exit bcm47xx_wdt_exit(void)
 
294
{
 
295
        if (!nowayout)
 
296
                bcm47xx_wdt_stop();
 
297
 
 
298
        misc_deregister(&bcm47xx_wdt_miscdev);
 
299
 
 
300
        unregister_reboot_notifier(&bcm47xx_wdt_notifier);
 
301
}
 
302
 
 
303
module_init(bcm47xx_wdt_init);
 
304
module_exit(bcm47xx_wdt_exit);
 
305
 
 
306
MODULE_AUTHOR("Aleksandar Radovanovic");
 
307
MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
 
308
MODULE_LICENSE("GPL");
 
309
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);