brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 3776030 Raw
525 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *	W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip4 *5 *	(c) Copyright 2005  Jose Goncalves <jose.goncalves@inov.pt>6 *7 *      Based on w83877f_wdt.c by Scott Jennings,8 *           and wdt977.c by Woody Suwalski9 *10 *			-----------------------11 */12 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt14 15#include <linux/module.h>16#include <linux/moduleparam.h>17#include <linux/types.h>18#include <linux/kernel.h>19#include <linux/fs.h>20#include <linux/miscdevice.h>21#include <linux/init.h>22#include <linux/ioport.h>23#include <linux/watchdog.h>24#include <linux/notifier.h>25#include <linux/reboot.h>26#include <linux/uaccess.h>27#include <linux/io.h>28 29 30#define WATCHDOG_VERSION  "1.00"31#define WATCHDOG_NAME     "W83977F WDT"32 33#define IO_INDEX_PORT     0x3F034#define IO_DATA_PORT      (IO_INDEX_PORT+1)35 36#define UNLOCK_DATA       0x8737#define LOCK_DATA         0xAA38#define DEVICE_REGISTER   0x0739 40#define	DEFAULT_TIMEOUT   45		/* default timeout in seconds */41 42static	int timeout = DEFAULT_TIMEOUT;43static	int timeoutW;			/* timeout in watchdog counter units */44static	unsigned long timer_alive;45static	int testmode;46static	char expect_close;47static	DEFINE_SPINLOCK(spinlock);48 49module_param(timeout, int, 0);50MODULE_PARM_DESC(timeout,51		"Watchdog timeout in seconds (15..7635), default="52				__MODULE_STRING(DEFAULT_TIMEOUT) ")");53module_param(testmode, int, 0);54MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");55 56static bool nowayout = WATCHDOG_NOWAYOUT;57module_param(nowayout, bool, 0);58MODULE_PARM_DESC(nowayout,59		"Watchdog cannot be stopped once started (default="60				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");61 62/*63 * Start the watchdog64 */65 66static int wdt_start(void)67{68	unsigned long flags;69 70	spin_lock_irqsave(&spinlock, flags);71 72	/* Unlock the SuperIO chip */73	outb_p(UNLOCK_DATA, IO_INDEX_PORT);74	outb_p(UNLOCK_DATA, IO_INDEX_PORT);75 76	/*77	 * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4.78	 * F2 has the timeout in watchdog counter units.79	 * F3 is set to enable watchdog LED blink at timeout.80	 * F4 is used to just clear the TIMEOUT'ed state (bit 0).81	 */82	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);83	outb_p(0x08, IO_DATA_PORT);84	outb_p(0xF2, IO_INDEX_PORT);85	outb_p(timeoutW, IO_DATA_PORT);86	outb_p(0xF3, IO_INDEX_PORT);87	outb_p(0x08, IO_DATA_PORT);88	outb_p(0xF4, IO_INDEX_PORT);89	outb_p(0x00, IO_DATA_PORT);90 91	/* Set device Aux2 active */92	outb_p(0x30, IO_INDEX_PORT);93	outb_p(0x01, IO_DATA_PORT);94 95	/*96	 * Select device Aux1 (dev=7) to set GP16 as the watchdog output97	 * (in reg E6) and GP13 as the watchdog LED output (in reg E3).98	 * Map GP16 at pin 119.99	 * In test mode watch the bit 0 on F4 to indicate "triggered" or100	 * check watchdog LED on SBC.101	 */102	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);103	outb_p(0x07, IO_DATA_PORT);104	if (!testmode) {105		unsigned pin_map;106 107		outb_p(0xE6, IO_INDEX_PORT);108		outb_p(0x0A, IO_DATA_PORT);109		outb_p(0x2C, IO_INDEX_PORT);110		pin_map = inb_p(IO_DATA_PORT);111		pin_map |= 0x10;112		pin_map &= ~(0x20);113		outb_p(0x2C, IO_INDEX_PORT);114		outb_p(pin_map, IO_DATA_PORT);115	}116	outb_p(0xE3, IO_INDEX_PORT);117	outb_p(0x08, IO_DATA_PORT);118 119	/* Set device Aux1 active */120	outb_p(0x30, IO_INDEX_PORT);121	outb_p(0x01, IO_DATA_PORT);122 123	/* Lock the SuperIO chip */124	outb_p(LOCK_DATA, IO_INDEX_PORT);125 126	spin_unlock_irqrestore(&spinlock, flags);127 128	pr_info("activated\n");129 130	return 0;131}132 133/*134 * Stop the watchdog135 */136 137static int wdt_stop(void)138{139	unsigned long flags;140 141	spin_lock_irqsave(&spinlock, flags);142 143	/* Unlock the SuperIO chip */144	outb_p(UNLOCK_DATA, IO_INDEX_PORT);145	outb_p(UNLOCK_DATA, IO_INDEX_PORT);146 147	/*148	 * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4.149	 * F2 is reset to its default value (watchdog timer disabled).150	 * F3 is reset to its default state.151	 * F4 clears the TIMEOUT'ed state (bit 0) - back to default.152	 */153	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);154	outb_p(0x08, IO_DATA_PORT);155	outb_p(0xF2, IO_INDEX_PORT);156	outb_p(0xFF, IO_DATA_PORT);157	outb_p(0xF3, IO_INDEX_PORT);158	outb_p(0x00, IO_DATA_PORT);159	outb_p(0xF4, IO_INDEX_PORT);160	outb_p(0x00, IO_DATA_PORT);161	outb_p(0xF2, IO_INDEX_PORT);162	outb_p(0x00, IO_DATA_PORT);163 164	/*165	 * Select device Aux1 (dev=7) to set GP16 (in reg E6) and166	 * Gp13 (in reg E3) as inputs.167	 */168	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);169	outb_p(0x07, IO_DATA_PORT);170	if (!testmode) {171		outb_p(0xE6, IO_INDEX_PORT);172		outb_p(0x01, IO_DATA_PORT);173	}174	outb_p(0xE3, IO_INDEX_PORT);175	outb_p(0x01, IO_DATA_PORT);176 177	/* Lock the SuperIO chip */178	outb_p(LOCK_DATA, IO_INDEX_PORT);179 180	spin_unlock_irqrestore(&spinlock, flags);181 182	pr_info("shutdown\n");183 184	return 0;185}186 187/*188 * Send a keepalive ping to the watchdog189 * This is done by simply re-writing the timeout to reg. 0xF2190 */191 192static int wdt_keepalive(void)193{194	unsigned long flags;195 196	spin_lock_irqsave(&spinlock, flags);197 198	/* Unlock the SuperIO chip */199	outb_p(UNLOCK_DATA, IO_INDEX_PORT);200	outb_p(UNLOCK_DATA, IO_INDEX_PORT);201 202	/* Select device Aux2 (device=8) to kick watchdog reg F2 */203	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);204	outb_p(0x08, IO_DATA_PORT);205	outb_p(0xF2, IO_INDEX_PORT);206	outb_p(timeoutW, IO_DATA_PORT);207 208	/* Lock the SuperIO chip */209	outb_p(LOCK_DATA, IO_INDEX_PORT);210 211	spin_unlock_irqrestore(&spinlock, flags);212 213	return 0;214}215 216/*217 * Set the watchdog timeout value218 */219 220static int wdt_set_timeout(int t)221{222	unsigned int tmrval;223 224	/*225	 * Convert seconds to watchdog counter time units, rounding up.226	 * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup227	 * value. This information is supplied in the PCM-5335 manual and was228	 * checked by me on a real board. This is a bit strange because W83977f229	 * datasheet says counter unit is in minutes!230	 */231	if (t < 15)232		return -EINVAL;233 234	tmrval = ((t + 15) + 29) / 30;235 236	if (tmrval > 255)237		return -EINVAL;238 239	/*240	 * timeout is the timeout in seconds,241	 * timeoutW is the timeout in watchdog counter units.242	 */243	timeoutW = tmrval;244	timeout = (timeoutW * 30) - 15;245	return 0;246}247 248/*249 * Get the watchdog status250 */251 252static int wdt_get_status(int *status)253{254	int new_status;255	unsigned long flags;256 257	spin_lock_irqsave(&spinlock, flags);258 259	/* Unlock the SuperIO chip */260	outb_p(UNLOCK_DATA, IO_INDEX_PORT);261	outb_p(UNLOCK_DATA, IO_INDEX_PORT);262 263	/* Select device Aux2 (device=8) to read watchdog reg F4 */264	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);265	outb_p(0x08, IO_DATA_PORT);266	outb_p(0xF4, IO_INDEX_PORT);267	new_status = inb_p(IO_DATA_PORT);268 269	/* Lock the SuperIO chip */270	outb_p(LOCK_DATA, IO_INDEX_PORT);271 272	spin_unlock_irqrestore(&spinlock, flags);273 274	*status = 0;275	if (new_status & 1)276		*status |= WDIOF_CARDRESET;277 278	return 0;279}280 281 282/*283 *	/dev/watchdog handling284 */285 286static int wdt_open(struct inode *inode, struct file *file)287{288	/* If the watchdog is alive we don't need to start it again */289	if (test_and_set_bit(0, &timer_alive))290		return -EBUSY;291 292	if (nowayout)293		__module_get(THIS_MODULE);294 295	wdt_start();296	return stream_open(inode, file);297}298 299static int wdt_release(struct inode *inode, struct file *file)300{301	/*302	 * Shut off the timer.303	 * Lock it in if it's a module and we set nowayout304	 */305	if (expect_close == 42) {306		wdt_stop();307		clear_bit(0, &timer_alive);308	} else {309		wdt_keepalive();310		pr_crit("unexpected close, not stopping watchdog!\n");311	}312	expect_close = 0;313	return 0;314}315 316/*317 *      wdt_write:318 *      @file: file handle to the watchdog319 *      @buf: buffer to write (unused as data does not matter here320 *      @count: count of bytes321 *      @ppos: pointer to the position to write. No seeks allowed322 *323 *      A write to a watchdog device is defined as a keepalive signal. Any324 *      write of data will do, as we don't define content meaning.325 */326 327static ssize_t wdt_write(struct file *file, const char __user *buf,328			    size_t count, loff_t *ppos)329{330	/* See if we got the magic character 'V' and reload the timer */331	if (count) {332		if (!nowayout) {333			size_t ofs;334 335			/* note: just in case someone wrote the336			   magic character long ago */337			expect_close = 0;338 339			/* scan to see whether or not we got the340			   magic character */341			for (ofs = 0; ofs != count; ofs++) {342				char c;343				if (get_user(c, buf + ofs))344					return -EFAULT;345				if (c == 'V')346					expect_close = 42;347			}348		}349 350		/* someone wrote to us, we should restart timer */351		wdt_keepalive();352	}353	return count;354}355 356/*357 *      wdt_ioctl:358 *      @inode: inode of the device359 *      @file: file handle to the device360 *      @cmd: watchdog command361 *      @arg: argument pointer362 *363 *      The watchdog API defines a common set of functions for all watchdogs364 *      according to their available features.365 */366 367static const struct watchdog_info ident = {368	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,369	.firmware_version =	1,370	.identity = WATCHDOG_NAME,371};372 373static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)374{375	int status;376	int new_options, retval = -EINVAL;377	int new_timeout;378	union {379		struct watchdog_info __user *ident;380		int __user *i;381	} uarg;382 383	uarg.i = (int __user *)arg;384 385	switch (cmd) {386	case WDIOC_GETSUPPORT:387		return copy_to_user(uarg.ident, &ident,388						sizeof(ident)) ? -EFAULT : 0;389 390	case WDIOC_GETSTATUS:391		wdt_get_status(&status);392		return put_user(status, uarg.i);393 394	case WDIOC_GETBOOTSTATUS:395		return put_user(0, uarg.i);396 397	case WDIOC_SETOPTIONS:398		if (get_user(new_options, uarg.i))399			return -EFAULT;400 401		if (new_options & WDIOS_DISABLECARD) {402			wdt_stop();403			retval = 0;404		}405 406		if (new_options & WDIOS_ENABLECARD) {407			wdt_start();408			retval = 0;409		}410 411		return retval;412 413	case WDIOC_KEEPALIVE:414		wdt_keepalive();415		return 0;416 417	case WDIOC_SETTIMEOUT:418		if (get_user(new_timeout, uarg.i))419			return -EFAULT;420 421		if (wdt_set_timeout(new_timeout))422			return -EINVAL;423 424		wdt_keepalive();425		fallthrough;426 427	case WDIOC_GETTIMEOUT:428		return put_user(timeout, uarg.i);429 430	default:431		return -ENOTTY;432 433	}434}435 436static int wdt_notify_sys(struct notifier_block *this, unsigned long code,437	void *unused)438{439	if (code == SYS_DOWN || code == SYS_HALT)440		wdt_stop();441	return NOTIFY_DONE;442}443 444static const struct file_operations wdt_fops = {445	.owner		= THIS_MODULE,446	.write		= wdt_write,447	.unlocked_ioctl	= wdt_ioctl,448	.compat_ioctl	= compat_ptr_ioctl,449	.open		= wdt_open,450	.release	= wdt_release,451};452 453static struct miscdevice wdt_miscdev = {454	.minor		= WATCHDOG_MINOR,455	.name		= "watchdog",456	.fops		= &wdt_fops,457};458 459static struct notifier_block wdt_notifier = {460	.notifier_call = wdt_notify_sys,461};462 463static int __init w83977f_wdt_init(void)464{465	int rc;466 467	pr_info("driver v%s\n", WATCHDOG_VERSION);468 469	/*470	 * Check that the timeout value is within it's range;471	 * if not reset to the default472	 */473	if (wdt_set_timeout(timeout)) {474		wdt_set_timeout(DEFAULT_TIMEOUT);475		pr_info("timeout value must be 15 <= timeout <= 7635, using %d\n",476			DEFAULT_TIMEOUT);477	}478 479	if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {480		pr_err("I/O address 0x%04x already in use\n", IO_INDEX_PORT);481		rc = -EIO;482		goto err_out;483	}484 485	rc = register_reboot_notifier(&wdt_notifier);486	if (rc) {487		pr_err("cannot register reboot notifier (err=%d)\n", rc);488		goto err_out_region;489	}490 491	rc = misc_register(&wdt_miscdev);492	if (rc) {493		pr_err("cannot register miscdev on minor=%d (err=%d)\n",494		       wdt_miscdev.minor, rc);495		goto err_out_reboot;496	}497 498	pr_info("initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",499		timeout, nowayout, testmode);500 501	return 0;502 503err_out_reboot:504	unregister_reboot_notifier(&wdt_notifier);505err_out_region:506	release_region(IO_INDEX_PORT, 2);507err_out:508	return rc;509}510 511static void __exit w83977f_wdt_exit(void)512{513	wdt_stop();514	misc_deregister(&wdt_miscdev);515	unregister_reboot_notifier(&wdt_notifier);516	release_region(IO_INDEX_PORT, 2);517}518 519module_init(w83977f_wdt_init);520module_exit(w83977f_wdt_exit);521 522MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");523MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");524MODULE_LICENSE("GPL");525