Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2019 NXP.
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/io.h>
0008 #include <linux/iopoll.h>
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/reboot.h>
0014 #include <linux/watchdog.h>
0015 
0016 #define WDOG_CS         0x0
0017 #define WDOG_CS_CMD32EN     BIT(13)
0018 #define WDOG_CS_ULK     BIT(11)
0019 #define WDOG_CS_RCS     BIT(10)
0020 #define LPO_CLK         0x1
0021 #define LPO_CLK_SHIFT       8
0022 #define WDOG_CS_CLK     (LPO_CLK << LPO_CLK_SHIFT)
0023 #define WDOG_CS_EN      BIT(7)
0024 #define WDOG_CS_UPDATE      BIT(5)
0025 #define WDOG_CS_WAIT        BIT(1)
0026 #define WDOG_CS_STOP        BIT(0)
0027 
0028 #define WDOG_CNT    0x4
0029 #define WDOG_TOVAL  0x8
0030 
0031 #define REFRESH_SEQ0    0xA602
0032 #define REFRESH_SEQ1    0xB480
0033 #define REFRESH     ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
0034 
0035 #define UNLOCK_SEQ0 0xC520
0036 #define UNLOCK_SEQ1 0xD928
0037 #define UNLOCK      ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
0038 
0039 #define DEFAULT_TIMEOUT 60
0040 #define MAX_TIMEOUT 128
0041 #define WDOG_CLOCK_RATE 1000
0042 #define WDOG_WAIT_TIMEOUT   20
0043 
0044 static bool nowayout = WATCHDOG_NOWAYOUT;
0045 module_param(nowayout, bool, 0000);
0046 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0047          __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0048 
0049 struct imx7ulp_wdt_device {
0050     struct watchdog_device wdd;
0051     void __iomem *base;
0052     struct clk *clk;
0053 };
0054 
0055 static int imx7ulp_wdt_wait(void __iomem *base, u32 mask)
0056 {
0057     u32 val = readl(base + WDOG_CS);
0058 
0059     if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val,
0060                                val & mask, 0,
0061                                WDOG_WAIT_TIMEOUT))
0062         return -ETIMEDOUT;
0063 
0064     return 0;
0065 }
0066 
0067 static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
0068 {
0069     struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
0070 
0071     u32 val = readl(wdt->base + WDOG_CS);
0072     int ret;
0073 
0074     local_irq_disable();
0075     writel(UNLOCK, wdt->base + WDOG_CNT);
0076     ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
0077     if (ret)
0078         goto enable_out;
0079     if (enable)
0080         writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
0081     else
0082         writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
0083     imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
0084 
0085 enable_out:
0086     local_irq_enable();
0087 
0088     return ret;
0089 }
0090 
0091 static bool imx7ulp_wdt_is_enabled(void __iomem *base)
0092 {
0093     u32 val = readl(base + WDOG_CS);
0094 
0095     return val & WDOG_CS_EN;
0096 }
0097 
0098 static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
0099 {
0100     struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
0101 
0102     writel(REFRESH, wdt->base + WDOG_CNT);
0103 
0104     return 0;
0105 }
0106 
0107 static int imx7ulp_wdt_start(struct watchdog_device *wdog)
0108 {
0109     return imx7ulp_wdt_enable(wdog, true);
0110 }
0111 
0112 static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
0113 {
0114     return imx7ulp_wdt_enable(wdog, false);
0115 }
0116 
0117 static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
0118                    unsigned int timeout)
0119 {
0120     struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
0121     u32 val = WDOG_CLOCK_RATE * timeout;
0122     int ret;
0123 
0124     local_irq_disable();
0125     writel(UNLOCK, wdt->base + WDOG_CNT);
0126     ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
0127     if (ret)
0128         goto timeout_out;
0129     writel(val, wdt->base + WDOG_TOVAL);
0130     imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
0131 
0132     wdog->timeout = timeout;
0133 
0134 timeout_out:
0135     local_irq_enable();
0136 
0137     return ret;
0138 }
0139 
0140 static int imx7ulp_wdt_restart(struct watchdog_device *wdog,
0141                    unsigned long action, void *data)
0142 {
0143     struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
0144     int ret;
0145 
0146     ret = imx7ulp_wdt_enable(wdog, true);
0147     if (ret)
0148         return ret;
0149 
0150     ret = imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
0151     if (ret)
0152         return ret;
0153 
0154     /* wait for wdog to fire */
0155     while (true)
0156         ;
0157 
0158     return NOTIFY_DONE;
0159 }
0160 
0161 static const struct watchdog_ops imx7ulp_wdt_ops = {
0162     .owner = THIS_MODULE,
0163     .start = imx7ulp_wdt_start,
0164     .stop  = imx7ulp_wdt_stop,
0165     .ping  = imx7ulp_wdt_ping,
0166     .set_timeout = imx7ulp_wdt_set_timeout,
0167     .restart = imx7ulp_wdt_restart,
0168 };
0169 
0170 static const struct watchdog_info imx7ulp_wdt_info = {
0171     .identity = "i.MX7ULP watchdog timer",
0172     .options  = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
0173             WDIOF_MAGICCLOSE,
0174 };
0175 
0176 static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
0177 {
0178     u32 val;
0179     int ret;
0180 
0181     local_irq_disable();
0182     /* unlock the wdog for reconfiguration */
0183     writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
0184     writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
0185     ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK);
0186     if (ret)
0187         goto init_out;
0188 
0189     /* set an initial timeout value in TOVAL */
0190     writel(timeout, base + WDOG_TOVAL);
0191     /* enable 32bit command sequence and reconfigure */
0192     val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
0193           WDOG_CS_WAIT | WDOG_CS_STOP;
0194     writel(val, base + WDOG_CS);
0195     imx7ulp_wdt_wait(base, WDOG_CS_RCS);
0196 
0197 init_out:
0198     local_irq_enable();
0199 
0200     return ret;
0201 }
0202 
0203 static void imx7ulp_wdt_action(void *data)
0204 {
0205     clk_disable_unprepare(data);
0206 }
0207 
0208 static int imx7ulp_wdt_probe(struct platform_device *pdev)
0209 {
0210     struct imx7ulp_wdt_device *imx7ulp_wdt;
0211     struct device *dev = &pdev->dev;
0212     struct watchdog_device *wdog;
0213     int ret;
0214 
0215     imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
0216     if (!imx7ulp_wdt)
0217         return -ENOMEM;
0218 
0219     platform_set_drvdata(pdev, imx7ulp_wdt);
0220 
0221     imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
0222     if (IS_ERR(imx7ulp_wdt->base))
0223         return PTR_ERR(imx7ulp_wdt->base);
0224 
0225     imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
0226     if (IS_ERR(imx7ulp_wdt->clk)) {
0227         dev_err(dev, "Failed to get watchdog clock\n");
0228         return PTR_ERR(imx7ulp_wdt->clk);
0229     }
0230 
0231     ret = clk_prepare_enable(imx7ulp_wdt->clk);
0232     if (ret)
0233         return ret;
0234 
0235     ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
0236     if (ret)
0237         return ret;
0238 
0239     wdog = &imx7ulp_wdt->wdd;
0240     wdog->info = &imx7ulp_wdt_info;
0241     wdog->ops = &imx7ulp_wdt_ops;
0242     wdog->min_timeout = 1;
0243     wdog->max_timeout = MAX_TIMEOUT;
0244     wdog->parent = dev;
0245     wdog->timeout = DEFAULT_TIMEOUT;
0246 
0247     watchdog_init_timeout(wdog, 0, dev);
0248     watchdog_stop_on_reboot(wdog);
0249     watchdog_stop_on_unregister(wdog);
0250     watchdog_set_drvdata(wdog, imx7ulp_wdt);
0251     ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
0252     if (ret)
0253         return ret;
0254 
0255     return devm_watchdog_register_device(dev, wdog);
0256 }
0257 
0258 static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
0259 {
0260     struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
0261 
0262     if (watchdog_active(&imx7ulp_wdt->wdd))
0263         imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
0264 
0265     clk_disable_unprepare(imx7ulp_wdt->clk);
0266 
0267     return 0;
0268 }
0269 
0270 static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
0271 {
0272     struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
0273     u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
0274     int ret;
0275 
0276     ret = clk_prepare_enable(imx7ulp_wdt->clk);
0277     if (ret)
0278         return ret;
0279 
0280     if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
0281         imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
0282 
0283     if (watchdog_active(&imx7ulp_wdt->wdd))
0284         imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
0285 
0286     return 0;
0287 }
0288 
0289 static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
0290              imx7ulp_wdt_resume);
0291 
0292 static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
0293     { .compatible = "fsl,imx7ulp-wdt", },
0294     { /* sentinel */ }
0295 };
0296 MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
0297 
0298 static struct platform_driver imx7ulp_wdt_driver = {
0299     .probe      = imx7ulp_wdt_probe,
0300     .driver     = {
0301         .name   = "imx7ulp-wdt",
0302         .pm = &imx7ulp_wdt_pm_ops,
0303         .of_match_table = imx7ulp_wdt_dt_ids,
0304     },
0305 };
0306 module_platform_driver(imx7ulp_wdt_driver);
0307 
0308 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
0309 MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
0310 MODULE_LICENSE("GPL v2");