Files
Jon Hunter 96cae7f9de drivers: Fix from_timer() for Linux v6.16
In Linux v6.16, commit 41cb08555c41 ("treewide, timers: Rename
from_timer() to timer_container_of()") renamed 'from_timer()' to
'timer_container_of()'. Given that this is a macro we can simplfy see if
the macro 'timer_container_of' is defined and if so use this otherwise
fall back to 'from_timer()'. Update the necessary drivers to fix the
build for Linux v6.16.

JIRA LINQPJ14-60

Change-Id: I7f622b5d046a92da2ade755e6a697c1810f61275
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3383387
(cherry picked from commit 320ec84efd492c0c2711c69104fabc30b4f15ecb)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3461850
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
2025-10-01 15:27:42 -07:00

289 lines
7.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2014-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
/*
* A platform based Software Watchdog Device
*/
#include <nvidia/conftest.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define TIMER_MARGIN 60 /* Default is 60 seconds */
struct softdog_platform_wdt {
struct watchdog_device wdt_dev;
struct device *dev;
unsigned int soft_margin;
bool nowayout;
int soft_noboot;
int soft_panic;
struct timer_list watchdog_ticktock;
struct notifier_block nb;
int is_stopped;
};
/* If the timer expires.. */
static void softdog_platform_watchdog_fire(struct timer_list *t)
{
#if defined(timer_container_of) /* Linux v6.16 */
struct softdog_platform_wdt *swdt = timer_container_of(swdt, t, watchdog_ticktock);
#else
struct softdog_platform_wdt *swdt = from_timer(swdt, t, watchdog_ticktock);
#endif
if (swdt->is_stopped)
return;
if (swdt->soft_noboot)
dev_crit(swdt->dev, "Triggered - Reboot ignored\n");
else if (swdt->soft_panic) {
dev_crit(swdt->dev, "Initiating panic\n");
panic("Software Watchdog Timer expired");
} else {
dev_crit(swdt->dev, "Initiating system reboot\n");
emergency_restart();
dev_crit(swdt->dev, "Reboot didn't ?????\n");
}
}
/* Softdog operations */
static int softdog_platform_ping(struct watchdog_device *wdt)
{
struct softdog_platform_wdt *swdt = watchdog_get_drvdata(wdt);
if (swdt->is_stopped)
return 0;
mod_timer(&swdt->watchdog_ticktock, jiffies+(wdt->timeout*HZ));
return 0;
}
static int softdog_platform_start(struct watchdog_device *wdt)
{
struct softdog_platform_wdt *swdt = watchdog_get_drvdata(wdt);
swdt->is_stopped = false;
mod_timer(&swdt->watchdog_ticktock, jiffies+(wdt->timeout*HZ));
return 0;
}
static int softdog_platform_stop(struct watchdog_device *wdt)
{
struct softdog_platform_wdt *swdt = watchdog_get_drvdata(wdt);
swdt->is_stopped = true;
return 0;
}
static int softdog_platform_set_timeout(struct watchdog_device *wdt,
unsigned int t)
{
wdt->timeout = t;
return 0;
}
/* Notifier for system down */
static int softdog_platform_notify_sys(struct notifier_block *this,
unsigned long code, void *ptr)
{
struct softdog_platform_wdt *swdt = container_of(this,
struct softdog_platform_wdt, nb);
if (code == SYS_DOWN || code == SYS_HALT)
/* Turn the WDT off */
softdog_platform_stop(&swdt->wdt_dev);
return NOTIFY_DONE;
}
static struct watchdog_info softdog_platform_info = {
.identity = "Software Watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
static struct watchdog_ops softdog_platform_ops = {
.owner = THIS_MODULE,
.start = softdog_platform_start,
.stop = softdog_platform_stop,
.ping = softdog_platform_ping,
.set_timeout = softdog_platform_set_timeout,
};
static int softdog_platform_probe(struct platform_device *pdev)
{
struct softdog_platform_wdt *swdt;
struct device_node *np = pdev->dev.of_node;
u32 pval;
int ret;
if (!np) {
dev_err(&pdev->dev, "Only DT registration supported\n");
return -ENODEV;
}
swdt = devm_kzalloc(&pdev->dev, sizeof(*swdt), GFP_KERNEL);
if (!swdt)
return -ENOMEM;
swdt->nowayout = of_property_read_bool(np, "watchdog,nowayout");
swdt->soft_noboot = !of_property_read_bool(np, "watchdog,reboot");
swdt->soft_panic = of_property_read_bool(np, "watchdog,panic");
ret = of_property_read_u32(np, "watchdog,margin", &pval);
if (!ret)
swdt->soft_margin = pval;
else
swdt->soft_margin = TIMER_MARGIN;
/*
* Check that the soft_margin value is within it's range;
* if not reset to the default
*/
if (swdt->soft_margin < 1 || swdt->soft_margin > 65535) {
dev_err(&pdev->dev,
"soft_margin must be 0 to 65536, using %d\n",
TIMER_MARGIN);
return -EINVAL;
}
swdt->wdt_dev.timeout = swdt->soft_margin;
swdt->nb.notifier_call = softdog_platform_notify_sys;
swdt->dev = &pdev->dev;
watchdog_set_nowayout(&swdt->wdt_dev, swdt->nowayout);
watchdog_set_drvdata(&swdt->wdt_dev, swdt);
platform_set_drvdata(pdev, swdt);
swdt->wdt_dev.info = &softdog_platform_info;
swdt->wdt_dev.ops = &softdog_platform_ops;
swdt->wdt_dev.min_timeout = 1;
swdt->wdt_dev.max_timeout = 0xFFFF;
timer_setup(&swdt->watchdog_ticktock, softdog_platform_watchdog_fire, 0);
ret = register_reboot_notifier(&swdt->nb);
if (ret) {
dev_err(&pdev->dev,
"cannot register reboot notifier (err=%d)\n", ret);
goto timer_del;
}
ret = watchdog_register_device(&swdt->wdt_dev);
if (ret) {
dev_err(&pdev->dev, "Watchdog register failed: %d\n", ret);
goto reboot_unreg;
}
dev_info(&pdev->dev, "Software Watchdog Timer: initialized\n");
return 0;
reboot_unreg:
unregister_reboot_notifier(&swdt->nb);
timer_del:
#if defined(NV_TIMER_DELETE_PRESENT) /* Linux v6.15 */
timer_delete_sync(&swdt->watchdog_ticktock);
#else
del_timer_sync(&swdt->watchdog_ticktock);
#endif
return ret;
}
static int softdog_platform_remove(struct platform_device *pdev)
{
struct softdog_platform_wdt *swdt = platform_get_drvdata(pdev);
#if defined(NV_TIMER_DELETE_PRESENT) /* Linux v6.15 */
timer_delete_sync(&swdt->watchdog_ticktock);
#else
del_timer_sync(&swdt->watchdog_ticktock);
#endif
watchdog_unregister_device(&swdt->wdt_dev);
unregister_reboot_notifier(&swdt->nb);
return 0;
}
static void softdog_platform_shutdown(struct platform_device *pdev)
{
struct softdog_platform_wdt *swdt = platform_get_drvdata(pdev);
#if defined(NV_TIMER_DELETE_PRESENT) /* Linux v6.15 */
timer_delete_sync(&swdt->watchdog_ticktock);
#else
del_timer_sync(&swdt->watchdog_ticktock);
#endif
}
#ifdef CONFIG_PM_SLEEP
static int softdog_platform_suspend(struct device *dev)
{
struct softdog_platform_wdt *swdt = dev_get_drvdata(dev);
int ret;
ret = softdog_platform_stop(&swdt->wdt_dev);
if (ret < 0)
dev_err(swdt->dev, "wdt stop failed: %d\n", ret);
return 0;
}
static int softdog_platform_resume(struct device *dev)
{
struct softdog_platform_wdt *swdt = dev_get_drvdata(dev);
int ret;
ret = softdog_platform_start(&swdt->wdt_dev);
if (ret < 0)
dev_err(swdt->dev, "wdt start failed: %d\n", ret);
return 0;
}
#endif
static const struct dev_pm_ops softdog_platform_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(softdog_platform_suspend,
softdog_platform_resume)
};
static struct of_device_id softdog_platform_of_match[] = {
{ .compatible = "softdog-platform", },
{},
};
MODULE_DEVICE_TABLE(of, softdog_platform_of_match);
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
static void softdog_platform_remove_wrapper(struct platform_device *pdev)
{
softdog_platform_remove(pdev);
}
#else
static int softdog_platform_remove_wrapper(struct platform_device *pdev)
{
return softdog_platform_remove(pdev);
}
#endif
static struct platform_driver softdog_platform_driver = {
.driver = {
.name = "softdog-platform",
.owner = THIS_MODULE,
.of_match_table = softdog_platform_of_match,
.pm = &softdog_platform_pm_ops,
},
.probe = softdog_platform_probe,
.remove = softdog_platform_remove_wrapper,
.shutdown = softdog_platform_shutdown,
};
module_platform_driver(softdog_platform_driver);
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_DESCRIPTION("Software Watchdog Platform Device Driver");
MODULE_LICENSE("GPL v2");