Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2020 TOSHIBA CORPORATION
0004  * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation
0005  * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
0006  */
0007 
0008 #include <linux/clk.h>
0009 #include <linux/io.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/watchdog.h>
0015 
0016 #define WDT_CNT         0x00
0017 #define WDT_MIN         0x04
0018 #define WDT_MAX         0x08
0019 #define WDT_CTL         0x0c
0020 #define WDT_CMD         0x10
0021 #define WDT_CMD_CLEAR       0x4352
0022 #define WDT_CMD_START_STOP  0x5354
0023 #define WDT_DIV         0x30
0024 
0025 #define VISCONTI_WDT_FREQ   2000000 /* 2MHz */
0026 #define WDT_DEFAULT_TIMEOUT 10U /* in seconds */
0027 
0028 static bool nowayout = WATCHDOG_NOWAYOUT;
0029 module_param(nowayout, bool, 0);
0030 MODULE_PARM_DESC(
0031     nowayout,
0032     "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT)")");
0033 
0034 struct visconti_wdt_priv {
0035     struct watchdog_device wdev;
0036     void __iomem *base;
0037     u32 div;
0038 };
0039 
0040 static int visconti_wdt_start(struct watchdog_device *wdev)
0041 {
0042     struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
0043     u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
0044 
0045     writel(priv->div, priv->base + WDT_DIV);
0046     writel(0, priv->base + WDT_MIN);
0047     writel(timeout, priv->base + WDT_MAX);
0048     writel(0, priv->base + WDT_CTL);
0049     writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
0050 
0051     return 0;
0052 }
0053 
0054 static int visconti_wdt_stop(struct watchdog_device *wdev)
0055 {
0056     struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
0057 
0058     writel(1, priv->base + WDT_CTL);
0059     writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
0060 
0061     return 0;
0062 }
0063 
0064 static int visconti_wdt_ping(struct watchdog_device *wdd)
0065 {
0066     struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd);
0067 
0068     writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
0069 
0070     return 0;
0071 }
0072 
0073 static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev)
0074 {
0075     struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
0076     u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
0077     u32 cnt = readl(priv->base + WDT_CNT);
0078 
0079     if (timeout <= cnt)
0080         return 0;
0081     timeout -= cnt;
0082 
0083     return timeout / VISCONTI_WDT_FREQ;
0084 }
0085 
0086 static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
0087 {
0088     u32 val;
0089     struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
0090 
0091     wdev->timeout = timeout;
0092     val = wdev->timeout * VISCONTI_WDT_FREQ;
0093 
0094     /* Clear counter before setting timeout because WDT expires */
0095     writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
0096     writel(val, priv->base + WDT_MAX);
0097 
0098     return 0;
0099 }
0100 
0101 static const struct watchdog_info visconti_wdt_info = {
0102     .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
0103     .identity = "Visconti Watchdog",
0104 };
0105 
0106 static const struct watchdog_ops visconti_wdt_ops = {
0107     .owner      = THIS_MODULE,
0108     .start      = visconti_wdt_start,
0109     .stop       = visconti_wdt_stop,
0110     .ping       = visconti_wdt_ping,
0111     .get_timeleft   = visconti_wdt_get_timeleft,
0112     .set_timeout    = visconti_wdt_set_timeout,
0113 };
0114 
0115 static void visconti_clk_disable_unprepare(void *data)
0116 {
0117     clk_disable_unprepare(data);
0118 }
0119 
0120 static int visconti_wdt_probe(struct platform_device *pdev)
0121 {
0122     struct watchdog_device *wdev;
0123     struct visconti_wdt_priv *priv;
0124     struct device *dev = &pdev->dev;
0125     struct clk *clk;
0126     int ret;
0127     unsigned long clk_freq;
0128 
0129     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0130     if (!priv)
0131         return -ENOMEM;
0132 
0133     priv->base = devm_platform_ioremap_resource(pdev, 0);
0134     if (IS_ERR(priv->base))
0135         return PTR_ERR(priv->base);
0136 
0137     clk = devm_clk_get(dev, NULL);
0138     if (IS_ERR(clk))
0139         return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n");
0140 
0141     ret = clk_prepare_enable(clk);
0142     if (ret) {
0143         dev_err(dev, "Could not enable clock\n");
0144         return ret;
0145     }
0146 
0147     ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk);
0148     if (ret)
0149         return ret;
0150 
0151     clk_freq = clk_get_rate(clk);
0152     if (!clk_freq)
0153         return -EINVAL;
0154 
0155     priv->div = clk_freq / VISCONTI_WDT_FREQ;
0156 
0157     /* Initialize struct watchdog_device. */
0158     wdev = &priv->wdev;
0159     wdev->info = &visconti_wdt_info;
0160     wdev->ops = &visconti_wdt_ops;
0161     wdev->parent = dev;
0162     wdev->min_timeout = 1;
0163     wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ;
0164     wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT);
0165 
0166     watchdog_set_drvdata(wdev, priv);
0167     watchdog_set_nowayout(wdev, nowayout);
0168     watchdog_stop_on_unregister(wdev);
0169 
0170     /* This overrides the default timeout only if DT configuration was found */
0171     ret = watchdog_init_timeout(wdev, 0, dev);
0172     if (ret)
0173         dev_warn(dev, "Specified timeout value invalid, using default\n");
0174 
0175     return devm_watchdog_register_device(dev, wdev);
0176 }
0177 
0178 static const struct of_device_id visconti_wdt_of_match[] = {
0179     { .compatible = "toshiba,visconti-wdt", },
0180     {}
0181 };
0182 MODULE_DEVICE_TABLE(of, visconti_wdt_of_match);
0183 
0184 static struct platform_driver visconti_wdt_driver = {
0185     .driver = {
0186             .name = "visconti_wdt",
0187             .of_match_table = visconti_wdt_of_match,
0188         },
0189     .probe = visconti_wdt_probe,
0190 };
0191 module_platform_driver(visconti_wdt_driver);
0192 
0193 MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
0194 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
0195 MODULE_LICENSE("GPL v2");