cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sc1200wdt.c (11222B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
      4 *	(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 damages
      9 *	of any nature resulting due to the use of this software. This
     10 *	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 Garzik
     15 *					and Alan Cox.
     16 *	20020222 Zwane Mwaikambo	Added probing.
     17 *	20020225 Zwane Mwaikambo	Added ISAPNP support.
     18 *	20020412 Rob Radez		Broke out start/stop functions
     19 *		 <rob@osinvestor.com>	Return proper status instead of
     20 *					temperature warning
     21 *					Add WDIOC_GETBOOTSTATUS and
     22 *					WDIOC_SETOPTIONS ioctls
     23 *					Fix CONFIG_WATCHDOG_NOWAYOUT
     24 *	20020530 Joel Becker		Add Matt Domsch's nowayout module
     25 *					option
     26 *	20030116 Adam Belay		Updated to the latest pnp code
     27 */
     28
     29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     30
     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_PNP
     78static 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#endif
     85
     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 return
    158	 * KEEPALIVEPING which is a bit of a kludge because there's nothing
    159	 * else for enabled/disabled status
    160	 */
    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	.llseek		= no_llseek,
    308	.write		= sc1200wdt_write,
    309	.unlocked_ioctl = sc1200wdt_ioctl,
    310	.compat_ioctl	= compat_ptr_ioctl,
    311	.open		= sc1200wdt_open,
    312	.release	= sc1200wdt_release,
    313};
    314
    315static struct miscdevice sc1200wdt_miscdev = {
    316	.minor		= WATCHDOG_MINOR,
    317	.name		= "watchdog",
    318	.fops		= &sc1200wdt_fops,
    319};
    320
    321
    322static int __init sc1200wdt_probe(void)
    323{
    324	/* The probe works by reading the PMC3 register's default value of 0x0e
    325	 * there is one caveat, if the device disables the parallel port or any
    326	 * of the UARTs we won't be able to detect it.
    327	 * NB. This could be done with accuracy by reading the SID registers,
    328	 * but we don't have access to those io regions.
    329	 */
    330
    331	unsigned char reg;
    332
    333	sc1200wdt_read_data(PMC3, &reg);
    334	reg &= 0x0f;		/* we don't want the UART busy bits */
    335	return (reg == 0x0e) ? 0 : -ENODEV;
    336}
    337
    338
    339#if defined CONFIG_PNP
    340
    341static const struct pnp_device_id scl200wdt_pnp_devices[] = {
    342	/* National Semiconductor PC87307/PC97307 watchdog component */
    343	{.id = "NSC0800", .driver_data = 0},
    344	{.id = ""},
    345};
    346
    347static int scl200wdt_pnp_probe(struct pnp_dev *dev,
    348					const struct pnp_device_id *dev_id)
    349{
    350	/* this driver only supports one card at a time */
    351	if (wdt_dev || !isapnp)
    352		return -EBUSY;
    353
    354	wdt_dev = dev;
    355	io = pnp_port_start(wdt_dev, 0);
    356	io_len = pnp_port_len(wdt_dev, 0);
    357
    358	if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
    359		pr_err("Unable to register IO port %#x\n", io);
    360		return -EBUSY;
    361	}
    362
    363	pr_info("PnP device found at io port %#x/%d\n", io, io_len);
    364	return 0;
    365}
    366
    367static void scl200wdt_pnp_remove(struct pnp_dev *dev)
    368{
    369	if (wdt_dev) {
    370		release_region(io, io_len);
    371		wdt_dev = NULL;
    372	}
    373}
    374
    375static struct pnp_driver scl200wdt_pnp_driver = {
    376	.name		= "scl200wdt",
    377	.id_table	= scl200wdt_pnp_devices,
    378	.probe		= scl200wdt_pnp_probe,
    379	.remove		= scl200wdt_pnp_remove,
    380};
    381
    382#endif /* CONFIG_PNP */
    383
    384
    385static int __init sc1200wdt_init(void)
    386{
    387	int ret;
    388
    389	pr_info("%s\n", SC1200_MODULE_VER);
    390
    391#if defined CONFIG_PNP
    392	if (isapnp) {
    393		ret = pnp_register_driver(&scl200wdt_pnp_driver);
    394		if (ret)
    395			goto out_clean;
    396	}
    397#endif
    398
    399	if (io == -1) {
    400		pr_err("io parameter must be specified\n");
    401		ret = -EINVAL;
    402		goto out_pnp;
    403	}
    404
    405#if defined CONFIG_PNP
    406	/* now that the user has specified an IO port and we haven't detected
    407	 * any devices, disable pnp support */
    408	if (isapnp)
    409		pnp_unregister_driver(&scl200wdt_pnp_driver);
    410	isapnp = 0;
    411#endif
    412
    413	if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
    414		pr_err("Unable to register IO port %#x\n", io);
    415		ret = -EBUSY;
    416		goto out_pnp;
    417	}
    418
    419	ret = sc1200wdt_probe();
    420	if (ret)
    421		goto out_io;
    422
    423	ret = register_reboot_notifier(&sc1200wdt_notifier);
    424	if (ret) {
    425		pr_err("Unable to register reboot notifier err = %d\n", ret);
    426		goto out_io;
    427	}
    428
    429	ret = misc_register(&sc1200wdt_miscdev);
    430	if (ret) {
    431		pr_err("Unable to register miscdev on minor %d\n",
    432		       WATCHDOG_MINOR);
    433		goto out_rbt;
    434	}
    435
    436	/* ret = 0 */
    437
    438out_clean:
    439	return ret;
    440
    441out_rbt:
    442	unregister_reboot_notifier(&sc1200wdt_notifier);
    443
    444out_io:
    445	release_region(io, io_len);
    446
    447out_pnp:
    448#if defined CONFIG_PNP
    449	if (isapnp)
    450		pnp_unregister_driver(&scl200wdt_pnp_driver);
    451#endif
    452	goto out_clean;
    453}
    454
    455
    456static void __exit sc1200wdt_exit(void)
    457{
    458	misc_deregister(&sc1200wdt_miscdev);
    459	unregister_reboot_notifier(&sc1200wdt_notifier);
    460
    461#if defined CONFIG_PNP
    462	if (isapnp)
    463		pnp_unregister_driver(&scl200wdt_pnp_driver);
    464	else
    465#endif
    466	release_region(io, io_len);
    467}
    468
    469module_init(sc1200wdt_init);
    470module_exit(sc1200wdt_exit);
    471
    472MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
    473MODULE_DESCRIPTION(
    474	"Driver for National Semiconductor PC87307/PC97307 watchdog component");
    475MODULE_LICENSE("GPL");