Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for Atmel SAMA5D4 Watchdog Timer
0004  *
0005  * Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/interrupt.h>
0010 #include <linux/io.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/of_device.h>
0015 #include <linux/of_irq.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/reboot.h>
0018 #include <linux/watchdog.h>
0019 
0020 #include "at91sam9_wdt.h"
0021 
0022 /* minimum and maximum watchdog timeout, in seconds */
0023 #define MIN_WDT_TIMEOUT     1
0024 #define MAX_WDT_TIMEOUT     16
0025 #define WDT_DEFAULT_TIMEOUT MAX_WDT_TIMEOUT
0026 
0027 #define WDT_SEC2TICKS(s)    ((s) ? (((s) << 8) - 1) : 0)
0028 
0029 struct sama5d4_wdt {
0030     struct watchdog_device  wdd;
0031     void __iomem        *reg_base;
0032     u32         mr;
0033     u32         ir;
0034     unsigned long       last_ping;
0035     bool            need_irq;
0036     bool            sam9x60_support;
0037 };
0038 
0039 static int wdt_timeout;
0040 static bool nowayout = WATCHDOG_NOWAYOUT;
0041 
0042 module_param(wdt_timeout, int, 0);
0043 MODULE_PARM_DESC(wdt_timeout,
0044     "Watchdog timeout in seconds. (default = "
0045     __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
0046 
0047 module_param(nowayout, bool, 0);
0048 MODULE_PARM_DESC(nowayout,
0049     "Watchdog cannot be stopped once started (default="
0050     __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0051 
0052 #define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
0053 
0054 #define wdt_read(wdt, field) \
0055     readl_relaxed((wdt)->reg_base + (field))
0056 
0057 /* 4 slow clock periods is 4/32768 = 122.07µs*/
0058 #define WDT_DELAY   usecs_to_jiffies(123)
0059 
0060 static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
0061 {
0062     /*
0063      * WDT_CR and WDT_MR must not be modified within three slow clock
0064      * periods following a restart of the watchdog performed by a write
0065      * access in WDT_CR.
0066      */
0067     while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
0068         usleep_range(30, 125);
0069     writel_relaxed(val, wdt->reg_base + field);
0070     wdt->last_ping = jiffies;
0071 }
0072 
0073 static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
0074 {
0075     if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
0076         udelay(123);
0077     writel_relaxed(val, wdt->reg_base + field);
0078     wdt->last_ping = jiffies;
0079 }
0080 
0081 static int sama5d4_wdt_start(struct watchdog_device *wdd)
0082 {
0083     struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
0084 
0085     if (wdt->sam9x60_support) {
0086         writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER);
0087         wdt->mr &= ~AT91_SAM9X60_WDDIS;
0088     } else {
0089         wdt->mr &= ~AT91_WDT_WDDIS;
0090     }
0091     wdt_write(wdt, AT91_WDT_MR, wdt->mr);
0092 
0093     return 0;
0094 }
0095 
0096 static int sama5d4_wdt_stop(struct watchdog_device *wdd)
0097 {
0098     struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
0099 
0100     if (wdt->sam9x60_support) {
0101         writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR);
0102         wdt->mr |= AT91_SAM9X60_WDDIS;
0103     } else {
0104         wdt->mr |= AT91_WDT_WDDIS;
0105     }
0106     wdt_write(wdt, AT91_WDT_MR, wdt->mr);
0107 
0108     return 0;
0109 }
0110 
0111 static int sama5d4_wdt_ping(struct watchdog_device *wdd)
0112 {
0113     struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
0114 
0115     wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
0116 
0117     return 0;
0118 }
0119 
0120 static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
0121                  unsigned int timeout)
0122 {
0123     struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
0124     u32 value = WDT_SEC2TICKS(timeout);
0125 
0126     if (wdt->sam9x60_support) {
0127         wdt_write(wdt, AT91_SAM9X60_WLR,
0128               AT91_SAM9X60_SET_COUNTER(value));
0129 
0130         wdd->timeout = timeout;
0131         return 0;
0132     }
0133 
0134     wdt->mr &= ~AT91_WDT_WDV;
0135     wdt->mr |= AT91_WDT_SET_WDV(value);
0136 
0137     /*
0138      * WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
0139      * setting the WDDIS bit, and while it is set, the fields WDV and WDD
0140      * must not be modified.
0141      * If the watchdog is enabled, then the timeout can be updated. Else,
0142      * wait that the user enables it.
0143      */
0144     if (wdt_enabled)
0145         wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
0146 
0147     wdd->timeout = timeout;
0148 
0149     return 0;
0150 }
0151 
0152 static const struct watchdog_info sama5d4_wdt_info = {
0153     .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
0154     .identity = "Atmel SAMA5D4 Watchdog",
0155 };
0156 
0157 static const struct watchdog_ops sama5d4_wdt_ops = {
0158     .owner = THIS_MODULE,
0159     .start = sama5d4_wdt_start,
0160     .stop = sama5d4_wdt_stop,
0161     .ping = sama5d4_wdt_ping,
0162     .set_timeout = sama5d4_wdt_set_timeout,
0163 };
0164 
0165 static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
0166 {
0167     struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
0168     u32 reg;
0169 
0170     if (wdt->sam9x60_support)
0171         reg = wdt_read(wdt, AT91_SAM9X60_ISR);
0172     else
0173         reg = wdt_read(wdt, AT91_WDT_SR);
0174 
0175     if (reg) {
0176         pr_crit("Atmel Watchdog Software Reset\n");
0177         emergency_restart();
0178         pr_crit("Reboot didn't succeed\n");
0179     }
0180 
0181     return IRQ_HANDLED;
0182 }
0183 
0184 static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
0185 {
0186     const char *tmp;
0187 
0188     if (wdt->sam9x60_support)
0189         wdt->mr = AT91_SAM9X60_WDDIS;
0190     else
0191         wdt->mr = AT91_WDT_WDDIS;
0192 
0193     if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
0194         !strcmp(tmp, "software"))
0195         wdt->need_irq = true;
0196 
0197     if (of_property_read_bool(np, "atmel,idle-halt"))
0198         wdt->mr |= AT91_WDT_WDIDLEHLT;
0199 
0200     if (of_property_read_bool(np, "atmel,dbg-halt"))
0201         wdt->mr |= AT91_WDT_WDDBGHLT;
0202 
0203     return 0;
0204 }
0205 
0206 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
0207 {
0208     u32 reg, val;
0209 
0210     val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT);
0211     /*
0212      * When booting and resuming, the bootloader may have changed the
0213      * watchdog configuration.
0214      * If the watchdog is already running, we can safely update it.
0215      * Else, we have to disable it properly.
0216      */
0217     if (!wdt_enabled) {
0218         reg = wdt_read(wdt, AT91_WDT_MR);
0219         if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS)))
0220             wdt_write_nosleep(wdt, AT91_WDT_MR,
0221                       reg | AT91_SAM9X60_WDDIS);
0222         else if (!wdt->sam9x60_support &&
0223              (!(reg & AT91_WDT_WDDIS)))
0224             wdt_write_nosleep(wdt, AT91_WDT_MR,
0225                       reg | AT91_WDT_WDDIS);
0226     }
0227 
0228     if (wdt->sam9x60_support) {
0229         if (wdt->need_irq)
0230             wdt->ir = AT91_SAM9X60_PERINT;
0231         else
0232             wdt->mr |= AT91_SAM9X60_PERIODRST;
0233 
0234         wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir);
0235         wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val));
0236     } else {
0237         wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
0238         wdt->mr |= AT91_WDT_SET_WDV(val);
0239 
0240         if (wdt->need_irq)
0241             wdt->mr |= AT91_WDT_WDFIEN;
0242         else
0243             wdt->mr |= AT91_WDT_WDRSTEN;
0244     }
0245 
0246     wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
0247 
0248     return 0;
0249 }
0250 
0251 static int sama5d4_wdt_probe(struct platform_device *pdev)
0252 {
0253     struct device *dev = &pdev->dev;
0254     struct watchdog_device *wdd;
0255     struct sama5d4_wdt *wdt;
0256     void __iomem *regs;
0257     u32 irq = 0;
0258     int ret;
0259 
0260     wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0261     if (!wdt)
0262         return -ENOMEM;
0263 
0264     wdd = &wdt->wdd;
0265     wdd->timeout = WDT_DEFAULT_TIMEOUT;
0266     wdd->info = &sama5d4_wdt_info;
0267     wdd->ops = &sama5d4_wdt_ops;
0268     wdd->min_timeout = MIN_WDT_TIMEOUT;
0269     wdd->max_timeout = MAX_WDT_TIMEOUT;
0270     wdt->last_ping = jiffies;
0271 
0272     if (of_device_is_compatible(dev->of_node, "microchip,sam9x60-wdt") ||
0273         of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt"))
0274         wdt->sam9x60_support = true;
0275 
0276     watchdog_set_drvdata(wdd, wdt);
0277 
0278     regs = devm_platform_ioremap_resource(pdev, 0);
0279     if (IS_ERR(regs))
0280         return PTR_ERR(regs);
0281 
0282     wdt->reg_base = regs;
0283 
0284     ret = of_sama5d4_wdt_init(dev->of_node, wdt);
0285     if (ret)
0286         return ret;
0287 
0288     if (wdt->need_irq) {
0289         irq = irq_of_parse_and_map(dev->of_node, 0);
0290         if (!irq) {
0291             dev_warn(dev, "failed to get IRQ from DT\n");
0292             wdt->need_irq = false;
0293         }
0294     }
0295 
0296     if (wdt->need_irq) {
0297         ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
0298                        IRQF_SHARED | IRQF_IRQPOLL |
0299                        IRQF_NO_SUSPEND, pdev->name, pdev);
0300         if (ret) {
0301             dev_err(dev, "cannot register interrupt handler\n");
0302             return ret;
0303         }
0304     }
0305 
0306     watchdog_init_timeout(wdd, wdt_timeout, dev);
0307 
0308     ret = sama5d4_wdt_init(wdt);
0309     if (ret)
0310         return ret;
0311 
0312     watchdog_set_nowayout(wdd, nowayout);
0313 
0314     watchdog_stop_on_unregister(wdd);
0315     ret = devm_watchdog_register_device(dev, wdd);
0316     if (ret)
0317         return ret;
0318 
0319     platform_set_drvdata(pdev, wdt);
0320 
0321     dev_info(dev, "initialized (timeout = %d sec, nowayout = %d)\n",
0322          wdd->timeout, nowayout);
0323 
0324     return 0;
0325 }
0326 
0327 static const struct of_device_id sama5d4_wdt_of_match[] = {
0328     {
0329         .compatible = "atmel,sama5d4-wdt",
0330     },
0331     {
0332         .compatible = "microchip,sam9x60-wdt",
0333     },
0334     {
0335         .compatible = "microchip,sama7g5-wdt",
0336     },
0337 
0338     { }
0339 };
0340 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
0341 
0342 static int sama5d4_wdt_suspend_late(struct device *dev)
0343 {
0344     struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
0345 
0346     if (watchdog_active(&wdt->wdd))
0347         sama5d4_wdt_stop(&wdt->wdd);
0348 
0349     return 0;
0350 }
0351 
0352 static int sama5d4_wdt_resume_early(struct device *dev)
0353 {
0354     struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
0355 
0356     /*
0357      * FIXME: writing MR also pings the watchdog which may not be desired.
0358      * This should only be done when the registers are lost on suspend but
0359      * there is no way to get this information right now.
0360      */
0361     sama5d4_wdt_init(wdt);
0362 
0363     if (watchdog_active(&wdt->wdd))
0364         sama5d4_wdt_start(&wdt->wdd);
0365 
0366     return 0;
0367 }
0368 
0369 static const struct dev_pm_ops sama5d4_wdt_pm_ops = {
0370     LATE_SYSTEM_SLEEP_PM_OPS(sama5d4_wdt_suspend_late,
0371                  sama5d4_wdt_resume_early)
0372 };
0373 
0374 static struct platform_driver sama5d4_wdt_driver = {
0375     .probe      = sama5d4_wdt_probe,
0376     .driver     = {
0377         .name   = "sama5d4_wdt",
0378         .pm = pm_sleep_ptr(&sama5d4_wdt_pm_ops),
0379         .of_match_table = sama5d4_wdt_of_match,
0380     }
0381 };
0382 module_platform_driver(sama5d4_wdt_driver);
0383 
0384 MODULE_AUTHOR("Atmel Corporation");
0385 MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver");
0386 MODULE_LICENSE("GPL v2");