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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * drivers/char/watchdog/pnx4008_wdt.c
3
 *
4
 * Watchdog driver for PNX4008 board
5
 *
6
 * Authors: Dmitry Chigirev <source@mvista.com>,
7
 *          Vitaly Wool <vitalywool@gmail.com>
8
 * Based on sa1100 driver,
9
 * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
10
 *
11
 * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
12
 * the terms of the GNU General Public License version 2. This program
13
 * is licensed "as is" without any warranty of any kind, whether express
14
 * or implied.
15
 */
16
 
17
#include <linux/module.h>
18
#include <linux/moduleparam.h>
19
#include <linux/types.h>
20
#include <linux/kernel.h>
21
#include <linux/fs.h>
22
#include <linux/miscdevice.h>
23
#include <linux/watchdog.h>
24
#include <linux/init.h>
25
#include <linux/bitops.h>
26
#include <linux/ioport.h>
27
#include <linux/device.h>
28
#include <linux/platform_device.h>
29
#include <linux/clk.h>
30
#include <linux/spinlock.h>
31
 
32
#include <asm/hardware.h>
33
#include <asm/uaccess.h>
34
#include <asm/io.h>
35
 
36
#define MODULE_NAME "PNX4008-WDT: "
37
 
38
/* WatchDog Timer - Chapter 23 Page 207 */
39
 
40
#define DEFAULT_HEARTBEAT 19
41
#define MAX_HEARTBEAT     60
42
 
43
/* Watchdog timer register set definition */
44
#define WDTIM_INT(p)     ((p) + 0x0)
45
#define WDTIM_CTRL(p)    ((p) + 0x4)
46
#define WDTIM_COUNTER(p) ((p) + 0x8)
47
#define WDTIM_MCTRL(p)   ((p) + 0xC)
48
#define WDTIM_MATCH0(p)  ((p) + 0x10)
49
#define WDTIM_EMR(p)     ((p) + 0x14)
50
#define WDTIM_PULSE(p)   ((p) + 0x18)
51
#define WDTIM_RES(p)     ((p) + 0x1C)
52
 
53
/* WDTIM_INT bit definitions */
54
#define MATCH_INT      1
55
 
56
/* WDTIM_CTRL bit definitions */
57
#define COUNT_ENAB     1
58
#define RESET_COUNT    (1<<1)
59
#define DEBUG_EN       (1<<2)
60
 
61
/* WDTIM_MCTRL bit definitions */
62
#define MR0_INT        1
63
#undef  RESET_COUNT0
64
#define RESET_COUNT0   (1<<2)
65
#define STOP_COUNT0    (1<<2)
66
#define M_RES1         (1<<3)
67
#define M_RES2         (1<<4)
68
#define RESFRC1        (1<<5)
69
#define RESFRC2        (1<<6)
70
 
71
/* WDTIM_EMR bit definitions */
72
#define EXT_MATCH0      1
73
#define MATCH_OUTPUT_HIGH (2<<4)        /*a MATCH_CTRL setting */
74
 
75
/* WDTIM_RES bit definitions */
76
#define WDOG_RESET      1       /* read only */
77
 
78
#define WDOG_COUNTER_RATE 13000000      /*the counter clock is 13 MHz fixed */
79
 
80
static int nowayout = WATCHDOG_NOWAYOUT;
81
static int heartbeat = DEFAULT_HEARTBEAT;
82
 
83
static DEFINE_SPINLOCK(io_lock);
84
static unsigned long wdt_status;
85
#define WDT_IN_USE        0
86
#define WDT_OK_TO_CLOSE   1
87
#define WDT_REGION_INITED 2
88
#define WDT_DEVICE_INITED 3
89
 
90
static unsigned long boot_status;
91
 
92
static struct resource  *wdt_mem;
93
static void __iomem     *wdt_base;
94
struct clk              *wdt_clk;
95
 
