Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Retu watchdog driver
0004  *
0005  * Copyright (C) 2004, 2005 Nokia Corporation
0006  *
0007  * Based on code written by Amit Kucheria and Michael Buesch.
0008  * Rewritten by Aaro Koskinen.
0009  */
0010 
0011 #include <linux/devm-helpers.h>
0012 #include <linux/slab.h>
0013 #include <linux/errno.h>
0014 #include <linux/device.h>
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/mfd/retu.h>
0018 #include <linux/watchdog.h>
0019 #include <linux/platform_device.h>
0020 
0021 /* Watchdog timer values in seconds */
0022 #define RETU_WDT_MAX_TIMER  63
0023 
0024 struct retu_wdt_dev {
0025     struct retu_dev     *rdev;
0026     struct device       *dev;
0027     struct delayed_work ping_work;
0028 };
0029 
0030 /*
0031  * Since Retu watchdog cannot be disabled in hardware, we must kick it
0032  * with a timer until userspace watchdog software takes over. If
0033  * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
0034  */
0035 static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
0036 {
0037     retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
0038     schedule_delayed_work(&wdev->ping_work,
0039             round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
0040 }
0041 
0042 static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
0043 {
0044     retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
0045     cancel_delayed_work_sync(&wdev->ping_work);
0046 }
0047 
0048 static void retu_wdt_ping_work(struct work_struct *work)
0049 {
0050     struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
0051                         struct retu_wdt_dev, ping_work);
0052     retu_wdt_ping_enable(wdev);
0053 }
0054 
0055 static int retu_wdt_start(struct watchdog_device *wdog)
0056 {
0057     struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0058 
0059     retu_wdt_ping_disable(wdev);
0060 
0061     return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
0062 }
0063 
0064 static int retu_wdt_stop(struct watchdog_device *wdog)
0065 {
0066     struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0067 
0068     retu_wdt_ping_enable(wdev);
0069 
0070     return 0;
0071 }
0072 
0073 static int retu_wdt_ping(struct watchdog_device *wdog)
0074 {
0075     struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0076 
0077     return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
0078 }
0079 
0080 static int retu_wdt_set_timeout(struct watchdog_device *wdog,
0081                 unsigned int timeout)
0082 {
0083     struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
0084 
0085     wdog->timeout = timeout;
0086     return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
0087 }
0088 
0089 static const struct watchdog_info retu_wdt_info = {
0090     .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
0091     .identity = "Retu watchdog",
0092 };
0093 
0094 static const struct watchdog_ops retu_wdt_ops = {
0095     .owner      = THIS_MODULE,
0096     .start      = retu_wdt_start,
0097     .stop       = retu_wdt_stop,
0098     .ping       = retu_wdt_ping,
0099     .set_timeout    = retu_wdt_set_timeout,
0100 };
0101 
0102 static int retu_wdt_probe(struct platform_device *pdev)
0103 {
0104     struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
0105     bool nowayout = WATCHDOG_NOWAYOUT;
0106     struct watchdog_device *retu_wdt;
0107     struct retu_wdt_dev *wdev;
0108     int ret;
0109 
0110     retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
0111     if (!retu_wdt)
0112         return -ENOMEM;
0113 
0114     wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
0115     if (!wdev)
0116         return -ENOMEM;
0117 
0118     retu_wdt->info      = &retu_wdt_info;
0119     retu_wdt->ops       = &retu_wdt_ops;
0120     retu_wdt->timeout   = RETU_WDT_MAX_TIMER;
0121     retu_wdt->min_timeout   = 0;
0122     retu_wdt->max_timeout   = RETU_WDT_MAX_TIMER;
0123     retu_wdt->parent    = &pdev->dev;
0124 
0125     watchdog_set_drvdata(retu_wdt, wdev);
0126     watchdog_set_nowayout(retu_wdt, nowayout);
0127 
0128     wdev->rdev      = rdev;
0129     wdev->dev       = &pdev->dev;
0130 
0131     ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work,
0132                        retu_wdt_ping_work);
0133     if (ret)
0134         return ret;
0135 
0136     ret = devm_watchdog_register_device(&pdev->dev, retu_wdt);
0137     if (ret < 0)
0138         return ret;
0139 
0140     if (nowayout)
0141         retu_wdt_ping(retu_wdt);
0142     else
0143         retu_wdt_ping_enable(wdev);
0144 
0145     return 0;
0146 }
0147 
0148 static struct platform_driver retu_wdt_driver = {
0149     .probe      = retu_wdt_probe,
0150     .driver     = {
0151         .name   = "retu-wdt",
0152     },
0153 };
0154 module_platform_driver(retu_wdt_driver);
0155 
0156 MODULE_ALIAS("platform:retu-wdt");
0157 MODULE_DESCRIPTION("Retu watchdog");
0158 MODULE_AUTHOR("Amit Kucheria");
0159 MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
0160 MODULE_LICENSE("GPL");