1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2026 Nuvoton technology corporation. 4 * 5 * Author: Zi-Yu Chen <zychennvt@gmail.com> 6 */ 7 8 #include <linux/bitfield.h> 9 #include <linux/bits.h> 10 #include <linux/clk.h> 11 #include <linux/err.h> 12 #include <linux/init.h> 13 #include <linux/interrupt.h> 14 #include <linux/io.h> 15 #include <linux/iopoll.h> 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/moduleparam.h> 19 #include <linux/of.h> 20 #include <linux/platform_device.h> 21 #include <linux/pm_wakeirq.h> 22 #include <linux/property.h> 23 #include <linux/types.h> 24 #include <linux/watchdog.h> 25 26 #define DRV_NAME "ma35d1-wdt" 27 28 #define REG_WDT_CTL 0x00 29 #define REG_WDT_RSTCNT 0x08 30 31 #define TOUTSEL GENMASK(11, 8) 32 #define WDTEN BIT(7) 33 #define INTEN BIT(6) 34 #define WKF BIT(5) 35 #define WKEN BIT(4) 36 #define IF BIT(3) 37 #define RSTF BIT(2) 38 #define RSTEN BIT(1) 39 #define SYNC BIT(30) 40 41 #define WDT_DEFAULT_TIMEOUT 32 42 #define RESET_COUNTER 0x00005AA5 43 44 static bool nowayout = WATCHDOG_NOWAYOUT; 45 static unsigned int timeout; 46 47 struct ma35d1_wdt_dev { 48 struct watchdog_device wdt_dev; 49 spinlock_t lock; 50 void __iomem *wdt_base; 51 struct clk *clk; 52 unsigned long clk_rate; 53 int irq; 54 }; 55 56 static int ma35d1_wdt_wait_sync(struct ma35d1_wdt_dev *ma35d1_wdt) 57 { 58 unsigned int val; 59 60 return readl_relaxed_poll_timeout_atomic(ma35d1_wdt->wdt_base + 61 REG_WDT_CTL, val, !(val & SYNC), 10, 125); 62 } 63 64 /** 65 * ma35d1_wdt_stop - Stop the watchdog. 66 * 67 * @wdt_dev: watchdog device 68 * 69 * Read the contents of the CTL register, clear the WDTEN bit 70 * in the register and set the access key for successful write. 71 * 72 * Return: 0 on success, negative error otherwise. 73 */ 74 static int ma35d1_wdt_stop(struct watchdog_device *wdt_dev) 75 { 76 struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); 77 unsigned int val; 78 int ret; 79 80 guard(spinlock_irqsave)(&ma35d1_wdt->lock); 81 val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); 82 val &= ~WDTEN; 83 writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); 84 ret = ma35d1_wdt_wait_sync(ma35d1_wdt); 85 if (ret) { 86 dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n"); 87 return ret; 88 } 89 return 0; 90 } 91 92 static int ma35d1_wdt_ping(struct watchdog_device *wdt_dev) 93 { 94 struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); 95 96 writel_relaxed(RESET_COUNTER, ma35d1_wdt->wdt_base + REG_WDT_RSTCNT); 97 98 return 0; 99 } 100 101 static int ma35d1_wdt_set_timeout(struct watchdog_device *wdt_dev, 102 unsigned int timeout) 103 { 104 struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); 105 unsigned long target_ticks; 106 unsigned int val, i; 107 static const uint8_t toutsel_shifts[] = { 4, 6, 8, 10, 12, 108 14, 16, 18, 20 }; 109 110 if (timeout < (ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms / 1000)) { 111 target_ticks = (unsigned long)timeout * ma35d1_wdt->clk_rate; 112 113 for (i = 0; i < ARRAY_SIZE(toutsel_shifts); i++) { 114 if ((1UL << toutsel_shifts[i]) >= target_ticks) 115 break; 116 } 117 /* To avoid truncation errors (0 seconds) during division. */ 118 wdt_dev->timeout = 119 (1UL << toutsel_shifts[i]) / ma35d1_wdt->clk_rate; 120 if (wdt_dev->timeout == 0) 121 wdt_dev->timeout = 1; 122 123 } else { 124 i = ARRAY_SIZE(toutsel_shifts) - 1; 125 wdt_dev->timeout = timeout; 126 } 127 128 guard(spinlock_irqsave)(&ma35d1_wdt->lock); 129 val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); 130 val &= ~TOUTSEL; 131 val |= FIELD_PREP(TOUTSEL, i); 132 writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); 133 134 ma35d1_wdt_ping(wdt_dev); 135 return 0; 136 } 137 138 static int ma35d1_wdt_start(struct watchdog_device *wdt_dev) 139 { 140 struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev); 141 void __iomem *wdt_base = ma35d1_wdt->wdt_base; 142 unsigned int val; 143 int ret; 144 145 ret = ma35d1_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 146 if (ret < 0) 147 return ret; 148 149 guard(spinlock_irqsave)(&ma35d1_wdt->lock); 150 val = readl_relaxed(wdt_base + REG_WDT_CTL); 151 val |= (WDTEN | RSTEN); 152 153 writel_relaxed(val, wdt_base + REG_WDT_CTL); 154 ret = ma35d1_wdt_wait_sync(ma35d1_wdt); 155 if (ret) { 156 dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n"); 157 return ret; 158 } 159 160 writel_relaxed(RESET_COUNTER, wdt_base + REG_WDT_RSTCNT); 161 162 return 0; 163 } 164 165 static const struct watchdog_info ma35d1_wdt_info = { 166 .identity = DRV_NAME, 167 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | 168 WDIOF_CARDRESET, 169 }; 170 171 static const struct watchdog_ops ma35d1_wdt_ops = { 172 .owner = THIS_MODULE, 173 .start = ma35d1_wdt_start, 174 .stop = ma35d1_wdt_stop, 175 .ping = ma35d1_wdt_ping, 176 .set_timeout = ma35d1_wdt_set_timeout, 177 }; 178 179 static irqreturn_t ma35d1_wdt_isr(int irq, void *dev_id) 180 { 181 struct ma35d1_wdt_dev *ma35d1_wdt = dev_id; 182 unsigned int val; 183 184 /* Clear the flag if set */ 185 guard(spinlock)(&ma35d1_wdt->lock); 186 val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); 187 writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); 188 189 return IRQ_HANDLED; 190 } 191 192 static int ma35d1_wdt_probe(struct platform_device *pdev) 193 { 194 struct device *dev = &pdev->dev; 195 struct ma35d1_wdt_dev *ma35d1_wdt; 196 unsigned long clk_rate, val; 197 int ret; 198 199 ma35d1_wdt = devm_kzalloc(dev, sizeof(*ma35d1_wdt), GFP_KERNEL); 200 if (!ma35d1_wdt) 201 return -ENOMEM; 202 203 spin_lock_init(&ma35d1_wdt->lock); 204 platform_set_drvdata(pdev, ma35d1_wdt); 205 206 ma35d1_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0); 207 if (IS_ERR(ma35d1_wdt->wdt_base)) 208 return PTR_ERR(ma35d1_wdt->wdt_base); 209 210 ma35d1_wdt->clk = devm_clk_get_enabled(dev, NULL); 211 if (IS_ERR(ma35d1_wdt->clk)) 212 return dev_err_probe(dev, PTR_ERR(ma35d1_wdt->clk), 213 "Can't get Watchdog clock\n"); 214 215 clk_rate = clk_get_rate(ma35d1_wdt->clk); 216 if (!clk_rate) 217 return -EINVAL; 218 ma35d1_wdt->clk_rate = clk_rate; 219 220 ma35d1_wdt->irq = platform_get_irq(pdev, 0); 221 if (ma35d1_wdt->irq < 0) 222 return dev_err_probe(dev, ma35d1_wdt->irq, 223 "failed to get irq\n"); 224 225 ma35d1_wdt->wdt_dev.info = &ma35d1_wdt_info; 226 ma35d1_wdt->wdt_dev.ops = &ma35d1_wdt_ops; 227 ma35d1_wdt->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT; 228 ma35d1_wdt->wdt_dev.min_timeout = 1; 229 ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms = (1U << 20) * 1000 / clk_rate; 230 ma35d1_wdt->wdt_dev.parent = dev; 231 232 val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); 233 if (val & RSTF) { 234 ma35d1_wdt->wdt_dev.bootstatus = WDIOF_CARDRESET; 235 writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); 236 } 237 238 if (val & WDTEN) 239 set_bit(WDOG_HW_RUNNING, &ma35d1_wdt->wdt_dev.status); 240 241 watchdog_set_drvdata(&ma35d1_wdt->wdt_dev, ma35d1_wdt); 242 watchdog_set_nowayout(&ma35d1_wdt->wdt_dev, nowayout); 243 watchdog_init_timeout(&ma35d1_wdt->wdt_dev, timeout, &pdev->dev); 244 245 ma35d1_wdt_set_timeout(&ma35d1_wdt->wdt_dev, 246 ma35d1_wdt->wdt_dev.timeout); 247 248 ret = devm_request_irq(dev, ma35d1_wdt->irq, ma35d1_wdt_isr, 0, 249 dev_name(dev), ma35d1_wdt); 250 if (ret) 251 return dev_err_probe(dev, ret, "cannot claim IRQ %d\n", 252 ma35d1_wdt->irq); 253 254 if (device_property_read_bool(dev, "wakeup-source")) { 255 ret = devm_device_init_wakeup(dev); 256 if (ret) 257 return ret; 258 259 ret = devm_pm_set_wake_irq(dev, ma35d1_wdt->irq); 260 if (ret) 261 return ret; 262 } 263 264 watchdog_stop_on_reboot(&ma35d1_wdt->wdt_dev); 265 watchdog_stop_on_unregister(&ma35d1_wdt->wdt_dev); 266 ret = devm_watchdog_register_device(dev, &ma35d1_wdt->wdt_dev); 267 if (ret) 268 return ret; 269 270 return 0; 271 } 272 273 static int ma35d1_wdt_suspend(struct device *dev) 274 { 275 struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev); 276 277 if (watchdog_active(&ma35d1_wdt->wdt_dev) || 278 watchdog_hw_running(&ma35d1_wdt->wdt_dev)) { 279 u32 val; 280 int ret; 281 282 guard(spinlock_irqsave)(&ma35d1_wdt->lock); 283 val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); 284 285 if (device_may_wakeup(dev)) { 286 val &= ~RSTEN; 287 val |= (INTEN | WKEN); 288 } else { 289 val &= ~(WDTEN | RSTEN); 290 } 291 writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); 292 ret = ma35d1_wdt_wait_sync(ma35d1_wdt); 293 if (ret) { 294 dev_err(dev, "Wait for WDTEN SYNC timeout!\n"); 295 return ret; 296 } 297 } 298 299 return 0; 300 } 301 302 static int ma35d1_wdt_resume(struct device *dev) 303 { 304 struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev); 305 306 if (watchdog_active(&ma35d1_wdt->wdt_dev) || 307 watchdog_hw_running(&ma35d1_wdt->wdt_dev)) { 308 u32 val; 309 int ret; 310 311 guard(spinlock_irqsave)(&ma35d1_wdt->lock); 312 val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL); 313 314 if (device_may_wakeup(dev)) { 315 val |= RSTEN; 316 val &= ~(INTEN | WKEN); 317 } else { 318 val |= (WDTEN | RSTEN); 319 } 320 writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL); 321 writel_relaxed(RESET_COUNTER, 322 ma35d1_wdt->wdt_base + REG_WDT_RSTCNT); 323 ret = ma35d1_wdt_wait_sync(ma35d1_wdt); 324 if (ret) { 325 dev_err(dev, "Wait for WDTEN SYNC timeout!\n"); 326 return ret; 327 } 328 } 329 return 0; 330 } 331 332 static DEFINE_SIMPLE_DEV_PM_OPS(ma35d1_wdt_pm_ops, ma35d1_wdt_suspend, 333 ma35d1_wdt_resume); 334 335 static const struct of_device_id ma35d1_wdt_dt_ids[] = { 336 { .compatible = "nuvoton,ma35d1-wdt" }, 337 { /* sentinel */ }, 338 }; 339 MODULE_DEVICE_TABLE(of, ma35d1_wdt_dt_ids); 340 341 static struct platform_driver ma35d1_wdt_driver = { 342 .probe = ma35d1_wdt_probe, 343 .driver = { 344 .name = DRV_NAME, 345 .pm = pm_ptr(&ma35d1_wdt_pm_ops), 346 .of_match_table = ma35d1_wdt_dt_ids, 347 }, 348 }; 349 350 module_platform_driver(ma35d1_wdt_driver); 351 352 module_param(timeout, uint, 0); 353 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); 354 355 module_param(nowayout, bool, 0); 356 MODULE_PARM_DESC( 357 nowayout, 358 "Watchdog cannot be stopped once started (default=" __MODULE_STRING( 359 WATCHDOG_NOWAYOUT) ")"); 360 361 MODULE_AUTHOR("Zi-Yu Chen <zychennvt@gmail.com>"); 362 MODULE_DESCRIPTION("Nuvoton MA35D1 Watchdog Timer Driver"); 363 MODULE_LICENSE("GPL"); 364