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

simatic-ipc-wdt.c (6014B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Siemens SIMATIC IPC driver for Watchdogs
      4 *
      5 * Copyright (c) Siemens AG, 2020-2021
      6 *
      7 * Authors:
      8 *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
      9 */
     10
     11#include <linux/device.h>
     12#include <linux/errno.h>
     13#include <linux/init.h>
     14#include <linux/io.h>
     15#include <linux/ioport.h>
     16#include <linux/kernel.h>
     17#include <linux/module.h>
     18#include <linux/pci.h>
     19#include <linux/platform_data/x86/simatic-ipc-base.h>
     20#include <linux/platform_device.h>
     21#include <linux/sizes.h>
     22#include <linux/util_macros.h>
     23#include <linux/watchdog.h>
     24
     25#define WD_ENABLE_IOADR			0x62
     26#define WD_TRIGGER_IOADR		0x66
     27#define GPIO_COMMUNITY0_PORT_ID		0xaf
     28#define PAD_CFG_DW0_GPP_A_23		0x4b8
     29#define SAFE_EN_N_427E			0x01
     30#define SAFE_EN_N_227E			0x04
     31#define WD_ENABLED			0x01
     32#define WD_TRIGGERED			0x80
     33#define WD_MACROMODE			0x02
     34
     35#define TIMEOUT_MIN	2
     36#define TIMEOUT_DEF	64
     37#define TIMEOUT_MAX	64
     38
     39#define GP_STATUS_REG_227E	0x404D	/* IO PORT for SAFE_EN_N on 227E */
     40
     41static bool nowayout = WATCHDOG_NOWAYOUT;
     42module_param(nowayout, bool, 0000);
     43MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
     44		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     45
     46static struct resource gp_status_reg_227e_res =
     47	DEFINE_RES_IO_NAMED(GP_STATUS_REG_227E, SZ_1, KBUILD_MODNAME);
     48
     49static struct resource io_resource_enable =
     50	DEFINE_RES_IO_NAMED(WD_ENABLE_IOADR, SZ_1,
     51			    KBUILD_MODNAME " WD_ENABLE_IOADR");
     52
     53static struct resource io_resource_trigger =
     54	DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1,
     55			    KBUILD_MODNAME " WD_TRIGGER_IOADR");
     56
     57/* the actual start will be discovered with pci, 0 is a placeholder */
     58static struct resource mem_resource =
     59	DEFINE_RES_MEM_NAMED(0, SZ_4, "WD_RESET_BASE_ADR");
     60
     61static u32 wd_timeout_table[] = {2, 4, 6, 8, 16, 32, 48, 64 };
     62static void __iomem *wd_reset_base_addr;
     63
     64static int wd_start(struct watchdog_device *wdd)
     65{
     66	outb(inb(WD_ENABLE_IOADR) | WD_ENABLED, WD_ENABLE_IOADR);
     67	return 0;
     68}
     69
     70static int wd_stop(struct watchdog_device *wdd)
     71{
     72	outb(inb(WD_ENABLE_IOADR) & ~WD_ENABLED, WD_ENABLE_IOADR);
     73	return 0;
     74}
     75
     76static int wd_ping(struct watchdog_device *wdd)
     77{
     78	inb(WD_TRIGGER_IOADR);
     79	return 0;
     80}
     81
     82static int wd_set_timeout(struct watchdog_device *wdd, unsigned int t)
     83{
     84	int timeout_idx = find_closest(t, wd_timeout_table,
     85				       ARRAY_SIZE(wd_timeout_table));
     86
     87	outb((inb(WD_ENABLE_IOADR) & 0xc7) | timeout_idx << 3, WD_ENABLE_IOADR);
     88	wdd->timeout = wd_timeout_table[timeout_idx];
     89	return 0;
     90}
     91
     92static const struct watchdog_info wdt_ident = {
     93	.options	= WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
     94			  WDIOF_SETTIMEOUT,
     95	.identity	= KBUILD_MODNAME,
     96};
     97
     98static const struct watchdog_ops wdt_ops = {
     99	.owner		= THIS_MODULE,
    100	.start		= wd_start,
    101	.stop		= wd_stop,
    102	.ping		= wd_ping,
    103	.set_timeout	= wd_set_timeout,
    104};
    105
    106static void wd_secondary_enable(u32 wdtmode)
    107{
    108	u16 resetbit;
    109
    110	/* set safe_en_n so we are not just WDIOF_ALARMONLY */
    111	if (wdtmode == SIMATIC_IPC_DEVICE_227E) {
    112		/* enable SAFE_EN_N on GP_STATUS_REG_227E */
    113		resetbit = inb(GP_STATUS_REG_227E);
    114		outb(resetbit & ~SAFE_EN_N_227E, GP_STATUS_REG_227E);
    115	} else {
    116		/* enable SAFE_EN_N on PCH D1600 */
    117		resetbit = ioread16(wd_reset_base_addr);
    118		iowrite16(resetbit & ~SAFE_EN_N_427E, wd_reset_base_addr);
    119	}
    120}
    121
    122static int wd_setup(u32 wdtmode)
    123{
    124	unsigned int bootstatus = 0;
    125	int timeout_idx;
    126
    127	timeout_idx = find_closest(TIMEOUT_DEF, wd_timeout_table,
    128				   ARRAY_SIZE(wd_timeout_table));
    129
    130	if (inb(WD_ENABLE_IOADR) & WD_TRIGGERED)
    131		bootstatus |= WDIOF_CARDRESET;
    132
    133	/* reset alarm bit, set macro mode, and set timeout */
    134	outb(WD_TRIGGERED | WD_MACROMODE | timeout_idx << 3, WD_ENABLE_IOADR);
    135
    136	wd_secondary_enable(wdtmode);
    137
    138	return bootstatus;
    139}
    140
    141static struct watchdog_device wdd_data = {
    142	.info = &wdt_ident,
    143	.ops = &wdt_ops,
    144	.min_timeout = TIMEOUT_MIN,
    145	.max_timeout = TIMEOUT_MAX
    146};
    147
    148static int simatic_ipc_wdt_probe(struct platform_device *pdev)
    149{
    150	struct simatic_ipc_platform *plat = pdev->dev.platform_data;
    151	struct device *dev = &pdev->dev;
    152	struct resource *res;
    153
    154	switch (plat->devmode) {
    155	case SIMATIC_IPC_DEVICE_227E:
    156		if (!devm_request_region(dev, gp_status_reg_227e_res.start,
    157					 resource_size(&gp_status_reg_227e_res),
    158					 KBUILD_MODNAME)) {
    159			dev_err(dev,
    160				"Unable to register IO resource at %pR\n",
    161				&gp_status_reg_227e_res);
    162			return -EBUSY;
    163		}
    164		fallthrough;
    165	case SIMATIC_IPC_DEVICE_427E:
    166		wdd_data.parent = dev;
    167		break;
    168	default:
    169		return -EINVAL;
    170	}
    171
    172	if (!devm_request_region(dev, io_resource_enable.start,
    173				 resource_size(&io_resource_enable),
    174				 io_resource_enable.name)) {
    175		dev_err(dev,
    176			"Unable to register IO resource at %#x\n",
    177			WD_ENABLE_IOADR);
    178		return -EBUSY;
    179	}
    180
    181	if (!devm_request_region(dev, io_resource_trigger.start,
    182				 resource_size(&io_resource_trigger),
    183				 io_resource_trigger.name)) {
    184		dev_err(dev,
    185			"Unable to register IO resource at %#x\n",
    186			WD_TRIGGER_IOADR);
    187		return -EBUSY;
    188	}
    189
    190	if (plat->devmode == SIMATIC_IPC_DEVICE_427E) {
    191		res = &mem_resource;
    192
    193		/* get GPIO base from PCI */
    194		res->start = simatic_ipc_get_membase0(PCI_DEVFN(0x1f, 1));
    195		if (res->start == 0)
    196			return -ENODEV;
    197
    198		/* do the final address calculation */
    199		res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) +
    200			     PAD_CFG_DW0_GPP_A_23;
    201		res->end += res->start;
    202
    203		wd_reset_base_addr = devm_ioremap_resource(dev, res);
    204		if (IS_ERR(wd_reset_base_addr))
    205			return PTR_ERR(wd_reset_base_addr);
    206	}
    207
    208	wdd_data.bootstatus = wd_setup(plat->devmode);
    209	if (wdd_data.bootstatus)
    210		dev_warn(dev, "last reboot caused by watchdog reset\n");
    211
    212	watchdog_set_nowayout(&wdd_data, nowayout);
    213	watchdog_stop_on_reboot(&wdd_data);
    214	return devm_watchdog_register_device(dev, &wdd_data);
    215}
    216
    217static struct platform_driver simatic_ipc_wdt_driver = {
    218	.probe = simatic_ipc_wdt_probe,
    219	.driver = {
    220		.name = KBUILD_MODNAME,
    221	},
    222};
    223
    224module_platform_driver(simatic_ipc_wdt_driver);
    225
    226MODULE_LICENSE("GPL v2");
    227MODULE_ALIAS("platform:" KBUILD_MODNAME);
    228MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");