Commit 03ec58568a3c66cac4b23ff74db95c966a1521de

Authored by Florian Fainelli
Committed by Wim Van Sebroeck
1 parent b3e8f2c13a

[WATCHDOG] Add support for the IDT RC32434 watchdog

Add driver for the IDT RC32434 SoC built-in watchdog.

Signed-off-by: Florian Fainelli <florian.fainelli@telecomint.eu>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

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

drivers/watchdog/Kconfig
... ... @@ -643,6 +643,16 @@
643 643  
644 644 # MIPS Architecture
645 645  
  646 +config RC32434_WDT
  647 + tristate "IDT RC32434 SoC Watchdog Timer"
  648 + depends on MIKROTIK_RB532
  649 + help
  650 + Hardware driver for the IDT RC32434 SoC built-in
  651 + watchdog timer.
  652 +
  653 + To compile this driver as a module, choose M here: the
  654 + module will be called rc32434_wdt.
  655 +
646 656 config INDYDOG
647 657 tristate "Indy/I2 Hardware Watchdog"
648 658 depends on SGI_HAS_INDYDOG
drivers/watchdog/Makefile
... ... @@ -95,6 +95,7 @@
95 95 # M68KNOMMU Architecture
96 96  
97 97 # MIPS Architecture
  98 +obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
