14a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/*
24a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * drivers/char/watchdog/sp805-wdt.c
34a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR *
44a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * Watchdog driver for ARM SP805 watchdog module
54a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR *
64a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * Copyright (C) 2010 ST Microelectronics
74a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * Viresh Kumar<viresh.kumar@st.com>
84a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR *
94a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * This file is licensed under the terms of the GNU General Public
104a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * License version 2 or later. This program is licensed "as is" without any
114a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * warranty of any kind, whether express or implied.
124a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR */
134a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
144a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/device.h>
154a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/resource.h>
164a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/amba/bus.h>
174a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/bitops.h>
184a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/clk.h>
194a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/fs.h>
204a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/init.h>
214a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/io.h>
224a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/ioport.h>
234a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/kernel.h>
244a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/math64.h>
254a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/miscdevice.h>
264a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/module.h>
274a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/moduleparam.h>
2816ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar#include <linux/pm.h>
294a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/slab.h>
304a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/spinlock.h>
314a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/types.h>
324a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/uaccess.h>
334a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#include <linux/watchdog.h>
344a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
354a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* default timeout in seconds */
364a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define DEFAULT_TIMEOUT		60
374a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
384a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define MODULE_NAME		"sp805-wdt"
394a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
404a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* watchdog register offsets and masks */
414a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTLOAD			0x000
424a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define LOAD_MIN	0x00000001
434a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define LOAD_MAX	0xFFFFFFFF
444a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTVALUE		0x004
454a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTCONTROL		0x008
464a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	/* control register masks */
474a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define	INT_ENABLE	(1 << 0)
484a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define	RESET_ENABLE	(1 << 1)
494a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTINTCLR		0x00C
504a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTRIS			0x010
514a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTMIS			0x014
524a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define INT_MASK	(1 << 0)
534a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR#define WDTLOCK			0xC00
544a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define	UNLOCK		0x1ACCE551
554a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define	LOCK		0x00000001
564a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
574a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/**
584a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR * struct sp805_wdt: sp805 wdt device structure
59bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @lock: spin lock protecting dev structure and io access
60bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @base: base address of wdt
61bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @clk: clock structure of wdt
62bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @adev: amba device structure of wdt
63bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @status: current status of wdt
64bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @load_val: load value to be set for current timeout
65bfae14b679063d85f10579bbfd532db58b122e29Viresh Kumar * @timeout: current programmed timeout
664a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR */
674a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstruct sp805_wdt {
684a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spinlock_t			lock;
694a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	void __iomem			*base;
704a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	struct clk			*clk;
714a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	struct amba_device		*adev;
724a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	unsigned long			status;
734a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define WDT_BUSY		0
744a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	#define WDT_CAN_BE_CLOSED	1
754a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	unsigned int			load_val;
764a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	unsigned int			timeout;
774a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR};
784a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
794a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* local variables */
804a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic struct sp805_wdt *wdt;
8186a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
824a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
834a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* This routine finds load value that will reset system in required timout */
844a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic void wdt_setload(unsigned int timeout)
854a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
864a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	u64 load, rate;
874a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
884a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	rate = clk_get_rate(wdt->clk);
894a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
904a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	/*
914a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	 * sp805 runs counter with given value twice, after the end of first
924a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	 * counter it gives an interrupt and then starts counter again. If
9325985edcedea6396277003854657b5f3cb31a628Lucas De Marchi	 * interrupt already occurred then it resets the system. This is why
944a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	 * load is half of what should be required.
954a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	 */
964a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	load = div_u64(rate, 2) * timeout - 1;
974a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
984a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	load = (load > LOAD_MAX) ? LOAD_MAX : load;
994a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	load = (load < LOAD_MIN) ? LOAD_MIN : load;
1004a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1014a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_lock(&wdt->lock);
1024a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt->load_val = load;
1034a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	/* roundup timeout to closest positive integer value */
1044a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
1054a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_unlock(&wdt->lock);
1064a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
1074a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1084a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* returns number of seconds left for reset to occur */
1094a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic u32 wdt_timeleft(void)
1104a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
1114a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	u64 load, rate;
1124a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1134a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	rate = clk_get_rate(wdt->clk);
1144a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1154a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_lock(&wdt->lock);
116d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	load = readl_relaxed(wdt->base + WDTVALUE);
1174a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1184a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	/*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
119d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK))
1204a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		load += wdt->load_val + 1;
1214a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_unlock(&wdt->lock);
1224a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1234a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return div_u64(load, rate);
1244a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
1254a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1264a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* enables watchdog timers reset */
1274a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic void wdt_enable(void)
1284a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
1294a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_lock(&wdt->lock);
1304a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
131d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
132d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
133d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
134d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
135d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(LOCK, wdt->base + WDTLOCK);
1364a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
137081d83a3393f65adc94fc4240b9926be3054f9dcNick Bowler	/* Flush posted writes. */
138d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	readl_relaxed(wdt->base + WDTLOCK);
1394a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_unlock(&wdt->lock);
1404a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
1414a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1424a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR/* disables watchdog timers reset */
1434a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic void wdt_disable(void)
1444a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
1454a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_lock(&wdt->lock);
1464a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
147d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
148d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(0, wdt->base + WDTCONTROL);
149d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	writel_relaxed(LOCK, wdt->base + WDTLOCK);
1504a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
151081d83a3393f65adc94fc4240b9926be3054f9dcNick Bowler	/* Flush posted writes. */
152d2e8919bcfb63b662945d0e5cb9338f0f2229d10Viresh Kumar	readl_relaxed(wdt->base + WDTLOCK);
1534a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_unlock(&wdt->lock);
1544a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
1554a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1564a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic ssize_t sp805_wdt_write(struct file *file, const char *data,
1574a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		size_t len, loff_t *ppos)
1584a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
1594a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (len) {
1604a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		if (!nowayout) {
1614a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR			size_t i;
1624a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1634a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR			clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
1644a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1654a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR			for (i = 0; i != len; i++) {
1664a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR				char c;
1674a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1684a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR				if (get_user(c, data + i))
1694a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR					return -EFAULT;
1704a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR				/* Check for Magic Close character */
1714a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR				if (c == 'V') {
1724a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR					set_bit(WDT_CAN_BE_CLOSED,
1734a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR							&wdt->status);
1744a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR					break;
1754a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR				}
1764a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR			}
1774a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		}
1784a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		wdt_enable();
1794a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
1804a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return len;
1814a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
1824a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1834a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic const struct watchdog_info ident = {
1844a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
1854a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.identity = MODULE_NAME,
1864a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR};
1874a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1884a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
1894a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		unsigned long arg)
1904a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
1914a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	int ret = -ENOTTY;
1924a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	unsigned int timeout;
1934a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
1944a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	switch (cmd) {
1954a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	case WDIOC_GETSUPPORT:
1964a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = copy_to_user((struct watchdog_info *)arg, &ident,
1974a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR				sizeof(ident)) ? -EFAULT : 0;
1984a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		break;
1994a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2004a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	case WDIOC_GETSTATUS:
2014a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = put_user(0, (int *)arg);
2024a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		break;
2034a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2044a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	case WDIOC_KEEPALIVE:
2054a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		wdt_enable();
2064a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = 0;
2074a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		break;
2084a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2094a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	case WDIOC_SETTIMEOUT:
2104a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = get_user(timeout, (unsigned int *)arg);
2114a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		if (ret)
2124a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR			break;
2134a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2144a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		wdt_setload(timeout);
2154a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2164a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		wdt_enable();
2174a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		/* Fall through */
2184a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2194a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	case WDIOC_GETTIMEOUT:
2204a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = put_user(wdt->timeout, (unsigned int *)arg);
2214a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		break;
2224a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	case WDIOC_GETTIMELEFT:
2234a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = put_user(wdt_timeleft(), (unsigned int *)arg);
2244a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		break;
2254a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
2264a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return ret;
2274a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
2284a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2294a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic int sp805_wdt_open(struct inode *inode, struct file *file)
2304a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
2314a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	int ret = 0;
2324a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2334a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (test_and_set_bit(WDT_BUSY, &wdt->status))
2344a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		return -EBUSY;
2354a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2364a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	ret = clk_enable(wdt->clk);
2374a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (ret) {
2384a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		dev_err(&wdt->adev->dev, "clock enable fail");
2394a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		goto err;
2404a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
2414a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2424a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt_enable();
2434a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2444a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	/* can not be closed, once enabled */
2454a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
2464a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return nonseekable_open(inode, file);
2474a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2484a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARerr:
2494a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	clear_bit(WDT_BUSY, &wdt->status);
2504a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return ret;
2514a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
2524a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2534a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic int sp805_wdt_release(struct inode *inode, struct file *file)
2544a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
2554a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
2564a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		clear_bit(WDT_BUSY, &wdt->status);
2574a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
2584a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		return 0;
2594a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
2604a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2614a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt_disable();
2624a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	clk_disable(wdt->clk);
2634a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	clear_bit(WDT_BUSY, &wdt->status);
2644a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2654a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return 0;
2664a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
2674a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2684a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic const struct file_operations sp805_wdt_fops = {
2694a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.owner = THIS_MODULE,
2704a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.llseek = no_llseek,
2714a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.write = sp805_wdt_write,
2724a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.unlocked_ioctl = sp805_wdt_ioctl,
2734a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.open = sp805_wdt_open,
2744a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.release = sp805_wdt_release,
2754a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR};
2764a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2774a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic struct miscdevice sp805_wdt_miscdev = {
2784a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.minor = WATCHDOG_MINOR,
2794a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.name = "watchdog",
2804a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.fops = &sp805_wdt_fops,
2814a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR};
2824a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
2834a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic int __devinit
284aa25afad2ca60d19457849ea75e9c31236f4e174Russell Kingsp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
2854a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
2864a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	int ret = 0;
2874a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
288fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar	if (!devm_request_mem_region(&adev->dev, adev->res.start,
289fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar				resource_size(&adev->res), "sp805_wdt")) {
2904a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		dev_warn(&adev->dev, "Failed to get memory region resource\n");
2914a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = -ENOENT;
2924a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		goto err;
2934a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
2944a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
295fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar	wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
2964a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (!wdt) {
2974a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		dev_warn(&adev->dev, "Kzalloc failed\n");
2984a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = -ENOMEM;
299fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar		goto err;
300fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar	}
301fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar
302fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar	wdt->base = devm_ioremap(&adev->dev, adev->res.start,
303fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar			resource_size(&adev->res));
304fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar	if (!wdt->base) {
305fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar		ret = -ENOMEM;
306fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar		dev_warn(&adev->dev, "ioremap fail\n");
307fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar		goto err;
3084a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
3094a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3104a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt->clk = clk_get(&adev->dev, NULL);
3114a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (IS_ERR(wdt->clk)) {
3124a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		dev_warn(&adev->dev, "Clock not found\n");
3134a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		ret = PTR_ERR(wdt->clk);
314fb35a5ad5b4b2c3806b52b0159f4d5a0ad205c0fViresh Kumar		goto err;
3154a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
3164a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3174a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt->adev = adev;
3184a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	spin_lock_init(&wdt->lock);
3194a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	wdt_setload(DEFAULT_TIMEOUT);
3204a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3214a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	ret = misc_register(&sp805_wdt_miscdev);
3224a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	if (ret < 0) {
3234a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		dev_warn(&adev->dev, "cannot register misc device\n");
3244a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		goto err_misc_register;
3254a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	}
3264a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3274a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	dev_info(&adev->dev, "registration successful\n");
3284a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return 0;
3294a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3304a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARerr_misc_register:
3314a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	clk_put(wdt->clk);
3324a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARerr:
3334a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	dev_err(&adev->dev, "Probe Failed!!!\n");
3344a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return ret;
3354a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
3364a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3374a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic int __devexit sp805_wdt_remove(struct amba_device *adev)
3384a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR{
3394a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	misc_deregister(&sp805_wdt_miscdev);
3404a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	clk_put(wdt->clk);
3414a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3424a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	return 0;
3434a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR}
3444a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
34516ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar#ifdef CONFIG_PM
34616ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumarstatic int sp805_wdt_suspend(struct device *dev)
34716ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar{
34816ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	if (test_bit(WDT_BUSY, &wdt->status)) {
34916ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		wdt_disable();
35016ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		clk_disable(wdt->clk);
35116ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	}
35216ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar
35316ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	return 0;
35416ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar}
35516ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar
35616ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumarstatic int sp805_wdt_resume(struct device *dev)
35716ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar{
35816ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	int ret = 0;
35916ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar
36016ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	if (test_bit(WDT_BUSY, &wdt->status)) {
36116ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		ret = clk_enable(wdt->clk);
36216ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		if (ret) {
36316ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar			dev_err(dev, "clock enable fail");
36416ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar			return ret;
36516ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		}
36616ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		wdt_enable();
36716ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	}
36816ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar
36916ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar	return ret;
37016ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar}
37116ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar#endif /* CONFIG_PM */
37216ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar
37316ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumarstatic SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend,
37416ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		sp805_wdt_resume);
37516ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar
376bb558dac6776386a4d6994540a009231dcf53ee1Nick Bowlerstatic struct amba_id sp805_wdt_ids[] = {
3774a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	{
3784a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		.id	= 0x00141805,
3794a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		.mask	= 0x00ffffff,
3804a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	},
3814a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	{ 0, 0 },
3824a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR};
3834a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
38417885b05b31c0d310f859982d2a56f167274547eDave MartinMODULE_DEVICE_TABLE(amba, sp805_wdt_ids);
38517885b05b31c0d310f859982d2a56f167274547eDave Martin
3864a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARstatic struct amba_driver sp805_wdt_driver = {
3874a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.drv = {
3884a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		.name	= MODULE_NAME,
38916ac4abe0d0ea0a8d42d6a2a7de2a4a00bbf5b40Viresh Kumar		.pm	= &sp805_wdt_dev_pm_ops,
3904a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	},
3914a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.id_table	= sp805_wdt_ids,
3924a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.probe		= sp805_wdt_probe,
3934a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR	.remove = __devexit_p(sp805_wdt_remove),
3944a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR};
3954a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
3969e5ed094c89e55fbf11d2e81d60be98eb12346c0viresh kumarmodule_amba_driver(sp805_wdt_driver);
3974a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
39886a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
3994a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARMODULE_PARM_DESC(nowayout,
4004a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR		"Set to 1 to keep watchdog running after device release");
4014a370278e1041d4c62719bcd773e9c620e775901Viresh KUMAR
4024a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARMODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
4034a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARMODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
4044a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARMODULE_LICENSE("GPL");
4054a370278e1041d4c62719bcd773e9c620e775901Viresh KUMARMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
406