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

rave-sp-wdt.c (8288B)


      1// SPDX-License-Identifier: GPL-2.0+
      2
      3/*
      4 * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE
      5 * Supervisory Processor(SP) MCU
      6 *
      7 * Copyright (C) 2017 Zodiac Inflight Innovation
      8 *
      9 */
     10
     11#include <linux/delay.h>
     12#include <linux/kernel.h>
     13#include <linux/mfd/rave-sp.h>
     14#include <linux/module.h>
     15#include <linux/nvmem-consumer.h>
     16#include <linux/of_device.h>
     17#include <linux/platform_device.h>
     18#include <linux/reboot.h>
     19#include <linux/slab.h>
     20#include <linux/watchdog.h>
     21
     22enum {
     23	RAVE_SP_RESET_BYTE = 1,
     24	RAVE_SP_RESET_REASON_NORMAL = 0,
     25	RAVE_SP_RESET_DELAY_MS = 500,
     26};
     27
     28/**
     29 * struct rave_sp_wdt_variant - RAVE SP watchdog variant
     30 *
     31 * @max_timeout:	Largest possible watchdog timeout setting
     32 * @min_timeout:	Smallest possible watchdog timeout setting
     33 *
     34 * @configure:		Function to send configuration command
     35 * @restart:		Function to send "restart" command
     36 */
     37struct rave_sp_wdt_variant {
     38	unsigned int max_timeout;
     39	unsigned int min_timeout;
     40
     41	int (*configure)(struct watchdog_device *, bool);
     42	int (*restart)(struct watchdog_device *);
     43};
     44
     45/**
     46 * struct rave_sp_wdt - RAVE SP watchdog
     47 *
     48 * @wdd:		Underlying watchdog device
     49 * @sp:			Pointer to parent RAVE SP device
     50 * @variant:		Device specific variant information
     51 * @reboot_notifier:	Reboot notifier implementing machine reset
     52 */
     53struct rave_sp_wdt {
     54	struct watchdog_device wdd;
     55	struct rave_sp *sp;
     56	const struct rave_sp_wdt_variant *variant;
     57	struct notifier_block reboot_notifier;
     58};
     59
     60static struct rave_sp_wdt *to_rave_sp_wdt(struct watchdog_device *wdd)
     61{
     62	return container_of(wdd, struct rave_sp_wdt, wdd);
     63}
     64
     65static int rave_sp_wdt_exec(struct watchdog_device *wdd, void *data,
     66			    size_t data_size)
     67{
     68	return rave_sp_exec(to_rave_sp_wdt(wdd)->sp,
     69			    data, data_size, NULL, 0);
     70}
     71
     72static int rave_sp_wdt_legacy_configure(struct watchdog_device *wdd, bool on)
     73{
     74	u8 cmd[] = {
     75		[0] = RAVE_SP_CMD_SW_WDT,
     76		[1] = 0,
     77		[2] = 0,
     78		[3] = on,
     79		[4] = on ? wdd->timeout : 0,
     80	};
     81
     82	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
     83}
     84
     85static int rave_sp_wdt_rdu_configure(struct watchdog_device *wdd, bool on)
     86{
     87	u8 cmd[] = {
     88		[0] = RAVE_SP_CMD_SW_WDT,
     89		[1] = 0,
     90		[2] = on,
     91		[3] = (u8)wdd->timeout,
     92		[4] = (u8)(wdd->timeout >> 8),
     93	};
     94
     95	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
     96}
     97
     98/**
     99 * rave_sp_wdt_configure - Configure watchdog device
    100 *
    101 * @wdd:	Device to configure
    102 * @on:		Desired state of the watchdog timer (ON/OFF)
    103 *
    104 * This function configures two aspects of the watchdog timer:
    105 *
    106 *  - Wheither it is ON or OFF
    107 *  - Its timeout duration
    108 *
    109 * with first aspect specified via function argument and second via
    110 * the value of 'wdd->timeout'.
    111 */
    112static int rave_sp_wdt_configure(struct watchdog_device *wdd, bool on)
    113{
    114	return to_rave_sp_wdt(wdd)->variant->configure(wdd, on);
    115}
    116
    117static int rave_sp_wdt_legacy_restart(struct watchdog_device *wdd)
    118{
    119	u8 cmd[] = {
    120		[0] = RAVE_SP_CMD_RESET,
    121		[1] = 0,
    122		[2] = RAVE_SP_RESET_BYTE
    123	};
    124
    125	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
    126}
    127
    128static int rave_sp_wdt_rdu_restart(struct watchdog_device *wdd)
    129{
    130	u8 cmd[] = {
    131		[0] = RAVE_SP_CMD_RESET,
    132		[1] = 0,
    133		[2] = RAVE_SP_RESET_BYTE,
    134		[3] = RAVE_SP_RESET_REASON_NORMAL
    135	};
    136
    137	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
    138}
    139
    140static int rave_sp_wdt_reboot_notifier(struct notifier_block *nb,
    141				       unsigned long action, void *data)
    142{
    143	/*
    144	 * Restart handler is called in atomic context which means we
    145	 * can't communicate to SP via UART. Luckily for use SP will
    146	 * wait 500ms before actually resetting us, so we ask it to do
    147	 * so here and let the rest of the system go on wrapping
    148	 * things up.
    149	 */
    150	if (action == SYS_DOWN || action == SYS_HALT) {
    151		struct rave_sp_wdt *sp_wd =
    152			container_of(nb, struct rave_sp_wdt, reboot_notifier);
    153
    154		const int ret = sp_wd->variant->restart(&sp_wd->wdd);
    155
    156		if (ret < 0)
    157			dev_err(sp_wd->wdd.parent,
    158				"Failed to issue restart command (%d)", ret);
    159		return NOTIFY_OK;
    160	}
    161
    162	return NOTIFY_DONE;
    163}
    164
    165static int rave_sp_wdt_restart(struct watchdog_device *wdd,
    166			       unsigned long action, void *data)
    167{
    168	/*
    169	 * The actual work was done by reboot notifier above. SP
    170	 * firmware waits 500 ms before issuing reset, so let's hang
    171	 * here for twice that delay and hopefuly we'd never reach
    172	 * the return statement.
    173	 */
    174	mdelay(2 * RAVE_SP_RESET_DELAY_MS);
    175
    176	return -EIO;
    177}
    178
    179static int rave_sp_wdt_start(struct watchdog_device *wdd)
    180{
    181	int ret;
    182
    183	ret = rave_sp_wdt_configure(wdd, true);
    184	if (!ret)
    185		set_bit(WDOG_HW_RUNNING, &wdd->status);
    186
    187	return ret;
    188}
    189
    190static int rave_sp_wdt_stop(struct watchdog_device *wdd)
    191{
    192	return rave_sp_wdt_configure(wdd, false);
    193}
    194
    195static int rave_sp_wdt_set_timeout(struct watchdog_device *wdd,
    196				   unsigned int timeout)
    197{
    198	wdd->timeout = timeout;
    199
    200	return rave_sp_wdt_configure(wdd, watchdog_active(wdd));
    201}
    202
    203static int rave_sp_wdt_ping(struct watchdog_device *wdd)
    204{
    205	u8 cmd[] = {
    206		[0] = RAVE_SP_CMD_PET_WDT,
    207		[1] = 0,
    208	};
    209
    210	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
    211}
    212
    213static const struct watchdog_info rave_sp_wdt_info = {
    214	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    215	.identity = "RAVE SP Watchdog",
    216};
    217
    218static const struct watchdog_ops rave_sp_wdt_ops = {
    219	.owner = THIS_MODULE,
    220	.start = rave_sp_wdt_start,
    221	.stop = rave_sp_wdt_stop,
    222	.ping = rave_sp_wdt_ping,
    223	.set_timeout = rave_sp_wdt_set_timeout,
    224	.restart = rave_sp_wdt_restart,
    225};
    226
    227static const struct rave_sp_wdt_variant rave_sp_wdt_legacy = {
    228	.max_timeout = 255,
    229	.min_timeout = 1,
    230	.configure = rave_sp_wdt_legacy_configure,
    231	.restart   = rave_sp_wdt_legacy_restart,
    232};
    233
    234static const struct rave_sp_wdt_variant rave_sp_wdt_rdu = {
    235	.max_timeout = 180,
    236	.min_timeout = 60,
    237	.configure = rave_sp_wdt_rdu_configure,
    238	.restart   = rave_sp_wdt_rdu_restart,
    239};
    240
    241static const struct of_device_id rave_sp_wdt_of_match[] = {
    242	{
    243		.compatible = "zii,rave-sp-watchdog-legacy",
    244		.data = &rave_sp_wdt_legacy,
    245	},
    246	{
    247		.compatible = "zii,rave-sp-watchdog",
    248		.data = &rave_sp_wdt_rdu,
    249	},
    250	{ /* sentinel */ }
    251};
    252
    253static int rave_sp_wdt_probe(struct platform_device *pdev)
    254{
    255	struct device *dev = &pdev->dev;
    256	struct watchdog_device *wdd;
    257	struct rave_sp_wdt *sp_wd;
    258	struct nvmem_cell *cell;
    259	__le16 timeout = 0;
    260	int ret;
    261
    262	sp_wd = devm_kzalloc(dev, sizeof(*sp_wd), GFP_KERNEL);
    263	if (!sp_wd)
    264		return -ENOMEM;
    265
    266	sp_wd->variant = of_device_get_match_data(dev);
    267	sp_wd->sp      = dev_get_drvdata(dev->parent);
    268
    269	wdd              = &sp_wd->wdd;
    270	wdd->parent      = dev;
    271	wdd->info        = &rave_sp_wdt_info;
    272	wdd->ops         = &rave_sp_wdt_ops;
    273	wdd->min_timeout = sp_wd->variant->min_timeout;
    274	wdd->max_timeout = sp_wd->variant->max_timeout;
    275	wdd->status      = WATCHDOG_NOWAYOUT_INIT_STATUS;
    276	wdd->timeout     = 60;
    277
    278	cell = nvmem_cell_get(dev, "wdt-timeout");
    279	if (!IS_ERR(cell)) {
    280		size_t len;
    281		void *value = nvmem_cell_read(cell, &len);
    282
    283		if (!IS_ERR(value)) {
    284			memcpy(&timeout, value, min(len, sizeof(timeout)));
    285			kfree(value);
    286		}
    287		nvmem_cell_put(cell);
    288	}
    289	watchdog_init_timeout(wdd, le16_to_cpu(timeout), dev);
    290	watchdog_set_restart_priority(wdd, 255);
    291	watchdog_stop_on_unregister(wdd);
    292
    293	sp_wd->reboot_notifier.notifier_call = rave_sp_wdt_reboot_notifier;
    294	ret = devm_register_reboot_notifier(dev, &sp_wd->reboot_notifier);
    295	if (ret) {
    296		dev_err(dev, "Failed to register reboot notifier\n");
    297		return ret;
    298	}
    299
    300	/*
    301	 * We don't know if watchdog is running now. To be sure, let's
    302	 * start it and depend on watchdog core to ping it
    303	 */
    304	wdd->max_hw_heartbeat_ms = wdd->max_timeout * 1000;
    305	ret = rave_sp_wdt_start(wdd);
    306	if (ret) {
    307		dev_err(dev, "Watchdog didn't start\n");
    308		return ret;
    309	}
    310
    311	ret = devm_watchdog_register_device(dev, wdd);
    312	if (ret) {
    313		rave_sp_wdt_stop(wdd);
    314		return ret;
    315	}
    316
    317	return 0;
    318}
    319
    320static struct platform_driver rave_sp_wdt_driver = {
    321	.probe = rave_sp_wdt_probe,
    322	.driver = {
    323		.name = KBUILD_MODNAME,
    324		.of_match_table = rave_sp_wdt_of_match,
    325	},
    326};
    327
    328module_platform_driver(rave_sp_wdt_driver);
    329
    330MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match);
    331MODULE_LICENSE("GPL");
    332MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
    333MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
    334MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
    335MODULE_DESCRIPTION("RAVE SP Watchdog driver");
    336MODULE_ALIAS("platform:rave-sp-watchdog");