Commit bb133450ee95746a9387f12de8bd738e79c21433

Authored by Hans-Christian Egtvedt
Committed by Wim Van Sebroeck
1 parent 2ffbb8377c

[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>

Showing 1 changed file with 65 additions and 4 deletions Side-by-side Diff

drivers/watchdog/at32ap700x_wdt.c
... ... @@ -6,6 +6,19 @@
6 6 * This program is free software; you can redistribute it and/or modify
7 7 * it under the terms of the GNU General Public License version 2 as
8 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 24 #include <linux/init.h>
... ... @@ -44,6 +57,13 @@
44 57  
45 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 67 #define WDT_BIT(name) (1 << WDT_##name)
48 68 #define WDT_BF(name, value) ((value) << WDT_##name)
49 69  
... ... @@ -56,6 +76,7 @@
56 76 void __iomem *regs;
57 77 spinlock_t io_lock;
58 78 int timeout;
  79 + int boot_status;
59 80 unsigned long users;
60 81 struct miscdevice miscdev;
61 82 };
... ... @@ -126,7 +147,7 @@
126 147 at32_wdt_stop();
127 148 } else {
128 149 dev_dbg(wdt->miscdev.parent,
129   - "Unexpected close, not stopping watchdog!\n");
  150 + "unexpected close, not stopping watchdog!\n");
130 151 at32_wdt_pat();
131 152 }
132 153 clear_bit(1, &wdt->users);
... ... @@ -154,6 +175,33 @@
154 175 return 0;
155 176 }
156 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 +
157 205 static struct watchdog_info at32_wdt_info = {
158 206 .identity = "at32ap700x watchdog",
159 207 .options = WDIOF_SETTIMEOUT |
160 208  
... ... @@ -194,10 +242,12 @@
194 242 case WDIOC_GETTIMEOUT:
195 243 ret = put_user(wdt->timeout, p);
196 244 break;
197   - case WDIOC_GETSTATUS: /* fall through */
198   - case WDIOC_GETBOOTSTATUS:
  245 + case WDIOC_GETSTATUS:
199 246 ret = put_user(0, p);
200 247 break;
  248 + case WDIOC_GETBOOTSTATUS:
  249 + ret = put_user(wdt->boot_status, p);
  250 + break;
201 251 case WDIOC_SETOPTIONS:
202 252 ret = get_user(time, p);
203 253 if (ret)
204 254  
... ... @@ -282,8 +332,19 @@
282 332 dev_dbg(&pdev->dev, "could not map I/O memory\n");
283 333 goto err_free;
284 334 }
  335 +
285 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 348 wdt->miscdev.minor = WATCHDOG_MINOR;
288 349 wdt->miscdev.name = "watchdog";
289 350 wdt->miscdev.fops = &at32_wdt_fops;