11e6d320f40685694708cef872edb10f4f9175989Bryan Wu/* 21e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Blackfin On-Chip Watchdog Driver 31e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 41e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Originally based on softdog.c 53dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger * Copyright 2006-2010 Analog Devices Inc. 61e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Copyright 2006-2007 Michele d'Amico 729fa0586de4fe518f122a915b8c6e92d12e8ca7fAlan Cox * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk> 81e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 91e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Enter bugs at http://blackfin.uclinux.org/ 101e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 111e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Licensed under the GPL-2 or later. 121e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 131e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches 161e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/platform_device.h> 171e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/module.h> 181e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/moduleparam.h> 191e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/types.h> 201e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/timer.h> 211e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/miscdevice.h> 221e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/watchdog.h> 231e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/fs.h> 241e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/init.h> 251e6d320f40685694708cef872edb10f4f9175989Bryan Wu#include <linux/interrupt.h> 269a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox#include <linux/uaccess.h> 27089ab0791d127e8ada526c4b4d18b7584be8acf0Wim Van Sebroeck#include <asm/blackfin.h> 2842bd5d499455fe4235bb82cffe937a4089a8bba9Mike Frysinger#include <asm/bfin_watchdog.h> 291e6d320f40685694708cef872edb10f4f9175989Bryan Wu 30a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck#define stamp(fmt, args...) \ 31a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) 321e6d320f40685694708cef872edb10f4f9175989Bryan Wu#define stampit() stamp("here i am") 331e6d320f40685694708cef872edb10f4f9175989Bryan Wu 341e6d320f40685694708cef872edb10f4f9175989Bryan Wu#define WATCHDOG_NAME "bfin-wdt" 351e6d320f40685694708cef872edb10f4f9175989Bryan Wu 361e6d320f40685694708cef872edb10f4f9175989Bryan Wu/* The BF561 has two watchdogs (one per core), but since Linux 371e6d320f40685694708cef872edb10f4f9175989Bryan Wu * only runs on core A, we'll just work with that one. 381e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 391e6d320f40685694708cef872edb10f4f9175989Bryan Wu#ifdef BF561_FAMILY 401e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_read_WDOG_CTL() bfin_read_WDOGA_CTL() 411e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_read_WDOG_CNT() bfin_read_WDOGA_CNT() 421e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_read_WDOG_STAT() bfin_read_WDOGA_STAT() 431e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_write_WDOG_CTL(x) bfin_write_WDOGA_CTL(x) 441e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_write_WDOG_CNT(x) bfin_write_WDOGA_CNT(x) 451e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x) 461e6d320f40685694708cef872edb10f4f9175989Bryan Wu#endif 471e6d320f40685694708cef872edb10f4f9175989Bryan Wu 481e6d320f40685694708cef872edb10f4f9175989Bryan Wu/* some defaults */ 491e6d320f40685694708cef872edb10f4f9175989Bryan Wu#define WATCHDOG_TIMEOUT 20 501e6d320f40685694708cef872edb10f4f9175989Bryan Wu 511e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic unsigned int timeout = WATCHDOG_TIMEOUT; 5286a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT; 5342747d712de56cf2087b702d2ad90af114c53138Wim Van Sebroeckstatic const struct watchdog_info bfin_wdt_info; 541e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic unsigned long open_check; 551e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic char expect_close; 56bf6350a3dfcbd0a0811d7c210beacb66e90eca47Jiri Slabystatic DEFINE_SPINLOCK(bfin_wdt_spinlock); 571e6d320f40685694708cef872edb10f4f9175989Bryan Wu 581e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 591e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive 601e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 615f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck * The Userspace watchdog got a KeepAlive: schedule the next timeout. 621e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 631e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_keepalive(void) 641e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 651e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 661e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_write_WDOG_STAT(0); 671e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 681e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 691e6d320f40685694708cef872edb10f4f9175989Bryan Wu 701e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 711e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_stop - Stop the Watchdog 721e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 731e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Stops the on-chip watchdog. 741e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 751e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_stop(void) 761e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 771e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 781e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_write_WDOG_CTL(WDEN_DISABLE); 791e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 801e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 811e6d320f40685694708cef872edb10f4f9175989Bryan Wu 821e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 831e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_start - Start the Watchdog 841e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 851e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Starts the on-chip watchdog. Automatically loads WDOG_CNT 861e6d320f40685694708cef872edb10f4f9175989Bryan Wu * into WDOG_STAT for us. 871e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 881e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_start(void) 891e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 901e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 911e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET); 921e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 931e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 941e6d320f40685694708cef872edb10f4f9175989Bryan Wu 951e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 961e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_running - Check Watchdog status 971e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 981e6d320f40685694708cef872edb10f4f9175989Bryan Wu * See if the watchdog is running. 991e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 1001e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_running(void) 1011e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 1021e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 1031e6d320f40685694708cef872edb10f4f9175989Bryan Wu return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE); 1041e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 1051e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1061e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 1071e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_set_timeout - Set the Userspace Watchdog timeout 1081e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @t: new timeout value (in seconds) 1091e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 1101e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Translate the specified timeout in seconds into System Clock 1111e6d320f40685694708cef872edb10f4f9175989Bryan Wu * terms which is what the on-chip Watchdog requires. 1121e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 1131e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_set_timeout(unsigned long t) 1141e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 1153dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger u32 cnt, max_t, sclk; 1161e6d320f40685694708cef872edb10f4f9175989Bryan Wu unsigned long flags; 1171e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1183dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger sclk = get_sclk(); 1193dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger max_t = -1 / sclk; 1203dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger cnt = t * sclk; 1213dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt); 1221e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1233dae93ec3ee1fceec69f40ef9b97892ce62ba7a5Mike Frysinger if (t > max_t) { 12427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_warn("timeout value is too large\n"); 1251e6d320f40685694708cef872edb10f4f9175989Bryan Wu return -EINVAL; 1261e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 1271e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1281e6d320f40685694708cef872edb10f4f9175989Bryan Wu spin_lock_irqsave(&bfin_wdt_spinlock, flags); 1291e6d320f40685694708cef872edb10f4f9175989Bryan Wu { 1301e6d320f40685694708cef872edb10f4f9175989Bryan Wu int run = bfin_wdt_running(); 1311e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_stop(); 1321e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_write_WDOG_CNT(cnt); 1339a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox if (run) 1349a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox bfin_wdt_start(); 1351e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 1361e6d320f40685694708cef872edb10f4f9175989Bryan Wu spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); 1371e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1381e6d320f40685694708cef872edb10f4f9175989Bryan Wu timeout = t; 1391e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1401e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 1411e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 1421e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1431e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 1441e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_open - Open the Device 1451e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @inode: inode of device 1461e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @file: file handle of device 1471e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 1481e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Watchdog device is opened and started. 1491e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 1501e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_open(struct inode *inode, struct file *file) 1511e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 1521e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 1531e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1541e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (test_and_set_bit(0, &open_check)) 1551e6d320f40685694708cef872edb10f4f9175989Bryan Wu return -EBUSY; 1561e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1571e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (nowayout) 1581e6d320f40685694708cef872edb10f4f9175989Bryan Wu __module_get(THIS_MODULE); 1591e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1601e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_keepalive(); 1611e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_start(); 1621e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1631e6d320f40685694708cef872edb10f4f9175989Bryan Wu return nonseekable_open(inode, file); 1641e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 1651e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1661e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 1671e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_close - Close the Device 1681e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @inode: inode of device 1691e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @file: file handle of device 1701e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 1711e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Watchdog device is closed and stopped. 1721e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 1731e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_release(struct inode *inode, struct file *file) 1741e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 1751e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 1761e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1779a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox if (expect_close == 42) 1781e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_stop(); 1799a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox else { 18027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_crit("Unexpected close, not stopping watchdog!\n"); 1811e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_keepalive(); 1821e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 1831e6d320f40685694708cef872edb10f4f9175989Bryan Wu expect_close = 0; 1841e6d320f40685694708cef872edb10f4f9175989Bryan Wu clear_bit(0, &open_check); 1851e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 1861e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 1871e6d320f40685694708cef872edb10f4f9175989Bryan Wu 1881e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 1891e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_write - Write to Device 1901e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @file: file handle of device 1911e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @buf: buffer to write 1921e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @count: length of buffer 1931e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @ppos: offset 1941e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 1951e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Pings the watchdog on write. 1961e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 1971e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic ssize_t bfin_wdt_write(struct file *file, const char __user *data, 1989a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox size_t len, loff_t *ppos) 1991e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 2001e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 2011e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2021e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (len) { 2031e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (!nowayout) { 2041e6d320f40685694708cef872edb10f4f9175989Bryan Wu size_t i; 2051e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2061e6d320f40685694708cef872edb10f4f9175989Bryan Wu /* In case it was set long ago */ 2071e6d320f40685694708cef872edb10f4f9175989Bryan Wu expect_close = 0; 2081e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2091e6d320f40685694708cef872edb10f4f9175989Bryan Wu for (i = 0; i != len; i++) { 2101e6d320f40685694708cef872edb10f4f9175989Bryan Wu char c; 2111e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (get_user(c, data + i)) 2121e6d320f40685694708cef872edb10f4f9175989Bryan Wu return -EFAULT; 2131e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (c == 'V') 2141e6d320f40685694708cef872edb10f4f9175989Bryan Wu expect_close = 42; 2151e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 2161e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 2171e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_keepalive(); 2181e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 2191e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2201e6d320f40685694708cef872edb10f4f9175989Bryan Wu return len; 2211e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 2221e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2231e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 2241e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_ioctl - Query Device 2251e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @file: file handle of device 2261e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @cmd: watchdog command 2271e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @arg: argument 2281e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 2291e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Query basic information from the device or ping it, as outlined by the 2301e6d320f40685694708cef872edb10f4f9175989Bryan Wu * watchdog API. 2311e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 2329a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Coxstatic long bfin_wdt_ioctl(struct file *file, 2339a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox unsigned int cmd, unsigned long arg) 2341e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 2351e6d320f40685694708cef872edb10f4f9175989Bryan Wu void __user *argp = (void __user *)arg; 2361e6d320f40685694708cef872edb10f4f9175989Bryan Wu int __user *p = argp; 2371e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2381e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 2391e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2401e6d320f40685694708cef872edb10f4f9175989Bryan Wu switch (cmd) { 2419a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox case WDIOC_GETSUPPORT: 2429a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info))) 2439a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox return -EFAULT; 2449a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox else 2451e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 2469a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox case WDIOC_GETSTATUS: 2479a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox case WDIOC_GETBOOTSTATUS: 2489a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p); 2499a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox case WDIOC_SETOPTIONS: { 2509a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox unsigned long flags; 2519a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox int options, ret = -EINVAL; 2529a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox 2539a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox if (get_user(options, p)) 2549a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox return -EFAULT; 2559a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox 2569a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox spin_lock_irqsave(&bfin_wdt_spinlock, flags); 2579a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox if (options & WDIOS_DISABLECARD) { 2589a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox bfin_wdt_stop(); 2599a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox ret = 0; 2601e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 2619a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox if (options & WDIOS_ENABLECARD) { 2629a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox bfin_wdt_start(); 2639a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox ret = 0; 2641e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 2659a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); 2669a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox return ret; 2679a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox } 2680c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck case WDIOC_KEEPALIVE: 2690c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck bfin_wdt_keepalive(); 2700c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck return 0; 2710c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck case WDIOC_SETTIMEOUT: { 2720c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck int new_timeout; 2730c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck 2740c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck if (get_user(new_timeout, p)) 2750c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck return -EFAULT; 2760c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck if (bfin_wdt_set_timeout(new_timeout)) 2770c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck return -EINVAL; 2780c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck } 2790c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck /* Fall */ 2800c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck case WDIOC_GETTIMEOUT: 2810c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck return put_user(timeout, p); 2829a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox default: 2839a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox return -ENOTTY; 2841e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 2851e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 2861e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2871e6d320f40685694708cef872edb10f4f9175989Bryan Wu#ifdef CONFIG_PM 2881e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int state_before_suspend; 2891e6d320f40685694708cef872edb10f4f9175989Bryan Wu 2901e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 2911e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_suspend - suspend the watchdog 2921e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @pdev: device being suspended 2931e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @state: requested suspend state 2941e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 2951e6d320f40685694708cef872edb10f4f9175989Bryan Wu * Remember if the watchdog was running and stop it. 2961e6d320f40685694708cef872edb10f4f9175989Bryan Wu * TODO: is this even right? Doesn't seem to be any 2971e6d320f40685694708cef872edb10f4f9175989Bryan Wu * standard in the watchdog world ... 2981e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 2991e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state) 3001e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 3011e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 3021e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3031e6d320f40685694708cef872edb10f4f9175989Bryan Wu state_before_suspend = bfin_wdt_running(); 3041e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_stop(); 3051e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3061e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 3071e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 3081e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3091e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 3101e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_resume - resume the watchdog 3111e6d320f40685694708cef872edb10f4f9175989Bryan Wu * @pdev: device being resumed 3121e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 3131e6d320f40685694708cef872edb10f4f9175989Bryan Wu * If the watchdog was running, turn it back on. 3141e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 3151e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int bfin_wdt_resume(struct platform_device *pdev) 3161e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 3171e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 3181e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3191e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (state_before_suspend) { 3201e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_set_timeout(timeout); 3211e6d320f40685694708cef872edb10f4f9175989Bryan Wu bfin_wdt_start(); 3221e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 3231e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3241e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 3251e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 3261e6d320f40685694708cef872edb10f4f9175989Bryan Wu#else 3271e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_wdt_suspend NULL 3281e6d320f40685694708cef872edb10f4f9175989Bryan Wu# define bfin_wdt_resume NULL 3291e6d320f40685694708cef872edb10f4f9175989Bryan Wu#endif 3301e6d320f40685694708cef872edb10f4f9175989Bryan Wu 331b47a166ed0baaaa30112532bad41b21e7c5e4d31Jan Engelhardtstatic const struct file_operations bfin_wdt_fops = { 3329a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox .owner = THIS_MODULE, 3339a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox .llseek = no_llseek, 3345f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck .write = bfin_wdt_write, 3359a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox .unlocked_ioctl = bfin_wdt_ioctl, 3369a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox .open = bfin_wdt_open, 3379a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox .release = bfin_wdt_release, 3381e6d320f40685694708cef872edb10f4f9175989Bryan Wu}; 3391e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3401e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic struct miscdevice bfin_wdt_miscdev = { 3411e6d320f40685694708cef872edb10f4f9175989Bryan Wu .minor = WATCHDOG_MINOR, 3421e6d320f40685694708cef872edb10f4f9175989Bryan Wu .name = "watchdog", 3431e6d320f40685694708cef872edb10f4f9175989Bryan Wu .fops = &bfin_wdt_fops, 3441e6d320f40685694708cef872edb10f4f9175989Bryan Wu}; 3451e6d320f40685694708cef872edb10f4f9175989Bryan Wu 34642747d712de56cf2087b702d2ad90af114c53138Wim Van Sebroeckstatic const struct watchdog_info bfin_wdt_info = { 3471e6d320f40685694708cef872edb10f4f9175989Bryan Wu .identity = "Blackfin Watchdog", 3481e6d320f40685694708cef872edb10f4f9175989Bryan Wu .options = WDIOF_SETTIMEOUT | 3499a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox WDIOF_KEEPALIVEPING | 3509a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox WDIOF_MAGICCLOSE, 3511e6d320f40685694708cef872edb10f4f9175989Bryan Wu}; 3521e6d320f40685694708cef872edb10f4f9175989Bryan Wu 3531e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 35493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * bfin_wdt_probe - Initialize module 3551e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 356168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck * Registers the misc device. Actual device 3571e6d320f40685694708cef872edb10f4f9175989Bryan Wu * initialization is handled by bfin_wdt_open(). 3581e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 3592d991a164a61858012651e13c59521975504e260Bill Pembertonstatic int bfin_wdt_probe(struct platform_device *pdev) 36093539b194696a6291e6895be07d4241c8d972c4bMike Frysinger{ 36193539b194696a6291e6895be07d4241c8d972c4bMike Frysinger int ret; 36293539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 36393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger ret = misc_register(&bfin_wdt_miscdev); 36493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger if (ret) { 36527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("cannot register miscdev on minor=%d (err=%d)\n", 36627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches WATCHDOG_MINOR, ret); 36793539b194696a6291e6895be07d4241c8d972c4bMike Frysinger return ret; 36893539b194696a6291e6895be07d4241c8d972c4bMike Frysinger } 36993539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 37027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_info("initialized: timeout=%d sec (nowayout=%d)\n", 37127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches timeout, nowayout); 37293539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 37393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger return 0; 37493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger} 37593539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 37693539b194696a6291e6895be07d4241c8d972c4bMike Frysinger/** 37793539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * bfin_wdt_remove - Initialize module 37893539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * 379168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck * Unregisters the misc device. Actual device 38093539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * deinitialization is handled by bfin_wdt_close(). 38193539b194696a6291e6895be07d4241c8d972c4bMike Frysinger */ 3824b12b896c27c3b54592816606679f5b02f638930Bill Pembertonstatic int bfin_wdt_remove(struct platform_device *pdev) 38393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger{ 38493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger misc_deregister(&bfin_wdt_miscdev); 38593539b194696a6291e6895be07d4241c8d972c4bMike Frysinger return 0; 38693539b194696a6291e6895be07d4241c8d972c4bMike Frysinger} 38793539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 388168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck/** 389168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck * bfin_wdt_shutdown - Soft Shutdown Handler 390168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck * 391168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck * Handles the soft shutdown event. 392168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck */ 393168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeckstatic void bfin_wdt_shutdown(struct platform_device *pdev) 394168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck{ 395168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck stampit(); 396168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck 397168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck bfin_wdt_stop(); 398168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck} 399168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck 40093539b194696a6291e6895be07d4241c8d972c4bMike Frysingerstatic struct platform_device *bfin_wdt_device; 40193539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 40293539b194696a6291e6895be07d4241c8d972c4bMike Frysingerstatic struct platform_driver bfin_wdt_driver = { 40393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger .probe = bfin_wdt_probe, 40482268714bdf06bc06135efb707a9de590ab2d294Bill Pemberton .remove = bfin_wdt_remove, 405168b5251adddb1554926cfb94f79a8a28bc6ebe5Wim Van Sebroeck .shutdown = bfin_wdt_shutdown, 40693539b194696a6291e6895be07d4241c8d972c4bMike Frysinger .suspend = bfin_wdt_suspend, 40793539b194696a6291e6895be07d4241c8d972c4bMike Frysinger .resume = bfin_wdt_resume, 40893539b194696a6291e6895be07d4241c8d972c4bMike Frysinger .driver = { 40993539b194696a6291e6895be07d4241c8d972c4bMike Frysinger .name = WATCHDOG_NAME, 41093539b194696a6291e6895be07d4241c8d972c4bMike Frysinger .owner = THIS_MODULE, 41193539b194696a6291e6895be07d4241c8d972c4bMike Frysinger }, 41293539b194696a6291e6895be07d4241c8d972c4bMike Frysinger}; 41393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger 41493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger/** 41593539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * bfin_wdt_init - Initialize module 41693539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * 41793539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * Checks the module params and registers the platform device & driver. 41893539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * Real work is in the platform probe function. 41993539b194696a6291e6895be07d4241c8d972c4bMike Frysinger */ 4201e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic int __init bfin_wdt_init(void) 4211e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 4221e6d320f40685694708cef872edb10f4f9175989Bryan Wu int ret; 4231e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4241e6d320f40685694708cef872edb10f4f9175989Bryan Wu stampit(); 4251e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4261e6d320f40685694708cef872edb10f4f9175989Bryan Wu /* Check that the timeout value is within range */ 4271e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (bfin_wdt_set_timeout(timeout)) 4281e6d320f40685694708cef872edb10f4f9175989Bryan Wu return -EINVAL; 4291e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4301e6d320f40685694708cef872edb10f4f9175989Bryan Wu /* Since this is an on-chip device and needs no board-specific 4311e6d320f40685694708cef872edb10f4f9175989Bryan Wu * resources, we'll handle all the platform device stuff here. 4321e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 43393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger ret = platform_driver_register(&bfin_wdt_driver); 4341e6d320f40685694708cef872edb10f4f9175989Bryan Wu if (ret) { 43527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("unable to register driver\n"); 4361e6d320f40685694708cef872edb10f4f9175989Bryan Wu return ret; 4371e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 4381e6d320f40685694708cef872edb10f4f9175989Bryan Wu 439a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME, 440a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck -1, NULL, 0); 44193539b194696a6291e6895be07d4241c8d972c4bMike Frysinger if (IS_ERR(bfin_wdt_device)) { 44227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches pr_err("unable to register device\n"); 44393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger platform_driver_unregister(&bfin_wdt_driver); 44493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger return PTR_ERR(bfin_wdt_device); 4451e6d320f40685694708cef872edb10f4f9175989Bryan Wu } 4461e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4471e6d320f40685694708cef872edb10f4f9175989Bryan Wu return 0; 4481e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 4491e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4501e6d320f40685694708cef872edb10f4f9175989Bryan Wu/** 4511e6d320f40685694708cef872edb10f4f9175989Bryan Wu * bfin_wdt_exit - Deinitialize module 4521e6d320f40685694708cef872edb10f4f9175989Bryan Wu * 45393539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * Back out the platform device & driver steps. Real work is in the 45493539b194696a6291e6895be07d4241c8d972c4bMike Frysinger * platform remove function. 4551e6d320f40685694708cef872edb10f4f9175989Bryan Wu */ 4561e6d320f40685694708cef872edb10f4f9175989Bryan Wustatic void __exit bfin_wdt_exit(void) 4571e6d320f40685694708cef872edb10f4f9175989Bryan Wu{ 45893539b194696a6291e6895be07d4241c8d972c4bMike Frysinger platform_device_unregister(bfin_wdt_device); 45993539b194696a6291e6895be07d4241c8d972c4bMike Frysinger platform_driver_unregister(&bfin_wdt_driver); 4601e6d320f40685694708cef872edb10f4f9175989Bryan Wu} 4611e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4621e6d320f40685694708cef872edb10f4f9175989Bryan Wumodule_init(bfin_wdt_init); 4631e6d320f40685694708cef872edb10f4f9175989Bryan Wumodule_exit(bfin_wdt_exit); 4641e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4651e6d320f40685694708cef872edb10f4f9175989Bryan WuMODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>"); 4661e6d320f40685694708cef872edb10f4f9175989Bryan WuMODULE_DESCRIPTION("Blackfin Watchdog Device Driver"); 4671e6d320f40685694708cef872edb10f4f9175989Bryan WuMODULE_LICENSE("GPL"); 4681e6d320f40685694708cef872edb10f4f9175989Bryan Wu 4691e6d320f40685694708cef872edb10f4f9175989Bryan Wumodule_param(timeout, uint, 0); 4709a5f50d34b0927d2f43549b6d172b8f458b6b620Alan CoxMODULE_PARM_DESC(timeout, 4719a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" 4729a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 4731e6d320f40685694708cef872edb10f4f9175989Bryan Wu 47486a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0); 4759a5f50d34b0927d2f43549b6d172b8f458b6b620Alan CoxMODULE_PARM_DESC(nowayout, 4769a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox "Watchdog cannot be stopped once started (default=" 4779a5f50d34b0927d2f43549b6d172b8f458b6b620Alan Cox __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 478