Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: Add Cadence WDT driver

Add Cadence WDT driver. This is used by Xilinx Zynq.

Signed-off-by: Harini Katakam <harinik@xilinx.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Harini Katakam and committed by
Wim Van Sebroeck
58bf0164 4846e378

+525
+8
drivers/watchdog/Kconfig
··· 157 157 Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will 158 158 reboot your system when the timeout is reached. 159 159 160 + config CADENCE_WATCHDOG 161 + tristate "Cadence Watchdog Timer" 162 + depends on ARM 163 + select WATCHDOG_CORE 164 + help 165 + Say Y here if you want to include support for the watchdog 166 + timer in the Xilinx Zynq. 167 + 160 168 config 21285_WATCHDOG 161 169 tristate "DC21285 watchdog" 162 170 depends on FOOTBRIDGE
+1
drivers/watchdog/Makefile
··· 32 32 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o 33 33 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o 34 34 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o 35 + obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o 35 36 obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o 36 37 obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o 37 38 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
+516
drivers/watchdog/cadence_wdt.c
··· 1 + /* 2 + * Cadence WDT driver - Used by Xilinx Zynq 3 + * 4 + * Copyright (C) 2010 - 2014 Xilinx, Inc. 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + */ 11 + 12 + #include <linux/clk.h> 13 + #include <linux/init.h> 14 + #include <linux/interrupt.h> 15 + #include <linux/io.h> 16 + #include <linux/irq.h> 17 + #include <linux/kernel.h> 18 + #include <linux/module.h> 19 + #include <linux/of.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/reboot.h> 22 + #include <linux/watchdog.h> 23 + 24 + #define CDNS_WDT_DEFAULT_TIMEOUT 10 25 + /* Supports 1 - 516 sec */ 26 + #define CDNS_WDT_MIN_TIMEOUT 1 27 + #define CDNS_WDT_MAX_TIMEOUT 516 28 + 29 + /* Restart key */ 30 + #define CDNS_WDT_RESTART_KEY 0x00001999 31 + 32 + /* Counter register access key */ 33 + #define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 34 + 35 + /* Counter value divisor */ 36 + #define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 37 + 38 + /* Clock prescaler value and selection */ 39 + #define CDNS_WDT_PRESCALE_64 64 40 + #define CDNS_WDT_PRESCALE_512 512 41 + #define CDNS_WDT_PRESCALE_4096 4096 42 + #define CDNS_WDT_PRESCALE_SELECT_64 1 43 + #define CDNS_WDT_PRESCALE_SELECT_512 2 44 + #define CDNS_WDT_PRESCALE_SELECT_4096 3 45 + 46 + /* Input clock frequency */ 47 + #define CDNS_WDT_CLK_10MHZ 10000000 48 + #define CDNS_WDT_CLK_75MHZ 75000000 49 + 50 + /* Counter maximum value */ 51 + #define CDNS_WDT_COUNTER_MAX 0xFFF 52 + 53 + static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; 54 + static int nowayout = WATCHDOG_NOWAYOUT; 55 + 56 + module_param(wdt_timeout, int, 0); 57 + MODULE_PARM_DESC(wdt_timeout, 58 + "Watchdog time in seconds. (default=" 59 + __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")"); 60 + 61 + module_param(nowayout, int, 0); 62 + MODULE_PARM_DESC(nowayout, 63 + "Watchdog cannot be stopped once started (default=" 64 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 65 + 66 + /** 67 + * struct cdns_wdt - Watchdog device structure 68 + * @regs: baseaddress of device 69 + * @rst: reset flag 70 + * @clk: struct clk * of a clock source 71 + * @prescaler: for saving prescaler value 72 + * @ctrl_clksel: counter clock prescaler selection 73 + * @io_lock: spinlock for IO register access 74 + * @cdns_wdt_device: watchdog device structure 75 + * @cdns_wdt_notifier: notifier structure 76 + * 77 + * Structure containing parameters specific to cadence watchdog. 78 + */ 79 + struct cdns_wdt { 80 + void __iomem *regs; 81 + bool rst; 82 + struct clk *clk; 83 + u32 prescaler; 84 + u32 ctrl_clksel; 85 + spinlock_t io_lock; 86 + struct watchdog_device cdns_wdt_device; 87 + struct notifier_block cdns_wdt_notifier; 88 + }; 89 + 90 + /* Write access to Registers */ 91 + static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) 92 + { 93 + writel_relaxed(val, wdt->regs + offset); 94 + } 95 + 96 + /*************************Register Map**************************************/ 97 + 98 + /* Register Offsets for the WDT */ 99 + #define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ 100 + #define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ 101 + #define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ 102 + #define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ 103 + 104 + /* 105 + * Zero Mode Register - This register controls how the time out is indicated 106 + * and also contains the access code to allow writes to the register (0xABC). 107 + */ 108 + #define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ 109 + #define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ 110 + #define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ 111 + #define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ 112 + #define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ 113 + /* 114 + * Counter Control register - This register controls how fast the timer runs 115 + * and the reset value and also contains the access code to allow writes to 116 + * the register. 117 + */ 118 + #define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ 119 + 120 + /** 121 + * cdns_wdt_stop - Stop the watchdog. 122 + * 123 + * @wdd: watchdog device 124 + * 125 + * Read the contents of the ZMR register, clear the WDEN bit 126 + * in the register and set the access key for successful write. 127 + * 128 + * Return: always 0 129 + */ 130 + static int cdns_wdt_stop(struct watchdog_device *wdd) 131 + { 132 + struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); 133 + 134 + spin_lock(&wdt->io_lock); 135 + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, 136 + CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); 137 + spin_unlock(&wdt->io_lock); 138 + 139 + return 0; 140 + } 141 + 142 + /** 143 + * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). 144 + * 145 + * @wdd: watchdog device 146 + * 147 + * Write the restart key value (0x00001999) to the restart register. 148 + * 149 + * Return: always 0 150 + */ 151 + static int cdns_wdt_reload(struct watchdog_device *wdd) 152 + { 153 + struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); 154 + 155 + spin_lock(&wdt->io_lock); 156 + cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, 157 + CDNS_WDT_RESTART_KEY); 158 + spin_unlock(&wdt->io_lock); 159 + 160 + return 0; 161 + } 162 + 163 + /** 164 + * cdns_wdt_start - Enable and start the watchdog. 165 + * 166 + * @wdd: watchdog device 167 + * 168 + * The counter value is calculated according to the formula: 169 + * calculated count = (timeout * clock) / prescaler + 1. 170 + * The calculated count is divided by 0x1000 to obtain the field value 171 + * to write to counter control register. 172 + * Clears the contents of prescaler and counter reset value. Sets the 173 + * prescaler to 4096 and the calculated count and access key 174 + * to write to CCR Register. 175 + * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) 176 + * or Interrupt signal(IRQEN) with a specified cycles and the access 177 + * key to write to ZMR Register. 178 + * 179 + * Return: always 0 180 + */ 181 + static int cdns_wdt_start(struct watchdog_device *wdd) 182 + { 183 + struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); 184 + unsigned int data = 0; 185 + unsigned short count; 186 + unsigned long clock_f = clk_get_rate(wdt->clk); 187 + 188 + /* 189 + * Counter value divisor to obtain the value of 190 + * counter reset to be written to control register. 191 + */ 192 + count = (wdd->timeout * (clock_f / wdt->prescaler)) / 193 + CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; 194 + 195 + if (count > CDNS_WDT_COUNTER_MAX) 196 + count = CDNS_WDT_COUNTER_MAX; 197 + 198 + spin_lock(&wdt->io_lock); 199 + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, 200 + CDNS_WDT_ZMR_ZKEY_VAL); 201 + 202 + count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; 203 + 204 + /* Write counter access key first to be able write to register */ 205 + data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; 206 + cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); 207 + data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | 208 + CDNS_WDT_ZMR_ZKEY_VAL; 209 + 210 + /* Reset on timeout if specified in device tree. */ 211 + if (wdt->rst) { 212 + data |= CDNS_WDT_ZMR_RSTEN_MASK; 213 + data &= ~CDNS_WDT_ZMR_IRQEN_MASK; 214 + } else { 215 + data &= ~CDNS_WDT_ZMR_RSTEN_MASK; 216 + data |= CDNS_WDT_ZMR_IRQEN_MASK; 217 + } 218 + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); 219 + cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, 220 + CDNS_WDT_RESTART_KEY); 221 + spin_unlock(&wdt->io_lock); 222 + 223 + return 0; 224 + } 225 + 226 + /** 227 + * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. 228 + * 229 + * @wdd: watchdog device 230 + * @new_time: new timeout value that needs to be set 231 + * Return: 0 on success 232 + * 233 + * Update the watchdog_device timeout with new value which is used when 234 + * cdns_wdt_start is called. 235 + */ 236 + static int cdns_wdt_settimeout(struct watchdog_device *wdd, 237 + unsigned int new_time) 238 + { 239 + wdd->timeout = new_time; 240 + 241 + return cdns_wdt_start(wdd); 242 + } 243 + 244 + /** 245 + * cdns_wdt_irq_handler - Notifies of watchdog timeout. 246 + * 247 + * @irq: interrupt number 248 + * @dev_id: pointer to a platform device structure 249 + * Return: IRQ_HANDLED 250 + * 251 + * The handler is invoked when the watchdog times out and a 252 + * reset on timeout has not been enabled. 253 + */ 254 + static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) 255 + { 256 + struct platform_device *pdev = dev_id; 257 + 258 + dev_info(&pdev->dev, 259 + "Watchdog timed out. Internal reset not enabled\n"); 260 + 261 + return IRQ_HANDLED; 262 + } 263 + 264 + /* 265 + * Info structure used to indicate the features supported by the device 266 + * to the upper layers. This is defined in watchdog.h header file. 267 + */ 268 + static struct watchdog_info cdns_wdt_info = { 269 + .identity = "cdns_wdt watchdog", 270 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 271 + WDIOF_MAGICCLOSE, 272 + }; 273 + 274 + /* Watchdog Core Ops */ 275 + static struct watchdog_ops cdns_wdt_ops = { 276 + .owner = THIS_MODULE, 277 + .start = cdns_wdt_start, 278 + .stop = cdns_wdt_stop, 279 + .ping = cdns_wdt_reload, 280 + .set_timeout = cdns_wdt_settimeout, 281 + }; 282 + 283 + /** 284 + * cdns_wdt_notify_sys - Notifier for reboot or shutdown. 285 + * 286 + * @this: handle to notifier block 287 + * @code: turn off indicator 288 + * @unused: unused 289 + * Return: NOTIFY_DONE 290 + * 291 + * This notifier is invoked whenever the system reboot or shutdown occur 292 + * because we need to disable the WDT before system goes down as WDT might 293 + * reset on the next boot. 294 + */ 295 + static int cdns_wdt_notify_sys(struct notifier_block *this, unsigned long code, 296 + void *unused) 297 + { 298 + struct cdns_wdt *wdt = container_of(this, struct cdns_wdt, 299 + cdns_wdt_notifier); 300 + if (code == SYS_DOWN || code == SYS_HALT) 301 + cdns_wdt_stop(&wdt->cdns_wdt_device); 302 + 303 + return NOTIFY_DONE; 304 + } 305 + 306 + /************************Platform Operations*****************************/ 307 + /** 308 + * cdns_wdt_probe - Probe call for the device. 309 + * 310 + * @pdev: handle to the platform device structure. 311 + * Return: 0 on success, negative error otherwise. 312 + * 313 + * It does all the memory allocation and registration for the device. 314 + */ 315 + static int cdns_wdt_probe(struct platform_device *pdev) 316 + { 317 + struct resource *res; 318 + int ret, irq; 319 + unsigned long clock_f; 320 + struct cdns_wdt *wdt; 321 + struct watchdog_device *cdns_wdt_device; 322 + 323 + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 324 + if (!wdt) 325 + return -ENOMEM; 326 + 327 + cdns_wdt_device = &wdt->cdns_wdt_device; 328 + cdns_wdt_device->info = &cdns_wdt_info; 329 + cdns_wdt_device->ops = &cdns_wdt_ops; 330 + cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT; 331 + cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT; 332 + cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT; 333 + 334 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 335 + wdt->regs = devm_ioremap_resource(&pdev->dev, res); 336 + if (IS_ERR(wdt->regs)) 337 + return PTR_ERR(wdt->regs); 338 + 339 + /* Register the interrupt */ 340 + wdt->rst = of_property_read_bool(pdev->dev.of_node, "reset-on-timeout"); 341 + irq = platform_get_irq(pdev, 0); 342 + if (!wdt->rst && irq >= 0) { 343 + ret = devm_request_irq(&pdev->dev, irq, cdns_wdt_irq_handler, 0, 344 + pdev->name, pdev); 345 + if (ret) { 346 + dev_err(&pdev->dev, 347 + "cannot register interrupt handler err=%d\n", 348 + ret); 349 + return ret; 350 + } 351 + } 352 + 353 + /* Initialize the members of cdns_wdt structure */ 354 + cdns_wdt_device->parent = &pdev->dev; 355 + 356 + ret = watchdog_init_timeout(cdns_wdt_device, wdt_timeout, &pdev->dev); 357 + if (ret) { 358 + dev_err(&pdev->dev, "unable to set timeout value\n"); 359 + return ret; 360 + } 361 + 362 + watchdog_set_nowayout(cdns_wdt_device, nowayout); 363 + watchdog_set_drvdata(cdns_wdt_device, wdt); 364 + 365 + wdt->clk = devm_clk_get(&pdev->dev, NULL); 366 + if (IS_ERR(wdt->clk)) { 367 + dev_err(&pdev->dev, "input clock not found\n"); 368 + ret = PTR_ERR(wdt->clk); 369 + return ret; 370 + } 371 + 372 + ret = clk_prepare_enable(wdt->clk); 373 + if (ret) { 374 + dev_err(&pdev->dev, "unable to enable clock\n"); 375 + return ret; 376 + } 377 + 378 + clock_f = clk_get_rate(wdt->clk); 379 + if (clock_f <= CDNS_WDT_CLK_75MHZ) { 380 + wdt->prescaler = CDNS_WDT_PRESCALE_512; 381 + wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; 382 + } else { 383 + wdt->prescaler = CDNS_WDT_PRESCALE_4096; 384 + wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; 385 + } 386 + 387 + spin_lock_init(&wdt->io_lock); 388 + 389 + wdt->cdns_wdt_notifier.notifier_call = &cdns_wdt_notify_sys; 390 + ret = register_reboot_notifier(&wdt->cdns_wdt_notifier); 391 + if (ret != 0) { 392 + dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", 393 + ret); 394 + goto err_clk_disable; 395 + } 396 + 397 + ret = watchdog_register_device(cdns_wdt_device); 398 + if (ret) { 399 + dev_err(&pdev->dev, "Failed to register wdt device\n"); 400 + goto err_clk_disable; 401 + } 402 + platform_set_drvdata(pdev, wdt); 403 + 404 + dev_dbg(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", 405 + wdt->regs, cdns_wdt_device->timeout, 406 + nowayout ? ", nowayout" : ""); 407 + 408 + return 0; 409 + 410 + err_clk_disable: 411 + clk_disable_unprepare(wdt->clk); 412 + 413 + return ret; 414 + } 415 + 416 + /** 417 + * cdns_wdt_remove - Probe call for the device. 418 + * 419 + * @pdev: handle to the platform device structure. 420 + * Return: 0 on success, otherwise negative error. 421 + * 422 + * Unregister the device after releasing the resources. 423 + */ 424 + static int cdns_wdt_remove(struct platform_device *pdev) 425 + { 426 + struct cdns_wdt *wdt = platform_get_drvdata(pdev); 427 + 428 + cdns_wdt_stop(&wdt->cdns_wdt_device); 429 + watchdog_unregister_device(&wdt->cdns_wdt_device); 430 + unregister_reboot_notifier(&wdt->cdns_wdt_notifier); 431 + clk_disable_unprepare(wdt->clk); 432 + 433 + return 0; 434 + } 435 + 436 + /** 437 + * cdns_wdt_shutdown - Stop the device. 438 + * 439 + * @pdev: handle to the platform structure. 440 + * 441 + */ 442 + static void cdns_wdt_shutdown(struct platform_device *pdev) 443 + { 444 + struct cdns_wdt *wdt = platform_get_drvdata(pdev); 445 + 446 + cdns_wdt_stop(&wdt->cdns_wdt_device); 447 + clk_disable_unprepare(wdt->clk); 448 + } 449 + 450 + /** 451 + * cdns_wdt_suspend - Stop the device. 452 + * 453 + * @dev: handle to the device structure. 454 + * Return: 0 always. 455 + */ 456 + static int __maybe_unused cdns_wdt_suspend(struct device *dev) 457 + { 458 + struct platform_device *pdev = container_of(dev, 459 + struct platform_device, dev); 460 + struct cdns_wdt *wdt = platform_get_drvdata(pdev); 461 + 462 + cdns_wdt_stop(&wdt->cdns_wdt_device); 463 + clk_disable_unprepare(wdt->clk); 464 + 465 + return 0; 466 + } 467 + 468 + /** 469 + * cdns_wdt_resume - Resume the device. 470 + * 471 + * @dev: handle to the device structure. 472 + * Return: 0 on success, errno otherwise. 473 + */ 474 + static int __maybe_unused cdns_wdt_resume(struct device *dev) 475 + { 476 + int ret; 477 + struct platform_device *pdev = container_of(dev, 478 + struct platform_device, dev); 479 + struct cdns_wdt *wdt = platform_get_drvdata(pdev); 480 + 481 + ret = clk_prepare_enable(wdt->clk); 482 + if (ret) { 483 + dev_err(dev, "unable to enable clock\n"); 484 + return ret; 485 + } 486 + cdns_wdt_start(&wdt->cdns_wdt_device); 487 + 488 + return 0; 489 + } 490 + 491 + static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); 492 + 493 + static struct of_device_id cdns_wdt_of_match[] = { 494 + { .compatible = "cdns,wdt-r1p2", }, 495 + { /* end of table */ } 496 + }; 497 + MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); 498 + 499 + /* Driver Structure */ 500 + static struct platform_driver cdns_wdt_driver = { 501 + .probe = cdns_wdt_probe, 502 + .remove = cdns_wdt_remove, 503 + .shutdown = cdns_wdt_shutdown, 504 + .driver = { 505 + .name = "cdns-wdt", 506 + .owner = THIS_MODULE, 507 + .of_match_table = cdns_wdt_of_match, 508 + .pm = &cdns_wdt_pm_ops, 509 + }, 510 + }; 511 + 512 + module_platform_driver(cdns_wdt_driver); 513 + 514 + MODULE_AUTHOR("Xilinx, Inc."); 515 + MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); 516 + MODULE_LICENSE("GPL");