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

via_wdt.c (7628B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * VIA Chipset Watchdog Driver
      4 *
      5 * Copyright (C) 2011 Sigfox
      6 * Author: Marc Vertes <marc.vertes@sigfox.com>
      7 * Based on a preliminary version from Harald Welte <HaraldWelte@viatech.com>
      8 * Timer code by Wim Van Sebroeck <wim@iguana.be>
      9 *
     10 * Caveat: PnP must be enabled in BIOS to allow full access to watchdog
     11 * control registers. If not, the watchdog must be configured in BIOS manually.
     12 */
     13
     14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     15
     16#include <linux/device.h>
     17#include <linux/io.h>
     18#include <linux/jiffies.h>
     19#include <linux/module.h>
     20#include <linux/pci.h>
     21#include <linux/timer.h>
     22#include <linux/watchdog.h>
     23
     24/* Configuration registers relative to the pci device */
     25#define VIA_WDT_MMIO_BASE	0xe8	/* MMIO region base address */
     26#define VIA_WDT_CONF		0xec	/* watchdog enable state */
     27
     28/* Relevant bits for the VIA_WDT_CONF register */
     29#define VIA_WDT_CONF_ENABLE	0x01	/* 1: enable watchdog */
     30#define VIA_WDT_CONF_MMIO	0x02	/* 1: enable watchdog MMIO */
     31
     32/*
     33 * The MMIO region contains the watchdog control register and the
     34 * hardware timer counter.
     35 */
     36#define VIA_WDT_MMIO_LEN	8	/* MMIO region length in bytes */
     37#define VIA_WDT_CTL		0	/* MMIO addr+0: state/control reg. */
     38#define VIA_WDT_COUNT		4	/* MMIO addr+4: timer counter reg. */
     39
     40/* Bits for the VIA_WDT_CTL register */
     41#define VIA_WDT_RUNNING		0x01	/* 0: stop, 1: running */
     42#define VIA_WDT_FIRED		0x02	/* 1: restarted by expired watchdog */
     43#define VIA_WDT_PWROFF		0x04	/* 0: reset, 1: poweroff */
     44#define VIA_WDT_DISABLED	0x08	/* 1: timer is disabled */
     45#define VIA_WDT_TRIGGER		0x80	/* 1: start a new countdown */
     46
     47/* Hardware heartbeat in seconds */
     48#define WDT_HW_HEARTBEAT 1
     49
     50/* Timer heartbeat (500ms) */
     51#define WDT_HEARTBEAT	(HZ/2)	/* should be <= ((WDT_HW_HEARTBEAT*HZ)/2) */
     52
     53/* User space timeout in seconds */
     54#define WDT_TIMEOUT_MAX	1023	/* approx. 17 min. */
     55#define WDT_TIMEOUT	60
     56static int timeout = WDT_TIMEOUT;
     57module_param(timeout, int, 0);
     58MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 "
     59	"(default = " __MODULE_STRING(WDT_TIMEOUT) ")");
     60
     61static bool nowayout = WATCHDOG_NOWAYOUT;
     62module_param(nowayout, bool, 0);
     63MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
     64	"(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     65
     66static struct watchdog_device wdt_dev;
     67static struct resource wdt_res;
     68static void __iomem *wdt_mem;
     69static unsigned int mmio;
     70static void wdt_timer_tick(struct timer_list *unused);
     71static DEFINE_TIMER(timer, wdt_timer_tick);
     72					/* The timer that pings the watchdog */
     73static unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
     74
     75static inline void wdt_reset(void)
     76{
     77	unsigned int ctl = readl(wdt_mem);
     78
     79	writel(ctl | VIA_WDT_TRIGGER, wdt_mem);
     80}
     81
     82/*
     83 * Timer tick: the timer will make sure that the watchdog timer hardware
     84 * is being reset in time. The conditions to do this are:
     85 *  1) the watchdog timer has been started and /dev/watchdog is open
     86 *     and there is still time left before userspace should send the
     87 *     next heartbeat/ping. (note: the internal heartbeat is much smaller
     88 *     then the external/userspace heartbeat).
     89 *  2) the watchdog timer has been stopped by userspace.
     90 */
     91static void wdt_timer_tick(struct timer_list *unused)
     92{
     93	if (time_before(jiffies, next_heartbeat) ||
     94	   (!watchdog_active(&wdt_dev))) {
     95		wdt_reset();
     96		mod_timer(&timer, jiffies + WDT_HEARTBEAT);
     97	} else
     98		pr_crit("I will reboot your machine !\n");
     99}
    100
    101static int wdt_ping(struct watchdog_device *wdd)
    102{
    103	/* calculate when the next userspace timeout will be */
    104	next_heartbeat = jiffies + wdd->timeout * HZ;
    105	return 0;
    106}
    107
    108static int wdt_start(struct watchdog_device *wdd)
    109{
    110	unsigned int ctl = readl(wdt_mem);
    111
    112	writel(wdd->timeout, wdt_mem + VIA_WDT_COUNT);
    113	writel(ctl | VIA_WDT_RUNNING | VIA_WDT_TRIGGER, wdt_mem);
    114	wdt_ping(wdd);
    115	mod_timer(&timer, jiffies + WDT_HEARTBEAT);
    116	return 0;
    117}
    118
    119static int wdt_stop(struct watchdog_device *wdd)
    120{
    121	unsigned int ctl = readl(wdt_mem);
    122
    123	writel(ctl & ~VIA_WDT_RUNNING, wdt_mem);
    124	return 0;
    125}
    126
    127static int wdt_set_timeout(struct watchdog_device *wdd,
    128			   unsigned int new_timeout)
    129{
    130	writel(new_timeout, wdt_mem + VIA_WDT_COUNT);
    131	wdd->timeout = new_timeout;
    132	return 0;
    133}
    134
    135static const struct watchdog_info wdt_info = {
    136	.identity =	"VIA watchdog",
    137	.options =	WDIOF_CARDRESET |
    138			WDIOF_SETTIMEOUT |
    139			WDIOF_MAGICCLOSE |
    140			WDIOF_KEEPALIVEPING,
    141};
    142
    143static const struct watchdog_ops wdt_ops = {
    144	.owner =	THIS_MODULE,
    145	.start =	wdt_start,
    146	.stop =		wdt_stop,
    147	.ping =		wdt_ping,
    148	.set_timeout =	wdt_set_timeout,
    149};
    150
    151static struct watchdog_device wdt_dev = {
    152	.info =		&wdt_info,
    153	.ops =		&wdt_ops,
    154	.min_timeout =	1,
    155	.max_timeout =	WDT_TIMEOUT_MAX,
    156};
    157
    158static int wdt_probe(struct pci_dev *pdev,
    159			       const struct pci_device_id *ent)
    160{
    161	unsigned char conf;
    162	int ret = -ENODEV;
    163
    164	if (pci_enable_device(pdev)) {
    165		dev_err(&pdev->dev, "cannot enable PCI device\n");
    166		return -ENODEV;
    167	}
    168
    169	/*
    170	 * Allocate a MMIO region which contains watchdog control register
    171	 * and counter, then configure the watchdog to use this region.
    172	 * This is possible only if PnP is properly enabled in BIOS.
    173	 * If not, the watchdog must be configured in BIOS manually.
    174	 */
    175	if (allocate_resource(&iomem_resource, &wdt_res, VIA_WDT_MMIO_LEN,
    176			      0xf0000000, 0xffffff00, 0xff, NULL, NULL)) {
    177		dev_err(&pdev->dev, "MMIO allocation failed\n");
    178		goto err_out_disable_device;
    179	}
    180
    181	pci_write_config_dword(pdev, VIA_WDT_MMIO_BASE, wdt_res.start);
    182	pci_read_config_byte(pdev, VIA_WDT_CONF, &conf);
    183	conf |= VIA_WDT_CONF_ENABLE | VIA_WDT_CONF_MMIO;
    184	pci_write_config_byte(pdev, VIA_WDT_CONF, conf);
    185
    186	pci_read_config_dword(pdev, VIA_WDT_MMIO_BASE, &mmio);
    187	if (mmio) {
    188		dev_info(&pdev->dev, "VIA Chipset watchdog MMIO: %x\n", mmio);
    189	} else {
    190		dev_err(&pdev->dev, "MMIO setting failed. Check BIOS.\n");
    191		goto err_out_resource;
    192	}
    193
    194	if (!request_mem_region(mmio, VIA_WDT_MMIO_LEN, "via_wdt")) {
    195		dev_err(&pdev->dev, "MMIO region busy\n");
    196		goto err_out_resource;
    197	}
    198
    199	wdt_mem = ioremap(mmio, VIA_WDT_MMIO_LEN);
    200	if (wdt_mem == NULL) {
    201		dev_err(&pdev->dev, "cannot remap VIA wdt MMIO registers\n");
    202		goto err_out_release;
    203	}
    204
    205	if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
    206		timeout = WDT_TIMEOUT;
    207
    208	wdt_dev.timeout = timeout;
    209	wdt_dev.parent = &pdev->dev;
    210	watchdog_set_nowayout(&wdt_dev, nowayout);
    211	if (readl(wdt_mem) & VIA_WDT_FIRED)
    212		wdt_dev.bootstatus |= WDIOF_CARDRESET;
    213
    214	ret = watchdog_register_device(&wdt_dev);
    215	if (ret)
    216		goto err_out_iounmap;
    217
    218	/* start triggering, in case of watchdog already enabled by BIOS */
    219	mod_timer(&timer, jiffies + WDT_HEARTBEAT);
    220	return 0;
    221
    222err_out_iounmap:
    223	iounmap(wdt_mem);
    224err_out_release:
    225	release_mem_region(mmio, VIA_WDT_MMIO_LEN);
    226err_out_resource:
    227	release_resource(&wdt_res);
    228err_out_disable_device:
    229	pci_disable_device(pdev);
    230	return ret;
    231}
    232
    233static void wdt_remove(struct pci_dev *pdev)
    234{
    235	watchdog_unregister_device(&wdt_dev);
    236	del_timer_sync(&timer);
    237	iounmap(wdt_mem);
    238	release_mem_region(mmio, VIA_WDT_MMIO_LEN);
    239	release_resource(&wdt_res);
    240	pci_disable_device(pdev);
    241}
    242
    243static const struct pci_device_id wdt_pci_table[] = {
    244	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
    245	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
    246	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
    247	{ 0 }
    248};
    249
    250static struct pci_driver wdt_driver = {
    251	.name		= "via_wdt",
    252	.id_table	= wdt_pci_table,
    253	.probe		= wdt_probe,
    254	.remove		= wdt_remove,
    255};
    256
    257module_pci_driver(wdt_driver);
    258
    259MODULE_AUTHOR("Marc Vertes");
    260MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
    261MODULE_LICENSE("GPL");