Commit f8394f61c66f48b1fe9d6964ddce492d7f9a4cd9

Authored by Gabor Juhos
Committed by Wim Van Sebroeck
1 parent 456c730153

watchdog: add driver for the Atheros AR71XX/AR724X/AR913X SoCs

This patch adds a driver for the built-in hardware watchdog device
of the Atheros AR71XX/AR724X/AR913X SoCs.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

Showing 3 changed files with 313 additions and 0 deletions Side-by-side Diff

drivers/watchdog/Kconfig
... ... @@ -866,6 +866,13 @@
866 866  
867 867 # MIPS Architecture
868 868  
  869 +config ATH79_WDT
  870 + tristate "Atheros AR71XX/AR724X/AR913X hardware watchdog"
  871 + depends on ATH79
  872 + help
  873 + Hardware driver for the built-in watchdog timer on the Atheros
  874 + AR71XX/AR724X/AR913X SoCs.
  875 +
869 876 config BCM47XX_WDT
870 877 tristate "Broadcom BCM47xx Watchdog Timer"
871 878 depends on BCM47XX
drivers/watchdog/Makefile
... ... @@ -110,6 +110,7 @@
110 110 # M68KNOMMU Architecture
111 111  
112 112 # MIPS Architecture
  113 +obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
113 114 obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
114 115 obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
115 116 obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
drivers/watchdog/ath79_wdt.c
  1 +/*
  2 + * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
  3 + *
  4 + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
  5 + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  6 + *
  7 + * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
  8 + * Author: Deepak Saxena <dsaxena@plexity.net>
  9 + * Copyright 2004 (c) MontaVista, Software, Inc.
  10 + *
  11 + * which again was based on sa1100 driver,
  12 + * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  13 + *
  14 + * This program is free software; you can redistribute it and/or modify it
  15 + * under the terms of the GNU General Public License version 2 as published
  16 + * by the Free Software Foundation.
  17 + *
  18 + */
  19 +
  20 +#include <linux/bitops.h>
  21 +#include <linux/errno.h>
  22 +#include <linux/fs.h>
  23 +#include <linux/init.h>
  24 +#include <linux/kernel.h>
  25 +#include <linux/miscdevice.h>
  26 +#include <linux/module.h>
  27 +#include <linux/moduleparam.h>
  28 +#include <linux/platform_device.h>
  29 +#include <linux/types.h>
  30 +#include <linux/watchdog.h>
  31 +#include <linux/clk.h>
  32 +#include <linux/err.h>
  33 +
  34 +#include <asm/mach-ath79/ath79.h>
  35 +#include <asm/mach-ath79/ar71xx_regs.h>
  36 +
  37 +#define DRIVER_NAME "ath79-wdt"
  38 +
  39 +#define WDT_TIMEOUT 15 /* seconds */
  40 +
  41 +#define WDOG_CTRL_LAST_RESET BIT(31)
  42 +#define WDOG_CTRL_ACTION_MASK 3
  43 +#define WDOG_CTRL_ACTION_NONE 0 /* no action */
  44 +#define WDOG_CTRL_ACTION_GPI 1 /* general purpose interrupt */
  45 +#define WDOG_CTRL_ACTION_NMI 2 /* NMI */
  46 +#define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */
  47 +
  48 +static int nowayout = WATCHDOG_NOWAYOUT;
  49 +module_param(nowayout, int, 0);
  50 +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  51 + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  52 +
  53 +static int timeout = WDT_TIMEOUT;
  54 +module_param(timeout, int, 0);
  55 +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
  56 + "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
  57 +
  58 +static unsigned long wdt_flags;
  59 +
  60 +#define WDT_FLAGS_BUSY 0
  61 +#define WDT_FLAGS_EXPECT_CLOSE 1
  62 +
  63 +static struct clk *wdt_clk;
  64 +static unsigned long wdt_freq;
  65 +static int boot_status;
  66 +static int max_timeout;
  67 +
  68 +static inline void ath79_wdt_keepalive(void)
  69 +{
  70 + ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout);
  71 +}
  72 +
  73 +static inline void ath79_wdt_enable(void)
  74 +{
  75 + ath79_wdt_keepalive();
  76 + ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR);
  77 +}
  78 +
  79 +static inline void ath79_wdt_disable(void)
  80 +{
  81 + ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE);
  82 +}
  83 +
  84 +static int ath79_wdt_set_timeout(int val)
  85 +{
  86 + if (val < 1 || val > max_timeout)
  87 + return -EINVAL;
  88 +
  89 + timeout = val;
  90 + ath79_wdt_keepalive();
  91 +
  92 + return 0;
  93 +}
  94 +
  95 +static int ath79_wdt_open(struct inode *inode, struct file *file)
  96 +{
  97 + if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
  98 + return -EBUSY;
  99 +
  100 + clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
  101 + ath79_wdt_enable();
  102 +
  103 + return nonseekable_open(inode, file);
  104 +}
  105 +
  106 +static int ath79_wdt_release(struct inode *inode, struct file *file)
  107 +{
  108 + if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
  109 + ath79_wdt_disable();
  110 + else {
  111 + pr_crit(DRIVER_NAME ": device closed unexpectedly, "
  112 + "watchdog timer will not stop!\n");
  113 + ath79_wdt_keepalive();
  114 + }
  115 +
  116 + clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
  117 + clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
  118 +
  119 + return 0;
  120 +}
  121 +
  122 +static ssize_t ath79_wdt_write(struct file *file, const char *data,
  123 + size_t len, loff_t *ppos)
  124 +{
  125 + if (len) {
  126 + if (!nowayout) {
  127 + size_t i;
  128 +
  129 + clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
  130 +
  131 + for (i = 0; i != len; i++) {
  132 + char c;
  133 +
  134 + if (get_user(c, data + i))
  135 + return -EFAULT;
  136 +
  137 + if (c == 'V')
  138 + set_bit(WDT_FLAGS_EXPECT_CLOSE,
  139 + &wdt_flags);
  140 + }
  141 + }
  142 +
  143 + ath79_wdt_keepalive();
  144 + }
  145 +
  146 + return len;
  147 +}
  148 +
  149 +static const struct watchdog_info ath79_wdt_info = {
  150 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
  151 + WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
  152 + .firmware_version = 0,
  153 + .identity = "ATH79 watchdog",
  154 +};
  155 +
  156 +static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
  157 + unsigned long arg)
  158 +{
  159 + void __user *argp = (void __user *)arg;
  160 + int __user *p = argp;
  161 + int err;
  162 + int t;
  163 +
  164 + switch (cmd) {
  165 + case WDIOC_GETSUPPORT:
  166 + err = copy_to_user(argp, &ath79_wdt_info,
  167 + sizeof(ath79_wdt_info)) ? -EFAULT : 0;
  168 + break;
  169 +
  170 + case WDIOC_GETSTATUS:
  171 + err = put_user(0, p);
  172 + break;
  173 +
  174 + case WDIOC_GETBOOTSTATUS:
  175 + err = put_user(boot_status, p);
  176 + break;
  177 +
  178 + case WDIOC_KEEPALIVE:
  179 + ath79_wdt_keepalive();
  180 + err = 0;
  181 + break;
  182 +
  183 + case WDIOC_SETTIMEOUT:
  184 + err = get_user(t, p);
  185 + if (err)
  186 + break;
  187 +
  188 + err = ath79_wdt_set_timeout(t);
  189 + if (err)
  190 + break;
  191 +
  192 + /* fallthrough */
  193 + case WDIOC_GETTIMEOUT:
  194 + err = put_user(timeout, p);
  195 + break;
  196 +
  197 + default:
  198 + err = -ENOTTY;
  199 + break;
  200 + }
  201 +
  202 + return err;
  203 +}
  204 +
  205 +static const struct file_operations ath79_wdt_fops = {
  206 + .owner = THIS_MODULE,
  207 + .llseek = no_llseek,
  208 + .write = ath79_wdt_write,
  209 + .unlocked_ioctl = ath79_wdt_ioctl,
  210 + .open = ath79_wdt_open,
  211 + .release = ath79_wdt_release,
  212 +};
  213 +
  214 +static struct miscdevice ath79_wdt_miscdev = {
  215 + .minor = WATCHDOG_MINOR,
  216 + .name = "watchdog",
  217 + .fops = &ath79_wdt_fops,
  218 +};
  219 +
  220 +static int __devinit ath79_wdt_probe(struct platform_device *pdev)
  221 +{
  222 + u32 ctrl;
  223 + int err;
  224 +
  225 + wdt_clk = clk_get(&pdev->dev, "wdt");
  226 + if (IS_ERR(wdt_clk))
  227 + return PTR_ERR(wdt_clk);
  228 +
  229 + err = clk_enable(wdt_clk);
  230 + if (err)
  231 + goto err_clk_put;
  232 +
  233 + wdt_freq = clk_get_rate(wdt_clk);
  234 + if (!wdt_freq) {
  235 + err = -EINVAL;
  236 + goto err_clk_disable;
  237 + }
  238 +
  239 + max_timeout = (0xfffffffful / wdt_freq);
  240 + if (timeout < 1 || timeout > max_timeout) {
  241 + timeout = max_timeout;
  242 + dev_info(&pdev->dev,
  243 + "timeout value must be 0 < timeout < %d, using %d\n",
  244 + max_timeout, timeout);
  245 + }
  246 +
  247 + ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
  248 + boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
  249 +
  250 + err = misc_register(&ath79_wdt_miscdev);
  251 + if (err) {
  252 + dev_err(&pdev->dev,
  253 + "unable to register misc device, err=%d\n", err);
  254 + goto err_clk_disable;
  255 + }
  256 +
  257 + return 0;
  258 +
  259 +err_clk_disable:
  260 + clk_disable(wdt_clk);
  261 +err_clk_put:
  262 + clk_put(wdt_clk);
  263 + return err;
  264 +}
  265 +
  266 +static int __devexit ath79_wdt_remove(struct platform_device *pdev)
  267 +{
  268 + misc_deregister(&ath79_wdt_miscdev);
  269 + clk_disable(wdt_clk);
  270 + clk_put(wdt_clk);
  271 + return 0;
  272 +}
  273 +
  274 +static void ath97_wdt_shutdown(struct platform_device *pdev)
  275 +{
  276 + ath79_wdt_disable();
  277 +}
  278 +
  279 +static struct platform_driver ath79_wdt_driver = {
  280 + .remove = __devexit_p(ath79_wdt_remove),
  281 + .shutdown = ath97_wdt_shutdown,
  282 + .driver = {
  283 + .name = DRIVER_NAME,
  284 + .owner = THIS_MODULE,
  285 + },
  286 +};
  287 +
  288 +static int __init ath79_wdt_init(void)
  289 +{
  290 + return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe);
  291 +}
  292 +module_init(ath79_wdt_init);
  293 +
  294 +static void __exit ath79_wdt_exit(void)
  295 +{
  296 + platform_driver_unregister(&ath79_wdt_driver);
  297 +}
  298 +module_exit(ath79_wdt_exit);
  299 +
  300 +MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
  301 +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
  302 +MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
  303 +MODULE_LICENSE("GPL v2");
  304 +MODULE_ALIAS("platform:" DRIVER_NAME);
  305 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);