root/drivers/watchdog/sama5d4_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. wdt_write
  2. wdt_write_nosleep
  3. sama5d4_wdt_start
  4. sama5d4_wdt_stop
  5. sama5d4_wdt_ping
  6. sama5d4_wdt_set_timeout
  7. sama5d4_wdt_irq_handler
  8. of_sama5d4_wdt_init
  9. sama5d4_wdt_init
  10. sama5d4_wdt_probe
  11. sama5d4_wdt_suspend_late
  12. sama5d4_wdt_resume_early

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Driver for Atmel SAMA5D4 Watchdog Timer
   4  *
   5  * Copyright (C) 2015 Atmel Corporation
   6  */
   7 
   8 #include <linux/delay.h>
   9 #include <linux/interrupt.h>
  10 #include <linux/io.h>
  11 #include <linux/kernel.h>
  12 #include <linux/module.h>
  13 #include <linux/of.h>
  14 #include <linux/of_irq.h>
  15 #include <linux/platform_device.h>
  16 #include <linux/reboot.h>
  17 #include <linux/watchdog.h>
  18 
  19 #include "at91sam9_wdt.h"
  20 
  21 /* minimum and maximum watchdog timeout, in seconds */
  22 #define MIN_WDT_TIMEOUT         1
  23 #define MAX_WDT_TIMEOUT         16
  24 #define WDT_DEFAULT_TIMEOUT     MAX_WDT_TIMEOUT
  25 
  26 #define WDT_SEC2TICKS(s)        ((s) ? (((s) << 8) - 1) : 0)
  27 
  28 struct sama5d4_wdt {
  29         struct watchdog_device  wdd;
  30         void __iomem            *reg_base;
  31         u32                     mr;
  32         unsigned long           last_ping;
  33 };
  34 
  35 static int wdt_timeout;
  36 static bool nowayout = WATCHDOG_NOWAYOUT;
  37 
  38 module_param(wdt_timeout, int, 0);
  39 MODULE_PARM_DESC(wdt_timeout,
  40         "Watchdog timeout in seconds. (default = "
  41         __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
  42 
  43 module_param(nowayout, bool, 0);
  44 MODULE_PARM_DESC(nowayout,
  45         "Watchdog cannot be stopped once started (default="
  46         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  47 
  48 #define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
  49 
  50 #define wdt_read(wdt, field) \
  51         readl_relaxed((wdt)->reg_base + (field))
  52 
  53 /* 4 slow clock periods is 4/32768 = 122.07µs*/
  54 #define WDT_DELAY       usecs_to_jiffies(123)
  55 
  56 static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
  57 {
  58         /*
  59          * WDT_CR and WDT_MR must not be modified within three slow clock
  60          * periods following a restart of the watchdog performed by a write
  61          * access in WDT_CR.
  62          */
  63         while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
  64                 usleep_range(30, 125);
  65         writel_relaxed(val, wdt->reg_base + field);
  66         wdt->last_ping = jiffies;
  67 }
  68 
  69 static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
  70 {
  71         if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
  72                 udelay(123);
  73         writel_relaxed(val, wdt->reg_base + field);
  74         wdt->last_ping = jiffies;
  75 }
  76 
  77 static int sama5d4_wdt_start(struct watchdog_device *wdd)
  78 {
  79         struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
  80 
  81         wdt->mr &= ~AT91_WDT_WDDIS;
  82         wdt_write(wdt, AT91_WDT_MR, wdt->mr);
  83 
  84         return 0;
  85 }
  86 
  87 static int sama5d4_wdt_stop(struct watchdog_device *wdd)
  88 {
  89         struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
  90 
  91         wdt->mr |= AT91_WDT_WDDIS;
  92         wdt_write(wdt, AT91_WDT_MR, wdt->mr);
  93 
  94         return 0;
  95 }
  96 
  97 static int sama5d4_wdt_ping(struct watchdog_device *wdd)
  98 {
  99         struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
 100 
 101         wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
 102 
 103         return 0;
 104 }
 105 
 106 static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
 107                                  unsigned int timeout)
 108 {
 109         struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
 110         u32 value = WDT_SEC2TICKS(timeout);
 111 
 112         wdt->mr &= ~AT91_WDT_WDV;
 113         wdt->mr |= AT91_WDT_SET_WDV(value);
 114 
 115         /*
 116          * WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
 117          * setting the WDDIS bit, and while it is set, the fields WDV and WDD
 118          * must not be modified.
 119          * If the watchdog is enabled, then the timeout can be updated. Else,
 120          * wait that the user enables it.
 121          */
 122         if (wdt_enabled)
 123                 wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
 124 
 125         wdd->timeout = timeout;
 126 
 127         return 0;
 128 }
 129 
 130 static const struct watchdog_info sama5d4_wdt_info = {
 131         .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
 132         .identity = "Atmel SAMA5D4 Watchdog",
 133 };
 134 
 135 static const struct watchdog_ops sama5d4_wdt_ops = {
 136         .owner = THIS_MODULE,
 137         .start = sama5d4_wdt_start,
 138         .stop = sama5d4_wdt_stop,
 139         .ping = sama5d4_wdt_ping,
 140         .set_timeout = sama5d4_wdt_set_timeout,
 141 };
 142 
 143 static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
 144 {
 145         struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
 146 
 147         if (wdt_read(wdt, AT91_WDT_SR)) {
 148                 pr_crit("Atmel Watchdog Software Reset\n");
 149                 emergency_restart();
 150                 pr_crit("Reboot didn't succeed\n");
 151         }
 152 
 153         return IRQ_HANDLED;
 154 }
 155 
 156 static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
 157 {
 158         const char *tmp;
 159 
 160         wdt->mr = AT91_WDT_WDDIS;
 161 
 162         if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
 163             !strcmp(tmp, "software"))
 164                 wdt->mr |= AT91_WDT_WDFIEN;
 165         else
 166                 wdt->mr |= AT91_WDT_WDRSTEN;
 167 
 168         if (of_property_read_bool(np, "atmel,idle-halt"))
 169                 wdt->mr |= AT91_WDT_WDIDLEHLT;
 170 
 171         if (of_property_read_bool(np, "atmel,dbg-halt"))
 172                 wdt->mr |= AT91_WDT_WDDBGHLT;
 173 
 174         return 0;
 175 }
 176 
 177 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
 178 {
 179         u32 reg;
 180         /*
 181          * When booting and resuming, the bootloader may have changed the
 182          * watchdog configuration.
 183          * If the watchdog is already running, we can safely update it.
 184          * Else, we have to disable it properly.
 185          */
 186         if (wdt_enabled) {
 187                 wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
 188         } else {
 189                 reg = wdt_read(wdt, AT91_WDT_MR);
 190                 if (!(reg & AT91_WDT_WDDIS))
 191                         wdt_write_nosleep(wdt, AT91_WDT_MR,
 192                                           reg | AT91_WDT_WDDIS);
 193         }
 194         return 0;
 195 }
 196 
 197 static int sama5d4_wdt_probe(struct platform_device *pdev)
 198 {
 199         struct device *dev = &pdev->dev;
 200         struct watchdog_device *wdd;
 201         struct sama5d4_wdt *wdt;
 202         void __iomem *regs;
 203         u32 irq = 0;
 204         u32 timeout;
 205         int ret;
 206 
 207         wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
 208         if (!wdt)
 209                 return -ENOMEM;
 210 
 211         wdd = &wdt->wdd;
 212         wdd->timeout = WDT_DEFAULT_TIMEOUT;
 213         wdd->info = &sama5d4_wdt_info;
 214         wdd->ops = &sama5d4_wdt_ops;
 215         wdd->min_timeout = MIN_WDT_TIMEOUT;
 216         wdd->max_timeout = MAX_WDT_TIMEOUT;
 217         wdt->last_ping = jiffies;
 218 
 219         watchdog_set_drvdata(wdd, wdt);
 220 
 221         regs = devm_platform_ioremap_resource(pdev, 0);
 222         if (IS_ERR(regs))
 223                 return PTR_ERR(regs);
 224 
 225         wdt->reg_base = regs;
 226 
 227         irq = irq_of_parse_and_map(dev->of_node, 0);
 228         if (!irq)
 229                 dev_warn(dev, "failed to get IRQ from DT\n");
 230 
 231         ret = of_sama5d4_wdt_init(dev->of_node, wdt);
 232         if (ret)
 233                 return ret;
 234 
 235         if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
 236                 ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
 237                                        IRQF_SHARED | IRQF_IRQPOLL |
 238                                        IRQF_NO_SUSPEND, pdev->name, pdev);
 239                 if (ret) {
 240                         dev_err(dev, "cannot register interrupt handler\n");
 241                         return ret;
 242                 }
 243         }
 244 
 245         watchdog_init_timeout(wdd, wdt_timeout, dev);
 246 
 247         timeout = WDT_SEC2TICKS(wdd->timeout);
 248 
 249         wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
 250         wdt->mr |= AT91_WDT_SET_WDV(timeout);
 251 
 252         ret = sama5d4_wdt_init(wdt);
 253         if (ret)
 254                 return ret;
 255 
 256         watchdog_set_nowayout(wdd, nowayout);
 257 
 258         watchdog_stop_on_unregister(wdd);
 259         ret = devm_watchdog_register_device(dev, wdd);
 260         if (ret)
 261                 return ret;
 262 
 263         platform_set_drvdata(pdev, wdt);
 264 
 265         dev_info(dev, "initialized (timeout = %d sec, nowayout = %d)\n",
 266                  wdd->timeout, nowayout);
 267 
 268         return 0;
 269 }
 270 
 271 static const struct of_device_id sama5d4_wdt_of_match[] = {
 272         { .compatible = "atmel,sama5d4-wdt", },
 273         { }
 274 };
 275 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
 276 
 277 #ifdef CONFIG_PM_SLEEP
 278 static int sama5d4_wdt_suspend_late(struct device *dev)
 279 {
 280         struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
 281 
 282         if (watchdog_active(&wdt->wdd))
 283                 sama5d4_wdt_stop(&wdt->wdd);
 284 
 285         return 0;
 286 }
 287 
 288 static int sama5d4_wdt_resume_early(struct device *dev)
 289 {
 290         struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
 291 
 292         /*
 293          * FIXME: writing MR also pings the watchdog which may not be desired.
 294          * This should only be done when the registers are lost on suspend but
 295          * there is no way to get this information right now.
 296          */
 297         sama5d4_wdt_init(wdt);
 298 
 299         if (watchdog_active(&wdt->wdd))
 300                 sama5d4_wdt_start(&wdt->wdd);
 301 
 302         return 0;
 303 }
 304 #endif
 305 
 306 static const struct dev_pm_ops sama5d4_wdt_pm_ops = {
 307         SET_LATE_SYSTEM_SLEEP_PM_OPS(sama5d4_wdt_suspend_late,
 308                         sama5d4_wdt_resume_early)
 309 };
 310 
 311 static struct platform_driver sama5d4_wdt_driver = {
 312         .probe          = sama5d4_wdt_probe,
 313         .driver         = {
 314                 .name   = "sama5d4_wdt",
 315                 .pm     = &sama5d4_wdt_pm_ops,
 316                 .of_match_table = sama5d4_wdt_of_match,
 317         }
 318 };
 319 module_platform_driver(sama5d4_wdt_driver);
 320 
 321 MODULE_AUTHOR("Atmel Corporation");
 322 MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver");
 323 MODULE_LICENSE("GPL v2");

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