0001
0002
0003
0004
0005
0006
0007
0008
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
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
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
0146
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
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
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");