Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * sl28cpld watchdog driver
0004  *
0005  * Copyright 2020 Kontron Europe GmbH
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/mod_devicetable.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/property.h>
0013 #include <linux/regmap.h>
0014 #include <linux/watchdog.h>
0015 
0016 /*
0017  * Watchdog timer block registers.
0018  */
0019 #define WDT_CTRL            0x00
0020 #define  WDT_CTRL_EN            BIT(0)
0021 #define  WDT_CTRL_LOCK          BIT(2)
0022 #define  WDT_CTRL_ASSERT_SYS_RESET  BIT(6)
0023 #define  WDT_CTRL_ASSERT_WDT_TIMEOUT    BIT(7)
0024 #define WDT_TIMEOUT         0x01
0025 #define WDT_KICK            0x02
0026 #define  WDT_KICK_VALUE         0x6b
0027 #define WDT_COUNT           0x03
0028 
0029 #define WDT_DEFAULT_TIMEOUT     10
0030 
0031 static bool nowayout = WATCHDOG_NOWAYOUT;
0032 module_param(nowayout, bool, 0);
0033 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0034                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0035 
0036 static int timeout;
0037 module_param(timeout, int, 0);
0038 MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
0039 
0040 struct sl28cpld_wdt {
0041     struct watchdog_device wdd;
0042     struct regmap *regmap;
0043     u32 offset;
0044     bool assert_wdt_timeout;
0045 };
0046 
0047 static int sl28cpld_wdt_ping(struct watchdog_device *wdd)
0048 {
0049     struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0050 
0051     return regmap_write(wdt->regmap, wdt->offset + WDT_KICK,
0052                 WDT_KICK_VALUE);
0053 }
0054 
0055 static int sl28cpld_wdt_start(struct watchdog_device *wdd)
0056 {
0057     struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0058     unsigned int val;
0059 
0060     val = WDT_CTRL_EN | WDT_CTRL_ASSERT_SYS_RESET;
0061     if (wdt->assert_wdt_timeout)
0062         val |= WDT_CTRL_ASSERT_WDT_TIMEOUT;
0063     if (nowayout)
0064         val |= WDT_CTRL_LOCK;
0065 
0066     return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL,
0067                   val, val);
0068 }
0069 
0070 static int sl28cpld_wdt_stop(struct watchdog_device *wdd)
0071 {
0072     struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0073 
0074     return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL,
0075                   WDT_CTRL_EN, 0);
0076 }
0077 
0078 static unsigned int sl28cpld_wdt_get_timeleft(struct watchdog_device *wdd)
0079 {
0080     struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0081     unsigned int val;
0082     int ret;
0083 
0084     ret = regmap_read(wdt->regmap, wdt->offset + WDT_COUNT, &val);
0085     if (ret)
0086         return 0;
0087 
0088     return val;
0089 }
0090 
0091 static int sl28cpld_wdt_set_timeout(struct watchdog_device *wdd,
0092                     unsigned int timeout)
0093 {
0094     struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
0095     int ret;
0096 
0097     ret = regmap_write(wdt->regmap, wdt->offset + WDT_TIMEOUT, timeout);
0098     if (ret)
0099         return ret;
0100 
0101     wdd->timeout = timeout;
0102 
0103     return 0;
0104 }
0105 
0106 static const struct watchdog_info sl28cpld_wdt_info = {
0107     .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
0108     .identity = "sl28cpld watchdog",
0109 };
0110 
0111 static const struct watchdog_ops sl28cpld_wdt_ops = {
0112     .owner = THIS_MODULE,
0113     .start = sl28cpld_wdt_start,
0114     .stop = sl28cpld_wdt_stop,
0115     .ping = sl28cpld_wdt_ping,
0116     .set_timeout = sl28cpld_wdt_set_timeout,
0117     .get_timeleft = sl28cpld_wdt_get_timeleft,
0118 };
0119 
0120 static int sl28cpld_wdt_probe(struct platform_device *pdev)
0121 {
0122     struct watchdog_device *wdd;
0123     struct sl28cpld_wdt *wdt;
0124     unsigned int status;
0125     unsigned int val;
0126     int ret;
0127 
0128     if (!pdev->dev.parent)
0129         return -ENODEV;
0130 
0131     wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
0132     if (!wdt)
0133         return -ENOMEM;
0134 
0135     wdt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
0136     if (!wdt->regmap)
0137         return -ENODEV;
0138 
0139     ret = device_property_read_u32(&pdev->dev, "reg", &wdt->offset);
0140     if (ret)
0141         return -EINVAL;
0142 
0143     wdt->assert_wdt_timeout = device_property_read_bool(&pdev->dev,
0144                                 "kontron,assert-wdt-timeout-pin");
0145 
0146     /* initialize struct watchdog_device */
0147     wdd = &wdt->wdd;
0148     wdd->parent = &pdev->dev;
0149     wdd->info = &sl28cpld_wdt_info;
0150     wdd->ops = &sl28cpld_wdt_ops;
0151     wdd->min_timeout = 1;
0152     wdd->max_timeout = 255;
0153 
0154     watchdog_set_drvdata(wdd, wdt);
0155     watchdog_stop_on_reboot(wdd);
0156 
0157     /*
0158      * Read the status early, in case of an error, we haven't modified the
0159      * hardware.
0160      */
0161     ret = regmap_read(wdt->regmap, wdt->offset + WDT_CTRL, &status);
0162     if (ret)
0163         return ret;
0164 
0165     /*
0166      * Initial timeout value, may be overwritten by device tree or module
0167      * parameter in watchdog_init_timeout().
0168      *
0169      * Reading a zero here means that either the hardware has a default
0170      * value of zero (which is very unlikely and definitely a hardware
0171      * bug) or the bootloader set it to zero. In any case, we handle
0172      * this case gracefully and set out own timeout.
0173      */
0174     ret = regmap_read(wdt->regmap, wdt->offset + WDT_TIMEOUT, &val);
0175     if (ret)
0176         return ret;
0177 
0178     if (val)
0179         wdd->timeout = val;
0180     else
0181         wdd->timeout = WDT_DEFAULT_TIMEOUT;
0182 
0183     watchdog_init_timeout(wdd, timeout, &pdev->dev);
0184     sl28cpld_wdt_set_timeout(wdd, wdd->timeout);
0185 
0186     /* if the watchdog is locked, we set nowayout */
0187     if (status & WDT_CTRL_LOCK)
0188         nowayout = true;
0189     watchdog_set_nowayout(wdd, nowayout);
0190 
0191     /*
0192      * If watchdog is already running, keep it enabled, but make
0193      * sure its mode is set correctly.
0194      */
0195     if (status & WDT_CTRL_EN) {
0196         sl28cpld_wdt_start(wdd);
0197         set_bit(WDOG_HW_RUNNING, &wdd->status);
0198     }
0199 
0200     ret = devm_watchdog_register_device(&pdev->dev, wdd);
0201     if (ret < 0) {
0202         dev_err(&pdev->dev, "failed to register watchdog device\n");
0203         return ret;
0204     }
0205 
0206     dev_info(&pdev->dev, "initial timeout %d sec%s\n",
0207          wdd->timeout, nowayout ? ", nowayout" : "");
0208 
0209     return 0;
0210 }
0211 
0212 static const struct of_device_id sl28cpld_wdt_of_match[] = {
0213     { .compatible = "kontron,sl28cpld-wdt" },
0214     {}
0215 };
0216 MODULE_DEVICE_TABLE(of, sl28cpld_wdt_of_match);
0217 
0218 static struct platform_driver sl28cpld_wdt_driver = {
0219     .probe = sl28cpld_wdt_probe,
0220     .driver = {
0221         .name = "sl28cpld-wdt",
0222         .of_match_table = sl28cpld_wdt_of_match,
0223     },
0224 };
0225 module_platform_driver(sl28cpld_wdt_driver);
0226 
0227 MODULE_DESCRIPTION("sl28cpld Watchdog Driver");
0228 MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
0229 MODULE_LICENSE("GPL");