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

advantechwdt.c (7797B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 *	Advantech Single Board Computer WDT driver
      4 *
      5 *	(c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
      6 *
      7 *	Based on acquirewdt.c which is based on wdt.c.
      8 *	Original copyright messages:
      9 *
     10 *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
     11 *						All Rights Reserved.
     12 *
     13 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
     14 *	warranty for any of this software. This material is provided
     15 *	"AS-IS" and at no charge.
     16 *
     17 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
     18 *
     19 *	14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
     20 *	    Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
     21 *
     22 *	16-Oct-2002 Rob Radez <rob@osinvestor.com>
     23 *	    Clean up ioctls, clean up init + exit, add expect close support,
     24 *	    add wdt_start and wdt_stop as parameters.
     25 */
     26
     27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     28
     29#include <linux/module.h>
     30#include <linux/moduleparam.h>
     31#include <linux/types.h>
     32#include <linux/miscdevice.h>
     33#include <linux/watchdog.h>
     34#include <linux/fs.h>
     35#include <linux/ioport.h>
     36#include <linux/platform_device.h>
     37#include <linux/init.h>
     38#include <linux/io.h>
     39#include <linux/uaccess.h>
     40
     41
     42#define DRV_NAME "advantechwdt"
     43#define WATCHDOG_NAME "Advantech WDT"
     44#define WATCHDOG_TIMEOUT 60		/* 60 sec default timeout */
     45
     46/* the watchdog platform device */
     47static struct platform_device *advwdt_platform_device;
     48static unsigned long advwdt_is_open;
     49static char adv_expect_close;
     50
     51/*
     52 *	You must set these - there is no sane way to probe for this board.
     53 *
     54 *	To enable or restart, write the timeout value in seconds (1 to 63)
     55 *	to I/O port wdt_start.  To disable, read I/O port wdt_stop.
     56 *	Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
     57 *	check your manual (at least the PCA-6159 seems to be different -
     58 *	the manual says wdt_stop is 0x43, not 0x443).
     59 *	(0x43 is also a write-only control register for the 8254 timer!)
     60 */
     61
     62static int wdt_stop = 0x443;
     63module_param(wdt_stop, int, 0);
     64MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
     65
     66static int wdt_start = 0x443;
     67module_param(wdt_start, int, 0);
     68MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
     69
     70static int timeout = WATCHDOG_TIMEOUT;	/* in seconds */
     71module_param(timeout, int, 0);
     72MODULE_PARM_DESC(timeout,
     73	"Watchdog timeout in seconds. 1<= timeout <=63, default="
     74		__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
     75
     76static bool nowayout = WATCHDOG_NOWAYOUT;
     77module_param(nowayout, bool, 0);
     78MODULE_PARM_DESC(nowayout,
     79	"Watchdog cannot be stopped once started (default="
     80		__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     81
     82/*
     83 *	Watchdog Operations
     84 */
     85
     86static void advwdt_ping(void)
     87{
     88	/* Write a watchdog value */
     89	outb_p(timeout, wdt_start);
     90}
     91
     92static void advwdt_disable(void)
     93{
     94	inb_p(wdt_stop);
     95}
     96
     97static int advwdt_set_heartbeat(int t)
     98{
     99	if (t < 1 || t > 63)
    100		return -EINVAL;
    101	timeout = t;
    102	return 0;
    103}
    104
    105/*
    106 *	/dev/watchdog handling
    107 */
    108
    109static ssize_t advwdt_write(struct file *file, const char __user *buf,
    110						size_t count, loff_t *ppos)
    111{
    112	if (count) {
    113		if (!nowayout) {
    114			size_t i;
    115
    116			adv_expect_close = 0;
    117
    118			for (i = 0; i != count; i++) {
    119				char c;
    120				if (get_user(c, buf + i))
    121					return -EFAULT;
    122				if (c == 'V')
    123					adv_expect_close = 42;
    124			}
    125		}
    126		advwdt_ping();
    127	}
    128	return count;
    129}
    130
    131static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    132{
    133	int new_timeout;
    134	void __user *argp = (void __user *)arg;
    135	int __user *p = argp;
    136	static const struct watchdog_info ident = {
    137		.options = WDIOF_KEEPALIVEPING |
    138			   WDIOF_SETTIMEOUT |
    139			   WDIOF_MAGICCLOSE,
    140		.firmware_version = 1,
    141		.identity = WATCHDOG_NAME,
    142	};
    143
    144	switch (cmd) {
    145	case WDIOC_GETSUPPORT:
    146		if (copy_to_user(argp, &ident, sizeof(ident)))
    147			return -EFAULT;
    148		break;
    149
    150	case WDIOC_GETSTATUS:
    151	case WDIOC_GETBOOTSTATUS:
    152		return put_user(0, p);
    153
    154	case WDIOC_SETOPTIONS:
    155	{
    156		int options, retval = -EINVAL;
    157
    158		if (get_user(options, p))
    159			return -EFAULT;
    160		if (options & WDIOS_DISABLECARD) {
    161			advwdt_disable();
    162			retval = 0;
    163		}
    164		if (options & WDIOS_ENABLECARD) {
    165			advwdt_ping();
    166			retval = 0;
    167		}
    168		return retval;
    169	}
    170	case WDIOC_KEEPALIVE:
    171		advwdt_ping();
    172		break;
    173
    174	case WDIOC_SETTIMEOUT:
    175		if (get_user(new_timeout, p))
    176			return -EFAULT;
    177		if (advwdt_set_heartbeat(new_timeout))
    178			return -EINVAL;
    179		advwdt_ping();
    180		fallthrough;
    181	case WDIOC_GETTIMEOUT:
    182		return put_user(timeout, p);
    183	default:
    184		return -ENOTTY;
    185	}
    186	return 0;
    187}
    188
    189static int advwdt_open(struct inode *inode, struct file *file)
    190{
    191	if (test_and_set_bit(0, &advwdt_is_open))
    192		return -EBUSY;
    193	/*
    194	 *	Activate
    195	 */
    196
    197	advwdt_ping();
    198	return stream_open(inode, file);
    199}
    200
    201static int advwdt_close(struct inode *inode, struct file *file)
    202{
    203	if (adv_expect_close == 42) {
    204		advwdt_disable();
    205	} else {
    206		pr_crit("Unexpected close, not stopping watchdog!\n");
    207		advwdt_ping();
    208	}
    209	clear_bit(0, &advwdt_is_open);
    210	adv_expect_close = 0;
    211	return 0;
    212}
    213
    214/*
    215 *	Kernel Interfaces
    216 */
    217
    218static const struct file_operations advwdt_fops = {
    219	.owner		= THIS_MODULE,
    220	.llseek		= no_llseek,
    221	.write		= advwdt_write,
    222	.unlocked_ioctl	= advwdt_ioctl,
    223	.compat_ioctl	= compat_ptr_ioctl,
    224	.open		= advwdt_open,
    225	.release	= advwdt_close,
    226};
    227
    228static struct miscdevice advwdt_miscdev = {
    229	.minor	= WATCHDOG_MINOR,
    230	.name	= "watchdog",
    231	.fops	= &advwdt_fops,
    232};
    233
    234/*
    235 *	Init & exit routines
    236 */
    237
    238static int __init advwdt_probe(struct platform_device *dev)
    239{
    240	int ret;
    241
    242	if (wdt_stop != wdt_start) {
    243		if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
    244			pr_err("I/O address 0x%04x already in use\n",
    245			       wdt_stop);
    246			ret = -EIO;
    247			goto out;
    248		}
    249	}
    250
    251	if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
    252		pr_err("I/O address 0x%04x already in use\n", wdt_start);
    253		ret = -EIO;
    254		goto unreg_stop;
    255	}
    256
    257	/* Check that the heartbeat value is within it's range ;
    258	 * if not reset to the default */
    259	if (advwdt_set_heartbeat(timeout)) {
    260		advwdt_set_heartbeat(WATCHDOG_TIMEOUT);
    261		pr_info("timeout value must be 1<=x<=63, using %d\n", timeout);
    262	}
    263
    264	ret = misc_register(&advwdt_miscdev);
    265	if (ret != 0) {
    266		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
    267		       WATCHDOG_MINOR, ret);
    268		goto unreg_regions;
    269	}
    270	pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
    271		timeout, nowayout);
    272out:
    273	return ret;
    274unreg_regions:
    275	release_region(wdt_start, 1);
    276unreg_stop:
    277	if (wdt_stop != wdt_start)
    278		release_region(wdt_stop, 1);
    279	goto out;
    280}
    281
    282static int advwdt_remove(struct platform_device *dev)
    283{
    284	misc_deregister(&advwdt_miscdev);
    285	release_region(wdt_start, 1);
    286	if (wdt_stop != wdt_start)
    287		release_region(wdt_stop, 1);
    288
    289	return 0;
    290}
    291
    292static void advwdt_shutdown(struct platform_device *dev)
    293{
    294	/* Turn the WDT off if we have a soft shutdown */
    295	advwdt_disable();
    296}
    297
    298static struct platform_driver advwdt_driver = {
    299	.remove		= advwdt_remove,
    300	.shutdown	= advwdt_shutdown,
    301	.driver		= {
    302		.name	= DRV_NAME,
    303	},
    304};
    305
    306static int __init advwdt_init(void)
    307{
    308	int err;
    309
    310	pr_info("WDT driver for Advantech single board computer initialising\n");
    311
    312	advwdt_platform_device = platform_device_register_simple(DRV_NAME,
    313								-1, NULL, 0);
    314	if (IS_ERR(advwdt_platform_device))
    315		return PTR_ERR(advwdt_platform_device);
    316
    317	err = platform_driver_probe(&advwdt_driver, advwdt_probe);
    318	if (err)
    319		goto unreg_platform_device;
    320
    321	return 0;
    322
    323unreg_platform_device:
    324	platform_device_unregister(advwdt_platform_device);
    325	return err;
    326}
    327
    328static void __exit advwdt_exit(void)
    329{
    330	platform_device_unregister(advwdt_platform_device);
    331	platform_driver_unregister(&advwdt_driver);
    332	pr_info("Watchdog Module Unloaded\n");
    333}
    334
    335module_init(advwdt_init);
    336module_exit(advwdt_exit);
    337
    338MODULE_LICENSE("GPL");
    339MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
    340MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");