diff options
Diffstat (limited to 'kernel/ethosu_watchdog.c')
-rw-r--r-- | kernel/ethosu_watchdog.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/kernel/ethosu_watchdog.c b/kernel/ethosu_watchdog.c new file mode 100644 index 0000000..bde0803 --- /dev/null +++ b/kernel/ethosu_watchdog.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2022 Arm Limited. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include "ethosu_watchdog.h" + +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/moduleparam.h> +#include <linux/version.h> + +/**************************************************************************** + * Variables + ****************************************************************************/ + +static unsigned long watchdog_timeout_ms = 3000; +module_param(watchdog_timeout_ms, ulong, 0664); +MODULE_PARM_DESC(watchdog_timeout_ms, + "Watchdog timeout in milliseconds for unresponsive firmware."); + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static void ethosu_watchdog_update(struct ethosu_watchdog *wdog) +{ + int ret; + + ret = mod_timer(&wdog->timer, + jiffies + msecs_to_jiffies(watchdog_timeout_ms)); + + dev_info(wdog->dev, + "Wdog: Update watchdog timeout. ret=%d, timeout_ms=%lu, refcount=%u", ret, + watchdog_timeout_ms, atomic_read(&wdog->refcount)); +} + +static void ethosu_watchdog_work(struct work_struct *work) +{ + struct ethosu_watchdog *wdog = + container_of(work, struct ethosu_watchdog, work); + + dev_info(wdog->dev, "Wdog: Watchdog timeout. refcount=%u", + atomic_read(&wdog->refcount)); + + wdog->callback(wdog); +} + +static void ethosu_watchdog_timeout(struct timer_list *timer) +{ + struct ethosu_watchdog *wdog = + container_of(timer, struct ethosu_watchdog, timer); + + queue_work(system_unbound_wq, &wdog->work); +} + +#if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE +static void ethosu_watchdog_timeout_legacy(unsigned long data) +{ + ethosu_watchdog_timeout((struct timer_list *)data); +} + +#endif + +int ethosu_watchdog_init(struct ethosu_watchdog *wdog, + struct device *dev, + ethosu_watchdog_cb callback) +{ + wdog->dev = dev; + wdog->callback = callback; + atomic_set(&wdog->refcount, 0); + INIT_WORK(&wdog->work, ethosu_watchdog_work); + +#if KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE + timer_setup(&wdog->timer, ethosu_watchdog_timeout, 0); +#else + setup_timer(&wdog->timer, ethosu_watchdog_timeout_legacy, + (unsigned long)&wdog->timer); +#endif + + return 0; +} + +void ethosu_watchdog_deinit(struct ethosu_watchdog *wdog) +{ + del_timer(&wdog->timer); +} + +int ethosu_watchdog_reset(struct ethosu_watchdog *wdog) +{ + del_timer(&wdog->timer); + atomic_set(&wdog->refcount, 0); + + return 0; +} + +void ethosu_watchdog_inc(struct ethosu_watchdog *wdog) +{ + atomic_inc(&wdog->refcount); + ethosu_watchdog_update(wdog); +} + +void ethosu_watchdog_dec(struct ethosu_watchdog *wdog) +{ + if (atomic_dec_and_test(&wdog->refcount)) { + dev_info(wdog->dev, "Wdog: Cancel watchdog timeout"); + del_timer(&wdog->timer); + } else { + ethosu_watchdog_update(wdog); + } +} |