0001
0002
0003
0004
0005
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
0026 #define WDT_DEFAULT_TIMEOUT 10U
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
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
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
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");