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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * Watchdog driver for Atmel AT32AP700X devices
3
 *
4
 * Copyright (C) 2005-2006 Atmel Corporation
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 version 2 as
8
 * published by the Free Software Foundation.
9
 *
10
 *
11
 * Errata: WDT Clear is blocked after WDT Reset
12
 *
13
 * A watchdog timer event will, after reset, block writes to the WDT_CLEAR
14
 * register, preventing the program to clear the next Watchdog Timer Reset.
15
 *
16
 * If you still want to use the WDT after a WDT reset a small code can be
17
 * insterted at the startup checking the AVR32_PM.rcause register for WDT reset
18
 * and use a GPIO pin to reset the system. This method requires that one of the
19
 * GPIO pins are available and connected externally to the RESET_N pin. After
20
 * the GPIO pin has pulled down the reset line the GPIO will be reset and leave
21
 * the pin tristated with pullup.
22
 */
23
 
24
#include <linux/init.h>
25
#include <linux/kernel.h>
26
#include <linux/module.h>
27
#include <linux/moduleparam.h>
28
#include <linux/miscdevice.h>
29
#include <linux/fs.h>
30
#include <linux/platform_device.h>
31
#include <linux/watchdog.h>
32
#include <linux/uaccess.h>
33
#include <linux/io.h>
34
#include <linux/spinlock.h>
35
 
36
#define TIMEOUT_MIN             1
37
#define TIMEOUT_MAX             2
38
#define TIMEOUT_DEFAULT         TIMEOUT_MAX
39
 
40
/* module parameters */
41
static int timeout =  TIMEOUT_DEFAULT;
42
module_param(timeout, int, 0);
43
MODULE_PARM_DESC(timeout,
44
                "Timeout value. Limited to be 1 or 2 seconds. (default="
45
                __MODULE_STRING(TIMEOUT_DEFAULT) ")");
46
 
47
static int nowayout = WATCHDOG_NOWAYOUT;
48
module_param(nowayout, int, 0);
49
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
50
                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
51
 
52
/* Watchdog registers and write/read macro */
53
#define WDT_CTRL                0x00
54
#define WDT_CTRL_EN                0
55
#define WDT_CTRL_PSEL              8
56
#define WDT_CTRL_KEY              24
57
 
58
#define WDT_CLR                 0x04
59
 
60
#define WDT_RCAUSE              0x10
61
#define WDT_RCAUSE_POR             0
62
#define WDT_RCAUSE_EXT             2
63
#define WDT_RCAUSE_WDT             3
64
#define WDT_RCAUSE_JTAG            4
65
#define WDT_RCAUSE_SERP            5
66
 
67
#define WDT_BIT(name)           (1 << WDT_##name)
68
#define WDT_BF(name, value)     ((value) << WDT_##name)
69
 
70
#define wdt_readl(dev, reg)                             \
71
        __raw_readl((dev)->regs + WDT_##reg)
72
#define wdt_writel(dev, reg, value)                     \
73
        __raw_writel((value), (dev)->regs + WDT_##reg)
74
 
75
struct wdt_at32ap700x {
76
        void __iomem            *regs;
77
        spinlock_t              io_lock;
78
        int                     timeout;
79
        int                     boot_status;
80
        unsigned long           users;
81
        struct miscdevice       miscdev;
82
};
83
 
84
static struct wdt_at32ap700x *wdt;
85
static char expect_release;
86
 
87
/*
88
 * Disable the watchdog.
89
 */
90
static inline void at32_wdt_stop(void)
91
{
92
        unsigned long psel;
93
 
94
        spin_lock(&wdt->io_lock);
95
        psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f);
96
        wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55));
97
        wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa));
98
        spin_unlock(&wdt->io_lock);
99
}
100
 
101
/*
102
 * Enable and reset the watchdog.
103
 */
104
static inline void at32_wdt_start(void)
105
{
106
        /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */
107
        unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe;
108
 
109
        spin_lock(&wdt->io_lock);
110
        wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
111
                        | WDT_BF(CTRL_PSEL, psel)
112
                        | WDT_BF(CTRL_KEY, 0x55));
113
        wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
114
                        | WDT_BF(CTRL_PSEL, psel)
115
                        | WDT_BF(CTRL_KEY, 0xaa));
116
        spin_unlock(&wdt->io_lock);
117
}
118
 
119
/*
120
 * Pat the watchdog timer.
121
 */
122
static inline void at32_wdt_pat(void)
123
{
124
        spin_lock(&wdt->io_lock);
125
        wdt_writel(wdt, CLR, 0x42);
126
        spin_unlock(&wdt->io_lock);
127
}
128
 
129
/*
130
 * Watchdog device is opened, and watchdog starts running.
131
 */
132
static int at32_wdt_open(struct inode *inode, struct file *file)
133
{
134
        if (test_and_set_bit(1, &wdt->users))
135
                return -EBUSY;
136
 
137
        at32_wdt_start();
138
        return nonseekable_open(inode, file);
139
}
140
 
