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

w83977f_wdt.c (11912B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip
      4 *
      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 Suwalski
      9 *
     10 *			-----------------------
     11 */
     12
     13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     14
     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     0x3F0
     34#define IO_DATA_PORT      (IO_INDEX_PORT+1)
     35
     36#define UNLOCK_DATA       0x87
     37#define LOCK_DATA         0xAA
     38#define DEVICE_REGISTER   0x07
     39
     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 watchdog
     64 */
     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 output
     97	 * (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" or
    100	 * 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 watchdog
    135 */
    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) and
    166	 * 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 watchdog
    189 * This is done by simply re-writing the timeout to reg. 0xF2
    190 */
    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 value
    218 */
    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 startup
    227	 * value. This information is supplied in the PCM-5335 manual and was
    228	 * checked by me on a real board. This is a bit strange because W83977f
    229	 * 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 status
    250 */
    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 handling
    284 */
    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 nowayout
    304	 */
    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 watchdog
    319 *      @buf: buffer to write (unused as data does not matter here
    320 *      @count: count of bytes
    321 *      @ppos: pointer to the position to write. No seeks allowed
    322 *
    323 *      A write to a watchdog device is defined as a keepalive signal. Any
    324 *      write of data will do, as we 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 the
    336			   magic character long ago */
    337			expect_close = 0;
    338
    339			/* scan to see whether or not we got the
    340			   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 device
    359 *      @file: file handle to the device
    360 *      @cmd: watchdog command
    361 *      @arg: argument pointer
    362 *
    363 *      The watchdog API defines a common set of functions for all watchdogs
    364 *      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	.llseek		= no_llseek,
    447	.write		= wdt_write,
    448	.unlocked_ioctl	= wdt_ioctl,
    449	.compat_ioctl	= compat_ptr_ioctl,
    450	.open		= wdt_open,
    451	.release	= wdt_release,
    452};
    453
    454static struct miscdevice wdt_miscdev = {
    455	.minor		= WATCHDOG_MINOR,
    456	.name		= "watchdog",
    457	.fops		= &wdt_fops,
    458};
    459
    460static struct notifier_block wdt_notifier = {
    461	.notifier_call = wdt_notify_sys,
    462};
    463
    464static int __init w83977f_wdt_init(void)
    465{
    466	int rc;
    467
    468	pr_info("driver v%s\n", WATCHDOG_VERSION);
    469
    470	/*
    471	 * Check that the timeout value is within it's range;
    472	 * if not reset to the default
    473	 */
    474	if (wdt_set_timeout(timeout)) {
    475		wdt_set_timeout(DEFAULT_TIMEOUT);
    476		pr_info("timeout value must be 15 <= timeout <= 7635, using %d\n",
    477			DEFAULT_TIMEOUT);
    478	}
    479
    480	if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
    481		pr_err("I/O address 0x%04x already in use\n", IO_INDEX_PORT);
    482		rc = -EIO;
    483		goto err_out;
    484	}
    485
    486	rc = register_reboot_notifier(&wdt_notifier);
    487	if (rc) {
    488		pr_err("cannot register reboot notifier (err=%d)\n", rc);
    489		goto err_out_region;
    490	}
    491
    492	rc = misc_register(&wdt_miscdev);
    493	if (rc) {
    494		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    495		       wdt_miscdev.minor, rc);
    496		goto err_out_reboot;
    497	}
    498
    499	pr_info("initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
    500		timeout, nowayout, testmode);
    501
    502	return 0;
    503
    504err_out_reboot:
    505	unregister_reboot_notifier(&wdt_notifier);
    506err_out_region:
    507	release_region(IO_INDEX_PORT, 2);
    508err_out:
    509	return rc;
    510}
    511
    512static void __exit w83977f_wdt_exit(void)
    513{
    514	wdt_stop();
    515	misc_deregister(&wdt_miscdev);
    516	unregister_reboot_notifier(&wdt_notifier);
    517	release_region(IO_INDEX_PORT, 2);
    518}
    519
    520module_init(w83977f_wdt_init);
    521module_exit(w83977f_wdt_exit);
    522
    523MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");
    524MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");
    525MODULE_LICENSE("GPL");