1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2019 NXP. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/init.h> 8 #include <linux/io.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/platform_device.h> 13 #include <linux/reboot.h> 14 #include <linux/watchdog.h> 15 16 #define WDOG_CS 0x0 17 #define WDOG_CS_CMD32EN BIT(13) 18 #define WDOG_CS_ULK BIT(11) 19 #define WDOG_CS_RCS BIT(10) 20 #define LPO_CLK 0x1 21 #define LPO_CLK_SHIFT 8 22 #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) 23 #define WDOG_CS_EN BIT(7) 24 #define WDOG_CS_UPDATE BIT(5) 25 26 #define WDOG_CNT 0x4 27 #define WDOG_TOVAL 0x8 28 29 #define REFRESH_SEQ0 0xA602 30 #define REFRESH_SEQ1 0xB480 31 #define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0) 32 33 #define UNLOCK_SEQ0 0xC520 34 #define UNLOCK_SEQ1 0xD928 35 #define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0) 36 37 #define DEFAULT_TIMEOUT 60 38 #define MAX_TIMEOUT 128 39 #define WDOG_CLOCK_RATE 1000 40 41 static bool nowayout = WATCHDOG_NOWAYOUT; 42 module_param(nowayout, bool, 0000); 43 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 44 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 45 46 struct imx7ulp_wdt_device { 47 struct watchdog_device wdd; 48 void __iomem *base; 49 struct clk *clk; 50 }; 51 52 static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable) 53 { 54 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 55 56 u32 val = readl(wdt->base + WDOG_CS); 57 58 writel(UNLOCK, wdt->base + WDOG_CNT); 59 if (enable) 60 writel(val | WDOG_CS_EN, wdt->base + WDOG_CS); 61 else 62 writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS); 63 } 64 65 static bool imx7ulp_wdt_is_enabled(void __iomem *base) 66 { 67 u32 val = readl(base + WDOG_CS); 68 69 return val & WDOG_CS_EN; 70 } 71 72 static int imx7ulp_wdt_ping(struct watchdog_device *wdog) 73 { 74 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 75 76 writel(REFRESH, wdt->base + WDOG_CNT); 77 78 return 0; 79 } 80 81 static int imx7ulp_wdt_start(struct watchdog_device *wdog) 82 { 83 84 imx7ulp_wdt_enable(wdog, true); 85 86 return 0; 87 } 88 89 static int imx7ulp_wdt_stop(struct watchdog_device *wdog) 90 { 91 imx7ulp_wdt_enable(wdog, false); 92 93 return 0; 94 } 95 96 static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, 97 unsigned int timeout) 98 { 99 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 100 u32 val = WDOG_CLOCK_RATE * timeout; 101 102 writel(UNLOCK, wdt->base + WDOG_CNT); 103 writel(val, wdt->base + WDOG_TOVAL); 104 105 wdog->timeout = timeout; 106 107 return 0; 108 } 109 110 static int imx7ulp_wdt_restart(struct watchdog_device *wdog, 111 unsigned long action, void *data) 112 { 113 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 114 115 imx7ulp_wdt_enable(wdog, true); 116 imx7ulp_wdt_set_timeout(&wdt->wdd, 1); 117 118 /* wait for wdog to fire */ 119 while (true) 120 ; 121 122 return NOTIFY_DONE; 123 } 124 125 static const struct watchdog_ops imx7ulp_wdt_ops = { 126 .owner = THIS_MODULE, 127 .start = imx7ulp_wdt_start, 128 .stop = imx7ulp_wdt_stop, 129 .ping = imx7ulp_wdt_ping, 130 .set_timeout = imx7ulp_wdt_set_timeout, 131 .restart = imx7ulp_wdt_restart, 132 }; 133 134 static const struct watchdog_info imx7ulp_wdt_info = { 135 .identity = "i.MX7ULP watchdog timer", 136 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 137 WDIOF_MAGICCLOSE, 138 }; 139 140 static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) 141 { 142 u32 val; 143 144 /* unlock the wdog for reconfiguration */ 145 writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT); 146 writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT); 147 148 /* set an initial timeout value in TOVAL */ 149 writel(timeout, base + WDOG_TOVAL); 150 /* enable 32bit command sequence and reconfigure */ 151 val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE; 152 writel(val, base + WDOG_CS); 153 } 154 155 static void imx7ulp_wdt_action(void *data) 156 { 157 clk_disable_unprepare(data); 158 } 159 160 static int imx7ulp_wdt_probe(struct platform_device *pdev) 161 { 162 struct imx7ulp_wdt_device *imx7ulp_wdt; 163 struct device *dev = &pdev->dev; 164 struct watchdog_device *wdog; 165 int ret; 166 167 imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL); 168 if (!imx7ulp_wdt) 169 return -ENOMEM; 170 171 platform_set_drvdata(pdev, imx7ulp_wdt); 172 173 imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0); 174 if (IS_ERR(imx7ulp_wdt->base)) 175 return PTR_ERR(imx7ulp_wdt->base); 176 177 imx7ulp_wdt->clk = devm_clk_get(dev, NULL); 178 if (IS_ERR(imx7ulp_wdt->clk)) { 179 dev_err(dev, "Failed to get watchdog clock\n"); 180 return PTR_ERR(imx7ulp_wdt->clk); 181 } 182 183 ret = clk_prepare_enable(imx7ulp_wdt->clk); 184 if (ret) 185 return ret; 186 187 ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk); 188 if (ret) 189 return ret; 190 191 wdog = &imx7ulp_wdt->wdd; 192 wdog->info = &imx7ulp_wdt_info; 193 wdog->ops = &imx7ulp_wdt_ops; 194 wdog->min_timeout = 1; 195 wdog->max_timeout = MAX_TIMEOUT; 196 wdog->parent = dev; 197 wdog->timeout = DEFAULT_TIMEOUT; 198 199 watchdog_init_timeout(wdog, 0, dev); 200 watchdog_stop_on_reboot(wdog); 201 watchdog_stop_on_unregister(wdog); 202 watchdog_set_drvdata(wdog, imx7ulp_wdt); 203 imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE); 204 205 return devm_watchdog_register_device(dev, wdog); 206 } 207 208 static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev) 209 { 210 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); 211 212 if (watchdog_active(&imx7ulp_wdt->wdd)) 213 imx7ulp_wdt_stop(&imx7ulp_wdt->wdd); 214 215 clk_disable_unprepare(imx7ulp_wdt->clk); 216 217 return 0; 218 } 219 220 static int __maybe_unused imx7ulp_wdt_resume(struct device *dev) 221 { 222 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); 223 u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE; 224 int ret; 225 226 ret = clk_prepare_enable(imx7ulp_wdt->clk); 227 if (ret) 228 return ret; 229 230 if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base)) 231 imx7ulp_wdt_init(imx7ulp_wdt->base, timeout); 232 233 if (watchdog_active(&imx7ulp_wdt->wdd)) 234 imx7ulp_wdt_start(&imx7ulp_wdt->wdd); 235 236 return 0; 237 } 238 239 static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend, 240 imx7ulp_wdt_resume); 241 242 static const struct of_device_id imx7ulp_wdt_dt_ids[] = { 243 { .compatible = "fsl,imx7ulp-wdt", }, 244 { /* sentinel */ } 245 }; 246 MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids); 247 248 static struct platform_driver imx7ulp_wdt_driver = { 249 .probe = imx7ulp_wdt_probe, 250 .driver = { 251 .name = "imx7ulp-wdt", 252 .pm = &imx7ulp_wdt_pm_ops, 253 .of_match_table = imx7ulp_wdt_dt_ids, 254 }, 255 }; 256 module_platform_driver(imx7ulp_wdt_driver); 257 258 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 259 MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver"); 260 MODULE_LICENSE("GPL v2"); 261