brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · a7b814e Raw
195 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for watchdog device controlled through GPIO-line4 *5 * Author: 2013, Alexander Shiyan <shc_work@mail.ru>6 */7 8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/gpio/consumer.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/property.h>15#include <linux/watchdog.h>16 17static bool nowayout = WATCHDOG_NOWAYOUT;18module_param(nowayout, bool, 0);19MODULE_PARM_DESC(nowayout,20		"Watchdog cannot be stopped once started (default="21				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");22 23#define SOFT_TIMEOUT_MIN	124#define SOFT_TIMEOUT_DEF	6025 26enum {27	HW_ALGO_TOGGLE,28	HW_ALGO_LEVEL,29};30 31struct gpio_wdt_priv {32	struct gpio_desc	*gpiod;33	bool			state;34	bool			always_running;35	unsigned int		hw_algo;36	struct watchdog_device	wdd;37};38 39static void gpio_wdt_disable(struct gpio_wdt_priv *priv)40{41	/* Eternal ping */42	gpiod_set_value_cansleep(priv->gpiod, 1);43 44	/* Put GPIO back to tristate */45	if (priv->hw_algo == HW_ALGO_TOGGLE)46		gpiod_direction_input(priv->gpiod);47}48 49static int gpio_wdt_ping(struct watchdog_device *wdd)50{51	struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);52 53	switch (priv->hw_algo) {54	case HW_ALGO_TOGGLE:55		/* Toggle output pin */56		priv->state = !priv->state;57		gpiod_set_value_cansleep(priv->gpiod, priv->state);58		break;59	case HW_ALGO_LEVEL:60		/* Pulse */61		gpiod_set_value_cansleep(priv->gpiod, 1);62		udelay(1);63		gpiod_set_value_cansleep(priv->gpiod, 0);64		break;65	}66	return 0;67}68 69static int gpio_wdt_start(struct watchdog_device *wdd)70{71	struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);72 73	priv->state = 0;74	gpiod_direction_output(priv->gpiod, priv->state);75 76	set_bit(WDOG_HW_RUNNING, &wdd->status);77 78	return gpio_wdt_ping(wdd);79}80 81static int gpio_wdt_stop(struct watchdog_device *wdd)82{83	struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);84 85	if (!priv->always_running) {86		gpio_wdt_disable(priv);87	} else {88		set_bit(WDOG_HW_RUNNING, &wdd->status);89	}90 91	return 0;92}93 94static const struct watchdog_info gpio_wdt_ident = {95	.options	= WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |96			  WDIOF_SETTIMEOUT,97	.identity	= "GPIO Watchdog",98};99 100static const struct watchdog_ops gpio_wdt_ops = {101	.owner		= THIS_MODULE,102	.start		= gpio_wdt_start,103	.stop		= gpio_wdt_stop,104	.ping		= gpio_wdt_ping,105};106 107static int gpio_wdt_probe(struct platform_device *pdev)108{109	struct device *dev = &pdev->dev;110	struct gpio_wdt_priv *priv;111	enum gpiod_flags gflags;112	unsigned int hw_margin;113	const char *algo;114	int ret;115 116	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);117	if (!priv)118		return -ENOMEM;119 120	platform_set_drvdata(pdev, priv);121 122	ret = device_property_read_string(dev, "hw_algo", &algo);123	if (ret)124		return ret;125	if (!strcmp(algo, "toggle")) {126		priv->hw_algo = HW_ALGO_TOGGLE;127		gflags = GPIOD_IN;128	} else if (!strcmp(algo, "level")) {129		priv->hw_algo = HW_ALGO_LEVEL;130		gflags = GPIOD_OUT_LOW;131	} else {132		return -EINVAL;133	}134 135	priv->gpiod = devm_gpiod_get(dev, NULL, gflags);136	if (IS_ERR(priv->gpiod))137		return PTR_ERR(priv->gpiod);138 139	ret = device_property_read_u32(dev, "hw_margin_ms", &hw_margin);140	if (ret)141		return ret;142	/* Disallow values lower than 2 and higher than 65535 ms */143	if (hw_margin < 2 || hw_margin > 65535)144		return -EINVAL;145 146	priv->always_running = device_property_read_bool(dev, "always-running");147 148	watchdog_set_drvdata(&priv->wdd, priv);149 150	priv->wdd.info		= &gpio_wdt_ident;151	priv->wdd.ops		= &gpio_wdt_ops;152	priv->wdd.min_timeout	= SOFT_TIMEOUT_MIN;153	priv->wdd.max_hw_heartbeat_ms = hw_margin;154	priv->wdd.parent	= dev;155	priv->wdd.timeout	= SOFT_TIMEOUT_DEF;156 157	watchdog_init_timeout(&priv->wdd, 0, dev);158	watchdog_set_nowayout(&priv->wdd, nowayout);159 160	watchdog_stop_on_reboot(&priv->wdd);161 162	if (priv->always_running)163		gpio_wdt_start(&priv->wdd);164 165	return devm_watchdog_register_device(dev, &priv->wdd);166}167 168static const struct of_device_id gpio_wdt_dt_ids[] = {169	{ .compatible = "linux,wdt-gpio", },170	{ }171};172MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids);173 174static struct platform_driver gpio_wdt_driver = {175	.driver	= {176		.name		= "gpio-wdt",177		.of_match_table	= gpio_wdt_dt_ids,178	},179	.probe	= gpio_wdt_probe,180};181 182#ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL183static int __init gpio_wdt_init(void)184{185	return platform_driver_register(&gpio_wdt_driver);186}187arch_initcall(gpio_wdt_init);188#else189module_platform_driver(gpio_wdt_driver);190#endif191 192MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");193MODULE_DESCRIPTION("GPIO Watchdog");194MODULE_LICENSE("GPL");195