141
/*
142
 * Close the watchdog device.
143
 */
144
static int at32_wdt_close(struct inode *inode, struct file *file)
145
{
146
        if (expect_release == 42) {
147
                at32_wdt_stop();
148
        } else {
149
                dev_dbg(wdt->miscdev.parent,
150
                        "unexpected close, not stopping watchdog!\n");
151
                at32_wdt_pat();
152
        }
153
        clear_bit(1, &wdt->users);
154
        expect_release = 0;
155
        return 0;
156
}
157
 
158
/*
159
 * Change the watchdog time interval.
160
 */
161
static int at32_wdt_settimeout(int time)
162
{
163
        /*
164
         * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is
165
         * 2 ^ 16 allowing up to 2 seconds timeout.
166
         */
167
        if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX))
168
                return -EINVAL;
169
 
170
        /*
171
         * Set new watchdog time. It will be used when at32_wdt_start() is
172
         * called.
173
         */
174
        wdt->timeout = time;
175
        return 0;
176
}
177
 
178
/*
179
 * Get the watchdog status.
180
 */
181
static int at32_wdt_get_status(void)
182
{
183
        int rcause;
184
        int status = 0;
185
 
186
        rcause = wdt_readl(wdt, RCAUSE);
187
 
188
        switch (rcause) {
189
        case WDT_BIT(RCAUSE_EXT):
190
                status = WDIOF_EXTERN1;
191
                break;
192
        case WDT_BIT(RCAUSE_WDT):
193
                status = WDIOF_CARDRESET;
194
                break;
195
        case WDT_BIT(RCAUSE_POR):  /* fall through */
196
        case WDT_BIT(RCAUSE_JTAG): /* fall through */
197
        case WDT_BIT(RCAUSE_SERP): /* fall through */
198
        default:
199
                break;
200
        }
201
 
202
        return status;
203
}
204
 
205
static struct watchdog_info at32_wdt_info = {
206
        .identity       = "at32ap700x watchdog",
207
        .options        = WDIOF_SETTIMEOUT |
208
                          WDIOF_KEEPALIVEPING |
209
                          WDIOF_MAGICCLOSE,
210
};
211
 
212
/*
213
 * Handle commands from user-space.
214
 */
215
static int at32_wdt_ioctl(struct inode *inode, struct file *file,
216
                unsigned int cmd, unsigned long arg)
217
{
218
        int ret = -ENOTTY;
219
        int time;
220
        void __user *argp = (void __user *)arg;
221
        int __user *p = argp;
222
 
223
        switch (cmd) {
224
        case WDIOC_KEEPALIVE:
225
                at32_wdt_pat();
226
                ret = 0;
227
                break;
228
        case WDIOC_GETSUPPORT:
229
                ret = copy_to_user(argp, &at32_wdt_info,
230
                                sizeof(at32_wdt_info)) ? -EFAULT : 0;
231
                break;
232
        case WDIOC_SETTIMEOUT:
233
                ret = get_user(time, p);
234
                if (ret)
235
                        break;
236
                ret = at32_wdt_settimeout(time);
237
                if (ret)
238
                        break;
239
                /* Enable new time value */
240
                at32_wdt_start();
241
                /* fall through */
242
        case WDIOC_GETTIMEOUT:
243
                ret = put_user(wdt->timeout, p);
244
                break;
245
        case WDIOC_GETSTATUS:
246
                ret = put_user(0, p);
247
                break;
248
        case WDIOC_GETBOOTSTATUS:
249
                ret = put_user(wdt->boot_status, p);
250
                break;
251
        case WDIOC_SETOPTIONS:
252
                ret = get_user(time, p);
253
                if (ret)
254
                        break;
255
                if (time & WDIOS_DISABLECARD)
256
                        at32_wdt_stop();
257
                if (time & WDIOS_ENABLECARD)
258
                        at32_wdt_start();
259
                ret = 0;
260
                break;
261
        }
262
 
263
        return ret;
264
}
265
 
266
static ssize_t at32_wdt_write(struct file *file, const char __user *data,
267
                                size_t len, loff_t *ppos)
268
{
269
        /* See if we got the magic character 'V' and reload the timer */
270
        if (len) {
271
                if (!nowayout) {
272
                        size_t i;
273
 
274
                        /*
275
                         * note: just in case someone wrote the magic
276
                         * character five months ago...
277
                         */
278
                        expect_release = 0;
279
 
280
                        /*
281
                         * scan to see whether or not we got the magic
282
                         * character
283
                         */
284
                        for (i = 0; i != len; i++) {
285
                                char c;
286
                                if (get_user(c, data+i))
287
                                        return -EFAULT;
288
                                if (c == 'V')
289
                                        expect_release = 42;
290
                        }
291
                }
292
                /* someone wrote to us, we should pat the watchdog */
293
                at32_wdt_pat();
294
        }
295
        return len;
296
}
297
 
