1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2019 NXP. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/io.h> 8 #include <linux/iopoll.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/of_device.h> 13 #include <linux/platform_device.h> 14 #include <linux/reboot.h> 15 #include <linux/watchdog.h> 16 17 #define WDOG_CS 0x0 18 #define WDOG_CS_FLG BIT(14) 19 #define WDOG_CS_CMD32EN BIT(13) 20 #define WDOG_CS_PRES BIT(12) 21 #define WDOG_CS_ULK BIT(11) 22 #define WDOG_CS_RCS BIT(10) 23 #define LPO_CLK 0x1 24 #define LPO_CLK_SHIFT 8 25 #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) 26 #define WDOG_CS_EN BIT(7) 27 #define WDOG_CS_UPDATE BIT(5) 28 #define WDOG_CS_WAIT BIT(1) 29 #define WDOG_CS_STOP BIT(0) 30 31 #define WDOG_CNT 0x4 32 #define WDOG_TOVAL 0x8 33 34 #define REFRESH_SEQ0 0xA602 35 #define REFRESH_SEQ1 0xB480 36 #define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0) 37 38 #define UNLOCK_SEQ0 0xC520 39 #define UNLOCK_SEQ1 0xD928 40 #define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0) 41 42 #define DEFAULT_TIMEOUT 60 43 #define MAX_TIMEOUT 128 44 #define WDOG_CLOCK_RATE 1000 45 #define WDOG_ULK_WAIT_TIMEOUT 1000 46 #define WDOG_RCS_WAIT_TIMEOUT 10000 47 #define WDOG_RCS_POST_WAIT 3000 48 49 #define RETRY_MAX 5 50 51 static bool nowayout = WATCHDOG_NOWAYOUT; 52 module_param(nowayout, bool, 0000); 53 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 54 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 55 56 struct imx_wdt_hw_feature { 57 bool prescaler_enable; 58 u32 wdog_clock_rate; 59 }; 60 61 struct imx7ulp_wdt_device { 62 struct watchdog_device wdd; 63 void __iomem *base; 64 struct clk *clk; 65 bool post_rcs_wait; 66 const struct imx_wdt_hw_feature *hw; 67 }; 68 69 static int imx7ulp_wdt_wait_ulk(void __iomem *base) 70 { 71 u32 val = readl(base + WDOG_CS); 72 73 if (!(val & WDOG_CS_ULK) && 74 readl_poll_timeout_atomic(base + WDOG_CS, val, 75 val & WDOG_CS_ULK, 0, 76 WDOG_ULK_WAIT_TIMEOUT)) 77 return -ETIMEDOUT; 78 79 return 0; 80 } 81 82 static int imx7ulp_wdt_wait_rcs(struct imx7ulp_wdt_device *wdt) 83 { 84 int ret = 0; 85 u32 val = readl(wdt->base + WDOG_CS); 86 u64 timeout = (val & WDOG_CS_PRES) ? 87 WDOG_RCS_WAIT_TIMEOUT * 256 : WDOG_RCS_WAIT_TIMEOUT; 88 unsigned long wait_min = (val & WDOG_CS_PRES) ? 89 WDOG_RCS_POST_WAIT * 256 : WDOG_RCS_POST_WAIT; 90 91 if (!(val & WDOG_CS_RCS) && 92 readl_poll_timeout(wdt->base + WDOG_CS, val, val & WDOG_CS_RCS, 100, 93 timeout)) 94 ret = -ETIMEDOUT; 95 96 /* Wait 2.5 clocks after RCS done */ 97 if (wdt->post_rcs_wait) 98 usleep_range(wait_min, wait_min + 2000); 99 100 return ret; 101 } 102 103 static int _imx7ulp_wdt_enable(struct imx7ulp_wdt_device *wdt, bool enable) 104 { 105 u32 val = readl(wdt->base + WDOG_CS); 106 int ret; 107 108 local_irq_disable(); 109 writel(UNLOCK, wdt->base + WDOG_CNT); 110 ret = imx7ulp_wdt_wait_ulk(wdt->base); 111 if (ret) 112 goto enable_out; 113 if (enable) 114 writel(val | WDOG_CS_EN, wdt->base + WDOG_CS); 115 else 116 writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS); 117 118 local_irq_enable(); 119 ret = imx7ulp_wdt_wait_rcs(wdt); 120 121 return ret; 122 123 enable_out: 124 local_irq_enable(); 125 return ret; 126 } 127 128 static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable) 129 { 130 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 131 int ret; 132 u32 val; 133 u32 loop = RETRY_MAX; 134 135 do { 136 ret = _imx7ulp_wdt_enable(wdt, enable); 137 val = readl(wdt->base + WDOG_CS); 138 } while (--loop > 0 && ((!!(val & WDOG_CS_EN)) != enable || ret)); 139 140 if (loop == 0) 141 return -EBUSY; 142 143 return ret; 144 } 145 146 static int imx7ulp_wdt_ping(struct watchdog_device *wdog) 147 { 148 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 149 150 writel(REFRESH, wdt->base + WDOG_CNT); 151 152 return 0; 153 } 154 155 static int imx7ulp_wdt_start(struct watchdog_device *wdog) 156 { 157 return imx7ulp_wdt_enable(wdog, true); 158 } 159 160 static int imx7ulp_wdt_stop(struct watchdog_device *wdog) 161 { 162 return imx7ulp_wdt_enable(wdog, false); 163 } 164 165 static int _imx7ulp_wdt_set_timeout(struct imx7ulp_wdt_device *wdt, 166 unsigned int toval) 167 { 168 int ret; 169 170 local_irq_disable(); 171 writel(UNLOCK, wdt->base + WDOG_CNT); 172 ret = imx7ulp_wdt_wait_ulk(wdt->base); 173 if (ret) 174 goto timeout_out; 175 writel(toval, wdt->base + WDOG_TOVAL); 176 local_irq_enable(); 177 ret = imx7ulp_wdt_wait_rcs(wdt); 178 return ret; 179 180 timeout_out: 181 local_irq_enable(); 182 return ret; 183 } 184 185 static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, 186 unsigned int timeout) 187 { 188 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 189 u32 toval = wdt->hw->wdog_clock_rate * timeout; 190 u32 val; 191 int ret; 192 u32 loop = RETRY_MAX; 193 194 do { 195 ret = _imx7ulp_wdt_set_timeout(wdt, toval); 196 val = readl(wdt->base + WDOG_TOVAL); 197 } while (--loop > 0 && (val != toval || ret)); 198 199 if (loop == 0) 200 return -EBUSY; 201 202 wdog->timeout = timeout; 203 return ret; 204 } 205 206 static int imx7ulp_wdt_restart(struct watchdog_device *wdog, 207 unsigned long action, void *data) 208 { 209 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); 210 int ret; 211 212 ret = imx7ulp_wdt_enable(wdog, true); 213 if (ret) 214 return ret; 215 216 ret = imx7ulp_wdt_set_timeout(&wdt->wdd, 1); 217 if (ret) 218 return ret; 219 220 /* wait for wdog to fire */ 221 while (true) 222 ; 223 224 return NOTIFY_DONE; 225 } 226 227 static const struct watchdog_ops imx7ulp_wdt_ops = { 228 .owner = THIS_MODULE, 229 .start = imx7ulp_wdt_start, 230 .stop = imx7ulp_wdt_stop, 231 .ping = imx7ulp_wdt_ping, 232 .set_timeout = imx7ulp_wdt_set_timeout, 233 .restart = imx7ulp_wdt_restart, 234 }; 235 236 static const struct watchdog_info imx7ulp_wdt_info = { 237 .identity = "i.MX7ULP watchdog timer", 238 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 239 WDIOF_MAGICCLOSE, 240 }; 241 242 static int _imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout, unsigned int cs) 243 { 244 u32 val; 245 int ret; 246 247 local_irq_disable(); 248 249 val = readl(wdt->base + WDOG_CS); 250 if (val & WDOG_CS_CMD32EN) { 251 writel(UNLOCK, wdt->base + WDOG_CNT); 252 } else { 253 mb(); 254 /* unlock the wdog for reconfiguration */ 255 writel_relaxed(UNLOCK_SEQ0, wdt->base + WDOG_CNT); 256 writel_relaxed(UNLOCK_SEQ1, wdt->base + WDOG_CNT); 257 mb(); 258 } 259 260 ret = imx7ulp_wdt_wait_ulk(wdt->base); 261 if (ret) 262 goto init_out; 263 264 /* set an initial timeout value in TOVAL */ 265 writel(timeout, wdt->base + WDOG_TOVAL); 266 writel(cs, wdt->base + WDOG_CS); 267 local_irq_enable(); 268 ret = imx7ulp_wdt_wait_rcs(wdt); 269 270 return ret; 271 272 init_out: 273 local_irq_enable(); 274 return ret; 275 } 276 277 static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout) 278 { 279 /* enable 32bit command sequence and reconfigure */ 280 u32 val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE | 281 WDOG_CS_WAIT | WDOG_CS_STOP; 282 u32 cs, toval; 283 int ret; 284 u32 loop = RETRY_MAX; 285 286 if (wdt->hw->prescaler_enable) 287 val |= WDOG_CS_PRES; 288 289 do { 290 ret = _imx7ulp_wdt_init(wdt, timeout, val); 291 toval = readl(wdt->base + WDOG_TOVAL); 292 cs = readl(wdt->base + WDOG_CS); 293 cs &= ~(WDOG_CS_FLG | WDOG_CS_ULK | WDOG_CS_RCS); 294 } while (--loop > 0 && (cs != val || toval != timeout || ret)); 295 296 if (loop == 0) 297 return -EBUSY; 298 299 return ret; 300 } 301 302 static int imx7ulp_wdt_probe(struct platform_device *pdev) 303 { 304 struct imx7ulp_wdt_device *imx7ulp_wdt; 305 struct device *dev = &pdev->dev; 306 struct watchdog_device *wdog; 307 int ret; 308 309 imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL); 310 if (!imx7ulp_wdt) 311 return -ENOMEM; 312 313 platform_set_drvdata(pdev, imx7ulp_wdt); 314 315 imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0); 316 if (IS_ERR(imx7ulp_wdt->base)) 317 return PTR_ERR(imx7ulp_wdt->base); 318 319 imx7ulp_wdt->clk = devm_clk_get_enabled(dev, NULL); 320 if (IS_ERR(imx7ulp_wdt->clk)) { 321 dev_err(dev, "Failed to get watchdog clock\n"); 322 return PTR_ERR(imx7ulp_wdt->clk); 323 } 324 325 imx7ulp_wdt->post_rcs_wait = true; 326 if (of_device_is_compatible(dev->of_node, 327 "fsl,imx8ulp-wdt")) { 328 dev_info(dev, "imx8ulp wdt probe\n"); 329 imx7ulp_wdt->post_rcs_wait = false; 330 } else { 331 dev_info(dev, "imx7ulp wdt probe\n"); 332 } 333 334 wdog = &imx7ulp_wdt->wdd; 335 wdog->info = &imx7ulp_wdt_info; 336 wdog->ops = &imx7ulp_wdt_ops; 337 wdog->min_timeout = 1; 338 wdog->max_timeout = MAX_TIMEOUT; 339 wdog->parent = dev; 340 wdog->timeout = DEFAULT_TIMEOUT; 341 342 watchdog_init_timeout(wdog, 0, dev); 343 watchdog_stop_on_reboot(wdog); 344 watchdog_stop_on_unregister(wdog); 345 watchdog_set_drvdata(wdog, imx7ulp_wdt); 346 347 imx7ulp_wdt->hw = of_device_get_match_data(dev); 348 ret = imx7ulp_wdt_init(imx7ulp_wdt, wdog->timeout * imx7ulp_wdt->hw->wdog_clock_rate); 349 if (ret) 350 return ret; 351 352 return devm_watchdog_register_device(dev, wdog); 353 } 354 355 static int __maybe_unused imx7ulp_wdt_suspend_noirq(struct device *dev) 356 { 357 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); 358 359 if (watchdog_active(&imx7ulp_wdt->wdd)) 360 imx7ulp_wdt_stop(&imx7ulp_wdt->wdd); 361 362 clk_disable_unprepare(imx7ulp_wdt->clk); 363 364 return 0; 365 } 366 367 static int __maybe_unused imx7ulp_wdt_resume_noirq(struct device *dev) 368 { 369 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); 370 u32 timeout = imx7ulp_wdt->wdd.timeout * imx7ulp_wdt->hw->wdog_clock_rate; 371 int ret; 372 373 ret = clk_prepare_enable(imx7ulp_wdt->clk); 374 if (ret) 375 return ret; 376 377 if (watchdog_active(&imx7ulp_wdt->wdd)) { 378 imx7ulp_wdt_init(imx7ulp_wdt, timeout); 379 imx7ulp_wdt_start(&imx7ulp_wdt->wdd); 380 imx7ulp_wdt_ping(&imx7ulp_wdt->wdd); 381 } 382 383 return 0; 384 } 385 386 static const struct dev_pm_ops imx7ulp_wdt_pm_ops = { 387 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx7ulp_wdt_suspend_noirq, 388 imx7ulp_wdt_resume_noirq) 389 }; 390 391 static const struct imx_wdt_hw_feature imx7ulp_wdt_hw = { 392 .prescaler_enable = false, 393 .wdog_clock_rate = 1000, 394 }; 395 396 static const struct imx_wdt_hw_feature imx93_wdt_hw = { 397 .prescaler_enable = true, 398 .wdog_clock_rate = 125, 399 }; 400 401 static const struct of_device_id imx7ulp_wdt_dt_ids[] = { 402 { .compatible = "fsl,imx8ulp-wdt", .data = &imx7ulp_wdt_hw, }, 403 { .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, }, 404 { .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, }, 405 { /* sentinel */ } 406 }; 407 MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids); 408 409 static struct platform_driver imx7ulp_wdt_driver = { 410 .probe = imx7ulp_wdt_probe, 411 .driver = { 412 .name = "imx7ulp-wdt", 413 .pm = &imx7ulp_wdt_pm_ops, 414 .of_match_table = imx7ulp_wdt_dt_ids, 415 }, 416 }; 417 module_platform_driver(imx7ulp_wdt_driver); 418 419 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 420 MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver"); 421 MODULE_LICENSE("GPL v2"); 422