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

rdc321x_wdt.c (6704B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * RDC321x watchdog driver
      4 *
      5 * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
      6 *
      7 * This driver is highly inspired from the cpu5_wdt driver
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/moduleparam.h>
     12#include <linux/types.h>
     13#include <linux/errno.h>
     14#include <linux/miscdevice.h>
     15#include <linux/fs.h>
     16#include <linux/ioport.h>
     17#include <linux/timer.h>
     18#include <linux/completion.h>
     19#include <linux/jiffies.h>
     20#include <linux/platform_device.h>
     21#include <linux/watchdog.h>
     22#include <linux/io.h>
     23#include <linux/uaccess.h>
     24#include <linux/mfd/rdc321x.h>
     25
     26#define RDC_WDT_MASK	0x80000000 /* Mask */
     27#define RDC_WDT_EN	0x00800000 /* Enable bit */
     28#define RDC_WDT_WTI	0x00200000 /* Generate CPU reset/NMI/WDT on timeout */
     29#define RDC_WDT_RST	0x00100000 /* Reset bit */
     30#define RDC_WDT_WIF	0x00040000 /* WDT IRQ Flag */
     31#define RDC_WDT_IRT	0x00000100 /* IRQ Routing table */
     32#define RDC_WDT_CNT	0x00000001 /* WDT count */
     33
     34#define RDC_CLS_TMR	0x80003844 /* Clear timer */
     35
     36#define RDC_WDT_INTERVAL	(HZ/10+1)
     37
     38static int ticks = 1000;
     39
     40/* some device data */
     41
     42static struct {
     43	struct completion stop;
     44	int running;
     45	struct timer_list timer;
     46	int queue;
     47	int default_ticks;
     48	unsigned long inuse;
     49	spinlock_t lock;
     50	struct pci_dev *sb_pdev;
     51	int base_reg;
     52} rdc321x_wdt_device;
     53
     54/* generic helper functions */
     55
     56static void rdc321x_wdt_trigger(struct timer_list *unused)
     57{
     58	unsigned long flags;
     59	u32 val;
     60
     61	if (rdc321x_wdt_device.running)
     62		ticks--;
     63
     64	/* keep watchdog alive */
     65	spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
     66	pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
     67					rdc321x_wdt_device.base_reg, &val);
     68	val |= RDC_WDT_EN;
     69	pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
     70					rdc321x_wdt_device.base_reg, val);
     71	spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
     72
     73	/* requeue?? */
     74	if (rdc321x_wdt_device.queue && ticks)
     75		mod_timer(&rdc321x_wdt_device.timer,
     76				jiffies + RDC_WDT_INTERVAL);
     77	else {
     78		/* ticks doesn't matter anyway */
     79		complete(&rdc321x_wdt_device.stop);
     80	}
     81
     82}
     83
     84static void rdc321x_wdt_reset(void)
     85{
     86	ticks = rdc321x_wdt_device.default_ticks;
     87}
     88
     89static void rdc321x_wdt_start(void)
     90{
     91	unsigned long flags;
     92
     93	if (!rdc321x_wdt_device.queue) {
     94		rdc321x_wdt_device.queue = 1;
     95
     96		/* Clear the timer */
     97		spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
     98		pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
     99				rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
    100
    101		/* Enable watchdog and set the timeout to 81.92 us */
    102		pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
    103					rdc321x_wdt_device.base_reg,
    104					RDC_WDT_EN | RDC_WDT_CNT);
    105		spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
    106
    107		mod_timer(&rdc321x_wdt_device.timer,
    108				jiffies + RDC_WDT_INTERVAL);
    109	}
    110
    111	/* if process dies, counter is not decremented */
    112	rdc321x_wdt_device.running++;
    113}
    114
    115static int rdc321x_wdt_stop(void)
    116{
    117	if (rdc321x_wdt_device.running)
    118		rdc321x_wdt_device.running = 0;
    119
    120	ticks = rdc321x_wdt_device.default_ticks;
    121
    122	return -EIO;
    123}
    124
    125/* filesystem operations */
    126static int rdc321x_wdt_open(struct inode *inode, struct file *file)
    127{
    128	if (test_and_set_bit(0, &rdc321x_wdt_device.inuse))
    129		return -EBUSY;
    130
    131	return stream_open(inode, file);
    132}
    133
    134static int rdc321x_wdt_release(struct inode *inode, struct file *file)
    135{
    136	clear_bit(0, &rdc321x_wdt_device.inuse);
    137	return 0;
    138}
    139
    140static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
    141				unsigned long arg)
    142{
    143	void __user *argp = (void __user *)arg;
    144	u32 value;
    145	static const struct watchdog_info ident = {
    146		.options = WDIOF_CARDRESET,
    147		.identity = "RDC321x WDT",
    148	};
    149	unsigned long flags;
    150
    151	switch (cmd) {
    152	case WDIOC_KEEPALIVE:
    153		rdc321x_wdt_reset();
    154		break;
    155	case WDIOC_GETSTATUS:
    156		/* Read the value from the DATA register */
    157		spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
    158		pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
    159					rdc321x_wdt_device.base_reg, &value);
    160		spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
    161		if (copy_to_user(argp, &value, sizeof(u32)))
    162			return -EFAULT;
    163		break;
    164	case WDIOC_GETSUPPORT:
    165		if (copy_to_user(argp, &ident, sizeof(ident)))
    166			return -EFAULT;
    167		break;
    168	case WDIOC_SETOPTIONS:
    169		if (copy_from_user(&value, argp, sizeof(int)))
    170			return -EFAULT;
    171		switch (value) {
    172		case WDIOS_ENABLECARD:
    173			rdc321x_wdt_start();
    174			break;
    175		case WDIOS_DISABLECARD:
    176			return rdc321x_wdt_stop();
    177		default:
    178			return -EINVAL;
    179		}
    180		break;
    181	default:
    182		return -ENOTTY;
    183	}
    184	return 0;
    185}
    186
    187static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
    188				size_t count, loff_t *ppos)
    189{
    190	if (!count)
    191		return -EIO;
    192
    193	rdc321x_wdt_reset();
    194
    195	return count;
    196}
    197
    198static const struct file_operations rdc321x_wdt_fops = {
    199	.owner		= THIS_MODULE,
    200	.llseek		= no_llseek,
    201	.unlocked_ioctl	= rdc321x_wdt_ioctl,
    202	.compat_ioctl	= compat_ptr_ioctl,
    203	.open		= rdc321x_wdt_open,
    204	.write		= rdc321x_wdt_write,
    205	.release	= rdc321x_wdt_release,
    206};
    207
    208static struct miscdevice rdc321x_wdt_misc = {
    209	.minor	= WATCHDOG_MINOR,
    210	.name	= "watchdog",
    211	.fops	= &rdc321x_wdt_fops,
    212};
    213
    214static int rdc321x_wdt_probe(struct platform_device *pdev)
    215{
    216	int err;
    217	struct resource *r;
    218	struct rdc321x_wdt_pdata *pdata;
    219
    220	pdata = dev_get_platdata(&pdev->dev);
    221	if (!pdata) {
    222		dev_err(&pdev->dev, "no platform data supplied\n");
    223		return -ENODEV;
    224	}
    225
    226	r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
    227	if (!r) {
    228		dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
    229		return -ENODEV;
    230	}
    231
    232	rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
    233	rdc321x_wdt_device.base_reg = r->start;
    234	rdc321x_wdt_device.queue = 0;
    235	rdc321x_wdt_device.default_ticks = ticks;
    236
    237	err = misc_register(&rdc321x_wdt_misc);
    238	if (err < 0) {
    239		dev_err(&pdev->dev, "misc_register failed\n");
    240		return err;
    241	}
    242
    243	spin_lock_init(&rdc321x_wdt_device.lock);
    244
    245	/* Reset the watchdog */
    246	pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
    247				rdc321x_wdt_device.base_reg, RDC_WDT_RST);
    248
    249	init_completion(&rdc321x_wdt_device.stop);
    250
    251	clear_bit(0, &rdc321x_wdt_device.inuse);
    252
    253	timer_setup(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
    254
    255	dev_info(&pdev->dev, "watchdog init success\n");
    256
    257	return 0;
    258}
    259
    260static int rdc321x_wdt_remove(struct platform_device *pdev)
    261{
    262	if (rdc321x_wdt_device.queue) {
    263		rdc321x_wdt_device.queue = 0;
    264		wait_for_completion(&rdc321x_wdt_device.stop);
    265	}
    266
    267	misc_deregister(&rdc321x_wdt_misc);
    268
    269	return 0;
    270}
    271
    272static struct platform_driver rdc321x_wdt_driver = {
    273	.probe = rdc321x_wdt_probe,
    274	.remove = rdc321x_wdt_remove,
    275	.driver = {
    276		.name = "rdc321x-wdt",
    277	},
    278};
    279
    280module_platform_driver(rdc321x_wdt_driver);
    281
    282MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
    283MODULE_DESCRIPTION("RDC321x watchdog driver");
    284MODULE_LICENSE("GPL");