root/drivers/watchdog/cadence_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cdns_wdt_writereg
  2. cdns_wdt_stop
  3. cdns_wdt_reload
  4. cdns_wdt_start
  5. cdns_wdt_settimeout
  6. cdns_wdt_irq_handler
  7. cdns_clk_disable_unprepare
  8. cdns_wdt_probe
  9. cdns_wdt_suspend
  10. cdns_wdt_resume

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

/* [<][>][^][v][top][bottom][index][help] */