[go: nahoru, domu]

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