nv_tco.c revision 4562f53940432369df88e195ef8f9b642bdf7cd6
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	nv_tco 0.01:	TCO timer driver for NV chipsets
30d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	(c) Copyright 2005 Google Inc., All Rights Reserved.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
6bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	Based off i8xx_tco.c:
7bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	(c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
8bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	Reserved.
9bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *				http://www.kernelconcepts.de
10bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
11bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	This program is free software; you can redistribute it and/or
12bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	modify it under the terms of the GNU General Public License
13bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	as published by the Free Software Foundation; either version
14bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	2 of the License, or (at your option) any later version.
15bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
16bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	TCO timer driver for NV chipsets
17bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	based on softdog.c by Alan Cox <alan@redhat.com>
18bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie */
19bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie
20bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie/*
21bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	Includes, defines, variables, module parameters, ...
22bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie */
23bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie
24bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie#include <linux/module.h>
25bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie#include <linux/moduleparam.h>
26bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie#include <linux/types.h>
270d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie#include <linux/miscdevice.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/jiffies.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/platform_device.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/uaccess.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/io.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "nv_tco.h"
3984b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Module and version information */
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TCO_VERSION "0.01"
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TCO_MODULE_NAME "NV_TCO"
43d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard#define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION
44d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard#define PFX TCO_MODULE_NAME ": "
45d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard
46585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes/* internal variables */
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int tcobase;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEFINE_SPINLOCK(tco_lock);	/* Guards the hardware */
49d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packardstatic unsigned long timer_alive;
50585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnesstatic char tco_expect_close;
51d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packardstatic struct pci_dev *tco_pci;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* the watchdog platform device */
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct platform_device *nv_tco_platform_device;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* module parameters */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WATCHDOG_HEARTBEAT 30	/* 30 sec default heartbeat (2<heartbeat<39) */
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(heartbeat, int, 0);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, "
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    "default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
62d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard
63d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packardstatic int nowayout = WATCHDOG_NOWAYOUT;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(nowayout, int, 0);
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
66d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard		" (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
67d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard
68d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard/*
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Some TCO specific functions
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7120caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholtstatic inline unsigned char seconds_to_ticks(int seconds)
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* the internal timer is stored as ticks which decrement
74398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	 * every 0.6 seconds */
75398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	return (seconds * 10) / 6;
76398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard}
77398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
78398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic void tco_timer_start(void)
79398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard{
80398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	u32 val;
81398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	unsigned long flags;
82398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
83398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	spin_lock_irqsave(&tco_lock, flags);
84398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	val = inl(TCO_CNT(tcobase));
85398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	val &= ~TCO_CNT_TCOHALT;
86398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	outl(val, TCO_CNT(tcobase));
87398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	spin_unlock_irqrestore(&tco_lock, flags);
88398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard}
89398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
90398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic void tco_timer_stop(void)
91398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard{
92398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	u32 val;
93398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	unsigned long flags;
94398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
95398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	spin_lock_irqsave(&tco_lock, flags);
96398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	val = inl(TCO_CNT(tcobase));
97398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	val |= TCO_CNT_TCOHALT;
98398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	outl(val, TCO_CNT(tcobase));
99398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	spin_unlock_irqrestore(&tco_lock, flags);
100398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard}
101398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
102398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic void tco_timer_keepalive(void)
103398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard{
104398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	unsigned long flags;
105398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
106398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	spin_lock_irqsave(&tco_lock, flags);
107398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	outb(0x01, TCO_RLD(tcobase));
108398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	spin_unlock_irqrestore(&tco_lock, flags);
109398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard}
110398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
111398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic int tco_timer_set_heartbeat(int t)
112398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard{
113398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	int ret = 0;
114398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	unsigned char tmrval;
115398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	unsigned long flags;
116398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	u8 val;
117398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
118398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	/*
119398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	 * note seconds_to_ticks(t) > t, so if t > 0x3f, so is
12084b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie	 * tmrval=seconds_to_ticks(t).  Check that the count in seconds isn't
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * out of range on it's own (to avoid overflow in tmrval).
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (t < 0 || t > 0x3f)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
125585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	tmrval = seconds_to_ticks(t);
126585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* "Values of 0h-3h are ignored and should not be attempted" */
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmrval > 0x3f || tmrval < 0x04)
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Write new heartbeat to watchdog */
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&tco_lock, flags);
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = inb(TCO_TMR(tcobase));
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val &= 0xc0;
13584b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie	val |= tmrval;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(val, TCO_TMR(tcobase));
137ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	val = inb(TCO_TMR(tcobase));
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((val & 0x3f) != tmrval)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&tco_lock, flags);
142ed4cb4142b242d8090d3811d5eb4abf6aa985bc8Eric Anholt
143b5e89ed53ed8d24f83ba1941c07382af00ed238eDave Airlie	if (ret)
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
145ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes
146ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	heartbeat = t;
147ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	return 0;
148ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes}
149ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes
150ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes/*
151dc7a93190c21edbf3ed23e678ad04f852b9cff28Wang Zhenyu *	/dev/watchdog handling
152398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard */
153398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
154398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic int nv_tco_open(struct inode *inode, struct file *file)
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* /dev/watchdog can only be opened once */
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (test_and_set_bit(0, &timer_alive))
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
159ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reload and activate timer */
161ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	tco_timer_keepalive();
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_timer_start();
163da509d7a02cb54938776439edc81f057e39f81e0Dave Airlie	return nonseekable_open(inode, file);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int nv_tco_release(struct inode *inode, struct file *file)
16720caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt{
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Shut off the timer */
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tco_expect_close == 42) {
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tco_timer_stop();
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_CRIT PFX "Unexpected close, not stopping "
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "watchdog!\n");
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tco_timer_keepalive();
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear_bit(0, &timer_alive);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_expect_close = 0;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t nv_tco_write(struct file *file, const char __user *data,
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    size_t len, loff_t *ppos)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
184b5e89ed53ed8d24f83ba1941c07382af00ed238eDave Airlie	/* See if we got the magic character 'V' and reload the timer */
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len) {
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nowayout) {
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t i;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
19020caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt			 * note: just in case someone wrote the magic character
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * five months ago...
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tco_expect_close = 0;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
195a6b54f3f5050c0cbc0c35dd48064846c6302706bMichel Dänzer			/*
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * scan to see whether or not we got the magic
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * character
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (i = 0; i != len; i++) {
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				char c;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (get_user(c, data + i))
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -EFAULT;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (c == 'V')
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					tco_expect_close = 42;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20884b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie		/* someone wrote to us, we should reload the timer */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tco_timer_keepalive();
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return len;
212bf9d89295233ae2ba7b312c78ee5657307b09f4cHarvey Harrison}
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic long nv_tco_ioctl(struct file *file, unsigned int cmd,
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 unsigned long arg)
21620caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt{
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int new_options, retval = -EINVAL;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int new_heartbeat;
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int __user *p = argp;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static const struct watchdog_info ident = {
22220caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt		.options =		WDIOF_SETTIMEOUT |
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					WDIOF_KEEPALIVEPING |
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					WDIOF_MAGICCLOSE,
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.firmware_version =	0,
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.identity =		TCO_MODULE_NAME,
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
22820caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETSUPPORT:
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
232dc7a93190c21edbf3ed23e678ad04f852b9cff28Wang Zhenyu	case WDIOC_GETSTATUS:
233585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	case WDIOC_GETBOOTSTATUS:
234dc7a93190c21edbf3ed23e678ad04f852b9cff28Wang Zhenyu		return put_user(0, p);
235585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	case WDIOC_SETOPTIONS:
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(new_options, p))
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (new_options & WDIOS_DISABLECARD) {
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tco_timer_stop();
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = 0;
241c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt		}
242c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt		if (new_options & WDIOS_ENABLECARD) {
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tco_timer_keepalive();
244c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt			tco_timer_start();
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = 0;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
247c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt		return retval;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_KEEPALIVE:
249ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes		tco_timer_keepalive();
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_SETTIMEOUT:
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(new_heartbeat, p))
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tco_timer_set_heartbeat(new_heartbeat))
2550d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie			return -EINVAL;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tco_timer_keepalive();
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Fall through */
25820caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	case WDIOC_GETTIMEOUT:
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(heartbeat, p);
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOTTY;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Kernel Interfaces
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct file_operations nv_tco_fops = {
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner =		THIS_MODULE,
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek =		no_llseek,
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		nv_tco_write,
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unlocked_ioctl =	nv_tco_ioctl,
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			nv_tco_open,
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release =		nv_tco_release,
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice nv_tco_miscdev = {
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor =	WATCHDOG_MINOR,
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"watchdog",
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops =		&nv_tco_fops,
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Data for PCI driver interface
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This data only exists for exporting the supported
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * register a pci_driver, because someone else might one day
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * want to register another driver on the same PCI id.
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEFINE_PCI_DEVICE_TABLE(tco_pci_tbl) = {
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PCI_ANY_ID, PCI_ANY_ID, },
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PCI_ANY_ID, PCI_ANY_ID, },
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0, },			/* End of list */
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
299b5e89ed53ed8d24f83ba1941c07382af00ed238eDave AirlieMODULE_DEVICE_TABLE(pci, tco_pci_tbl);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Init & exit routines
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char __init nv_tco_getdevice(void)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pci_dev *dev = NULL;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 val;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Find the PCI device */
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for_each_pci_dev(dev) {
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pci_match_id(tco_pci_tbl, dev) != NULL) {
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tco_pci = dev;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!tco_pci)
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Find the base io port */
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_read_config_dword(tco_pci, 0x64, &val);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val &= 0xffff;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (val == 0x0001 || val == 0x0000) {
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Something is wrong here, bar isn't setup */
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR PFX "failed to get tcobase address\n");
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val &= 0xff00;
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tcobase = val + 0x40;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(tcobase, 0x10, "NV TCO")) {
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       tcobase);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
336bc5f4523f772cc7629c5c5a46cf4f2a07a5500b8Dave Airlie	}
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set a reasonable heartbeat before we stop the timer */
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_timer_set_heartbeat(30);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34184b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie	/*
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Stop the TCO before we change anything so we don't race with
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * a zeroed timer.
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_timer_keepalive();
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_timer_stop();
347de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie
34820caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	/* Disable SMI caused by TCO */
349de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	if (!request_region(MCP51_SMI_EN(tcobase), 4, "NV TCO")) {
350c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
351de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie		       MCP51_SMI_EN(tcobase));
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = inl(MCP51_SMI_EN(tcobase));
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val &= ~MCP51_SMI_EN_TCO;
35620caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	outl(val, MCP51_SMI_EN(tcobase));
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = inl(MCP51_SMI_EN(tcobase));
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(MCP51_SMI_EN(tcobase), 4);
35920caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	if (val & MCP51_SMI_EN_TCO) {
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR PFX "Could not disable SMI caused by TCO\n");
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check chipset's NO_REBOOT bit */
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
36620caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	val |= MCP51_SMBUS_SETUP_B_TCO_REBOOT;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(val & MCP51_SMBUS_SETUP_B_TCO_REBOOT)) {
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot "
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "disabled by hardware\n");
372de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie		goto out;
373de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	}
374de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie
375de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	return 1;
376de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlieout:
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(tcobase, 0x10);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
38084b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie
381c60ce623bd16137627009d05e311d877729f2ad6Dave Airliestatic int __devinit nv_tco_init(struct platform_device *dev)
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
385c60ce623bd16137627009d05e311d877729f2ad6Dave Airlie	/* Check whether or not the hardware watchdog is there */
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!nv_tco_getdevice())
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
38920caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	/* Check to see if last reboot was due to watchdog timeout */
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n",
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not ");
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Clear out the old status */
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outl(TCO_STS_RESET, TCO_STS(tcobase));
39520caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Check that the heartbeat value is within it's range.
398c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	 * If not, reset to the default.
399c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	 */
400c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	if (tco_timer_set_heartbeat(heartbeat)) {
401c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		heartbeat = WATCHDOG_HEARTBEAT;
40278eca43d0391f59c3b1505bb7bd38ff45b650aabAndrew Morton		tco_timer_set_heartbeat(heartbeat);
403c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, "
404c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		       "using %d\n", heartbeat);
405c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	}
406c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane
407c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	ret = misc_register(&nv_tco_miscdev);
408c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	if (ret != 0) {
409c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
410c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		       "(err=%d)\n", WATCHDOG_MINOR, ret);
411c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane		goto unreg_region;
412c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	}
413c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane
414c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	clear_bit(0, &timer_alive);
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_timer_stop();
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec "
419c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	       "(nowayout=%d)\n", tcobase, heartbeat, nowayout);
420c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane
421c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane	return 0;
422c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane
42384b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlieunreg_region:
424de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	release_region(tcobase, 0x10);
425de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	return ret;
426de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie}
427de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie
428af6061af0d9f84a4665f88186dc1ff9e4fb78330Dave Airliestatic void __devexit nv_tco_cleanup(void)
429c29b669caae4ed1630ef479e54bdde126a0378ecAlan Hourihane{
430af6061af0d9f84a4665f88186dc1ff9e4fb78330Dave Airlie	u32 val;
431af6061af0d9f84a4665f88186dc1ff9e4fb78330Dave Airlie
432de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	/* Stop the timer before we leave */
433de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	if (!nowayout)
434585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes		tco_timer_stop();
435585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes
436de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
437de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
438de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT;
439de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
440de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
44184b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie	if (val & MCP51_SMBUS_SETUP_B_TCO_REBOOT) {
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_CRIT PFX "Couldn't unset REBOOT bit.  Machine may "
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "soon reset\n");
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Deregister */
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&nv_tco_miscdev);
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(tcobase, 0x10);
44920caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devexit nv_tco_remove(struct platform_device *dev)
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tcobase)
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nv_tco_cleanup();
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void nv_tco_shutdown(struct platform_device *dev)
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tco_timer_stop();
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct platform_driver nv_tco_driver = {
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe		= nv_tco_init,
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove		= __devexit_p(nv_tco_remove),
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.shutdown	= nv_tco_shutdown,
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver		= {
469de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie		.owner	= THIS_MODULE,
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= TCO_MODULE_NAME,
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
47384b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init nv_tco_init_module(void)
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
477c60ce623bd16137627009d05e311d877729f2ad6Dave Airlie
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO PFX "NV TCO WatchDog Timer Driver v%s\n",
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       TCO_VERSION);
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = platform_driver_register(&nv_tco_driver);
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return err;
48420caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nv_tco_platform_device = platform_device_register_simple(
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					TCO_MODULE_NAME, -1, NULL, 0);
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(nv_tco_platform_device)) {
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = PTR_ERR(nv_tco_platform_device);
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto unreg_platform_driver;
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsunreg_platform_driver:
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	platform_driver_unregister(&nv_tco_driver);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4990790d5e148c0747499742a3c09ba5f1c07f9ed0dKeith Packardstatic void __exit nv_tco_cleanup_module(void)
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
50121f16289270447673a7263ccc0b22d562fb01ecbDave Airlie	platform_device_unregister(nv_tco_platform_device);
50221f16289270447673a7263ccc0b22d562fb01ecbDave Airlie	platform_driver_unregister(&nv_tco_driver);
50321f16289270447673a7263ccc0b22d562fb01ecbDave Airlie	printk(KERN_INFO PFX "NV TCO Watchdog Module Unloaded.\n");
50421f16289270447673a7263ccc0b22d562fb01ecbDave Airlie}
50521f16289270447673a7263ccc0b22d562fb01ecbDave Airlie
50621f16289270447673a7263ccc0b22d562fb01ecbDave Airliemodule_init(nv_tco_init_module);
50721f16289270447673a7263ccc0b22d562fb01ecbDave Airliemodule_exit(nv_tco_cleanup_module);
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Mike Waychison");
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("TCO timer driver for NV chipsets");
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds