1d0173278SGuenter Roeck // SPDX-License-Identifier: GPL-2.0+
243316044SWim Van Sebroeck /*
343316044SWim Van Sebroeck * watchdog_core.c
443316044SWim Van Sebroeck *
543316044SWim Van Sebroeck * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
643316044SWim Van Sebroeck * All Rights Reserved.
743316044SWim Van Sebroeck *
843316044SWim Van Sebroeck * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
943316044SWim Van Sebroeck *
1043316044SWim Van Sebroeck * This source code is part of the generic code that can be used
1143316044SWim Van Sebroeck * by all the watchdog timer drivers.
1243316044SWim Van Sebroeck *
1343316044SWim Van Sebroeck * Based on source code of the following authors:
1443316044SWim Van Sebroeck * Matt Domsch <Matt_Domsch@dell.com>,
1543316044SWim Van Sebroeck * Rob Radez <rob@osinvestor.com>,
1643316044SWim Van Sebroeck * Rusty Lynch <rusty@linux.co.intel.com>
1743316044SWim Van Sebroeck * Satyam Sharma <satyam@infradead.org>
1843316044SWim Van Sebroeck * Randy Dunlap <randy.dunlap@oracle.com>
1943316044SWim Van Sebroeck *
2043316044SWim Van Sebroeck * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
2143316044SWim Van Sebroeck * admit liability nor provide warranty for any of this software.
2243316044SWim Van Sebroeck * This material is provided "AS-IS" and at no charge.
2343316044SWim Van Sebroeck */
2443316044SWim Van Sebroeck
2543316044SWim Van Sebroeck #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2643316044SWim Van Sebroeck
2743316044SWim Van Sebroeck #include <linux/module.h> /* For EXPORT_SYMBOL/module stuff/... */
2843316044SWim Van Sebroeck #include <linux/types.h> /* For standard types */
2943316044SWim Van Sebroeck #include <linux/errno.h> /* For the -ENODEV/... values */
3043316044SWim Van Sebroeck #include <linux/kernel.h> /* For printk/panic/... */
312165bf52SDamien Riegel #include <linux/reboot.h> /* For restart handler */
3243316044SWim Van Sebroeck #include <linux/watchdog.h> /* For watchdog specific items */
3343316044SWim Van Sebroeck #include <linux/init.h> /* For __init/__exit/... */
3445f5fed3SAlan Cox #include <linux/idr.h> /* For ida_* macros */
35d6b469d9SAlan Cox #include <linux/err.h> /* For IS_ERR macros */
363048253eSFabio Porcedda #include <linux/of.h> /* For of_get_timeout_sec */
3760bcd91aSGrzegorz Jaszczyk #include <linux/suspend.h>
3843316044SWim Van Sebroeck
396cfb5aa8SWim Van Sebroeck #include "watchdog_core.h" /* For watchdog_dev_register/... */
4043316044SWim Van Sebroeck
41e25b091bSUwe Kleine-König #define CREATE_TRACE_POINTS
42e25b091bSUwe Kleine-König #include <trace/events/watchdog.h>
43e25b091bSUwe Kleine-König
4445f5fed3SAlan Cox static DEFINE_IDA(watchdog_ida);
4545f5fed3SAlan Cox
469232c806SDmitry Safonov static int stop_on_reboot = -1;
479232c806SDmitry Safonov module_param(stop_on_reboot, int, 0444);
489232c806SDmitry Safonov MODULE_PARM_DESC(stop_on_reboot, "Stop watchdogs on reboot (0=keep watching, 1=stop)");
499232c806SDmitry Safonov
50ef90174fSJean-Baptiste Theou /*
51ef90174fSJean-Baptiste Theou * Deferred Registration infrastructure.
52ef90174fSJean-Baptiste Theou *
53ef90174fSJean-Baptiste Theou * Sometimes watchdog drivers needs to be loaded as soon as possible,
54ef90174fSJean-Baptiste Theou * for example when it's impossible to disable it. To do so,
55ef90174fSJean-Baptiste Theou * raising the initcall level of the watchdog driver is a solution.
56ef90174fSJean-Baptiste Theou * But in such case, the miscdev is maybe not ready (subsys_initcall), and
57ef90174fSJean-Baptiste Theou * watchdog_core need miscdev to register the watchdog as a char device.
58ef90174fSJean-Baptiste Theou *
59ef90174fSJean-Baptiste Theou * The deferred registration infrastructure offer a way for the watchdog
60ef90174fSJean-Baptiste Theou * subsystem to register a watchdog properly, even before miscdev is ready.
61ef90174fSJean-Baptiste Theou */
62ef90174fSJean-Baptiste Theou
63ef90174fSJean-Baptiste Theou static DEFINE_MUTEX(wtd_deferred_reg_mutex);
64ef90174fSJean-Baptiste Theou static LIST_HEAD(wtd_deferred_reg_list);
65ef90174fSJean-Baptiste Theou static bool wtd_deferred_reg_done;
66ef90174fSJean-Baptiste Theou
watchdog_deferred_registration_add(struct watchdog_device * wdd)6780030425SWolfram Sang static void watchdog_deferred_registration_add(struct watchdog_device *wdd)
68ef90174fSJean-Baptiste Theou {
69ef90174fSJean-Baptiste Theou list_add_tail(&wdd->deferred,
70ef90174fSJean-Baptiste Theou &wtd_deferred_reg_list);
71ef90174fSJean-Baptiste Theou }
72ef90174fSJean-Baptiste Theou
watchdog_deferred_registration_del(struct watchdog_device * wdd)73ef90174fSJean-Baptiste Theou static void watchdog_deferred_registration_del(struct watchdog_device *wdd)
74ef90174fSJean-Baptiste Theou {
75ef90174fSJean-Baptiste Theou struct list_head *p, *n;
76ef90174fSJean-Baptiste Theou struct watchdog_device *wdd_tmp;
77ef90174fSJean-Baptiste Theou
78ef90174fSJean-Baptiste Theou list_for_each_safe(p, n, &wtd_deferred_reg_list) {
79ef90174fSJean-Baptiste Theou wdd_tmp = list_entry(p, struct watchdog_device,
80ef90174fSJean-Baptiste Theou deferred);
81ef90174fSJean-Baptiste Theou if (wdd_tmp == wdd) {
82ef90174fSJean-Baptiste Theou list_del(&wdd_tmp->deferred);
83ef90174fSJean-Baptiste Theou break;
84ef90174fSJean-Baptiste Theou }
85ef90174fSJean-Baptiste Theou }
86ef90174fSJean-Baptiste Theou }
87ef90174fSJean-Baptiste Theou
watchdog_check_min_max_timeout(struct watchdog_device * wdd)883048253eSFabio Porcedda static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
893048253eSFabio Porcedda {
903048253eSFabio Porcedda /*
913048253eSFabio Porcedda * Check that we have valid min and max timeout values, if
923048253eSFabio Porcedda * not reset them both to 0 (=not used or unknown)
933048253eSFabio Porcedda */
941894cad9SPratyush Anand if (!wdd->max_hw_heartbeat_ms && wdd->min_timeout > wdd->max_timeout) {
953048253eSFabio Porcedda pr_info("Invalid min and max timeout values, resetting to 0!\n");
963048253eSFabio Porcedda wdd->min_timeout = 0;
973048253eSFabio Porcedda wdd->max_timeout = 0;
983048253eSFabio Porcedda }
993048253eSFabio Porcedda }
1003048253eSFabio Porcedda
1013048253eSFabio Porcedda /**
1023048253eSFabio Porcedda * watchdog_init_timeout() - initialize the timeout field
1038bc86475SCorentin Labbe * @wdd: watchdog device
1043048253eSFabio Porcedda * @timeout_parm: timeout module parameter
1053048253eSFabio Porcedda * @dev: Device that stores the timeout-sec property
1063048253eSFabio Porcedda *
1073048253eSFabio Porcedda * Initialize the timeout field of the watchdog_device struct with either the
1083048253eSFabio Porcedda * timeout module parameter (if it is valid value) or the timeout-sec property
1093048253eSFabio Porcedda * (only if it is a valid value and the timeout_parm is out of bounds).
1103048253eSFabio Porcedda * If none of them are valid then we keep the old value (which should normally
11134ef4087SWolfram Sang * be the default timeout value). Note that for the module parameter, '0' means
11234ef4087SWolfram Sang * 'use default' while it is an invalid value for the timeout-sec property.
11334ef4087SWolfram Sang * It should simply be dropped if you want to use the default value then.
1143048253eSFabio Porcedda *
11534ef4087SWolfram Sang * A zero is returned on success or -EINVAL if all provided values are out of
11634ef4087SWolfram Sang * bounds.
1173048253eSFabio Porcedda */
watchdog_init_timeout(struct watchdog_device * wdd,unsigned int timeout_parm,struct device * dev)1183048253eSFabio Porcedda int watchdog_init_timeout(struct watchdog_device *wdd,
1193048253eSFabio Porcedda unsigned int timeout_parm, struct device *dev)
1203048253eSFabio Porcedda {
121e907972bSWolfram Sang const char *dev_str = wdd->parent ? dev_name(wdd->parent) :
122e907972bSWolfram Sang (const char *)wdd->info->identity;
1233048253eSFabio Porcedda unsigned int t = 0;
1243048253eSFabio Porcedda int ret = 0;
1253048253eSFabio Porcedda
1263048253eSFabio Porcedda watchdog_check_min_max_timeout(wdd);
1273048253eSFabio Porcedda
12834ef4087SWolfram Sang /* check the driver supplied value (likely a module parameter) first */
12934ef4087SWolfram Sang if (timeout_parm) {
13034ef4087SWolfram Sang if (!watchdog_timeout_invalid(wdd, timeout_parm)) {
1313048253eSFabio Porcedda wdd->timeout = timeout_parm;
13234ef4087SWolfram Sang return 0;
1333048253eSFabio Porcedda }
134e907972bSWolfram Sang pr_err("%s: driver supplied timeout (%u) out of range\n",
135e907972bSWolfram Sang dev_str, timeout_parm);
1363048253eSFabio Porcedda ret = -EINVAL;
13734ef4087SWolfram Sang }
1383048253eSFabio Porcedda
1393048253eSFabio Porcedda /* try to get the timeout_sec property */
14034ef4087SWolfram Sang if (dev && dev->of_node &&
14134ef4087SWolfram Sang of_property_read_u32(dev->of_node, "timeout-sec", &t) == 0) {
14234ef4087SWolfram Sang if (t && !watchdog_timeout_invalid(wdd, t)) {
1433048253eSFabio Porcedda wdd->timeout = t;
14434ef4087SWolfram Sang return 0;
14534ef4087SWolfram Sang }
146e907972bSWolfram Sang pr_err("%s: DT supplied timeout (%u) out of range\n", dev_str, t);
1473048253eSFabio Porcedda ret = -EINVAL;
14834ef4087SWolfram Sang }
1493048253eSFabio Porcedda
150e907972bSWolfram Sang if (ret < 0 && wdd->timeout)
151e907972bSWolfram Sang pr_warn("%s: falling back to default timeout (%u)\n", dev_str,
152e907972bSWolfram Sang wdd->timeout);
153e907972bSWolfram Sang
1543048253eSFabio Porcedda return ret;
1553048253eSFabio Porcedda }
1563048253eSFabio Porcedda EXPORT_SYMBOL_GPL(watchdog_init_timeout);
1573048253eSFabio Porcedda
watchdog_reboot_notifier(struct notifier_block * nb,unsigned long code,void * data)15869503e58SVladis Dronov static int watchdog_reboot_notifier(struct notifier_block *nb,
15969503e58SVladis Dronov unsigned long code, void *data)
16069503e58SVladis Dronov {
16169503e58SVladis Dronov struct watchdog_device *wdd;
16269503e58SVladis Dronov
16369503e58SVladis Dronov wdd = container_of(nb, struct watchdog_device, reboot_nb);
164*486a64b2SMeng Li if (code == SYS_DOWN || code == SYS_HALT || code == SYS_POWER_OFF) {
16512cee6efSGuenter Roeck if (watchdog_hw_running(wdd)) {
16669503e58SVladis Dronov int ret;
16769503e58SVladis Dronov
16869503e58SVladis Dronov ret = wdd->ops->stop(wdd);
169e25b091bSUwe Kleine-König trace_watchdog_stop(wdd, ret);
17069503e58SVladis Dronov if (ret)
17169503e58SVladis Dronov return NOTIFY_BAD;
17269503e58SVladis Dronov }
17369503e58SVladis Dronov }
17469503e58SVladis Dronov
17569503e58SVladis Dronov return NOTIFY_DONE;
17669503e58SVladis Dronov }
17769503e58SVladis Dronov
watchdog_restart_notifier(struct notifier_block * nb,unsigned long action,void * data)1782165bf52SDamien Riegel static int watchdog_restart_notifier(struct notifier_block *nb,
1792165bf52SDamien Riegel unsigned long action, void *data)
1802165bf52SDamien Riegel {
1812165bf52SDamien Riegel struct watchdog_device *wdd = container_of(nb, struct watchdog_device,
1822165bf52SDamien Riegel restart_nb);
1832165bf52SDamien Riegel
1842165bf52SDamien Riegel int ret;
1852165bf52SDamien Riegel
1864d8b229dSGuenter Roeck ret = wdd->ops->restart(wdd, action, data);
1872165bf52SDamien Riegel if (ret)
1882165bf52SDamien Riegel return NOTIFY_BAD;
1892165bf52SDamien Riegel
1902165bf52SDamien Riegel return NOTIFY_DONE;
1912165bf52SDamien Riegel }
1922165bf52SDamien Riegel
watchdog_pm_notifier(struct notifier_block * nb,unsigned long mode,void * data)19360bcd91aSGrzegorz Jaszczyk static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode,
19460bcd91aSGrzegorz Jaszczyk void *data)
19560bcd91aSGrzegorz Jaszczyk {
19660bcd91aSGrzegorz Jaszczyk struct watchdog_device *wdd;
19760bcd91aSGrzegorz Jaszczyk int ret = 0;
19860bcd91aSGrzegorz Jaszczyk
19960bcd91aSGrzegorz Jaszczyk wdd = container_of(nb, struct watchdog_device, pm_nb);
20060bcd91aSGrzegorz Jaszczyk
20160bcd91aSGrzegorz Jaszczyk switch (mode) {
20260bcd91aSGrzegorz Jaszczyk case PM_HIBERNATION_PREPARE:
20360bcd91aSGrzegorz Jaszczyk case PM_RESTORE_PREPARE:
20460bcd91aSGrzegorz Jaszczyk case PM_SUSPEND_PREPARE:
20560bcd91aSGrzegorz Jaszczyk ret = watchdog_dev_suspend(wdd);
20660bcd91aSGrzegorz Jaszczyk break;
20760bcd91aSGrzegorz Jaszczyk case PM_POST_HIBERNATION:
20860bcd91aSGrzegorz Jaszczyk case PM_POST_RESTORE:
20960bcd91aSGrzegorz Jaszczyk case PM_POST_SUSPEND:
21060bcd91aSGrzegorz Jaszczyk ret = watchdog_dev_resume(wdd);
21160bcd91aSGrzegorz Jaszczyk break;
21260bcd91aSGrzegorz Jaszczyk }
21360bcd91aSGrzegorz Jaszczyk
21460bcd91aSGrzegorz Jaszczyk if (ret)
21560bcd91aSGrzegorz Jaszczyk return NOTIFY_BAD;
21660bcd91aSGrzegorz Jaszczyk
21760bcd91aSGrzegorz Jaszczyk return NOTIFY_DONE;
21860bcd91aSGrzegorz Jaszczyk }
21960bcd91aSGrzegorz Jaszczyk
2202165bf52SDamien Riegel /**
2212165bf52SDamien Riegel * watchdog_set_restart_priority - Change priority of restart handler
2222165bf52SDamien Riegel * @wdd: watchdog device
2232165bf52SDamien Riegel * @priority: priority of the restart handler, should follow these guidelines:
2242165bf52SDamien Riegel * 0: use watchdog's restart function as last resort, has limited restart
2252165bf52SDamien Riegel * capabilies
2262165bf52SDamien Riegel * 128: default restart handler, use if no other handler is expected to be
2272165bf52SDamien Riegel * available and/or if restart is sufficient to restart the entire system
2282165bf52SDamien Riegel * 255: preempt all other handlers
2292165bf52SDamien Riegel *
2302165bf52SDamien Riegel * If a wdd->ops->restart function is provided when watchdog_register_device is
2312165bf52SDamien Riegel * called, it will be registered as a restart handler with the priority given
2322165bf52SDamien Riegel * here.
2332165bf52SDamien Riegel */
watchdog_set_restart_priority(struct watchdog_device * wdd,int priority)2342165bf52SDamien Riegel void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority)
2352165bf52SDamien Riegel {
2362165bf52SDamien Riegel wdd->restart_nb.priority = priority;
2372165bf52SDamien Riegel }
2382165bf52SDamien Riegel EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
2392165bf52SDamien Riegel
__watchdog_register_device(struct watchdog_device * wdd)240ef90174fSJean-Baptiste Theou static int __watchdog_register_device(struct watchdog_device *wdd)
24143316044SWim Van Sebroeck {
24232ecc639SGuenter Roeck int ret, id = -1;
24343316044SWim Van Sebroeck
24443316044SWim Van Sebroeck if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
24543316044SWim Van Sebroeck return -EINVAL;
24643316044SWim Van Sebroeck
24743316044SWim Van Sebroeck /* Mandatory operations need to be supported */
248d0684c8aSGuenter Roeck if (!wdd->ops->start || (!wdd->ops->stop && !wdd->max_hw_heartbeat_ms))
24943316044SWim Van Sebroeck return -EINVAL;
25043316044SWim Van Sebroeck
2513048253eSFabio Porcedda watchdog_check_min_max_timeout(wdd);
2523f43f68eSWim Van Sebroeck
2533f43f68eSWim Van Sebroeck /*
25443316044SWim Van Sebroeck * Note: now that all watchdog_device data has been verified, we
25543316044SWim Van Sebroeck * will not check this anymore in other functions. If data gets
25643316044SWim Van Sebroeck * corrupted in a later stage then we expect a kernel panic!
25743316044SWim Van Sebroeck */
25843316044SWim Van Sebroeck
2599dd4e173SJustin Chen /* Use alias for watchdog id if possible */
2609dd4e173SJustin Chen if (wdd->parent) {
2619dd4e173SJustin Chen ret = of_alias_get_id(wdd->parent->of_node, "watchdog");
2629dd4e173SJustin Chen if (ret >= 0)
2639dd4e173SJustin Chen id = ida_simple_get(&watchdog_ida, ret,
2649dd4e173SJustin Chen ret + 1, GFP_KERNEL);
2659dd4e173SJustin Chen }
2669dd4e173SJustin Chen
2679dd4e173SJustin Chen if (id < 0)
26845f5fed3SAlan Cox id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
2699dd4e173SJustin Chen
27045f5fed3SAlan Cox if (id < 0)
27145f5fed3SAlan Cox return id;
27245f5fed3SAlan Cox wdd->id = id;
27345f5fed3SAlan Cox
27443316044SWim Van Sebroeck ret = watchdog_dev_register(wdd);
27543316044SWim Van Sebroeck if (ret) {
27645f5fed3SAlan Cox ida_simple_remove(&watchdog_ida, id);
27745f5fed3SAlan Cox if (!(id == 0 && ret == -EBUSY))
27843316044SWim Van Sebroeck return ret;
27945f5fed3SAlan Cox
28045f5fed3SAlan Cox /* Retry in case a legacy watchdog module exists */
28145f5fed3SAlan Cox id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL);
28245f5fed3SAlan Cox if (id < 0)
28345f5fed3SAlan Cox return id;
28445f5fed3SAlan Cox wdd->id = id;
28545f5fed3SAlan Cox
28645f5fed3SAlan Cox ret = watchdog_dev_register(wdd);
28745f5fed3SAlan Cox if (ret) {
28845f5fed3SAlan Cox ida_simple_remove(&watchdog_ida, id);
28945f5fed3SAlan Cox return ret;
29045f5fed3SAlan Cox }
29143316044SWim Van Sebroeck }
29243316044SWim Van Sebroeck
2939232c806SDmitry Safonov /* Module parameter to force watchdog policy on reboot. */
2949232c806SDmitry Safonov if (stop_on_reboot != -1) {
2959232c806SDmitry Safonov if (stop_on_reboot)
2969232c806SDmitry Safonov set_bit(WDOG_STOP_ON_REBOOT, &wdd->status);
2979232c806SDmitry Safonov else
2989232c806SDmitry Safonov clear_bit(WDOG_STOP_ON_REBOOT, &wdd->status);
2999232c806SDmitry Safonov }
3009232c806SDmitry Safonov
30169503e58SVladis Dronov if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
3026f733cb2SWang Wensheng if (!wdd->ops->stop)
3036f733cb2SWang Wensheng pr_warn("watchdog%d: stop_on_reboot not supported\n", wdd->id);
3046f733cb2SWang Wensheng else {
30569503e58SVladis Dronov wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
30669503e58SVladis Dronov
30769503e58SVladis Dronov ret = register_reboot_notifier(&wdd->reboot_nb);
30869503e58SVladis Dronov if (ret) {
30969503e58SVladis Dronov pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
31069503e58SVladis Dronov wdd->id, ret);
31169503e58SVladis Dronov watchdog_dev_unregister(wdd);
31269503e58SVladis Dronov ida_simple_remove(&watchdog_ida, id);
31369503e58SVladis Dronov return ret;
31469503e58SVladis Dronov }
31569503e58SVladis Dronov }
3166f733cb2SWang Wensheng }
31769503e58SVladis Dronov
3182165bf52SDamien Riegel if (wdd->ops->restart) {
3192165bf52SDamien Riegel wdd->restart_nb.notifier_call = watchdog_restart_notifier;
3202165bf52SDamien Riegel
3212165bf52SDamien Riegel ret = register_restart_handler(&wdd->restart_nb);
3222165bf52SDamien Riegel if (ret)
323c01e0159SMasanari Iida pr_warn("watchdog%d: Cannot register restart handler (%d)\n",
3240254e953SGuenter Roeck wdd->id, ret);
3252165bf52SDamien Riegel }
3262165bf52SDamien Riegel
32760bcd91aSGrzegorz Jaszczyk if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) {
32860bcd91aSGrzegorz Jaszczyk wdd->pm_nb.notifier_call = watchdog_pm_notifier;
32960bcd91aSGrzegorz Jaszczyk
33060bcd91aSGrzegorz Jaszczyk ret = register_pm_notifier(&wdd->pm_nb);
33160bcd91aSGrzegorz Jaszczyk if (ret)
33260bcd91aSGrzegorz Jaszczyk pr_warn("watchdog%d: Cannot register pm handler (%d)\n",
33360bcd91aSGrzegorz Jaszczyk wdd->id, ret);
33460bcd91aSGrzegorz Jaszczyk }
33560bcd91aSGrzegorz Jaszczyk
33643316044SWim Van Sebroeck return 0;
33743316044SWim Van Sebroeck }
33843316044SWim Van Sebroeck
33943316044SWim Van Sebroeck /**
340ef90174fSJean-Baptiste Theou * watchdog_register_device() - register a watchdog device
341ef90174fSJean-Baptiste Theou * @wdd: watchdog device
34243316044SWim Van Sebroeck *
343ef90174fSJean-Baptiste Theou * Register a watchdog device with the kernel so that the
344ef90174fSJean-Baptiste Theou * watchdog timer can be accessed from userspace.
345ef90174fSJean-Baptiste Theou *
346ef90174fSJean-Baptiste Theou * A zero is returned on success and a negative errno code for
347ef90174fSJean-Baptiste Theou * failure.
34843316044SWim Van Sebroeck */
349ef90174fSJean-Baptiste Theou
watchdog_register_device(struct watchdog_device * wdd)350ef90174fSJean-Baptiste Theou int watchdog_register_device(struct watchdog_device *wdd)
351ef90174fSJean-Baptiste Theou {
352b608075eSWolfram Sang const char *dev_str;
35380030425SWolfram Sang int ret = 0;
354ef90174fSJean-Baptiste Theou
355ef90174fSJean-Baptiste Theou mutex_lock(&wtd_deferred_reg_mutex);
356ef90174fSJean-Baptiste Theou if (wtd_deferred_reg_done)
357ef90174fSJean-Baptiste Theou ret = __watchdog_register_device(wdd);
358ef90174fSJean-Baptiste Theou else
35980030425SWolfram Sang watchdog_deferred_registration_add(wdd);
360ef90174fSJean-Baptiste Theou mutex_unlock(&wtd_deferred_reg_mutex);
361b608075eSWolfram Sang
362b608075eSWolfram Sang if (ret) {
363b608075eSWolfram Sang dev_str = wdd->parent ? dev_name(wdd->parent) :
364b608075eSWolfram Sang (const char *)wdd->info->identity;
365b608075eSWolfram Sang pr_err("%s: failed to register watchdog device (err = %d)\n",
366b608075eSWolfram Sang dev_str, ret);
367b608075eSWolfram Sang }
368b608075eSWolfram Sang
369ef90174fSJean-Baptiste Theou return ret;
370ef90174fSJean-Baptiste Theou }
371ef90174fSJean-Baptiste Theou EXPORT_SYMBOL_GPL(watchdog_register_device);
372ef90174fSJean-Baptiste Theou
__watchdog_unregister_device(struct watchdog_device * wdd)373ef90174fSJean-Baptiste Theou static void __watchdog_unregister_device(struct watchdog_device *wdd)
37443316044SWim Van Sebroeck {
37543316044SWim Van Sebroeck if (wdd == NULL)
37643316044SWim Van Sebroeck return;
37743316044SWim Van Sebroeck
3782165bf52SDamien Riegel if (wdd->ops->restart)
3792165bf52SDamien Riegel unregister_restart_handler(&wdd->restart_nb);
3802165bf52SDamien Riegel
38169503e58SVladis Dronov if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))
38269503e58SVladis Dronov unregister_reboot_notifier(&wdd->reboot_nb);
38369503e58SVladis Dronov
38432ecc639SGuenter Roeck watchdog_dev_unregister(wdd);
38545f5fed3SAlan Cox ida_simple_remove(&watchdog_ida, wdd->id);
38643316044SWim Van Sebroeck }
387ef90174fSJean-Baptiste Theou
388ef90174fSJean-Baptiste Theou /**
389ef90174fSJean-Baptiste Theou * watchdog_unregister_device() - unregister a watchdog device
390ef90174fSJean-Baptiste Theou * @wdd: watchdog device to unregister
391ef90174fSJean-Baptiste Theou *
392ef90174fSJean-Baptiste Theou * Unregister a watchdog device that was previously successfully
393ef90174fSJean-Baptiste Theou * registered with watchdog_register_device().
394ef90174fSJean-Baptiste Theou */
395ef90174fSJean-Baptiste Theou
watchdog_unregister_device(struct watchdog_device * wdd)396ef90174fSJean-Baptiste Theou void watchdog_unregister_device(struct watchdog_device *wdd)
397ef90174fSJean-Baptiste Theou {
398ef90174fSJean-Baptiste Theou mutex_lock(&wtd_deferred_reg_mutex);
399ef90174fSJean-Baptiste Theou if (wtd_deferred_reg_done)
400ef90174fSJean-Baptiste Theou __watchdog_unregister_device(wdd);
401ef90174fSJean-Baptiste Theou else
402ef90174fSJean-Baptiste Theou watchdog_deferred_registration_del(wdd);
403ef90174fSJean-Baptiste Theou mutex_unlock(&wtd_deferred_reg_mutex);
404ef90174fSJean-Baptiste Theou }
405ef90174fSJean-Baptiste Theou
40643316044SWim Van Sebroeck EXPORT_SYMBOL_GPL(watchdog_unregister_device);
40743316044SWim Van Sebroeck
devm_watchdog_unregister_device(struct device * dev,void * res)40883fbae5aSNeil Armstrong static void devm_watchdog_unregister_device(struct device *dev, void *res)
40983fbae5aSNeil Armstrong {
41083fbae5aSNeil Armstrong watchdog_unregister_device(*(struct watchdog_device **)res);
41183fbae5aSNeil Armstrong }
41283fbae5aSNeil Armstrong
41383fbae5aSNeil Armstrong /**
41483fbae5aSNeil Armstrong * devm_watchdog_register_device() - resource managed watchdog_register_device()
41583fbae5aSNeil Armstrong * @dev: device that is registering this watchdog device
41683fbae5aSNeil Armstrong * @wdd: watchdog device
41783fbae5aSNeil Armstrong *
41883fbae5aSNeil Armstrong * Managed watchdog_register_device(). For watchdog device registered by this
41983fbae5aSNeil Armstrong * function, watchdog_unregister_device() is automatically called on driver
42083fbae5aSNeil Armstrong * detach. See watchdog_register_device() for more information.
42183fbae5aSNeil Armstrong */
devm_watchdog_register_device(struct device * dev,struct watchdog_device * wdd)42283fbae5aSNeil Armstrong int devm_watchdog_register_device(struct device *dev,
42383fbae5aSNeil Armstrong struct watchdog_device *wdd)
42483fbae5aSNeil Armstrong {
42583fbae5aSNeil Armstrong struct watchdog_device **rcwdd;
42683fbae5aSNeil Armstrong int ret;
42783fbae5aSNeil Armstrong
4282e91838bSGuenter Roeck rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*rcwdd),
42983fbae5aSNeil Armstrong GFP_KERNEL);
43083fbae5aSNeil Armstrong if (!rcwdd)
43183fbae5aSNeil Armstrong return -ENOMEM;
43283fbae5aSNeil Armstrong
43383fbae5aSNeil Armstrong ret = watchdog_register_device(wdd);
43483fbae5aSNeil Armstrong if (!ret) {
43583fbae5aSNeil Armstrong *rcwdd = wdd;
43683fbae5aSNeil Armstrong devres_add(dev, rcwdd);
43783fbae5aSNeil Armstrong } else {
43883fbae5aSNeil Armstrong devres_free(rcwdd);
43983fbae5aSNeil Armstrong }
44083fbae5aSNeil Armstrong
44183fbae5aSNeil Armstrong return ret;
44283fbae5aSNeil Armstrong }
44383fbae5aSNeil Armstrong EXPORT_SYMBOL_GPL(devm_watchdog_register_device);
44483fbae5aSNeil Armstrong
watchdog_deferred_registration(void)445ef90174fSJean-Baptiste Theou static int __init watchdog_deferred_registration(void)
446ef90174fSJean-Baptiste Theou {
447ef90174fSJean-Baptiste Theou mutex_lock(&wtd_deferred_reg_mutex);
448ef90174fSJean-Baptiste Theou wtd_deferred_reg_done = true;
449ef90174fSJean-Baptiste Theou while (!list_empty(&wtd_deferred_reg_list)) {
450ef90174fSJean-Baptiste Theou struct watchdog_device *wdd;
451ef90174fSJean-Baptiste Theou
452ef90174fSJean-Baptiste Theou wdd = list_first_entry(&wtd_deferred_reg_list,
453ef90174fSJean-Baptiste Theou struct watchdog_device, deferred);
454ef90174fSJean-Baptiste Theou list_del(&wdd->deferred);
455ef90174fSJean-Baptiste Theou __watchdog_register_device(wdd);
456ef90174fSJean-Baptiste Theou }
457ef90174fSJean-Baptiste Theou mutex_unlock(&wtd_deferred_reg_mutex);
458ef90174fSJean-Baptiste Theou return 0;
459ef90174fSJean-Baptiste Theou }
460ef90174fSJean-Baptiste Theou
watchdog_init(void)46145f5fed3SAlan Cox static int __init watchdog_init(void)
46245f5fed3SAlan Cox {
46332ecc639SGuenter Roeck int err;
46432ecc639SGuenter Roeck
46532ecc639SGuenter Roeck err = watchdog_dev_init();
46632ecc639SGuenter Roeck if (err < 0)
46732ecc639SGuenter Roeck return err;
468d6b469d9SAlan Cox
469ef90174fSJean-Baptiste Theou watchdog_deferred_registration();
470d6b469d9SAlan Cox return 0;
47145f5fed3SAlan Cox }
47245f5fed3SAlan Cox
watchdog_exit(void)47345f5fed3SAlan Cox static void __exit watchdog_exit(void)
47445f5fed3SAlan Cox {
47545f5fed3SAlan Cox watchdog_dev_exit();
47645f5fed3SAlan Cox ida_destroy(&watchdog_ida);
47745f5fed3SAlan Cox }
47845f5fed3SAlan Cox
479ef90174fSJean-Baptiste Theou subsys_initcall_sync(watchdog_init);
48045f5fed3SAlan Cox module_exit(watchdog_exit);
48145f5fed3SAlan Cox
48243316044SWim Van Sebroeck MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
48343316044SWim Van Sebroeck MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
48443316044SWim Van Sebroeck MODULE_DESCRIPTION("WatchDog Timer Driver Core");
48543316044SWim Van Sebroeck MODULE_LICENSE("GPL");
486