96
static void wdt_enable(void)
97
{
98
        spin_lock(&io_lock);
99
 
100
        if (wdt_clk)
101
                clk_set_rate(wdt_clk, 1);
102
 
103
        /* stop counter, initiate counter reset */
104
        __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
105
        /*wait for reset to complete. 100% guarantee event */
106
        while (__raw_readl(WDTIM_COUNTER(wdt_base)))
107
                cpu_relax();
108
        /* internal and external reset, stop after that */
109
        __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0,
110
                WDTIM_MCTRL(wdt_base));
111
        /* configure match output */
112
        __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base));
113
        /* clear interrupt, just in case */
114
        __raw_writel(MATCH_INT, WDTIM_INT(wdt_base));
115
        /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */
116
        __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base));
117
        __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base));
118
        /*enable counter, stop when debugger active */
119
        __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base));
120
 
121
        spin_unlock(&io_lock);
122
}
123
 
124
static void wdt_disable(void)
125
{
126
        spin_lock(&io_lock);
127
 
128
        __raw_writel(0, WDTIM_CTRL(wdt_base));   /*stop counter */
129
        if (wdt_clk)
130
                clk_set_rate(wdt_clk, 0);
131
 
132
        spin_unlock(&io_lock);
133
}
134
 
135
static int pnx4008_wdt_open(struct inode *inode, struct file *file)
136
{
137
        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
138
                return -EBUSY;
139
 
140
        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
141
 
142
        wdt_enable();
143
 
144
        return nonseekable_open(inode, file);
145
}
146
 
147
static ssize_t
148
pnx4008_wdt_write(struct file *file, const char *data, size_t len,
149
                  loff_t * ppos)
150
{
151
        if (len) {
152
                if (!nowayout) {
153
                        size_t i;
154
 
155
                        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
156
 
157
                        for (i = 0; i != len; i++) {
158
                                char c;
159
 
160
                                if (get_user(c, data + i))
161
                                        return -EFAULT;
162
                                if (c == 'V')
163
                                        set_bit(WDT_OK_TO_CLOSE, &wdt_status);
164
                        }
165
                }
166
                wdt_enable();
167
        }
168
 
169
        return len;
170
}
171
 
172
static struct watchdog_info ident = {
173
        .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
174
            WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
175
        .identity = "PNX4008 Watchdog",
176
};
177
 
178
static int
179
pnx4008_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
180
                  unsigned long arg)
181
{
182
        int ret = -ENOTTY;
183
        int time;
184
 
185
        switch (cmd) {
186
        case WDIOC_GETSUPPORT:
187
                ret = copy_to_user((struct watchdog_info *)arg, &ident,
188
                                   sizeof(ident)) ? -EFAULT : 0;
189
                break;
190
 
191
        case WDIOC_GETSTATUS:
192
                ret = put_user(0, (int *)arg);
193
                break;
194
 
195
        case WDIOC_GETBOOTSTATUS:
196
                ret = put_user(boot_status, (int *)arg);
197
                break;
198
 
199
        case WDIOC_SETTIMEOUT:
200
                ret = get_user(time, (int *)arg);
201
                if (ret)
202
                        break;
203
 
204
                if (time <= 0 || time > MAX_HEARTBEAT) {
205
                        ret = -EINVAL;
206
                        break;
207
                }
208
 
209
                heartbeat = time;
210
                wdt_enable();
211
                /* Fall through */
212
 
213
        case WDIOC_GETTIMEOUT:
214
                ret = put_user(heartbeat, (int *)arg);
215
                break;
216
 
217
        case WDIOC_KEEPALIVE:
218
                wdt_enable();
219
                ret = 0;
220
                break;
221
        }
222
        return ret;
223
}
224
 
225
static int pnx4008_wdt_release(struct inode *inode, struct file *file)
226
{
227
        if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status))
228
                printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n");
229
 
230
        wdt_disable();
231
        clear_bit(WDT_IN_USE, &wdt_status);
232
        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
233
 
234
        return 0;
235
}
236
 
237
static const struct file_operations pnx4008_wdt_fops = {
238
        .owner = THIS_MODULE,
239
        .llseek = no_llseek,
240
        .write = pnx4008_wdt_write,
241
        .ioctl = pnx4008_wdt_ioctl,
242
        .open = pnx4008_wdt_open,
243
        .release = pnx4008_wdt_release,
244
};
245
 
