1b9d36b851a5085d35b9598235a456604532f306eRussell King/* 2b9d36b851a5085d35b9598235a456604532f306eRussell King * Watchdog driver for the mpcore watchdog timer 3b9d36b851a5085d35b9598235a456604532f306eRussell King * 4b9d36b851a5085d35b9598235a456604532f306eRussell King * (c) Copyright 2004 ARM Limited 5b9d36b851a5085d35b9598235a456604532f306eRussell King * 6b9d36b851a5085d35b9598235a456604532f306eRussell King * Based on the SoftDog driver: 729fa0586de4fe518f122a915b8c6e92d12e8ca7fAlan Cox * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, 8143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck * All Rights Reserved. 9b9d36b851a5085d35b9598235a456604532f306eRussell King * 10b9d36b851a5085d35b9598235a456604532f306eRussell King * This program is free software; you can redistribute it and/or 11b9d36b851a5085d35b9598235a456604532f306eRussell King * modify it under the terms of the GNU General Public License 12b9d36b851a5085d35b9598235a456604532f306eRussell King * as published by the Free Software Foundation; either version 13b9d36b851a5085d35b9598235a456604532f306eRussell King * 2 of the License, or (at your option) any later version. 14b9d36b851a5085d35b9598235a456604532f306eRussell King * 15b9d36b851a5085d35b9598235a456604532f306eRussell King * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 16b9d36b851a5085d35b9598235a456604532f306eRussell King * warranty for any of this software. This material is provided 17b9d36b851a5085d35b9598235a456604532f306eRussell King * "AS-IS" and at no charge. 18b9d36b851a5085d35b9598235a456604532f306eRussell King * 19b9d36b851a5085d35b9598235a456604532f306eRussell King * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> 20b9d36b851a5085d35b9598235a456604532f306eRussell King * 21b9d36b851a5085d35b9598235a456604532f306eRussell King */ 2227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches 2327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches 25b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/module.h> 26b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/moduleparam.h> 27b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/types.h> 28b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/miscdevice.h> 29b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/watchdog.h> 30b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/fs.h> 31b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/reboot.h> 32b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/init.h> 33b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/interrupt.h> 34d052d1beff706920e82c5d55006b08e256b5df09Russell King#include <linux/platform_device.h> 3583ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox#include <linux/uaccess.h> 365a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 3798af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar#include <linux/io.h> 38ad4162f3712ddf25e148cff1e7dc37eafdff3e51Russell King 3998af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar#include <asm/smp_twd.h> 40b9d36b851a5085d35b9598235a456604532f306eRussell King 41b9d36b851a5085d35b9598235a456604532f306eRussell Kingstruct mpcore_wdt { 42b9d36b851a5085d35b9598235a456604532f306eRussell King unsigned long timer_alive; 43b9d36b851a5085d35b9598235a456604532f306eRussell King struct device *dev; 44b9d36b851a5085d35b9598235a456604532f306eRussell King void __iomem *base; 45b9d36b851a5085d35b9598235a456604532f306eRussell King int irq; 46b9d36b851a5085d35b9598235a456604532f306eRussell King unsigned int perturb; 47b9d36b851a5085d35b9598235a456604532f306eRussell King char expect_close; 48b9d36b851a5085d35b9598235a456604532f306eRussell King}; 49b9d36b851a5085d35b9598235a456604532f306eRussell King 50aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic struct platform_device *mpcore_wdt_pdev; 5198af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagarstatic DEFINE_SPINLOCK(wdt_lock); 52b9d36b851a5085d35b9598235a456604532f306eRussell King 53b9d36b851a5085d35b9598235a456604532f306eRussell King#define TIMER_MARGIN 60 54b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_margin = TIMER_MARGIN; 55b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_param(mpcore_margin, int, 0); 5683ab1a53f219c8139199633f60ab0ef88ef18c54Alan CoxMODULE_PARM_DESC(mpcore_margin, 5783ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default=" 5883ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox __MODULE_STRING(TIMER_MARGIN) ")"); 59b9d36b851a5085d35b9598235a456604532f306eRussell King 6086a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT; 6186a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0); 6283ab1a53f219c8139199633f60ab0ef88ef18c54Alan CoxMODULE_PARM_DESC(nowayout, 6383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox "Watchdog cannot be stopped once started (default=" 6483ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 65b9d36b851a5085d35b9598235a456604532f306eRussell King 66b9d36b851a5085d35b9598235a456604532f306eRussell King#define ONLY_TESTING 0 67b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_noboot = ONLY_TESTING; 68b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_param(mpcore_noboot, int, 0); 69a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van SebroeckMODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, " 70a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck "set to 1 to ignore reboots, 0 to reboot (default=" 71a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck __MODULE_STRING(ONLY_TESTING) ")"); 72b9d36b851a5085d35b9598235a456604532f306eRussell King 73b9d36b851a5085d35b9598235a456604532f306eRussell King/* 74b9d36b851a5085d35b9598235a456604532f306eRussell King * This is the interrupt handler. Note that we only use this 75b9d36b851a5085d35b9598235a456604532f306eRussell King * in testing mode, so don't actually do a reboot here. 76b9d36b851a5085d35b9598235a456604532f306eRussell King */ 777d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t mpcore_wdt_fire(int irq, void *arg) 78b9d36b851a5085d35b9598235a456604532f306eRussell King{ 79b9d36b851a5085d35b9598235a456604532f306eRussell King struct mpcore_wdt *wdt = arg; 80b9d36b851a5085d35b9598235a456604532f306eRussell King 81b9d36b851a5085d35b9598235a456604532f306eRussell King /* Check it really was our interrupt */ 82b9d36b851a5085d35b9598235a456604532f306eRussell King if (readl(wdt->base + TWD_WDOG_INTSTAT)) { 8383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox dev_printk(KERN_CRIT, wdt->dev, 8483ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox "Triggered - Reboot ignored.\n"); 85b9d36b851a5085d35b9598235a456604532f306eRussell King /* Clear the interrupt on the watchdog */ 86b9d36b851a5085d35b9598235a456604532f306eRussell King writel(1, wdt->base + TWD_WDOG_INTSTAT); 87b9d36b851a5085d35b9598235a456604532f306eRussell King return IRQ_HANDLED; 88b9d36b851a5085d35b9598235a456604532f306eRussell King } 89b9d36b851a5085d35b9598235a456604532f306eRussell King return IRQ_NONE; 90b9d36b851a5085d35b9598235a456604532f306eRussell King} 91b9d36b851a5085d35b9598235a456604532f306eRussell King 92b9d36b851a5085d35b9598235a456604532f306eRussell King/* 93b9d36b851a5085d35b9598235a456604532f306eRussell King * mpcore_wdt_keepalive - reload the timer 94b9d36b851a5085d35b9598235a456604532f306eRussell King * 95b9d36b851a5085d35b9598235a456604532f306eRussell King * Note that the spec says a DIFFERENT value must be written to the reload 96b9d36b851a5085d35b9598235a456604532f306eRussell King * register each time. The "perturb" variable deals with this by adding 1 97b9d36b851a5085d35b9598235a456604532f306eRussell King * to the count every other time the function is called. 98b9d36b851a5085d35b9598235a456604532f306eRussell King */ 99b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void mpcore_wdt_keepalive(struct mpcore_wdt *wdt) 100b9d36b851a5085d35b9598235a456604532f306eRussell King{ 10198af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar unsigned long count; 102b9d36b851a5085d35b9598235a456604532f306eRussell King 10398af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar spin_lock(&wdt_lock); 104b9d36b851a5085d35b9598235a456604532f306eRussell King /* Assume prescale is set to 256 */ 10598af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar count = __raw_readl(wdt->base + TWD_WDOG_COUNTER); 10698af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar count = (0xFFFFFFFFU - count) * (HZ / 5); 10798af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar count = (count / 256) * mpcore_margin; 108b9d36b851a5085d35b9598235a456604532f306eRussell King 109b9d36b851a5085d35b9598235a456604532f306eRussell King /* Reload the counter */ 110b9d36b851a5085d35b9598235a456604532f306eRussell King writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD); 111b9d36b851a5085d35b9598235a456604532f306eRussell King wdt->perturb = wdt->perturb ? 0 : 1; 11283ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox spin_unlock(&wdt_lock); 113b9d36b851a5085d35b9598235a456604532f306eRussell King} 114b9d36b851a5085d35b9598235a456604532f306eRussell King 115b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void mpcore_wdt_stop(struct mpcore_wdt *wdt) 116b9d36b851a5085d35b9598235a456604532f306eRussell King{ 11783ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox spin_lock(&wdt_lock); 118b9d36b851a5085d35b9598235a456604532f306eRussell King writel(0x12345678, wdt->base + TWD_WDOG_DISABLE); 119b9d36b851a5085d35b9598235a456604532f306eRussell King writel(0x87654321, wdt->base + TWD_WDOG_DISABLE); 120b9d36b851a5085d35b9598235a456604532f306eRussell King writel(0x0, wdt->base + TWD_WDOG_CONTROL); 12183ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox spin_unlock(&wdt_lock); 122b9d36b851a5085d35b9598235a456604532f306eRussell King} 123b9d36b851a5085d35b9598235a456604532f306eRussell King 124b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void mpcore_wdt_start(struct mpcore_wdt *wdt) 125b9d36b851a5085d35b9598235a456604532f306eRussell King{ 126b9d36b851a5085d35b9598235a456604532f306eRussell King dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n"); 127b9d36b851a5085d35b9598235a456604532f306eRussell King 128b9d36b851a5085d35b9598235a456604532f306eRussell King /* This loads the count register but does NOT start the count yet */ 129b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_keepalive(wdt); 130b9d36b851a5085d35b9598235a456604532f306eRussell King 131b9d36b851a5085d35b9598235a456604532f306eRussell King if (mpcore_noboot) { 132b9d36b851a5085d35b9598235a456604532f306eRussell King /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */ 133b9d36b851a5085d35b9598235a456604532f306eRussell King writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL); 134b9d36b851a5085d35b9598235a456604532f306eRussell King } else { 135b9d36b851a5085d35b9598235a456604532f306eRussell King /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */ 136b9d36b851a5085d35b9598235a456604532f306eRussell King writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL); 137b9d36b851a5085d35b9598235a456604532f306eRussell King } 138b9d36b851a5085d35b9598235a456604532f306eRussell King} 139b9d36b851a5085d35b9598235a456604532f306eRussell King 140b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_wdt_set_heartbeat(int t) 141b9d36b851a5085d35b9598235a456604532f306eRussell King{ 142b9d36b851a5085d35b9598235a456604532f306eRussell King if (t < 0x0001 || t > 0xFFFF) 143b9d36b851a5085d35b9598235a456604532f306eRussell King return -EINVAL; 144b9d36b851a5085d35b9598235a456604532f306eRussell King 145b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_margin = t; 146b9d36b851a5085d35b9598235a456604532f306eRussell King return 0; 147b9d36b851a5085d35b9598235a456604532f306eRussell King} 148b9d36b851a5085d35b9598235a456604532f306eRussell King 149b9d36b851a5085d35b9598235a456604532f306eRussell King/* 150b9d36b851a5085d35b9598235a456604532f306eRussell King * /dev/watchdog handling 151b9d36b851a5085d35b9598235a456604532f306eRussell King */ 152b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_wdt_open(struct inode *inode, struct file *file) 153b9d36b851a5085d35b9598235a456604532f306eRussell King{ 154aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev); 155b9d36b851a5085d35b9598235a456604532f306eRussell King 156b9d36b851a5085d35b9598235a456604532f306eRussell King if (test_and_set_bit(0, &wdt->timer_alive)) 157b9d36b851a5085d35b9598235a456604532f306eRussell King return -EBUSY; 158b9d36b851a5085d35b9598235a456604532f306eRussell King 159b9d36b851a5085d35b9598235a456604532f306eRussell King if (nowayout) 160b9d36b851a5085d35b9598235a456604532f306eRussell King __module_get(THIS_MODULE); 161b9d36b851a5085d35b9598235a456604532f306eRussell King 162b9d36b851a5085d35b9598235a456604532f306eRussell King file->private_data = wdt; 163b9d36b851a5085d35b9598235a456604532f306eRussell King 164b9d36b851a5085d35b9598235a456604532f306eRussell King /* 165b9d36b851a5085d35b9598235a456604532f306eRussell King * Activate timer 166b9d36b851a5085d35b9598235a456604532f306eRussell King */ 167b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_start(wdt); 168b9d36b851a5085d35b9598235a456604532f306eRussell King 169b9d36b851a5085d35b9598235a456604532f306eRussell King return nonseekable_open(inode, file); 170b9d36b851a5085d35b9598235a456604532f306eRussell King} 171b9d36b851a5085d35b9598235a456604532f306eRussell King 172b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_wdt_release(struct inode *inode, struct file *file) 173b9d36b851a5085d35b9598235a456604532f306eRussell King{ 174b9d36b851a5085d35b9598235a456604532f306eRussell King struct mpcore_wdt *wdt = file->private_data; 175b9d36b851a5085d35b9598235a456604532f306eRussell King 176b9d36b851a5085d35b9598235a456604532f306eRussell King /* 177b9d36b851a5085d35b9598235a456604532f306eRussell King * Shut off the timer. 1785f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck * Lock it in if it's a module and we set nowayout 179b9d36b851a5085d35b9598235a456604532f306eRussell King */ 18083ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox if (wdt->expect_close == 42) 181b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_stop(wdt); 18283ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox else { 18383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox dev_printk(KERN_CRIT, wdt->dev, 18483ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox "unexpected close, not stopping watchdog!\n"); 185b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_keepalive(wdt); 186b9d36b851a5085d35b9598235a456604532f306eRussell King } 187b9d36b851a5085d35b9598235a456604532f306eRussell King clear_bit(0, &wdt->timer_alive); 188b9d36b851a5085d35b9598235a456604532f306eRussell King wdt->expect_close = 0; 189b9d36b851a5085d35b9598235a456604532f306eRussell King return 0; 190b9d36b851a5085d35b9598235a456604532f306eRussell King} 191b9d36b851a5085d35b9598235a456604532f306eRussell King 19283ab1a53f219c8139199633f60ab0ef88ef18c54Alan Coxstatic ssize_t mpcore_wdt_write(struct file *file, const char *data, 19383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox size_t len, loff_t *ppos) 194b9d36b851a5085d35b9598235a456604532f306eRussell King{ 195b9d36b851a5085d35b9598235a456604532f306eRussell King struct mpcore_wdt *wdt = file->private_data; 196b9d36b851a5085d35b9598235a456604532f306eRussell King 197b9d36b851a5085d35b9598235a456604532f306eRussell King /* 198b9d36b851a5085d35b9598235a456604532f306eRussell King * Refresh the timer. 199b9d36b851a5085d35b9598235a456604532f306eRussell King */ 200b9d36b851a5085d35b9598235a456604532f306eRussell King if (len) { 201b9d36b851a5085d35b9598235a456604532f306eRussell King if (!nowayout) { 202b9d36b851a5085d35b9598235a456604532f306eRussell King size_t i; 203b9d36b851a5085d35b9598235a456604532f306eRussell King 204b9d36b851a5085d35b9598235a456604532f306eRussell King /* In case it was set long ago */ 205b9d36b851a5085d35b9598235a456604532f306eRussell King wdt->expect_close = 0; 206b9d36b851a5085d35b9598235a456604532f306eRussell King 207b9d36b851a5085d35b9598235a456604532f306eRussell King for (i = 0; i != len; i++) { 208b9d36b851a5085d35b9598235a456604532f306eRussell King char c; 209b9d36b851a5085d35b9598235a456604532f306eRussell King 210b9d36b851a5085d35b9598235a456604532f306eRussell King if (get_user(c, data + i)) 211b9d36b851a5085d35b9598235a456604532f306eRussell King return -EFAULT; 212b9d36b851a5085d35b9598235a456604532f306eRussell King if (c == 'V') 213b9d36b851a5085d35b9598235a456604532f306eRussell King wdt->expect_close = 42; 214b9d36b851a5085d35b9598235a456604532f306eRussell King } 215b9d36b851a5085d35b9598235a456604532f306eRussell King } 216b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_keepalive(wdt); 217b9d36b851a5085d35b9598235a456604532f306eRussell King } 218b9d36b851a5085d35b9598235a456604532f306eRussell King return len; 219b9d36b851a5085d35b9598235a456604532f306eRussell King} 220b9d36b851a5085d35b9598235a456604532f306eRussell King 22142747d712de56cf2087b702d2ad90af114c53138Wim Van Sebroeckstatic const struct watchdog_info ident = { 222b9d36b851a5085d35b9598235a456604532f306eRussell King .options = WDIOF_SETTIMEOUT | 223b9d36b851a5085d35b9598235a456604532f306eRussell King WDIOF_KEEPALIVEPING | 224b9d36b851a5085d35b9598235a456604532f306eRussell King WDIOF_MAGICCLOSE, 225b9d36b851a5085d35b9598235a456604532f306eRussell King .identity = "MPcore Watchdog", 226b9d36b851a5085d35b9598235a456604532f306eRussell King}; 227b9d36b851a5085d35b9598235a456604532f306eRussell King 22883ab1a53f219c8139199633f60ab0ef88ef18c54Alan Coxstatic long mpcore_wdt_ioctl(struct file *file, unsigned int cmd, 22983ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox unsigned long arg) 230b9d36b851a5085d35b9598235a456604532f306eRussell King{ 231b9d36b851a5085d35b9598235a456604532f306eRussell King struct mpcore_wdt *wdt = file->private_data; 232b9d36b851a5085d35b9598235a456604532f306eRussell King int ret; 233b9d36b851a5085d35b9598235a456604532f306eRussell King union { 234b9d36b851a5085d35b9598235a456604532f306eRussell King struct watchdog_info ident; 235b9d36b851a5085d35b9598235a456604532f306eRussell King int i; 236b9d36b851a5085d35b9598235a456604532f306eRussell King } uarg; 237b9d36b851a5085d35b9598235a456604532f306eRussell King 238b9d36b851a5085d35b9598235a456604532f306eRussell King if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg)) 239795b89d207d8ff5397f9ff1f4d44662aa7c821fcSamuel Tardieu return -ENOTTY; 240b9d36b851a5085d35b9598235a456604532f306eRussell King 241b9d36b851a5085d35b9598235a456604532f306eRussell King if (_IOC_DIR(cmd) & _IOC_WRITE) { 242b9d36b851a5085d35b9598235a456604532f306eRussell King ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd)); 243b9d36b851a5085d35b9598235a456604532f306eRussell King if (ret) 244b9d36b851a5085d35b9598235a456604532f306eRussell King return -EFAULT; 245b9d36b851a5085d35b9598235a456604532f306eRussell King } 246b9d36b851a5085d35b9598235a456604532f306eRussell King 247b9d36b851a5085d35b9598235a456604532f306eRussell King switch (cmd) { 248b9d36b851a5085d35b9598235a456604532f306eRussell King case WDIOC_GETSUPPORT: 249b9d36b851a5085d35b9598235a456604532f306eRussell King uarg.ident = ident; 250b9d36b851a5085d35b9598235a456604532f306eRussell King ret = 0; 251b9d36b851a5085d35b9598235a456604532f306eRussell King break; 252b9d36b851a5085d35b9598235a456604532f306eRussell King 2530c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck case WDIOC_GETSTATUS: 2540c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck case WDIOC_GETBOOTSTATUS: 2550c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck uarg.i = 0; 2560c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck ret = 0; 2570c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck break; 2580c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck 259b9d36b851a5085d35b9598235a456604532f306eRussell King case WDIOC_SETOPTIONS: 260b9d36b851a5085d35b9598235a456604532f306eRussell King ret = -EINVAL; 261b9d36b851a5085d35b9598235a456604532f306eRussell King if (uarg.i & WDIOS_DISABLECARD) { 262b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_stop(wdt); 263b9d36b851a5085d35b9598235a456604532f306eRussell King ret = 0; 264b9d36b851a5085d35b9598235a456604532f306eRussell King } 265b9d36b851a5085d35b9598235a456604532f306eRussell King if (uarg.i & WDIOS_ENABLECARD) { 266b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_start(wdt); 267b9d36b851a5085d35b9598235a456604532f306eRussell King ret = 0; 268b9d36b851a5085d35b9598235a456604532f306eRussell King } 269b9d36b851a5085d35b9598235a456604532f306eRussell King break; 270b9d36b851a5085d35b9598235a456604532f306eRussell King 271b9d36b851a5085d35b9598235a456604532f306eRussell King case WDIOC_KEEPALIVE: 272b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_keepalive(wdt); 273b9d36b851a5085d35b9598235a456604532f306eRussell King ret = 0; 274b9d36b851a5085d35b9598235a456604532f306eRussell King break; 275b9d36b851a5085d35b9598235a456604532f306eRussell King 276b9d36b851a5085d35b9598235a456604532f306eRussell King case WDIOC_SETTIMEOUT: 277b9d36b851a5085d35b9598235a456604532f306eRussell King ret = mpcore_wdt_set_heartbeat(uarg.i); 278b9d36b851a5085d35b9598235a456604532f306eRussell King if (ret) 279b9d36b851a5085d35b9598235a456604532f306eRussell King break; 280b9d36b851a5085d35b9598235a456604532f306eRussell King 281b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_keepalive(wdt); 282b9d36b851a5085d35b9598235a456604532f306eRussell King /* Fall */ 283b9d36b851a5085d35b9598235a456604532f306eRussell King case WDIOC_GETTIMEOUT: 284b9d36b851a5085d35b9598235a456604532f306eRussell King uarg.i = mpcore_margin; 285b9d36b851a5085d35b9598235a456604532f306eRussell King ret = 0; 286b9d36b851a5085d35b9598235a456604532f306eRussell King break; 287b9d36b851a5085d35b9598235a456604532f306eRussell King 288b9d36b851a5085d35b9598235a456604532f306eRussell King default: 289795b89d207d8ff5397f9ff1f4d44662aa7c821fcSamuel Tardieu return -ENOTTY; 290b9d36b851a5085d35b9598235a456604532f306eRussell King } 291b9d36b851a5085d35b9598235a456604532f306eRussell King 292b9d36b851a5085d35b9598235a456604532f306eRussell King if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { 293b9d36b851a5085d35b9598235a456604532f306eRussell King ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd)); 294b9d36b851a5085d35b9598235a456604532f306eRussell King if (ret) 295b9d36b851a5085d35b9598235a456604532f306eRussell King ret = -EFAULT; 296b9d36b851a5085d35b9598235a456604532f306eRussell King } 297b9d36b851a5085d35b9598235a456604532f306eRussell King return ret; 298b9d36b851a5085d35b9598235a456604532f306eRussell King} 299b9d36b851a5085d35b9598235a456604532f306eRussell King 300b9d36b851a5085d35b9598235a456604532f306eRussell King/* 301b9d36b851a5085d35b9598235a456604532f306eRussell King * System shutdown handler. Turn off the watchdog if we're 302b9d36b851a5085d35b9598235a456604532f306eRussell King * restarting or halting the system. 303b9d36b851a5085d35b9598235a456604532f306eRussell King */ 304aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic void mpcore_wdt_shutdown(struct platform_device *pdev) 305b9d36b851a5085d35b9598235a456604532f306eRussell King{ 306aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar struct mpcore_wdt *wdt = platform_get_drvdata(pdev); 307b9d36b851a5085d35b9598235a456604532f306eRussell King 308b9d36b851a5085d35b9598235a456604532f306eRussell King if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT) 309b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_stop(wdt); 310b9d36b851a5085d35b9598235a456604532f306eRussell King} 311b9d36b851a5085d35b9598235a456604532f306eRussell King 312b9d36b851a5085d35b9598235a456604532f306eRussell King/* 313b9d36b851a5085d35b9598235a456604532f306eRussell King * Kernel Interfaces 314b9d36b851a5085d35b9598235a456604532f306eRussell King */ 31562322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations mpcore_wdt_fops = { 316b9d36b851a5085d35b9598235a456604532f306eRussell King .owner = THIS_MODULE, 317b9d36b851a5085d35b9598235a456604532f306eRussell King .llseek = no_llseek, 318b9d36b851a5085d35b9598235a456604532f306eRussell King .write = mpcore_wdt_write, 31983ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox .unlocked_ioctl = mpcore_wdt_ioctl, 320b9d36b851a5085d35b9598235a456604532f306eRussell King .open = mpcore_wdt_open, 321b9d36b851a5085d35b9598235a456604532f306eRussell King .release = mpcore_wdt_release, 322b9d36b851a5085d35b9598235a456604532f306eRussell King}; 323b9d36b851a5085d35b9598235a456604532f306eRussell King 324b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic struct miscdevice mpcore_wdt_miscdev = { 325b9d36b851a5085d35b9598235a456604532f306eRussell King .minor = WATCHDOG_MINOR, 326b9d36b851a5085d35b9598235a456604532f306eRussell King .name = "watchdog", 327b9d36b851a5085d35b9598235a456604532f306eRussell King .fops = &mpcore_wdt_fops, 328b9d36b851a5085d35b9598235a456604532f306eRussell King}; 329b9d36b851a5085d35b9598235a456604532f306eRussell King 330aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int __devinit mpcore_wdt_probe(struct platform_device *pdev) 331b9d36b851a5085d35b9598235a456604532f306eRussell King{ 332b9d36b851a5085d35b9598235a456604532f306eRussell King struct mpcore_wdt *wdt; 333b9d36b851a5085d35b9598235a456604532f306eRussell King struct resource *res; 334b9d36b851a5085d35b9598235a456604532f306eRussell King int ret; 335b9d36b851a5085d35b9598235a456604532f306eRussell King 336b9d36b851a5085d35b9598235a456604532f306eRussell King /* We only accept one device, and it must have an id of -1 */ 337aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar if (pdev->id != -1) 338b9d36b851a5085d35b9598235a456604532f306eRussell King return -ENODEV; 339b9d36b851a5085d35b9598235a456604532f306eRussell King 340aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 34175f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar if (!res) 34275f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar return -ENODEV; 343b9d36b851a5085d35b9598235a456604532f306eRussell King 34475f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL); 34575f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar if (!wdt) 34675f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar return -ENOMEM; 347b9d36b851a5085d35b9598235a456604532f306eRussell King 348aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar wdt->dev = &pdev->dev; 349aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar wdt->irq = platform_get_irq(pdev, 0); 35060a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar if (wdt->irq >= 0) { 35160a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0, 35260a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar "mpcore_wdt", wdt); 35360a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar if (ret) { 35460a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar dev_printk(KERN_ERR, wdt->dev, 35560a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar "cannot register IRQ%d for watchdog\n", 35660a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar wdt->irq); 35760a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar return ret; 35860a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar } 359b9d36b851a5085d35b9598235a456604532f306eRussell King } 360b9d36b851a5085d35b9598235a456604532f306eRussell King 36175f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res)); 36275f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar if (!wdt->base) 36375f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar return -ENOMEM; 36475f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar 365aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar mpcore_wdt_miscdev.parent = &pdev->dev; 366b9d36b851a5085d35b9598235a456604532f306eRussell King ret = misc_register(&mpcore_wdt_miscdev); 367b9d36b851a5085d35b9598235a456604532f306eRussell King if (ret) { 36898af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar dev_printk(KERN_ERR, wdt->dev, 36983ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox "cannot register miscdev on minor=%d (err=%d)\n", 37083ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox WATCHDOG_MINOR, ret); 37175f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar return ret; 372b9d36b851a5085d35b9598235a456604532f306eRussell King } 373b9d36b851a5085d35b9598235a456604532f306eRussell King 374b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_stop(wdt); 375aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar platform_set_drvdata(pdev, wdt); 376aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar mpcore_wdt_pdev = pdev; 377b9d36b851a5085d35b9598235a456604532f306eRussell King 378b9d36b851a5085d35b9598235a456604532f306eRussell King return 0; 379b9d36b851a5085d35b9598235a456604532f306eRussell King} 380b9d36b851a5085d35b9598235a456604532f306eRussell King 381aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int __devexit mpcore_wdt_remove(struct platform_device *pdev) 382b9d36b851a5085d35b9598235a456604532f306eRussell King{ 383aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar platform_set_drvdata(pdev, NULL); 384b9d36b851a5085d35b9598235a456604532f306eRussell King 385b9d36b851a5085d35b9598235a456604532f306eRussell King misc_deregister(&mpcore_wdt_miscdev); 386b9d36b851a5085d35b9598235a456604532f306eRussell King 387aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar mpcore_wdt_pdev = NULL; 388b9d36b851a5085d35b9598235a456604532f306eRussell King 389b9d36b851a5085d35b9598235a456604532f306eRussell King return 0; 390b9d36b851a5085d35b9598235a456604532f306eRussell King} 391b9d36b851a5085d35b9598235a456604532f306eRussell King 392641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#ifdef CONFIG_PM 393aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg) 394641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham{ 395aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar struct mpcore_wdt *wdt = platform_get_drvdata(pdev); 396641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham mpcore_wdt_stop(wdt); /* Turn the WDT off */ 397641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham return 0; 398641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham} 399641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham 400aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int mpcore_wdt_resume(struct platform_device *pdev) 401641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham{ 402aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar struct mpcore_wdt *wdt = platform_get_drvdata(pdev); 403641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham /* re-activate timer */ 404641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham if (test_bit(0, &wdt->timer_alive)) 405641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham mpcore_wdt_start(wdt); 406641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham return 0; 407641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham} 408641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#else 409641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#define mpcore_wdt_suspend NULL 410641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#define mpcore_wdt_resume NULL 411641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#endif 412641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham 413f37d193c7c150c40059c7ce5de34e8b28a9cd4aeKay Sievers/* work with hotplug and coldplug */ 414f37d193c7c150c40059c7ce5de34e8b28a9cd4aeKay SieversMODULE_ALIAS("platform:mpcore_wdt"); 415f37d193c7c150c40059c7ce5de34e8b28a9cd4aeKay Sievers 4163ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell Kingstatic struct platform_driver mpcore_wdt_driver = { 417b9d36b851a5085d35b9598235a456604532f306eRussell King .probe = mpcore_wdt_probe, 418b9d36b851a5085d35b9598235a456604532f306eRussell King .remove = __devexit_p(mpcore_wdt_remove), 419641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham .suspend = mpcore_wdt_suspend, 420641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham .resume = mpcore_wdt_resume, 421b9d36b851a5085d35b9598235a456604532f306eRussell King .shutdown = mpcore_wdt_shutdown, 4223ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King .driver = { 4233ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King .owner = THIS_MODULE, 4243ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King .name = "mpcore_wdt", 4253ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King }, 426b9d36b851a5085d35b9598235a456604532f306eRussell King}; 427b9d36b851a5085d35b9598235a456604532f306eRussell King 428b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int __init mpcore_wdt_init(void) 429b9d36b851a5085d35b9598235a456604532f306eRussell King{ 430b9d36b851a5085d35b9598235a456604532f306eRussell King /* 431b9d36b851a5085d35b9598235a456604532f306eRussell King * Check that the margin value is within it's range; 432b9d36b851a5085d35b9598235a456604532f306eRussell King * if not reset to the default 433b9d36b851a5085d35b9598235a456604532f306eRussell King */ 434b9d36b851a5085d35b9598235a456604532f306eRussell King if (mpcore_wdt_set_heartbeat(mpcore_margin)) { 435b9d36b851a5085d35b9598235a456604532f306eRussell King mpcore_wdt_set_heartbeat(TIMER_MARGIN); 43627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n", 437b9d36b851a5085d35b9598235a456604532f306eRussell King TIMER_MARGIN); 438b9d36b851a5085d35b9598235a456604532f306eRussell King } 439b9d36b851a5085d35b9598235a456604532f306eRussell King 44027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n", 44127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches mpcore_noboot, mpcore_margin, nowayout); 442b9d36b851a5085d35b9598235a456604532f306eRussell King 4433ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King return platform_driver_register(&mpcore_wdt_driver); 444b9d36b851a5085d35b9598235a456604532f306eRussell King} 445b9d36b851a5085d35b9598235a456604532f306eRussell King 446b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void __exit mpcore_wdt_exit(void) 447b9d36b851a5085d35b9598235a456604532f306eRussell King{ 4483ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King platform_driver_unregister(&mpcore_wdt_driver); 449b9d36b851a5085d35b9598235a456604532f306eRussell King} 450b9d36b851a5085d35b9598235a456604532f306eRussell King 451b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_init(mpcore_wdt_init); 452b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_exit(mpcore_wdt_exit); 453b9d36b851a5085d35b9598235a456604532f306eRussell King 454b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_AUTHOR("ARM Limited"); 455b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_DESCRIPTION("MPcore Watchdog Device Driver"); 456b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_LICENSE("GPL"); 457b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 458