98 99 obj-$(CONFIG_INDYDOG) += indydog.o
99 100 obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
100 101 obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
drivers/watchdog/rc32434_wdt.c
  1 +/*
  2 + * IDT Interprise 79RC32434 watchdog driver
  3 + *
  4 + * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org>
  5 + * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
  6 + *
  7 + * based on
  8 + * SoftDog 0.05: A Software Watchdog Device
  9 + *
  10 + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
  11 + *
  12 + * This program is free software; you can redistribute it and/or
  13 + * modify it under the terms of the GNU General Public License
  14 + * as published by the Free Software Foundation; either version
  15 + * 2 of the License, or (at your option) any later version.
  16 + *
  17 + */
  18 +
  19 +#include <linux/module.h>
  20 +#include <linux/types.h>
  21 +#include <linux/kernel.h>
  22 +#include <linux/fs.h>
  23 +#include <linux/mm.h>
  24 +#include <linux/miscdevice.h>
  25 +#include <linux/watchdog.h>
  26 +#include <linux/reboot.h>
  27 +#include <linux/smp_lock.h>
  28 +#include <linux/init.h>
  29 +#include <linux/platform_device.h>
  30 +#include <linux/uaccess.h>
  31 +
  32 +#include <asm/bootinfo.h>
  33 +#include <asm/time.h>
  34 +#include <asm/mach-rc32434/integ.h>
  35 +
  36 +#define MAX_TIMEOUT 20
  37 +#define RC32434_WDT_INTERVAL (15 * HZ)
  38 +
  39 +#define VERSION "0.2"
  40 +
  41 +static struct {
  42 + struct completion stop;
  43 + int running;
  44 + struct timer_list timer;
  45 + int queue;
  46 + int default_ticks;
  47 + unsigned long inuse;
  48 +} rc32434_wdt_device;
  49 +
  50 +static struct integ __iomem *wdt_reg;
  51 +static int ticks = 100 * HZ;
  52 +
  53 +static int expect_close;
  54 +static int timeout;
  55 +
  56 +static int nowayout = WATCHDOG_NOWAYOUT;
  57 +module_param(nowayout, int, 0);
  58 +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  59 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  60 +
  61 +
  62 +static void rc32434_wdt_start(void)
  63 +{
  64 + u32 val;
  65 +
  66 + if (!rc32434_wdt_device.inuse) {
  67 + writel(0, &wdt_reg->wtcount);
  68 +
  69 + val = RC32434_ERR_WRE;
  70 + writel(readl(&wdt_reg->errcs) | val, &wdt_reg->errcs);
  71 +
  72 + val = RC32434_WTC_EN;
  73 + writel(readl(&wdt_reg->wtc) | val, &wdt_reg->wtc);
  74 + }
  75 + rc32434_wdt_device.running++;
  76 +}
  77 +
  78 +static void rc32434_wdt_stop(void)
  79 +{
  80 + u32 val;
  81 +
  82 + if (rc32434_wdt_device.running) {
  83 +
  84 + val = ~RC32434_WTC_EN;
  85 + writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc);
  86 +
  87 + val = ~RC32434_ERR_WRE;
  88 + writel(readl(&wdt_reg->errcs) & val, &wdt_reg->errcs);
  89 +
  90 + rc32434_wdt_device.running = 0;
  91 + }
  92 +}
  93 +
  94 +static void rc32434_wdt_set(int new_timeout)
  95 +{
  96 + u32 cmp = new_timeout * HZ;
  97 + u32 state, val;
  98 +
  99 + timeout = new_timeout;
  100 + /*
  101 + * store and disable WTC
  102 + */
  103 + state = (u32)(readl(&wdt_reg->wtc) & RC32434_WTC_EN);
  104 + val = ~RC32434_WTC_EN;
  105 + writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc);
  106 +
  107 + writel(0, &wdt_reg->wtcount);
  108 + writel(cmp, &wdt_reg->wtcompare);
  109 +
  110 + /*
  111 + * restore WTC
  112 + */
  113 +
  114 + writel(readl(&wdt_reg->wtc) | state, &wdt_reg);
  115 +}
  116 +
  117 +static void rc32434_wdt_reset(void)
  118 +{
  119 + ticks = rc32434_wdt_device.default_ticks;
  120 +}
  121 +
  122 +static void rc32434_wdt_update(unsigned long unused)
  123 +{
  124 + if (rc32434_wdt_device.running)
  125 + ticks--;
  126 +
  127 + writel(0, &wdt_reg->wtcount);
  128 +
  129 + if (rc32434_wdt_device.queue && ticks)
  130 + mod_timer(&rc32434_wdt_device.timer,
  131 + jiffies + RC32434_WDT_INTERVAL);
  132 + else
  133 + complete(&rc32434_wdt_device.stop);
  134 +}
  135 +
  136 +static int rc32434_wdt_open(struct inode *inode, struct file *file)
  137 +{
  138 + if (test_and_set_bit(0, &rc32434_wdt_device.inuse))
  139 + return -EBUSY;
  140 +
  141 + if (nowayout)
  142 + __module_get(THIS_MODULE);
  143 +
  144 + return nonseekable_open(inode, file);
  145 +}
  146 +
  147 +static int rc32434_wdt_release(struct inode *inode, struct file *file)
  148 +{
  149 + if (expect_close && nowayout == 0) {
  150 + rc32434_wdt_stop();
  151 + printk(KERN_INFO KBUILD_MODNAME ": disabling watchdog timer\n");
  152 + module_put(THIS_MODULE);
  153 + } else
  154 + printk(KERN_CRIT KBUILD_MODNAME
  155 + ": device closed unexpectedly. WDT will not stop !\n");
  156 +
  157 + clear_bit(0, &rc32434_wdt_device.inuse);
  158 + return 0;
  159 +}
  160 +
  161 +static ssize_t rc32434_wdt_write(struct file *file, const char *data,
  162 + size_t len, loff_t *ppos)
  163 +{
  164 + if (len) {
  165 + if (!nowayout) {
  166 + size_t i;
  167 +
  168 + /* In case it was set long ago */
  169 + expect_close = 0;
  170 +
  171 + for (i = 0; i != len; i++) {
  172 + char c;
  173 + if (get_user(c, data + i))
  174 + return -EFAULT;
  175 + if (c == 'V')
  176 + expect_close = 1;
  177 + }
  178 + }
  179 + rc32434_wdt_update(0);
  180 + return len;
  181 + }
  182 + return 0;
  183 +}
  184 +
  185 +static int rc32434_wdt_ioctl(struct inode *inode, struct file *file,
  186 + unsigned int cmd, unsigned long arg)
  187 +{
  188 + void __user *argp = (void __user *)arg;
  189 + int new_timeout;
  190 + unsigned int value;
  191 + static struct watchdog_info ident = {
  192 + .options = WDIOF_SETTIMEOUT |
  193 + WDIOF_KEEPALIVEPING |
  194 + WDIOF_MAGICCLOSE,
  195 + .identity = "RC32434_WDT Watchdog",
  196 + };
  197 + switch (cmd) {
  198 + case WDIOC_KEEPALIVE:
  199 + rc32434_wdt_reset();
  200 + break;
  201 + case WDIOC_GETSTATUS:
  202 + case WDIOC_GETBOOTSTATUS:
  203 + value = readl(&wdt_reg->wtcount);
  204 + if (copy_to_user(argp, &value, sizeof(int)))
  205 + return -EFAULT;
  206 + break;
  207 + case WDIOC_GETSUPPORT:
  208 + if (copy_to_user(argp, &ident, sizeof(ident)))
  209 + return -EFAULT;
  210 + break;
  211 + case WDIOC_SETOPTIONS:
  212 + if (copy_from_user(&value, argp, sizeof(int)))
  213 + return -EFAULT;
  214 + switch (value) {
  215 + case WDIOS_ENABLECARD:
  216 + rc32434_wdt_start();
  217 + break;
  218 + case WDIOS_DISABLECARD:
  219 + rc32434_wdt_stop();
  220 + default:
  221 + return -EINVAL;
  222 + }
  223 + break;
  224 + case WDIOC_SETTIMEOUT:
  225 + if (copy_from_user(&new_timeout, argp, sizeof(int)))
  226 + return -EFAULT;
  227 + if (new_timeout < 1)
  228 + return -EINVAL;
  229 + if (new_timeout > MAX_TIMEOUT)
  230 + return -EINVAL;
  231 + rc32434_wdt_set(new_timeout);
  232 + case WDIOC_GETTIMEOUT:
  233 + return copy_to_user(argp, &timeout, sizeof(int));
  234 + default:
  235 + return -ENOTTY;
  236 + }
  237 +
  238 + return 0;
  239 +}
  240 +
  241 +static struct file_operations rc32434_wdt_fops = {
  242 + .owner = THIS_MODULE,
  243 + .llseek = no_llseek,
  244 + .write = rc32434_wdt_write,
  245 + .ioctl = rc32434_wdt_ioctl,
  246 + .open = rc32434_wdt_open,
  247 + .release = rc32434_wdt_release,
  248 +};
  249 +
  250 +static struct miscdevice rc32434_wdt_miscdev = {
  251 + .minor = WATCHDOG_MINOR,
  252 + .name = "watchdog",
  253 + .fops = &rc32434_wdt_fops,
  254 +};
  255 +
  256 +static char banner[] = KERN_INFO KBUILD_MODNAME
  257 + ": Watchdog Timer version " VERSION ", timer margin: %d sec\n";
  258 +
  259 +static int rc32434_wdt_probe(struct platform_device *pdev)
  260 +{
  261 + int ret;
  262 + struct resource *r;
  263 +
  264 + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb500_wdt_res");
  265 + if (!r) {
  266 + printk(KERN_ERR KBUILD_MODNAME
  267 + "failed to retrieve resources\n");
  268 + return -ENODEV;
  269 + }
  270 +
  271 + wdt_reg = ioremap_nocache(r->start, r->end - r->start);
  272 + if (!wdt_reg) {
  273 + printk(KERN_ERR KBUILD_MODNAME
  274 + "failed to remap I/O resources\n");
  275 + return -ENXIO;
  276 + }
  277 +
  278 + ret = misc_register(&rc32434_wdt_miscdev);
  279 +
  280 + if (ret < 0) {
  281 + printk(KERN_ERR KBUILD_MODNAME
  282 + "failed to register watchdog device\n");
  283 + goto unmap;
  284 + }
  285 +
  286 + init_completion(&rc32434_wdt_device.stop);
  287 + rc32434_wdt_device.queue = 0;
  288 +
  289 + clear_bit(0, &rc32434_wdt_device.inuse);
  290 +
  291 + setup_timer(&rc32434_wdt_device.timer, rc32434_wdt_update, 0L);
  292 +
  293 + rc32434_wdt_device.default_ticks = ticks;
  294 +
  295 + rc32434_wdt_start();
  296 +
  297 + printk(banner, timeout);
  298 +
  299 + return 0;
  300 +
  301 +unmap:
  302 + iounmap(wdt_reg);
  303 + return ret;
  304 +}
  305 +
  306 +static int rc32434_wdt_remove(struct platform_device *pdev)
  307 +{
  308 + if (rc32434_wdt_device.queue) {
  309 + rc32434_wdt_device.queue = 0;
  310 + wait_for_completion(&rc32434_wdt_device.stop);
  311 + }
  312 + misc_deregister(&rc32434_wdt_miscdev);
  313 +
  314 + iounmap(wdt_reg);
  315 +
  316 + return 0;
  317 +}
  318 +
  319 +static struct platform_driver rc32434_wdt = {
  320 + .probe = rc32434_wdt_probe,
  321 + .remove = rc32434_wdt_remove,
  322 + .driver = {
  323 + .name = "rc32434_wdt",
  324 + }
  325 +};
  326 +
  327 +static int __init rc32434_wdt_init(void)
  328 +{
  329 + return platform_driver_register(&rc32434_wdt);
  330 +}
  331 +
  332 +static void __exit rc32434_wdt_exit(void)
  333 +{
  334 + platform_driver_unregister(&rc32434_wdt);
  335 +}
  336 +
  337 +module_init(rc32434_wdt_init);
  338 +module_exit(rc32434_wdt_exit);
  339 +
  340 +MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
  341 + "Florian Fainelli <florian@openwrt.org>");
  342 +MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
  343 +MODULE_LICENSE("GPL");
  344 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);