nuc900_wdt.c revision 82268714bdf06bc06135efb707a9de590ab2d294
10400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun/*
20400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * Copyright (c) 2009 Nuvoton technology corporation.
30400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun *
40400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * Wan ZongShun <mcuos.com@gmail.com>
50400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun *
60400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * This program is free software; you can redistribute it and/or modify
70400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * it under the terms of the GNU General Public License as published by
80400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * the Free Software Foundation;version 2 of the License.
90400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun *
100400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun */
110400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
120400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/bitops.h>
130400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/errno.h>
140400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/fs.h>
150400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/init.h>
160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/io.h>
170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/clk.h>
180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/kernel.h>
190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/miscdevice.h>
200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/module.h>
210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/moduleparam.h>
220400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/platform_device.h>
235a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/interrupt.h>
250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/types.h>
260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/watchdog.h>
270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/uaccess.h>
280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define REG_WTCR		0x1c
300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTCLK			(0x01 << 10)
310400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTE			(0x01 << 7)	/*wdt enable*/
320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTIS			(0x03 << 4)
330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTIF			(0x01 << 3)
340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTRF			(0x01 << 2)
350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTRE			(0x01 << 1)
360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTR			(0x01 << 0)
370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun/*
380400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * The watchdog time interval can be calculated via following formula:
390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * WTIS		real time interval (formula)
400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x00		((2^ 14 ) * ((external crystal freq) / 256))seconds
410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x01		((2^ 16 ) * ((external crystal freq) / 256))seconds
420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x02		((2^ 18 ) * ((external crystal freq) / 256))seconds
430400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x03		((2^ 20 ) * ((external crystal freq) / 256))seconds
440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun *
450400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * The external crystal freq is 15Mhz in the nuc900 evaluation board.
460400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
470400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x03 = +- 16.92 seconds..
480400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun */
490400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WDT_HW_TIMEOUT		0x02
500400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WDT_TIMEOUT		(HZ/2)
510400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WDT_HEARTBEAT		15
520400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
530400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int heartbeat = WDT_HEARTBEAT;
540400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunmodule_param(heartbeat, int, 0);
550400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
560400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
570400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
5886a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
5986a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
600400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
610400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
620400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
630400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstruct nuc900_wdt {
640400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct resource  *res;
650400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct clk	 *wdt_clock;
660400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct platform_device *pdev;
670400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	void __iomem	 *wdt_base;
680400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	char		 expect_close;
690400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct timer_list timer;
700400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spinlock_t       wdt_lock;
710400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned long next_heartbeat;
720400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
730400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
740400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic unsigned long nuc900wdt_busy;
75e352829a67c1a80c73dfad33ba9dca8ddf2ef0fdAxel Linstatic struct nuc900_wdt *nuc900_wdt;
760400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
770400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_keepalive(void)
780400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
790400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned int val;
800400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
810400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock(&nuc900_wdt->wdt_lock);
820400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
830400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
840400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val |= (WTR | WTIF);
850400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
860400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
870400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_unlock(&nuc900_wdt->wdt_lock);
880400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
890400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
900400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_start(void)
910400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
920400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned int val;
930400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
940400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock(&nuc900_wdt->wdt_lock);
950400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
960400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
970400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val |= (WTRE | WTE | WTR | WTCLK | WTIF);
980400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val &= ~WTIS;
990400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val |= (WDT_HW_TIMEOUT << 0x04);
1000400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
1010400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1020400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_unlock(&nuc900_wdt->wdt_lock);
1030400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1040400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
1050400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
1060400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1070400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1080400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_stop(void)
1090400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1100400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned int val;
1110400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1120400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	del_timer(&nuc900_wdt->timer);
1130400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1140400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock(&nuc900_wdt->wdt_lock);
1150400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
1170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val &= ~WTE;
1180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
1190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_unlock(&nuc900_wdt->wdt_lock);
1210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1220400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1230400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_ping(void)
1240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
1260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int nuc900_wdt_open(struct inode *inode, struct file *file)
1290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1310400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (test_and_set_bit(0, &nuc900wdt_busy))
1320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return -EBUSY;
1330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt_start();
1350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return nonseekable_open(inode, file);
1370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1380400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int nuc900_wdt_close(struct inode *inode, struct file *file)
1400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (nuc900_wdt->expect_close == 42)
1420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_stop();
1430400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	else {
1440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_crit(&nuc900_wdt->pdev->dev,
1450400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			"Unexpected close, not stopping watchdog!\n");
1460400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_ping();
1470400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
1480400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1490400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->expect_close = 0;
1500400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clear_bit(0, &nuc900wdt_busy);
1510400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return 0;
1520400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1530400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1540400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic const struct watchdog_info nuc900_wdt_info = {
1550400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.identity	= "nuc900 watchdog",
1560400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
1570400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun						WDIOF_MAGICCLOSE,
1580400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
1590400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1600400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic long nuc900_wdt_ioctl(struct file *file,
1610400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun					unsigned int cmd, unsigned long arg)
1620400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1630400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	void __user *argp = (void __user *)arg;
1640400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	int __user *p = argp;
1650400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	int new_value;
1660400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1670400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	switch (cmd) {
1680400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETSUPPORT:
1690400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return copy_to_user(argp, &nuc900_wdt_info,
1700400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
1710400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETSTATUS:
1720400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETBOOTSTATUS:
1730400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return put_user(0, p);
1740400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1750400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_KEEPALIVE:
1760400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_ping();
1770400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return 0;
1780400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1790400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_SETTIMEOUT:
1800400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		if (get_user(new_value, p))
1810400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			return -EFAULT;
1820400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1830400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		heartbeat = new_value;
1840400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_ping();
1850400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1860400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return put_user(new_value, p);
1870400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETTIMEOUT:
1880400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return put_user(heartbeat, p);
1890400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	default:
1900400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return -ENOTTY;
1910400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
1920400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1930400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1940400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
1950400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun						size_t len, loff_t *ppos)
1960400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1970400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (!len)
1980400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return 0;
1990400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2000400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	/* Scan for magic character */
2010400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (!nowayout) {
2020400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		size_t i;
2030400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2040400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt->expect_close = 0;
2050400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2060400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		for (i = 0; i < len; i++) {
2070400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			char c;
2080400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			if (get_user(c, data + i))
2090400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				return -EFAULT;
2100400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			if (c == 'V') {
2110400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				nuc900_wdt->expect_close = 42;
2120400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				break;
2130400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			}
2140400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		}
2150400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt_ping();
2180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return len;
2190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
2200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic void nuc900_wdt_timer_ping(unsigned long data)
2220400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
2230400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
2240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_keepalive();
2250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
2260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	} else
2270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
2280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
2290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic const struct file_operations nuc900wdt_fops = {
2310400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.owner		= THIS_MODULE,
2320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.llseek		= no_llseek,
2330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.unlocked_ioctl	= nuc900_wdt_ioctl,
2340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.open		= nuc900_wdt_open,
2350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.release	= nuc900_wdt_close,
2360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.write		= nuc900_wdt_write,
2370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
2380400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic struct miscdevice nuc900wdt_miscdev = {
2400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.minor		= WATCHDOG_MINOR,
2410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.name		= "watchdog",
2420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.fops		= &nuc900wdt_fops,
2430400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
2440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2450400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int __devinit nuc900wdt_probe(struct platform_device *pdev)
2460400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
2470400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	int ret = 0;
2480400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2490400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
2500400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (!nuc900_wdt)
2510400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return -ENOMEM;
2520400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2530400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->pdev = pdev;
2540400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2550400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock_init(&nuc900_wdt->wdt_lock);
2560400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2570400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2580400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (nuc900_wdt->res == NULL) {
2590400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "no memory resource specified\n");
2600400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		ret = -ENOENT;
2610400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		goto err_get;
2620400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2630400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2640400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (!request_mem_region(nuc900_wdt->res->start,
2650400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				resource_size(nuc900_wdt->res), pdev->name)) {
2660400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "failed to get memory region\n");
2670400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		ret = -ENOENT;
2680400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		goto err_get;
2690400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2700400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2710400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
2720400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun					resource_size(nuc900_wdt->res));
2730400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (nuc900_wdt->wdt_base == NULL) {
2740400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "failed to ioremap() region\n");
2750400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		ret = -EINVAL;
2760400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		goto err_req;
2770400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2780400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2790400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
2800400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (IS_ERR(nuc900_wdt->wdt_clock)) {
2810400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "failed to find watchdog clock source\n");
2820400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		ret = PTR_ERR(nuc900_wdt->wdt_clock);
2830400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		goto err_map;
2840400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2850400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2860400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_enable(nuc900_wdt->wdt_clock);
2870400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2880400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
2890400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2902865e770c9dddd40676eadf7c3dfe80aee7628e4Axel Lin	ret = misc_register(&nuc900wdt_miscdev);
2912865e770c9dddd40676eadf7c3dfe80aee7628e4Axel Lin	if (ret) {
2920400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
2930400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			WATCHDOG_MINOR, ret);
2940400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		goto err_clk;
2950400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2960400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2970400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return 0;
2980400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2990400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunerr_clk:
3000400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_disable(nuc900_wdt->wdt_clock);
3010400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_put(nuc900_wdt->wdt_clock);
3020400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunerr_map:
3030400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	iounmap(nuc900_wdt->wdt_base);
3040400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunerr_req:
3050400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	release_mem_region(nuc900_wdt->res->start,
3060400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun					resource_size(nuc900_wdt->res));
3070400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunerr_get:
3080400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	kfree(nuc900_wdt);
3090400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return ret;
3100400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
3110400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3120400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int __devexit nuc900wdt_remove(struct platform_device *pdev)
3130400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
3140400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	misc_deregister(&nuc900wdt_miscdev);
3150400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_disable(nuc900_wdt->wdt_clock);
3170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_put(nuc900_wdt->wdt_clock);
3180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	iounmap(nuc900_wdt->wdt_base);
3200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	release_mem_region(nuc900_wdt->res->start,
3220400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun					resource_size(nuc900_wdt->res));
3230400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	kfree(nuc900_wdt);
3250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return 0;
3270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
3280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic struct platform_driver nuc900wdt_driver = {
3300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.probe		= nuc900wdt_probe,
33182268714bdf06bc06135efb707a9de590ab2d294Bill Pemberton	.remove		= nuc900wdt_remove,
3320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.driver		= {
3330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		.name	= "nuc900-wdt",
3340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		.owner	= THIS_MODULE,
3350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	},
3360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
3370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
338b8ec61189f3b4cd9d1b2856342f5d7676151d01cAxel Linmodule_platform_driver(nuc900wdt_driver);
3390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
3410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_DESCRIPTION("Watchdog driver for NUC900");
3420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_LICENSE("GPL");
3430400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
3440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_ALIAS("platform:nuc900-wdt");
345