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

ni903x_wdt.c (6306B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 National Instruments Corp.
      4 */
      5
      6#include <linux/acpi.h>
      7#include <linux/device.h>
      8#include <linux/interrupt.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/watchdog.h>
     12
     13#define NIWD_CONTROL	0x01
     14#define NIWD_COUNTER2	0x02
     15#define NIWD_COUNTER1	0x03
     16#define NIWD_COUNTER0	0x04
     17#define NIWD_SEED2	0x05
     18#define NIWD_SEED1	0x06
     19#define NIWD_SEED0	0x07
     20
     21#define NIWD_IO_SIZE	0x08
     22
     23#define NIWD_CONTROL_MODE		0x80
     24#define NIWD_CONTROL_PROC_RESET		0x20
     25#define NIWD_CONTROL_PET		0x10
     26#define NIWD_CONTROL_RUNNING		0x08
     27#define NIWD_CONTROL_CAPTURECOUNTER	0x04
     28#define NIWD_CONTROL_RESET		0x02
     29#define NIWD_CONTROL_ALARM		0x01
     30
     31#define NIWD_PERIOD_NS		30720
     32#define NIWD_MIN_TIMEOUT	1
     33#define NIWD_MAX_TIMEOUT	515
     34#define NIWD_DEFAULT_TIMEOUT	60
     35
     36#define NIWD_NAME		"ni903x_wdt"
     37
     38struct ni903x_wdt {
     39	struct device *dev;
     40	u16 io_base;
     41	struct watchdog_device wdd;
     42};
     43
     44static unsigned int timeout;
     45module_param(timeout, uint, 0);
     46MODULE_PARM_DESC(timeout,
     47		 "Watchdog timeout in seconds. (default="
     48		 __MODULE_STRING(NIWD_DEFAULT_TIMEOUT) ")");
     49
     50static int nowayout = WATCHDOG_NOWAYOUT;
     51module_param(nowayout, int, S_IRUGO);
     52MODULE_PARM_DESC(nowayout,
     53		 "Watchdog cannot be stopped once started (default="
     54		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     55
     56static void ni903x_start(struct ni903x_wdt *wdt)
     57{
     58	u8 control = inb(wdt->io_base + NIWD_CONTROL);
     59
     60	outb(control | NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL);
     61	outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL);
     62}
     63
     64static int ni903x_wdd_set_timeout(struct watchdog_device *wdd,
     65				  unsigned int timeout)
     66{
     67	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
     68	u32 counter = timeout * (1000000000 / NIWD_PERIOD_NS);
     69
     70	outb(((0x00FF0000 & counter) >> 16), wdt->io_base + NIWD_SEED2);
     71	outb(((0x0000FF00 & counter) >> 8), wdt->io_base + NIWD_SEED1);
     72	outb((0x000000FF & counter), wdt->io_base + NIWD_SEED0);
     73
     74	wdd->timeout = timeout;
     75
     76	return 0;
     77}
     78
     79static unsigned int ni903x_wdd_get_timeleft(struct watchdog_device *wdd)
     80{
     81	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
     82	u8 control, counter0, counter1, counter2;
     83	u32 counter;
     84
     85	control = inb(wdt->io_base + NIWD_CONTROL);
     86	control |= NIWD_CONTROL_CAPTURECOUNTER;
     87	outb(control, wdt->io_base + NIWD_CONTROL);
     88
     89	counter2 = inb(wdt->io_base + NIWD_COUNTER2);
     90	counter1 = inb(wdt->io_base + NIWD_COUNTER1);
     91	counter0 = inb(wdt->io_base + NIWD_COUNTER0);
     92
     93	counter = (counter2 << 16) | (counter1 << 8) | counter0;
     94
     95	return counter / (1000000000 / NIWD_PERIOD_NS);
     96}
     97
     98static int ni903x_wdd_ping(struct watchdog_device *wdd)
     99{
    100	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
    101	u8 control;
    102
    103	control = inb(wdt->io_base + NIWD_CONTROL);
    104	outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL);
    105
    106	return 0;
    107}
    108
    109static int ni903x_wdd_start(struct watchdog_device *wdd)
    110{
    111	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
    112
    113	outb(NIWD_CONTROL_RESET | NIWD_CONTROL_PROC_RESET,
    114	     wdt->io_base + NIWD_CONTROL);
    115
    116	ni903x_wdd_set_timeout(wdd, wdd->timeout);
    117	ni903x_start(wdt);
    118
    119	return 0;
    120}
    121
    122static int ni903x_wdd_stop(struct watchdog_device *wdd)
    123{
    124	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
    125
    126	outb(NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL);
    127
    128	return 0;
    129}
    130
    131static acpi_status ni903x_resources(struct acpi_resource *res, void *data)
    132{
    133	struct ni903x_wdt *wdt = data;
    134	u16 io_size;
    135
    136	switch (res->type) {
    137	case ACPI_RESOURCE_TYPE_IO:
    138		if (wdt->io_base != 0) {
    139			dev_err(wdt->dev, "too many IO resources\n");
    140			return AE_ERROR;
    141		}
    142
    143		wdt->io_base = res->data.io.minimum;
    144		io_size = res->data.io.address_length;
    145
    146		if (io_size < NIWD_IO_SIZE) {
    147			dev_err(wdt->dev, "memory region too small\n");
    148			return AE_ERROR;
    149		}
    150
    151		if (!devm_request_region(wdt->dev, wdt->io_base, io_size,
    152					 NIWD_NAME)) {
    153			dev_err(wdt->dev, "failed to get memory region\n");
    154			return AE_ERROR;
    155		}
    156
    157		return AE_OK;
    158
    159	case ACPI_RESOURCE_TYPE_END_TAG:
    160	default:
    161		/* Ignore unsupported resources, e.g. IRQ */
    162		return AE_OK;
    163	}
    164}
    165
    166static const struct watchdog_info ni903x_wdd_info = {
    167	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    168	.identity = "NI Watchdog",
    169};
    170
    171static const struct watchdog_ops ni903x_wdd_ops = {
    172	.owner = THIS_MODULE,
    173	.start = ni903x_wdd_start,
    174	.stop = ni903x_wdd_stop,
    175	.ping = ni903x_wdd_ping,
    176	.set_timeout = ni903x_wdd_set_timeout,
    177	.get_timeleft = ni903x_wdd_get_timeleft,
    178};
    179
    180static int ni903x_acpi_add(struct acpi_device *device)
    181{
    182	struct device *dev = &device->dev;
    183	struct watchdog_device *wdd;
    184	struct ni903x_wdt *wdt;
    185	acpi_status status;
    186	int ret;
    187
    188	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
    189	if (!wdt)
    190		return -ENOMEM;
    191
    192	device->driver_data = wdt;
    193	wdt->dev = dev;
    194
    195	status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
    196				     ni903x_resources, wdt);
    197	if (ACPI_FAILURE(status) || wdt->io_base == 0) {
    198		dev_err(dev, "failed to get resources\n");
    199		return -ENODEV;
    200	}
    201
    202	wdd = &wdt->wdd;
    203	wdd->info = &ni903x_wdd_info;
    204	wdd->ops = &ni903x_wdd_ops;
    205	wdd->min_timeout = NIWD_MIN_TIMEOUT;
    206	wdd->max_timeout = NIWD_MAX_TIMEOUT;
    207	wdd->timeout = NIWD_DEFAULT_TIMEOUT;
    208	wdd->parent = dev;
    209	watchdog_set_drvdata(wdd, wdt);
    210	watchdog_set_nowayout(wdd, nowayout);
    211	watchdog_init_timeout(wdd, timeout, dev);
    212
    213	ret = watchdog_register_device(wdd);
    214	if (ret)
    215		return ret;
    216
    217	/* Switch from boot mode to user mode */
    218	outb(NIWD_CONTROL_RESET | NIWD_CONTROL_MODE,
    219	     wdt->io_base + NIWD_CONTROL);
    220
    221	dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
    222		wdt->io_base, timeout, nowayout);
    223
    224	return 0;
    225}
    226
    227static int ni903x_acpi_remove(struct acpi_device *device)
    228{
    229	struct ni903x_wdt *wdt = acpi_driver_data(device);
    230
    231	ni903x_wdd_stop(&wdt->wdd);
    232	watchdog_unregister_device(&wdt->wdd);
    233
    234	return 0;
    235}
    236
    237static const struct acpi_device_id ni903x_device_ids[] = {
    238	{"NIC775C", 0},
    239	{"", 0},
    240};
    241MODULE_DEVICE_TABLE(acpi, ni903x_device_ids);
    242
    243static struct acpi_driver ni903x_acpi_driver = {
    244	.name = NIWD_NAME,
    245	.ids = ni903x_device_ids,
    246	.ops = {
    247		.add = ni903x_acpi_add,
    248		.remove = ni903x_acpi_remove,
    249	},
    250};
    251
    252module_acpi_driver(ni903x_acpi_driver);
    253
    254MODULE_DESCRIPTION("NI 903x Watchdog");
    255MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@ni.com>");
    256MODULE_AUTHOR("Kyle Roeschley <kyle.roeschley@ni.com>");
    257MODULE_LICENSE("GPL");