brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 76a5871 Raw
475 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *	National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver4 *	(c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,5 *			All Rights Reserved.6 *	Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.7 *8 *	The author(s) of this software shall not be held liable for damages9 *	of any nature resulting due to the use of this software. This10 *	software is provided AS-IS with no warranties.11 *12 *	Changelog:13 *	20020220 Zwane Mwaikambo	Code based on datasheet, no hardware.14 *	20020221 Zwane Mwaikambo	Cleanups as suggested by Jeff Garzik15 *					and Alan Cox.16 *	20020222 Zwane Mwaikambo	Added probing.17 *	20020225 Zwane Mwaikambo	Added ISAPNP support.18 *	20020412 Rob Radez		Broke out start/stop functions19 *		 <rob@osinvestor.com>	Return proper status instead of20 *					temperature warning21 *					Add WDIOC_GETBOOTSTATUS and22 *					WDIOC_SETOPTIONS ioctls23 *					Fix CONFIG_WATCHDOG_NOWAYOUT24 *	20020530 Joel Becker		Add Matt Domsch's nowayout module25 *					option26 *	20030116 Adam Belay		Updated to the latest pnp code27 */28 29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt30 31#include <linux/module.h>32#include <linux/moduleparam.h>33#include <linux/miscdevice.h>34#include <linux/watchdog.h>35#include <linux/ioport.h>36#include <linux/spinlock.h>37#include <linux/notifier.h>38#include <linux/reboot.h>39#include <linux/init.h>40#include <linux/pnp.h>41#include <linux/fs.h>42#include <linux/semaphore.h>43#include <linux/io.h>44#include <linux/uaccess.h>45 46#define SC1200_MODULE_VER	"build 20020303"47#define SC1200_MODULE_NAME	"sc1200wdt"48 49#define	MAX_TIMEOUT	255	/* 255 minutes */50#define PMIR		(io)	/* Power Management Index Register */51#define PMDR		(io+1)	/* Power Management Data Register */52 53/* Data Register indexes */54#define FER1		0x00	/* Function enable register 1 */55#define FER2		0x01	/* Function enable register 2 */56#define PMC1		0x02	/* Power Management Ctrl 1 */57#define PMC2		0x03	/* Power Management Ctrl 2 */58#define PMC3		0x04	/* Power Management Ctrl 3 */59#define WDTO		0x05	/* Watchdog timeout register */60#define	WDCF		0x06	/* Watchdog config register */61#define WDST		0x07	/* Watchdog status register */62 63/* WDCF bitfields - which devices assert WDO */64#define KBC_IRQ		0x01	/* Keyboard Controller */65#define MSE_IRQ		0x02	/* Mouse */66#define UART1_IRQ	0x03	/* Serial0 */67#define UART2_IRQ	0x04	/* Serial1 */68/* 5 -7 are reserved */69 70static int timeout = 1;71static int io = -1;72static int io_len = 2;		/* for non plug and play */73static unsigned long open_flag;74static char expect_close;75static DEFINE_SPINLOCK(sc1200wdt_lock);	/* io port access serialisation */76 77#if defined CONFIG_PNP78static int isapnp = 1;79static struct pnp_dev *wdt_dev;80 81module_param(isapnp, int, 0);82MODULE_PARM_DESC(isapnp,83	"When set to 0 driver ISA PnP support will be disabled");84#endif85 86module_param_hw(io, int, ioport, 0);87MODULE_PARM_DESC(io, "io port");88module_param(timeout, int, 0);89MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");90 91static bool nowayout = WATCHDOG_NOWAYOUT;92module_param(nowayout, bool, 0);93MODULE_PARM_DESC(nowayout,94	"Watchdog cannot be stopped once started (default="95				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");96 97 98 99/* Read from Data Register */100static inline void __sc1200wdt_read_data(unsigned char index,101						unsigned char *data)102{103	outb_p(index, PMIR);104	*data = inb(PMDR);105}106 107static void sc1200wdt_read_data(unsigned char index, unsigned char *data)108{109	spin_lock(&sc1200wdt_lock);110	__sc1200wdt_read_data(index, data);111	spin_unlock(&sc1200wdt_lock);112}113 114/* Write to Data Register */115static inline void __sc1200wdt_write_data(unsigned char index,116						unsigned char data)117{118	outb_p(index, PMIR);119	outb(data, PMDR);120}121 122static inline void sc1200wdt_write_data(unsigned char index,123						unsigned char data)124{125	spin_lock(&sc1200wdt_lock);126	__sc1200wdt_write_data(index, data);127	spin_unlock(&sc1200wdt_lock);128}129 130 131static void sc1200wdt_start(void)132{133	unsigned char reg;134	spin_lock(&sc1200wdt_lock);135 136	__sc1200wdt_read_data(WDCF, &reg);137	/* assert WDO when any of the following interrupts are triggered too */138	reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);139	__sc1200wdt_write_data(WDCF, reg);140	/* set the timeout and get the ball rolling */141	__sc1200wdt_write_data(WDTO, timeout);142 143	spin_unlock(&sc1200wdt_lock);144}145 146static void sc1200wdt_stop(void)147{148	sc1200wdt_write_data(WDTO, 0);149}150 151/* This returns the status of the WDO signal, inactive high. */152static inline int sc1200wdt_status(void)153{154	unsigned char ret;155 156	sc1200wdt_read_data(WDST, &ret);157	/* If the bit is inactive, the watchdog is enabled, so return158	 * KEEPALIVEPING which is a bit of a kludge because there's nothing159	 * else for enabled/disabled status160	 */161	return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;162}163 164static int sc1200wdt_open(struct inode *inode, struct file *file)165{166	/* allow one at a time */167	if (test_and_set_bit(0, &open_flag))168		return -EBUSY;169 170	if (timeout > MAX_TIMEOUT)171		timeout = MAX_TIMEOUT;172 173	sc1200wdt_start();174	pr_info("Watchdog enabled, timeout = %d min(s)", timeout);175 176	return stream_open(inode, file);177}178 179 180static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,181						unsigned long arg)182{183	int new_timeout;184	void __user *argp = (void __user *)arg;185	int __user *p = argp;186	static const struct watchdog_info ident = {187		.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |188							WDIOF_MAGICCLOSE,189		.firmware_version = 0,190		.identity = "PC87307/PC97307",191	};192 193	switch (cmd) {194	case WDIOC_GETSUPPORT:195		if (copy_to_user(argp, &ident, sizeof(ident)))196			return -EFAULT;197		return 0;198 199	case WDIOC_GETSTATUS:200		return put_user(sc1200wdt_status(), p);201 202	case WDIOC_GETBOOTSTATUS:203		return put_user(0, p);204 205	case WDIOC_SETOPTIONS:206	{207		int options, retval = -EINVAL;208 209		if (get_user(options, p))210			return -EFAULT;211 212		if (options & WDIOS_DISABLECARD) {213			sc1200wdt_stop();214			retval = 0;215		}216 217		if (options & WDIOS_ENABLECARD) {218			sc1200wdt_start();219			retval = 0;220		}221 222		return retval;223	}224	case WDIOC_KEEPALIVE:225		sc1200wdt_write_data(WDTO, timeout);226		return 0;227 228	case WDIOC_SETTIMEOUT:229		if (get_user(new_timeout, p))230			return -EFAULT;231		/* the API states this is given in secs */232		new_timeout /= 60;233		if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)234			return -EINVAL;235		timeout = new_timeout;236		sc1200wdt_write_data(WDTO, timeout);237		fallthrough;	/* and return the new timeout */238 239	case WDIOC_GETTIMEOUT:240		return put_user(timeout * 60, p);241 242	default:243		return -ENOTTY;244	}245}246 247 248static int sc1200wdt_release(struct inode *inode, struct file *file)249{250	if (expect_close == 42) {251		sc1200wdt_stop();252		pr_info("Watchdog disabled\n");253	} else {254		sc1200wdt_write_data(WDTO, timeout);255		pr_crit("Unexpected close!, timeout = %d min(s)\n", timeout);256	}257	clear_bit(0, &open_flag);258	expect_close = 0;259 260	return 0;261}262 263 264static ssize_t sc1200wdt_write(struct file *file, const char __user *data,265						size_t len, loff_t *ppos)266{267	if (len) {268		if (!nowayout) {269			size_t i;270 271			expect_close = 0;272 273			for (i = 0; i != len; i++) {274				char c;275 276				if (get_user(c, data + i))277					return -EFAULT;278				if (c == 'V')279					expect_close = 42;280			}281		}282 283		sc1200wdt_write_data(WDTO, timeout);284		return len;285	}286 287	return 0;288}289 290 291static int sc1200wdt_notify_sys(struct notifier_block *this,292					unsigned long code, void *unused)293{294	if (code == SYS_DOWN || code == SYS_HALT)295		sc1200wdt_stop();296 297	return NOTIFY_DONE;298}299 300 301static struct notifier_block sc1200wdt_notifier = {302	.notifier_call =	sc1200wdt_notify_sys,303};304 305static const struct file_operations sc1200wdt_fops = {306	.owner		= THIS_MODULE,307	.write		= sc1200wdt_write,308	.unlocked_ioctl = sc1200wdt_ioctl,309	.compat_ioctl	= compat_ptr_ioctl,310	.open		= sc1200wdt_open,311	.release	= sc1200wdt_release,312};313 314static struct miscdevice sc1200wdt_miscdev = {315	.minor		= WATCHDOG_MINOR,316	.name		= "watchdog",317	.fops		= &sc1200wdt_fops,318};319 320 321static int __init sc1200wdt_probe(void)322{323	/* The probe works by reading the PMC3 register's default value of 0x0e324	 * there is one caveat, if the device disables the parallel port or any325	 * of the UARTs we won't be able to detect it.326	 * NB. This could be done with accuracy by reading the SID registers,327	 * but we don't have access to those io regions.328	 */329 330	unsigned char reg;331 332	sc1200wdt_read_data(PMC3, &reg);333	reg &= 0x0f;		/* we don't want the UART busy bits */334	return (reg == 0x0e) ? 0 : -ENODEV;335}336 337 338#if defined CONFIG_PNP339 340static const struct pnp_device_id scl200wdt_pnp_devices[] = {341	/* National Semiconductor PC87307/PC97307 watchdog component */342	{.id = "NSC0800", .driver_data = 0},343	{.id = ""},344};345 346static int scl200wdt_pnp_probe(struct pnp_dev *dev,347					const struct pnp_device_id *dev_id)348{349	/* this driver only supports one card at a time */350	if (wdt_dev || !isapnp)351		return -EBUSY;352 353	wdt_dev = dev;354	io = pnp_port_start(wdt_dev, 0);355	io_len = pnp_port_len(wdt_dev, 0);356 357	if (!request_region(io, io_len, SC1200_MODULE_NAME)) {358		pr_err("Unable to register IO port %#x\n", io);359		return -EBUSY;360	}361 362	pr_info("PnP device found at io port %#x/%d\n", io, io_len);363	return 0;364}365 366static void scl200wdt_pnp_remove(struct pnp_dev *dev)367{368	if (wdt_dev) {369		release_region(io, io_len);370		wdt_dev = NULL;371	}372}373 374static struct pnp_driver scl200wdt_pnp_driver = {375	.name		= "scl200wdt",376	.id_table	= scl200wdt_pnp_devices,377	.probe		= scl200wdt_pnp_probe,378	.remove		= scl200wdt_pnp_remove,379};380 381#endif /* CONFIG_PNP */382 383 384static int __init sc1200wdt_init(void)385{386	int ret;387 388	pr_info("%s\n", SC1200_MODULE_VER);389 390#if defined CONFIG_PNP391	if (isapnp) {392		ret = pnp_register_driver(&scl200wdt_pnp_driver);393		if (ret)394			goto out_clean;395	}396#endif397 398	if (io == -1) {399		pr_err("io parameter must be specified\n");400		ret = -EINVAL;401		goto out_pnp;402	}403 404#if defined CONFIG_PNP405	/* now that the user has specified an IO port and we haven't detected406	 * any devices, disable pnp support */407	if (isapnp)408		pnp_unregister_driver(&scl200wdt_pnp_driver);409	isapnp = 0;410#endif411 412	if (!request_region(io, io_len, SC1200_MODULE_NAME)) {413		pr_err("Unable to register IO port %#x\n", io);414		ret = -EBUSY;415		goto out_pnp;416	}417 418	ret = sc1200wdt_probe();419	if (ret)420		goto out_io;421 422	ret = register_reboot_notifier(&sc1200wdt_notifier);423	if (ret) {424		pr_err("Unable to register reboot notifier err = %d\n", ret);425		goto out_io;426	}427 428	ret = misc_register(&sc1200wdt_miscdev);429	if (ret) {430		pr_err("Unable to register miscdev on minor %d\n",431		       WATCHDOG_MINOR);432		goto out_rbt;433	}434 435	/* ret = 0 */436 437out_clean:438	return ret;439 440out_rbt:441	unregister_reboot_notifier(&sc1200wdt_notifier);442 443out_io:444	release_region(io, io_len);445 446out_pnp:447#if defined CONFIG_PNP448	if (isapnp)449		pnp_unregister_driver(&scl200wdt_pnp_driver);450#endif451	goto out_clean;452}453 454 455static void __exit sc1200wdt_exit(void)456{457	misc_deregister(&sc1200wdt_miscdev);458	unregister_reboot_notifier(&sc1200wdt_notifier);459 460#if defined CONFIG_PNP461	if (isapnp)462		pnp_unregister_driver(&scl200wdt_pnp_driver);463	else464#endif465	release_region(io, io_len);466}467 468module_init(sc1200wdt_init);469module_exit(sc1200wdt_exit);470 471MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");472MODULE_DESCRIPTION(473	"Driver for National Semiconductor PC87307/PC97307 watchdog component");474MODULE_LICENSE("GPL");475