Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * sunplus Watchdog Driver
0004  *
0005  * Copyright (C) 2021 Sunplus Technology Co., Ltd.
0006  *
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/io.h>
0011 #include <linux/module.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/reset.h>
0015 #include <linux/watchdog.h>
0016 
0017 #define WDT_CTRL        0x00
0018 #define WDT_CNT         0x04
0019 
0020 #define WDT_STOP        0x3877
0021 #define WDT_RESUME      0x4A4B
0022 #define WDT_CLRIRQ      0x7482
0023 #define WDT_UNLOCK      0xAB00
0024 #define WDT_LOCK        0xAB01
0025 #define WDT_CONMAX      0xDEAF
0026 
0027 /* TIMEOUT_MAX = ffff0/90kHz =11.65, so longer than 11 seconds will time out. */
0028 #define SP_WDT_MAX_TIMEOUT  11U
0029 #define SP_WDT_DEFAULT_TIMEOUT  10
0030 
0031 #define STC_CLK         90000
0032 
0033 #define DEVICE_NAME     "sunplus-wdt"
0034 
0035 static unsigned int timeout;
0036 module_param(timeout, int, 0);
0037 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
0038 
0039 static bool nowayout = WATCHDOG_NOWAYOUT;
0040 module_param(nowayout, bool, 0);
0041 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0042             __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0043 
0044 struct sp_wdt_priv {
0045     struct watchdog_device wdev;
0046     void __iomem *base;
0047     struct clk *clk;
0048     struct reset_control *rstc;
0049 };
0050 
0051 static int sp_wdt_restart(struct watchdog_device *wdev,
0052               unsigned long action, void *data)
0053 {
0054     struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
0055     void __iomem *base = priv->base;
0056 
0057     writel(WDT_STOP, base + WDT_CTRL);
0058     writel(WDT_UNLOCK, base + WDT_CTRL);
0059     writel(0x0001, base + WDT_CNT);
0060     writel(WDT_LOCK, base + WDT_CTRL);
0061     writel(WDT_RESUME, base + WDT_CTRL);
0062 
0063     return 0;
0064 }
0065 
0066 static int sp_wdt_ping(struct watchdog_device *wdev)
0067 {
0068     struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
0069     void __iomem *base = priv->base;
0070     u32 count;
0071 
0072     if (wdev->timeout > SP_WDT_MAX_TIMEOUT) {
0073         /* WDT_CONMAX sets the count to the maximum (down-counting). */
0074         writel(WDT_CONMAX, base + WDT_CTRL);
0075     } else {
0076         writel(WDT_UNLOCK, base + WDT_CTRL);
0077         /*
0078          * Watchdog timer is a 20-bit down-counting based on STC_CLK.
0079          * This register bits[16:0] is from bit[19:4] of the watchdog
0080          * timer counter.
0081          */
0082         count = (wdev->timeout * STC_CLK) >> 4;
0083         writel(count, base + WDT_CNT);
0084         writel(WDT_LOCK, base + WDT_CTRL);
0085     }
0086 
0087     return 0;
0088 }
0089 
0090 static int sp_wdt_stop(struct watchdog_device *wdev)
0091 {
0092     struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
0093     void __iomem *base = priv->base;
0094 
0095     writel(WDT_STOP, base + WDT_CTRL);
0096 
0097     return 0;
0098 }
0099 
0100 static int sp_wdt_start(struct watchdog_device *wdev)
0101 {
0102     struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
0103     void __iomem *base = priv->base;
0104 
0105     writel(WDT_RESUME, base + WDT_CTRL);
0106 
0107     return 0;
0108 }
0109 
0110 static unsigned int sp_wdt_get_timeleft(struct watchdog_device *wdev)
0111 {
0112     struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
0113     void __iomem *base = priv->base;
0114     u32 val;
0115 
0116     val = readl(base + WDT_CNT);
0117     val &= 0xffff;
0118     val = val << 4;
0119 
0120     return val;
0121 }
0122 
0123 static const struct watchdog_info sp_wdt_info = {
0124     .identity   = DEVICE_NAME,
0125     .options    = WDIOF_SETTIMEOUT |
0126               WDIOF_MAGICCLOSE |
0127               WDIOF_KEEPALIVEPING,
0128 };
0129 
0130 static const struct watchdog_ops sp_wdt_ops = {
0131     .owner      = THIS_MODULE,
0132     .start      = sp_wdt_start,
0133     .stop       = sp_wdt_stop,
0134     .ping       = sp_wdt_ping,
0135     .get_timeleft   = sp_wdt_get_timeleft,
0136     .restart    = sp_wdt_restart,
0137 };
0138 
0139 static void sp_clk_disable_unprepare(void *data)
0140 {
0141     clk_disable_unprepare(data);
0142 }
0143 
0144 static void sp_reset_control_assert(void *data)
0145 {
0146     reset_control_assert(data);
0147 }
0148 
0149 static int sp_wdt_probe(struct platform_device *pdev)
0150 {
0151     struct device *dev = &pdev->dev;
0152     struct sp_wdt_priv *priv;
0153     int ret;
0154 
0155     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0156     if (!priv)
0157         return -ENOMEM;
0158 
0159     priv->clk = devm_clk_get(dev, NULL);
0160     if (IS_ERR(priv->clk))
0161         return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n");
0162 
0163     ret = clk_prepare_enable(priv->clk);
0164     if (ret)
0165         return dev_err_probe(dev, ret, "Failed to enable clock\n");
0166 
0167     ret = devm_add_action_or_reset(dev, sp_clk_disable_unprepare, priv->clk);
0168     if (ret)
0169         return ret;
0170 
0171     /* The timer and watchdog shared the STC reset */
0172     priv->rstc = devm_reset_control_get_shared(dev, NULL);
0173     if (IS_ERR(priv->rstc))
0174         return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset\n");
0175 
0176     reset_control_deassert(priv->rstc);
0177 
0178     ret = devm_add_action_or_reset(dev, sp_reset_control_assert, priv->rstc);
0179     if (ret)
0180         return ret;
0181 
0182     priv->base = devm_platform_ioremap_resource(pdev, 0);
0183     if (IS_ERR(priv->base))
0184         return PTR_ERR(priv->base);
0185 
0186     priv->wdev.info = &sp_wdt_info;
0187     priv->wdev.ops = &sp_wdt_ops;
0188     priv->wdev.timeout = SP_WDT_DEFAULT_TIMEOUT;
0189     priv->wdev.max_hw_heartbeat_ms = SP_WDT_MAX_TIMEOUT * 1000;
0190     priv->wdev.min_timeout = 1;
0191     priv->wdev.parent = dev;
0192 
0193     watchdog_set_drvdata(&priv->wdev, priv);
0194     watchdog_init_timeout(&priv->wdev, timeout, dev);
0195     watchdog_set_nowayout(&priv->wdev, nowayout);
0196     watchdog_stop_on_reboot(&priv->wdev);
0197     watchdog_set_restart_priority(&priv->wdev, 128);
0198 
0199     return devm_watchdog_register_device(dev, &priv->wdev);
0200 }
0201 
0202 static const struct of_device_id sp_wdt_of_match[] = {
0203     {.compatible = "sunplus,sp7021-wdt", },
0204     { /* sentinel */ }
0205 };
0206 MODULE_DEVICE_TABLE(of, sp_wdt_of_match);
0207 
0208 static struct platform_driver sp_wdt_driver = {
0209     .probe = sp_wdt_probe,
0210     .driver = {
0211            .name = DEVICE_NAME,
0212            .of_match_table = sp_wdt_of_match,
0213     },
0214 };
0215 
0216 module_platform_driver(sp_wdt_driver);
0217 
0218 MODULE_AUTHOR("Xiantao Hu <xt.hu@cqplus1.com>");
0219 MODULE_DESCRIPTION("Sunplus Watchdog Timer Driver");
0220 MODULE_LICENSE("GPL");