brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · 5d1c217 Raw
337 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE5 * Supervisory Processor(SP) MCU6 *7 * Copyright (C) 2017 Zodiac Inflight Innovation8 *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.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 variant30 *31 * @max_timeout:	Largest possible watchdog timeout setting32 * @min_timeout:	Smallest possible watchdog timeout setting33 *34 * @configure:		Function to send configuration command35 * @restart:		Function to send "restart" command36 */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 watchdog47 *48 * @wdd:		Underlying watchdog device49 * @sp:			Pointer to parent RAVE SP device50 * @variant:		Device specific variant information51 * @reboot_notifier:	Reboot notifier implementing machine reset52 */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 device100 *101 * @wdd:	Device to configure102 * @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 OFF107 *  - Its timeout duration108 *109 * with first aspect specified via function argument and second via110 * 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_BYTE123	};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_NORMAL135	};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 we145	 * can't communicate to SP via UART. Luckily for use SP will146	 * wait 500ms before actually resetting us, so we ask it to do147	 * so here and let the rest of the system go on wrapping148	 * 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. SP170	 * firmware waits 500 ms before issuing reset, so let's hang171	 * here for twice that delay and hopefuly we'd never reach172	 * 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's302	 * start it and depend on watchdog core to ping it303	 */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");337