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

intel-mid_wdt.c (5021B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *      intel-mid_wdt: generic Intel MID SCU watchdog driver
      4 *
      5 *      Platforms supported so far:
      6 *      - Merrifield only
      7 *
      8 *      Copyright (C) 2014 Intel Corporation. All rights reserved.
      9 *      Contact: David Cohen <david.a.cohen@linux.intel.com>
     10 */
     11
     12#include <linux/interrupt.h>
     13#include <linux/module.h>
     14#include <linux/nmi.h>
     15#include <linux/platform_device.h>
     16#include <linux/watchdog.h>
     17#include <linux/platform_data/intel-mid_wdt.h>
     18
     19#include <asm/intel_scu_ipc.h>
     20#include <asm/intel-mid.h>
     21
     22#define IPC_WATCHDOG 0xf8
     23
     24#define MID_WDT_PRETIMEOUT		15
     25#define MID_WDT_TIMEOUT_MIN		(1 + MID_WDT_PRETIMEOUT)
     26#define MID_WDT_TIMEOUT_MAX		170
     27#define MID_WDT_DEFAULT_TIMEOUT		90
     28
     29/* SCU watchdog messages */
     30enum {
     31	SCU_WATCHDOG_START = 0,
     32	SCU_WATCHDOG_STOP,
     33	SCU_WATCHDOG_KEEPALIVE,
     34};
     35
     36struct mid_wdt {
     37	struct watchdog_device wd;
     38	struct device *dev;
     39	struct intel_scu_ipc_dev *scu;
     40};
     41
     42static inline int
     43wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
     44{
     45	struct intel_scu_ipc_dev *scu = mid->scu;
     46
     47	return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
     48						   inlen, size, NULL, 0);
     49}
     50
     51static int wdt_start(struct watchdog_device *wd)
     52{
     53	struct mid_wdt *mid = watchdog_get_drvdata(wd);
     54	int ret, in_size;
     55	int timeout = wd->timeout;
     56	struct ipc_wd_start {
     57		u32 pretimeout;
     58		u32 timeout;
     59	} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
     60
     61	/*
     62	 * SCU expects the input size for watchdog IPC to be 2 which is the
     63	 * size of the structure in dwords. SCU IPC normally takes bytes
     64	 * but this is a special case where we specify size to be different
     65	 * than inlen.
     66	 */
     67	in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
     68
     69	ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
     70			  sizeof(ipc_wd_start), in_size);
     71	if (ret)
     72		dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
     73
     74	return ret;
     75}
     76
     77static int wdt_ping(struct watchdog_device *wd)
     78{
     79	struct mid_wdt *mid = watchdog_get_drvdata(wd);
     80	int ret;
     81
     82	ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
     83	if (ret)
     84		dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
     85
     86	return ret;
     87}
     88
     89static int wdt_stop(struct watchdog_device *wd)
     90{
     91	struct mid_wdt *mid = watchdog_get_drvdata(wd);
     92	int ret;
     93
     94	ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
     95	if (ret)
     96		dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
     97
     98	return ret;
     99}
    100
    101static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
    102{
    103	panic("Kernel Watchdog");
    104
    105	/* This code should not be reached */
    106	return IRQ_HANDLED;
    107}
    108
    109static const struct watchdog_info mid_wdt_info = {
    110	.identity = "Intel MID SCU watchdog",
    111	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
    112};
    113
    114static const struct watchdog_ops mid_wdt_ops = {
    115	.owner = THIS_MODULE,
    116	.start = wdt_start,
    117	.stop = wdt_stop,
    118	.ping = wdt_ping,
    119};
    120
    121static int mid_wdt_probe(struct platform_device *pdev)
    122{
    123	struct device *dev = &pdev->dev;
    124	struct watchdog_device *wdt_dev;
    125	struct intel_mid_wdt_pdata *pdata = dev->platform_data;
    126	struct mid_wdt *mid;
    127	int ret;
    128
    129	if (!pdata) {
    130		dev_err(dev, "missing platform data\n");
    131		return -EINVAL;
    132	}
    133
    134	if (pdata->probe) {
    135		ret = pdata->probe(pdev);
    136		if (ret)
    137			return ret;
    138	}
    139
    140	mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
    141	if (!mid)
    142		return -ENOMEM;
    143
    144	mid->dev = dev;
    145	wdt_dev = &mid->wd;
    146
    147	wdt_dev->info = &mid_wdt_info;
    148	wdt_dev->ops = &mid_wdt_ops;
    149	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
    150	wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
    151	wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
    152	wdt_dev->parent = dev;
    153
    154	watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
    155	watchdog_set_drvdata(wdt_dev, mid);
    156
    157	mid->scu = devm_intel_scu_ipc_dev_get(dev);
    158	if (!mid->scu)
    159		return -EPROBE_DEFER;
    160
    161	ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
    162			       IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
    163			       wdt_dev);
    164	if (ret) {
    165		dev_err(dev, "error requesting warning irq %d\n", pdata->irq);
    166		return ret;
    167	}
    168
    169	/*
    170	 * The firmware followed by U-Boot leaves the watchdog running
    171	 * with the default threshold which may vary. When we get here
    172	 * we should make a decision to prevent any side effects before
    173	 * user space daemon will take care of it. The best option,
    174	 * taking into consideration that there is no way to read values
    175	 * back from hardware, is to enforce watchdog being run with
    176	 * deterministic values.
    177	 */
    178	ret = wdt_start(wdt_dev);
    179	if (ret)
    180		return ret;
    181
    182	/* Make sure the watchdog is serviced */
    183	set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
    184
    185	ret = devm_watchdog_register_device(dev, wdt_dev);
    186	if (ret)
    187		return ret;
    188
    189	dev_info(dev, "Intel MID watchdog device probed\n");
    190
    191	return 0;
    192}
    193
    194static struct platform_driver mid_wdt_driver = {
    195	.probe		= mid_wdt_probe,
    196	.driver		= {
    197		.name	= "intel_mid_wdt",
    198	},
    199};
    200
    201module_platform_driver(mid_wdt_driver);
    202
    203MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
    204MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
    205MODULE_LICENSE("GPL");