brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · 06a54c7 Raw
234 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *  Watchdog driver for Broadcom BCM47XX4 *5 *  Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>6 *  Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>7 *  Copyright (C) 2012-2013 Hauke Mehrtens <hauke@hauke-m.de>8 *9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 13#include <linux/bcm47xx_wdt.h>14#include <linux/bitops.h>15#include <linux/errno.h>16#include <linux/kernel.h>17#include <linux/module.h>18#include <linux/moduleparam.h>19#include <linux/platform_device.h>20#include <linux/types.h>21#include <linux/watchdog.h>22#include <linux/timer.h>23#include <linux/jiffies.h>24 25#define DRV_NAME		"bcm47xx_wdt"26 27#define WDT_DEFAULT_TIME	30	/* seconds */28#define WDT_SOFTTIMER_MAX	255	/* seconds */29#define WDT_SOFTTIMER_THRESHOLD	60	/* seconds */30 31static int timeout = WDT_DEFAULT_TIME;32static bool nowayout = WATCHDOG_NOWAYOUT;33 34module_param(timeout, int, 0);35MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default="36				__MODULE_STRING(WDT_DEFAULT_TIME) ")");37 38module_param(nowayout, bool, 0);39MODULE_PARM_DESC(nowayout,40		"Watchdog cannot be stopped once started (default="41				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");42 43static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)44{45	return container_of(wdd, struct bcm47xx_wdt, wdd);46}47 48static int bcm47xx_wdt_hard_keepalive(struct watchdog_device *wdd)49{50	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);51 52	wdt->timer_set_ms(wdt, wdd->timeout * 1000);53 54	return 0;55}56 57static int bcm47xx_wdt_hard_start(struct watchdog_device *wdd)58{59	return 0;60}61 62static int bcm47xx_wdt_hard_stop(struct watchdog_device *wdd)63{64	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);65 66	wdt->timer_set(wdt, 0);67 68	return 0;69}70 71static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,72					unsigned int new_time)73{74	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);75	u32 max_timer = wdt->max_timer_ms;76 77	if (new_time < 1 || new_time > max_timer / 1000) {78		pr_warn("timeout value must be 1<=x<=%d, using %d\n",79			max_timer / 1000, new_time);80		return -EINVAL;81	}82 83	wdd->timeout = new_time;84	return 0;85}86 87static int bcm47xx_wdt_restart(struct watchdog_device *wdd,88			       unsigned long action, void *data)89{90	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);91 92	wdt->timer_set(wdt, 1);93 94	return 0;95}96 97static const struct watchdog_ops bcm47xx_wdt_hard_ops = {98	.owner		= THIS_MODULE,99	.start		= bcm47xx_wdt_hard_start,100	.stop		= bcm47xx_wdt_hard_stop,101	.ping		= bcm47xx_wdt_hard_keepalive,102	.set_timeout	= bcm47xx_wdt_hard_set_timeout,103	.restart        = bcm47xx_wdt_restart,104};105 106static void bcm47xx_wdt_soft_timer_tick(struct timer_list *t)107{108	struct bcm47xx_wdt *wdt = from_timer(wdt, t, soft_timer);109	u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms);110 111	if (!atomic_dec_and_test(&wdt->soft_ticks)) {112		wdt->timer_set_ms(wdt, next_tick);113		mod_timer(&wdt->soft_timer, jiffies + HZ);114	} else {115		pr_crit("Watchdog will fire soon!!!\n");116	}117}118 119static int bcm47xx_wdt_soft_keepalive(struct watchdog_device *wdd)120{121	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);122 123	atomic_set(&wdt->soft_ticks, wdd->timeout);124 125	return 0;126}127 128static int bcm47xx_wdt_soft_start(struct watchdog_device *wdd)129{130	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);131 132	bcm47xx_wdt_soft_keepalive(wdd);133	bcm47xx_wdt_soft_timer_tick(&wdt->soft_timer);134 135	return 0;136}137 138static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)139{140	struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);141 142	del_timer_sync(&wdt->soft_timer);143	wdt->timer_set(wdt, 0);144 145	return 0;146}147 148static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,149					unsigned int new_time)150{151	if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) {152		pr_warn("timeout value must be 1<=x<=%d, using %d\n",153			WDT_SOFTTIMER_MAX, new_time);154		return -EINVAL;155	}156 157	wdd->timeout = new_time;158	return 0;159}160 161static const struct watchdog_info bcm47xx_wdt_info = {162	.identity	= DRV_NAME,163	.options	= WDIOF_SETTIMEOUT |164				WDIOF_KEEPALIVEPING |165				WDIOF_MAGICCLOSE,166};167 168static const struct watchdog_ops bcm47xx_wdt_soft_ops = {169	.owner		= THIS_MODULE,170	.start		= bcm47xx_wdt_soft_start,171	.stop		= bcm47xx_wdt_soft_stop,172	.ping		= bcm47xx_wdt_soft_keepalive,173	.set_timeout	= bcm47xx_wdt_soft_set_timeout,174	.restart        = bcm47xx_wdt_restart,175};176 177static int bcm47xx_wdt_probe(struct platform_device *pdev)178{179	int ret;180	bool soft;181	struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);182 183	if (!wdt)184		return -ENXIO;185 186	soft = wdt->max_timer_ms < WDT_SOFTTIMER_THRESHOLD * 1000;187 188	if (soft) {189		wdt->wdd.ops = &bcm47xx_wdt_soft_ops;190		timer_setup(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick, 0);191	} else {192		wdt->wdd.ops = &bcm47xx_wdt_hard_ops;193	}194 195	wdt->wdd.info = &bcm47xx_wdt_info;196	wdt->wdd.timeout = WDT_DEFAULT_TIME;197	wdt->wdd.parent = &pdev->dev;198	ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);199	if (ret)200		goto err_timer;201	watchdog_set_nowayout(&wdt->wdd, nowayout);202	watchdog_set_restart_priority(&wdt->wdd, 64);203	watchdog_stop_on_reboot(&wdt->wdd);204 205	ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);206	if (ret)207		goto err_timer;208 209	dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n",210		timeout, nowayout ? ", nowayout" : "",211		soft ? ", Software Timer" : "");212	return 0;213 214err_timer:215	if (soft)216		del_timer_sync(&wdt->soft_timer);217 218	return ret;219}220 221static struct platform_driver bcm47xx_wdt_driver = {222	.driver		= {223		.name	= "bcm47xx-wdt",224	},225	.probe		= bcm47xx_wdt_probe,226};227 228module_platform_driver(bcm47xx_wdt_driver);229 230MODULE_AUTHOR("Aleksandar Radovanovic");231MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");232MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");233MODULE_LICENSE("GPL");234