298
static const struct file_operations at32_wdt_fops = {
299
        .owner          = THIS_MODULE,
300
        .llseek         = no_llseek,
301
        .ioctl          = at32_wdt_ioctl,
302
        .open           = at32_wdt_open,
303
        .release        = at32_wdt_close,
304
        .write          = at32_wdt_write,
305
};
306
 
307
static int __init at32_wdt_probe(struct platform_device *pdev)
308
{
309
        struct resource *regs;
310
        int ret;
311
 
312
        if (wdt) {
313
                dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n");
314
                return -EBUSY;
315
        }
316
 
317
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
318
        if (!regs) {
319
                dev_dbg(&pdev->dev, "missing mmio resource\n");
320
                return -ENXIO;
321
        }
322
 
323
        wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL);
324
        if (!wdt) {
325
                dev_dbg(&pdev->dev, "no memory for wdt structure\n");
326
                return -ENOMEM;
327
        }
328
 
329
        wdt->regs = ioremap(regs->start, regs->end - regs->start + 1);
330
        if (!wdt->regs) {
331
                ret = -ENOMEM;
332
                dev_dbg(&pdev->dev, "could not map I/O memory\n");
333
                goto err_free;
334
        }
335
 
336
        spin_lock_init(&wdt->io_lock);
337
        wdt->boot_status = at32_wdt_get_status();
338
 
339
        /* Work-around for watchdog silicon errata. */
340
        if (wdt->boot_status & WDIOF_CARDRESET) {
341
                dev_info(&pdev->dev, "CPU must be reset with external "
342
                                "reset or POR due to silicon errata.\n");
343
                ret = -EIO;
344
                goto err_iounmap;
345
        } else {
346
                wdt->users = 0;
347
        }
348
        wdt->miscdev.minor = WATCHDOG_MINOR;
349
        wdt->miscdev.name = "watchdog";
350
        wdt->miscdev.fops = &at32_wdt_fops;
351
 
352
        if (at32_wdt_settimeout(timeout)) {
353
                at32_wdt_settimeout(TIMEOUT_DEFAULT);
354
                dev_dbg(&pdev->dev,
355
                        "default timeout invalid, set to %d sec.\n",
356
                        TIMEOUT_DEFAULT);
357
        }
358
 
359
        ret = misc_register(&wdt->miscdev);
360
        if (ret) {
361
                dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
362
                goto err_iounmap;
363
        }
364
 
365
        platform_set_drvdata(pdev, wdt);
366
        wdt->miscdev.parent = &pdev->dev;
367
        dev_info(&pdev->dev,
368
                "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n",
369
                wdt->regs, wdt->timeout, nowayout);
370
 
371
        return 0;
372
 
373
err_iounmap:
374
        iounmap(wdt->regs);
375
err_free:
376
        kfree(wdt);
377
        wdt = NULL;
378
        return ret;
379
}
380
 
381
static int __exit at32_wdt_remove(struct platform_device *pdev)
382
{
383
        if (wdt && platform_get_drvdata(pdev) == wdt) {
384
                /* Stop the timer before we leave */
385
                if (!nowayout)
386
                        at32_wdt_stop();
387
 
388
                misc_deregister(&wdt->miscdev);
389
                iounmap(wdt->regs);
390
                kfree(wdt);
391
                wdt = NULL;
392
                platform_set_drvdata(pdev, NULL);
393
        }
394
 
395
        return 0;
396
}
397
 
398
static void at32_wdt_shutdown(struct platform_device *pdev)
399
{
400
        at32_wdt_stop();
401
}
402
 
403
#ifdef CONFIG_PM
404
static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message)
405
{
406
        at32_wdt_stop();
407
        return 0;
408
}
409
 
410
static int at32_wdt_resume(struct platform_device *pdev)
411
{
412
        if (wdt->users)
413
                at32_wdt_start();
414
        return 0;
415
}
416
#else
417
#define at32_wdt_suspend NULL
418
#define at32_wdt_resume NULL
419
#endif
420
 
421
static struct platform_driver at32_wdt_driver = {
422
        .remove         = __exit_p(at32_wdt_remove),
423
        .suspend        = at32_wdt_suspend,
424
        .resume         = at32_wdt_resume,
425
        .driver         = {
426
                .name   = "at32_wdt",
427
                .owner  = THIS_MODULE,
428
        },
429
        .shutdown       = at32_wdt_shutdown,
430
};
431
 
432
static int __init at32_wdt_init(void)
433
{
434
        return platform_driver_probe(&at32_wdt_driver, at32_wdt_probe);
435
}
436
module_init(at32_wdt_init);
437
 
438
static void __exit at32_wdt_exit(void)
439
{
440
        platform_driver_unregister(&at32_wdt_driver);
441
}
442
module_exit(at32_wdt_exit);
443
 
444
MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
445
MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
446
MODULE_LICENSE("GPL");
447
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.