Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * MStar WDT driver
0004  *
0005  * Copyright (C) 2019 - 2021 Daniel Palmer
0006  * Copyright (C) 2021 Romain Perier
0007  *
0008  */
0009 
0010 #include <linux/clk.h>
0011 #include <linux/io.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/watchdog.h>
0016 
0017 #define REG_WDT_CLR         0x0
0018 #define REG_WDT_MAX_PRD_L       0x10
0019 #define REG_WDT_MAX_PRD_H       0x14
0020 
0021 #define MSC313E_WDT_MIN_TIMEOUT     1
0022 #define MSC313E_WDT_DEFAULT_TIMEOUT 30
0023 
0024 static unsigned int timeout;
0025 
0026 module_param(timeout, int, 0);
0027 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
0028 
0029 struct msc313e_wdt_priv {
0030     void __iomem *base;
0031     struct watchdog_device wdev;
0032     struct clk *clk;
0033 };
0034 
0035 static int msc313e_wdt_start(struct watchdog_device *wdev)
0036 {
0037     struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
0038     u32 timeout;
0039     int err;
0040 
0041     err = clk_prepare_enable(priv->clk);
0042     if (err)
0043         return err;
0044 
0045     timeout = wdev->timeout * clk_get_rate(priv->clk);
0046     writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
0047     writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
0048     writew(1, priv->base + REG_WDT_CLR);
0049     return 0;
0050 }
0051 
0052 static int msc313e_wdt_ping(struct watchdog_device *wdev)
0053 {
0054     struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
0055 
0056     writew(1, priv->base + REG_WDT_CLR);
0057     return 0;
0058 }
0059 
0060 static int msc313e_wdt_stop(struct watchdog_device *wdev)
0061 {
0062     struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
0063 
0064     writew(0, priv->base + REG_WDT_MAX_PRD_L);
0065     writew(0, priv->base + REG_WDT_MAX_PRD_H);
0066     writew(0, priv->base + REG_WDT_CLR);
0067     clk_disable_unprepare(priv->clk);
0068     return 0;
0069 }
0070 
0071 static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time)
0072 {
0073     wdev->timeout = new_time;
0074 
0075     return msc313e_wdt_start(wdev);
0076 }
0077 
0078 static const struct watchdog_info msc313e_wdt_ident = {
0079     .identity = "MSC313e watchdog",
0080     .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
0081 };
0082 
0083 static const struct watchdog_ops msc313e_wdt_ops = {
0084     .owner = THIS_MODULE,
0085     .start = msc313e_wdt_start,
0086     .stop = msc313e_wdt_stop,
0087     .ping = msc313e_wdt_ping,
0088     .set_timeout = msc313e_wdt_settimeout,
0089 };
0090 
0091 static const struct of_device_id msc313e_wdt_of_match[] = {
0092     { .compatible = "mstar,msc313e-wdt", },
0093     { /* sentinel */ }
0094 };
0095 MODULE_DEVICE_TABLE(of, msc313e_wdt_of_match);
0096 
0097 static int msc313e_wdt_probe(struct platform_device *pdev)
0098 {
0099     struct device *dev = &pdev->dev;
0100     struct msc313e_wdt_priv *priv;
0101 
0102     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0103     if (!priv)
0104         return -ENOMEM;
0105 
0106     priv->base = devm_platform_ioremap_resource(pdev, 0);
0107     if (IS_ERR(priv->base))
0108         return PTR_ERR(priv->base);
0109 
0110     priv->clk = devm_clk_get(dev, NULL);
0111     if (IS_ERR(priv->clk)) {
0112         dev_err(dev, "No input clock\n");
0113         return PTR_ERR(priv->clk);
0114     }
0115 
0116     priv->wdev.info = &msc313e_wdt_ident,
0117     priv->wdev.ops = &msc313e_wdt_ops,
0118     priv->wdev.parent = dev;
0119     priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT;
0120     priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
0121     priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
0122 
0123     /* If the period is non-zero the WDT is running */
0124     if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16))
0125         set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
0126 
0127     watchdog_set_drvdata(&priv->wdev, priv);
0128 
0129     watchdog_init_timeout(&priv->wdev, timeout, dev);
0130     watchdog_stop_on_reboot(&priv->wdev);
0131     watchdog_stop_on_unregister(&priv->wdev);
0132 
0133     return devm_watchdog_register_device(dev, &priv->wdev);
0134 }
0135 
0136 static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
0137 {
0138     struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
0139 
0140     if (watchdog_active(&priv->wdev))
0141         msc313e_wdt_stop(&priv->wdev);
0142 
0143     return 0;
0144 }
0145 
0146 static int __maybe_unused msc313e_wdt_resume(struct device *dev)
0147 {
0148     struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
0149 
0150     if (watchdog_active(&priv->wdev))
0151         msc313e_wdt_start(&priv->wdev);
0152 
0153     return 0;
0154 }
0155 
0156 static SIMPLE_DEV_PM_OPS(msc313e_wdt_pm_ops, msc313e_wdt_suspend, msc313e_wdt_resume);
0157 
0158 static struct platform_driver msc313e_wdt_driver = {
0159     .driver = {
0160         .name = "msc313e-wdt",
0161         .of_match_table = msc313e_wdt_of_match,
0162         .pm = &msc313e_wdt_pm_ops,
0163     },
0164     .probe = msc313e_wdt_probe,
0165 };
0166 module_platform_driver(msc313e_wdt_driver);
0167 
0168 MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
0169 MODULE_DESCRIPTION("Watchdog driver for MStar MSC313e");
0170 MODULE_LICENSE("GPL v2");