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