246
static struct miscdevice pnx4008_wdt_miscdev = {
247
        .minor = WATCHDOG_MINOR,
248
        .name = "watchdog",
249
        .fops = &pnx4008_wdt_fops,
250
};
251
 
252
static int pnx4008_wdt_probe(struct platform_device *pdev)
253
{
254
        int ret = 0, size;
255
        struct resource *res;
256
 
257
        if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
258
                heartbeat = DEFAULT_HEARTBEAT;
259
 
260
        printk(KERN_INFO MODULE_NAME
261
                "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat);
262
 
263
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
264
        if (res == NULL) {
265
                printk(KERN_INFO MODULE_NAME
266
                        "failed to get memory region resouce\n");
267
                return -ENOENT;
268
        }
269
 
270
        size = res->end - res->start + 1;
271
        wdt_mem = request_mem_region(res->start, size, pdev->name);
272
 
273
        if (wdt_mem == NULL) {
274
                printk(KERN_INFO MODULE_NAME "failed to get memory region\n");
275
                return -ENOENT;
276
        }
277
        wdt_base = (void __iomem *)IO_ADDRESS(res->start);
278
 
279
        wdt_clk = clk_get(&pdev->dev, "wdt_ck");
280
        if (IS_ERR(wdt_clk)) {
281
                ret = PTR_ERR(wdt_clk);
282
                release_resource(wdt_mem);
283
                kfree(wdt_mem);
284
                goto out;
285
        } else
286
                clk_set_rate(wdt_clk, 1);
287
 
288
        ret = misc_register(&pnx4008_wdt_miscdev);
289
        if (ret < 0) {
290
                printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
291
                release_resource(wdt_mem);
292
                kfree(wdt_mem);
293
                clk_set_rate(wdt_clk, 0);
294
        } else {
295
                boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
296
                    WDIOF_CARDRESET : 0;
297
                wdt_disable();          /*disable for now */
298
                set_bit(WDT_DEVICE_INITED, &wdt_status);
299
        }
300
 
301
out:
302
        return ret;
303
}
304
 
305
static int pnx4008_wdt_remove(struct platform_device *pdev)
306
{
307
        misc_deregister(&pnx4008_wdt_miscdev);
308
        if (wdt_clk) {
309
                clk_set_rate(wdt_clk, 0);
310
                clk_put(wdt_clk);
311
                wdt_clk = NULL;
312
        }
313
        if (wdt_mem) {
314
                release_resource(wdt_mem);
315
                kfree(wdt_mem);
316
                wdt_mem = NULL;
317
        }
318
        return 0;
319
}
320
 
321
static struct platform_driver platform_wdt_driver = {
322
        .driver = {
323
                .name = "watchdog",
324
        },
325
        .probe = pnx4008_wdt_probe,
326
        .remove = pnx4008_wdt_remove,
327
};
328
 
329
static int __init pnx4008_wdt_init(void)
330
{
331
        return platform_driver_register(&platform_wdt_driver);
332
}
333
 
334
static void __exit pnx4008_wdt_exit(void)
335
{
336
        platform_driver_unregister(&platform_wdt_driver);
337
}
338
 
339
module_init(pnx4008_wdt_init);
340
module_exit(pnx4008_wdt_exit);
341
 
342
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
343
MODULE_DESCRIPTION("PNX4008 Watchdog Driver");
344
 
345
module_param(heartbeat, int, 0);
346
MODULE_PARM_DESC(heartbeat,
347
                 "Watchdog heartbeat period in seconds from 1 to "
348
                 __MODULE_STRING(MAX_HEARTBEAT) ", default "
349
                 __MODULE_STRING(DEFAULT_HEARTBEAT));
350
 
351
module_param(nowayout, int, 0);
352
MODULE_PARM_DESC(nowayout,
353
                 "Set to 1 to keep watchdog running after device release");
354
 
355
MODULE_LICENSE("GPL");
356
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.