aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/at32ap700x_wdt.c
diff options
context:
space:
mode:
authorHans-Christian Egtvedt <hcegtvedt@atmel.com>2007-10-30 09:56:20 -0400
committerWim Van Sebroeck <wim@iguana.be>2007-11-19 16:08:57 -0500
commitbb133450ee95746a9387f12de8bd738e79c21433 (patch)
treefb3d97f06f27740df69b7e86e3ab924eda91bfce /drivers/watchdog/at32ap700x_wdt.c
parent2ffbb8377c7a0713baf6644e285adc27a5654582 (diff)
[WATCHDOG] at32ap700x_wdt: add support for boot status and add fix for silicon errata
This patch enables the watchdog to read out the reset cause after a boot and provide this to the user. The driver will now also return -EIO if probed when booting from a watchdog reset. This is due to a silicon errata in the AT32AP700x devices. Detailed description and work-arounds can be found in the errata section of the datasheet avilable from http://www.atmel.com/dyn/products/datasheets.asp?family_id=682 Signed-off-by: Hans-Christian Egtvedt <hcegtvedt@atmel.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'drivers/watchdog/at32ap700x_wdt.c')
-rw-r--r--drivers/watchdog/at32ap700x_wdt.c69
1 files changed, 65 insertions, 4 deletions
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c
index 54a516169d07..fb5ed6478f78 100644
--- a/drivers/watchdog/at32ap700x_wdt.c
+++ b/drivers/watchdog/at32ap700x_wdt.c
@@ -6,6 +6,19 @@
6 * This program is free software; you can redistribute it and/or modify 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 7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 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.
9 */ 22 */
10 23
11#include <linux/init.h> 24#include <linux/init.h>
@@ -44,6 +57,13 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
44 57
45#define WDT_CLR 0x04 58#define WDT_CLR 0x04
46 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
47#define WDT_BIT(name) (1 << WDT_##name) 67#define WDT_BIT(name) (1 << WDT_##name)
48#define WDT_BF(name, value) ((value) << WDT_##name) 68#define WDT_BF(name, value) ((value) << WDT_##name)
49 69
@@ -56,6 +76,7 @@ struct wdt_at32ap700x {
56 void __iomem *regs; 76 void __iomem *regs;
57 spinlock_t io_lock; 77 spinlock_t io_lock;
58 int timeout; 78 int timeout;
79 int boot_status;
59 unsigned long users; 80 unsigned long users;
60 struct miscdevice miscdev; 81 struct miscdevice miscdev;
61}; 82};
@@ -126,7 +147,7 @@ static int at32_wdt_close(struct inode *inode, struct file *file)
126 at32_wdt_stop(); 147 at32_wdt_stop();
127 } else { 148 } else {
128 dev_dbg(wdt->miscdev.parent, 149 dev_dbg(wdt->miscdev.parent,
129 "Unexpected close, not stopping watchdog!\n"); 150 "unexpected close, not stopping watchdog!\n");
130 at32_wdt_pat(); 151 at32_wdt_pat();
131 } 152 }
132 clear_bit(1, &wdt->users); 153 clear_bit(1, &wdt->users);
@@ -154,6 +175,33 @@ static int at32_wdt_settimeout(int time)
154 return 0; 175 return 0;
155} 176}
156 177
178/*
179 * Get the watchdog status.
180 */
181static 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
157static struct watchdog_info at32_wdt_info = { 205static struct watchdog_info at32_wdt_info = {
158 .identity = "at32ap700x watchdog", 206 .identity = "at32ap700x watchdog",
159 .options = WDIOF_SETTIMEOUT | 207 .options = WDIOF_SETTIMEOUT |
@@ -194,10 +242,12 @@ static int at32_wdt_ioctl(struct inode *inode, struct file *file,
194 case WDIOC_GETTIMEOUT: 242 case WDIOC_GETTIMEOUT:
195 ret = put_user(wdt->timeout, p); 243 ret = put_user(wdt->timeout, p);
196 break; 244 break;
197 case WDIOC_GETSTATUS: /* fall through */ 245 case WDIOC_GETSTATUS:
198 case WDIOC_GETBOOTSTATUS:
199 ret = put_user(0, p); 246 ret = put_user(0, p);
200 break; 247 break;
248 case WDIOC_GETBOOTSTATUS:
249 ret = put_user(wdt->boot_status, p);
250 break;
201 case WDIOC_SETOPTIONS: 251 case WDIOC_SETOPTIONS:
202 ret = get_user(time, p); 252 ret = get_user(time, p);
203 if (ret) 253 if (ret)
@@ -282,8 +332,19 @@ static int __init at32_wdt_probe(struct platform_device *pdev)
282 dev_dbg(&pdev->dev, "could not map I/O memory\n"); 332 dev_dbg(&pdev->dev, "could not map I/O memory\n");
283 goto err_free; 333 goto err_free;
284 } 334 }
335
285 spin_lock_init(&wdt->io_lock); 336 spin_lock_init(&wdt->io_lock);
286 wdt->users = 0; 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 }
287 wdt->miscdev.minor = WATCHDOG_MINOR; 348 wdt->miscdev.minor = WATCHDOG_MINOR;
288 wdt->miscdev.name = "watchdog"; 349 wdt->miscdev.name = "watchdog";
289 wdt->miscdev.fops = &at32_wdt_fops; 350 wdt->miscdev.fops = &at32_wdt_fops;