Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Watchdog driver for Faraday Technology FTWDT010
0004  *
0005  * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
0006  *
0007  * Inspired by the out-of-tree drivers from OpenWRT:
0008  * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
0009  */
0010 
0011 #include <linux/bitops.h>
0012 #include <linux/init.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/io.h>
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/of_device.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/slab.h>
0020 #include <linux/watchdog.h>
0021 
0022 #define FTWDT010_WDCOUNTER  0x0
0023 #define FTWDT010_WDLOAD     0x4
0024 #define FTWDT010_WDRESTART  0x8
0025 #define FTWDT010_WDCR       0xC
0026 
0027 #define WDRESTART_MAGIC     0x5AB9
0028 
0029 #define WDCR_CLOCK_5MHZ     BIT(4)
0030 #define WDCR_WDEXT      BIT(3)
0031 #define WDCR_WDINTR     BIT(2)
0032 #define WDCR_SYS_RST        BIT(1)
0033 #define WDCR_ENABLE     BIT(0)
0034 
0035 #define WDT_CLOCK       5000000     /* 5 MHz */
0036 
0037 struct ftwdt010_wdt {
0038     struct watchdog_device  wdd;
0039     struct device       *dev;
0040     void __iomem        *base;
0041     bool            has_irq;
0042 };
0043 
0044 static inline
0045 struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
0046 {
0047     return container_of(wdd, struct ftwdt010_wdt, wdd);
0048 }
0049 
0050 static int ftwdt010_wdt_start(struct watchdog_device *wdd)
0051 {
0052     struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
0053     u32 enable;
0054 
0055     writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
0056     writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
0057     /* set clock before enabling */
0058     enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
0059     writel(enable, gwdt->base + FTWDT010_WDCR);
0060     if (gwdt->has_irq)
0061         enable |= WDCR_WDINTR;
0062     enable |= WDCR_ENABLE;
0063     writel(enable, gwdt->base + FTWDT010_WDCR);
0064 
0065     return 0;
0066 }
0067 
0068 static int ftwdt010_wdt_stop(struct watchdog_device *wdd)
0069 {
0070     struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
0071 
0072     writel(0, gwdt->base + FTWDT010_WDCR);
0073 
0074     return 0;
0075 }
0076 
0077 static int ftwdt010_wdt_ping(struct watchdog_device *wdd)
0078 {
0079     struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
0080 
0081     writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
0082 
0083     return 0;
0084 }
0085 
0086 static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
0087                   unsigned int timeout)
0088 {
0089     wdd->timeout = timeout;
0090     if (watchdog_active(wdd))
0091         ftwdt010_wdt_start(wdd);
0092 
0093     return 0;
0094 }
0095 
0096 static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
0097 {
0098     struct ftwdt010_wdt *gwdt = data;
0099 
0100     watchdog_notify_pretimeout(&gwdt->wdd);
0101 
0102     return IRQ_HANDLED;
0103 }
0104 
0105 static const struct watchdog_ops ftwdt010_wdt_ops = {
0106     .start      = ftwdt010_wdt_start,
0107     .stop       = ftwdt010_wdt_stop,
0108     .ping       = ftwdt010_wdt_ping,
0109     .set_timeout    = ftwdt010_wdt_set_timeout,
0110     .owner      = THIS_MODULE,
0111 };
0112 
0113 static const struct watchdog_info ftwdt010_wdt_info = {
0114     .options    = WDIOF_KEEPALIVEPING
0115             | WDIOF_MAGICCLOSE
0116             | WDIOF_SETTIMEOUT,
0117     .identity   = KBUILD_MODNAME,
0118 };
0119 
0120 
0121 static int ftwdt010_wdt_probe(struct platform_device *pdev)
0122 {
0123     struct device *dev = &pdev->dev;
0124     struct ftwdt010_wdt *gwdt;
0125     unsigned int reg;
0126     int irq;
0127     int ret;
0128 
0129     gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
0130     if (!gwdt)
0131         return -ENOMEM;
0132 
0133     gwdt->base = devm_platform_ioremap_resource(pdev, 0);
0134     if (IS_ERR(gwdt->base))
0135         return PTR_ERR(gwdt->base);
0136 
0137     gwdt->dev = dev;
0138     gwdt->wdd.info = &ftwdt010_wdt_info;
0139     gwdt->wdd.ops = &ftwdt010_wdt_ops;
0140     gwdt->wdd.min_timeout = 1;
0141     gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK;
0142     gwdt->wdd.parent = dev;
0143 
0144     /*
0145      * If 'timeout-sec' unspecified in devicetree, assume a 13 second
0146      * default.
0147      */
0148     gwdt->wdd.timeout = 13U;
0149     watchdog_init_timeout(&gwdt->wdd, 0, dev);
0150 
0151     reg = readw(gwdt->base + FTWDT010_WDCR);
0152     if (reg & WDCR_ENABLE) {
0153         /* Watchdog was enabled by the bootloader, disable it. */
0154         reg &= ~WDCR_ENABLE;
0155         writel(reg, gwdt->base + FTWDT010_WDCR);
0156     }
0157 
0158     irq = platform_get_irq(pdev, 0);
0159     if (irq) {
0160         ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
0161                        "watchdog bark", gwdt);
0162         if (ret)
0163             return ret;
0164         gwdt->has_irq = true;
0165     }
0166 
0167     ret = devm_watchdog_register_device(dev, &gwdt->wdd);
0168     if (ret)
0169         return ret;
0170 
0171     /* Set up platform driver data */
0172     platform_set_drvdata(pdev, gwdt);
0173     dev_info(dev, "FTWDT010 watchdog driver enabled\n");
0174 
0175     return 0;
0176 }
0177 
0178 static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev)
0179 {
0180     struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
0181     unsigned int reg;
0182 
0183     reg = readw(gwdt->base + FTWDT010_WDCR);
0184     reg &= ~WDCR_ENABLE;
0185     writel(reg, gwdt->base + FTWDT010_WDCR);
0186 
0187     return 0;
0188 }
0189 
0190 static int __maybe_unused ftwdt010_wdt_resume(struct device *dev)
0191 {
0192     struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
0193     unsigned int reg;
0194 
0195     if (watchdog_active(&gwdt->wdd)) {
0196         reg = readw(gwdt->base + FTWDT010_WDCR);
0197         reg |= WDCR_ENABLE;
0198         writel(reg, gwdt->base + FTWDT010_WDCR);
0199     }
0200 
0201     return 0;
0202 }
0203 
0204 static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = {
0205     SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend,
0206                 ftwdt010_wdt_resume)
0207 };
0208 
0209 #ifdef CONFIG_OF
0210 static const struct of_device_id ftwdt010_wdt_match[] = {
0211     { .compatible = "faraday,ftwdt010" },
0212     { .compatible = "cortina,gemini-watchdog" },
0213     {},
0214 };
0215 MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match);
0216 #endif
0217 
0218 static struct platform_driver ftwdt010_wdt_driver = {
0219     .probe      = ftwdt010_wdt_probe,
0220     .driver     = {
0221         .name   = "ftwdt010-wdt",
0222         .of_match_table = of_match_ptr(ftwdt010_wdt_match),
0223         .pm = &ftwdt010_wdt_dev_pm_ops,
0224     },
0225 };
0226 module_platform_driver(ftwdt010_wdt_driver);
0227 MODULE_AUTHOR("Linus Walleij");
0228 MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010");
0229 MODULE_LICENSE("GPL");