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/io.h>
160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/clk.h>
170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/kernel.h>
180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/miscdevice.h>
190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/module.h>
200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/moduleparam.h>
210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/platform_device.h>
225a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
230400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/interrupt.h>
240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/types.h>
250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/watchdog.h>
260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#include <linux/uaccess.h>
270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define REG_WTCR		0x1c
290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTCLK			(0x01 << 10)
300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTE			(0x01 << 7)	/*wdt enable*/
310400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTIS			(0x03 << 4)
320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTIF			(0x01 << 3)
330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTRF			(0x01 << 2)
340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTRE			(0x01 << 1)
350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WTR			(0x01 << 0)
360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun/*
370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * The watchdog time interval can be calculated via following formula:
380400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * WTIS		real time interval (formula)
390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x00		((2^ 14 ) * ((external crystal freq) / 256))seconds
400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x01		((2^ 16 ) * ((external crystal freq) / 256))seconds
410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x02		((2^ 18 ) * ((external crystal freq) / 256))seconds
420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x03		((2^ 20 ) * ((external crystal freq) / 256))seconds
430400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun *
440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * The external crystal freq is 15Mhz in the nuc900 evaluation board.
450400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
460400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun * 0x03 = +- 16.92 seconds..
470400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun */
480400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WDT_HW_TIMEOUT		0x02
490400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WDT_TIMEOUT		(HZ/2)
500400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun#define WDT_HEARTBEAT		15
510400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
520400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int heartbeat = WDT_HEARTBEAT;
530400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunmodule_param(heartbeat, int, 0);
540400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
550400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
560400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
5786a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
5886a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
590400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
600400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
610400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
620400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstruct nuc900_wdt {
630400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct clk	 *wdt_clock;
640400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct platform_device *pdev;
650400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	void __iomem	 *wdt_base;
660400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	char		 expect_close;
670400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	struct timer_list timer;
680400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spinlock_t       wdt_lock;
690400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned long next_heartbeat;
700400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
710400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
720400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic unsigned long nuc900wdt_busy;
73e352829a67c1a80c73dfad33ba9dca8ddf2ef0fdAxel Linstatic struct nuc900_wdt *nuc900_wdt;
740400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
750400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_keepalive(void)
760400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
770400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned int val;
780400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
790400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock(&nuc900_wdt->wdt_lock);
800400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
810400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
820400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val |= (WTR | WTIF);
830400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
840400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
850400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_unlock(&nuc900_wdt->wdt_lock);
860400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
870400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
880400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_start(void)
890400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
900400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned int val;
910400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
920400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock(&nuc900_wdt->wdt_lock);
930400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
940400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
950400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val |= (WTRE | WTE | WTR | WTCLK | WTIF);
960400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val &= ~WTIS;
970400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val |= (WDT_HW_TIMEOUT << 0x04);
980400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
990400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1000400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_unlock(&nuc900_wdt->wdt_lock);
1010400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1020400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
1030400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
1040400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1050400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1060400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_stop(void)
1070400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1080400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	unsigned int val;
1090400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1100400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	del_timer(&nuc900_wdt->timer);
1110400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1120400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_lock(&nuc900_wdt->wdt_lock);
1130400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1140400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
1150400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	val &= ~WTE;
1160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
1170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	spin_unlock(&nuc900_wdt->wdt_lock);
1190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic inline void nuc900_wdt_ping(void)
1220400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1230400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
1240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int nuc900_wdt_open(struct inode *inode, struct file *file)
1270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (test_and_set_bit(0, &nuc900wdt_busy))
1300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return -EBUSY;
1310400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt_start();
1330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return nonseekable_open(inode, file);
1350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic int nuc900_wdt_close(struct inode *inode, struct file *file)
1380400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (nuc900_wdt->expect_close == 42)
1400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_stop();
1410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	else {
1420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_crit(&nuc900_wdt->pdev->dev,
1430400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			"Unexpected close, not stopping watchdog!\n");
1440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_ping();
1450400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
1460400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1470400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt->expect_close = 0;
1480400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clear_bit(0, &nuc900wdt_busy);
1490400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return 0;
1500400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1510400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1520400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic const struct watchdog_info nuc900_wdt_info = {
1530400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.identity	= "nuc900 watchdog",
1540400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
1550400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun						WDIOF_MAGICCLOSE,
1560400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
1570400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1580400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic long nuc900_wdt_ioctl(struct file *file,
1590400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun					unsigned int cmd, unsigned long arg)
1600400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1610400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	void __user *argp = (void __user *)arg;
1620400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	int __user *p = argp;
1630400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	int new_value;
1640400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1650400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	switch (cmd) {
1660400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETSUPPORT:
1670400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return copy_to_user(argp, &nuc900_wdt_info,
1680400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
1690400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETSTATUS:
1700400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETBOOTSTATUS:
1710400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return put_user(0, p);
1720400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1730400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_KEEPALIVE:
1740400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_ping();
1750400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return 0;
1760400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1770400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_SETTIMEOUT:
1780400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		if (get_user(new_value, p))
1790400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			return -EFAULT;
1800400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1810400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		heartbeat = new_value;
1820400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_ping();
1830400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1840400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return put_user(new_value, p);
1850400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	case WDIOC_GETTIMEOUT:
1860400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return put_user(heartbeat, p);
1870400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	default:
1880400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return -ENOTTY;
1890400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
1900400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
1910400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1920400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
1930400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun						size_t len, loff_t *ppos)
1940400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
1950400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (!len)
1960400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		return 0;
1970400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
1980400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	/* Scan for magic character */
1990400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (!nowayout) {
2000400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		size_t i;
2010400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2020400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt->expect_close = 0;
2030400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2040400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		for (i = 0; i < len; i++) {
2050400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			char c;
2060400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			if (get_user(c, data + i))
2070400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				return -EFAULT;
2080400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			if (c == 'V') {
2090400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				nuc900_wdt->expect_close = 42;
2100400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun				break;
2110400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			}
2120400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		}
2130400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2140400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2150400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	nuc900_wdt_ping();
2160400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return len;
2170400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
2180400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2190400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic void nuc900_wdt_timer_ping(unsigned long data)
2200400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
2210400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
2220400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		nuc900_wdt_keepalive();
2230400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
2240400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	} else
2250400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
2260400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
2270400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2280400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic const struct file_operations nuc900wdt_fops = {
2290400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.owner		= THIS_MODULE,
2300400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.llseek		= no_llseek,
2310400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.unlocked_ioctl	= nuc900_wdt_ioctl,
2320400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.open		= nuc900_wdt_open,
2330400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.release	= nuc900_wdt_close,
2340400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.write		= nuc900_wdt_write,
2350400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
2360400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2370400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic struct miscdevice nuc900wdt_miscdev = {
2380400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.minor		= WATCHDOG_MINOR,
2390400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.name		= "watchdog",
2400400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.fops		= &nuc900wdt_fops,
2410400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
2420400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2432d991a164a61858012651e13c59521975504e260Bill Pembertonstatic int nuc900wdt_probe(struct platform_device *pdev)
2440400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
2453666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han	struct resource *res;
2460400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	int ret = 0;
2470400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2483666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han	nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt),
2493666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han				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
2573666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2583666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han	nuc900_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
2593666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han	if (IS_ERR(nuc900_wdt->wdt_base))
2603666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han		return PTR_ERR(nuc900_wdt->wdt_base);
2610400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2623666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han	nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL);
2630400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	if (IS_ERR(nuc900_wdt->wdt_clock)) {
2640400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "failed to find watchdog clock source\n");
2653666eb028827c3a9df942cbc0a16388898c5a686Jingoo Han		return PTR_ERR(nuc900_wdt->wdt_clock);
2660400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2670400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2680400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_enable(nuc900_wdt->wdt_clock);
2690400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2700400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
2710400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2722865e770c9dddd40676eadf7c3dfe80aee7628e4Axel Lin	ret = misc_register(&nuc900wdt_miscdev);
2732865e770c9dddd40676eadf7c3dfe80aee7628e4Axel Lin	if (ret) {
2740400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
2750400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun			WATCHDOG_MINOR, ret);
2760400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		goto err_clk;
2770400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	}
2780400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2790400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return 0;
2800400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2810400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunerr_clk:
2820400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_disable(nuc900_wdt->wdt_clock);
2830400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return ret;
2840400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
2850400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2864b12b896c27c3b54592816606679f5b02f638930Bill Pembertonstatic int nuc900wdt_remove(struct platform_device *pdev)
2870400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun{
2880400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	misc_deregister(&nuc900wdt_miscdev);
2890400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2900400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	clk_disable(nuc900_wdt->wdt_clock);
2910400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2920400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	return 0;
2930400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun}
2940400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
2950400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunstatic struct platform_driver nuc900wdt_driver = {
2960400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.probe		= nuc900wdt_probe,
29782268714bdf06bc06135efb707a9de590ab2d294Bill Pemberton	.remove		= nuc900wdt_remove,
2980400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	.driver		= {
2990400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		.name	= "nuc900-wdt",
3000400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun		.owner	= THIS_MODULE,
3010400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun	},
3020400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun};
3030400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
304b8ec61189f3b4cd9d1b2856342f5d7676151d01cAxel Linmodule_platform_driver(nuc900wdt_driver);
3050400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShun
3060400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
3070400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_DESCRIPTION("Watchdog driver for NUC900");
3080400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_LICENSE("GPL");
3090400e3134b03336617138f9ebf2cd0f117ceef20Wan ZongShunMODULE_ALIAS("platform:nuc900-wdt");
310