Blame view
drivers/watchdog/ftwdt010_wdt.c
5.25 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
eca10ae60 watchdog: add dri... |
2 |
/* |
766a2aad6 watchdog: gemini/... |
3 |
* Watchdog driver for Faraday Technology FTWDT010 |
eca10ae60 watchdog: add dri... |
4 5 6 7 8 |
* * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> * * Inspired by the out-of-tree drivers from OpenWRT: * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> |
eca10ae60 watchdog: add dri... |
9 10 11 12 13 14 15 16 17 18 19 20 |
*/ #include <linux/bitops.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/watchdog.h> |
766a2aad6 watchdog: gemini/... |
21 22 23 24 |
#define FTWDT010_WDCOUNTER 0x0 #define FTWDT010_WDLOAD 0x4 #define FTWDT010_WDRESTART 0x8 #define FTWDT010_WDCR 0xC |
eca10ae60 watchdog: add dri... |
25 26 27 28 |
#define WDRESTART_MAGIC 0x5AB9 #define WDCR_CLOCK_5MHZ BIT(4) |
d5433fd60 watchdog: ftwdt01... |
29 30 |
#define WDCR_WDEXT BIT(3) #define WDCR_WDINTR BIT(2) |
eca10ae60 watchdog: add dri... |
31 32 33 34 |
#define WDCR_SYS_RST BIT(1) #define WDCR_ENABLE BIT(0) #define WDT_CLOCK 5000000 /* 5 MHz */ |
766a2aad6 watchdog: gemini/... |
35 |
struct ftwdt010_wdt { |
eca10ae60 watchdog: add dri... |
36 37 38 |
struct watchdog_device wdd; struct device *dev; void __iomem *base; |
d5433fd60 watchdog: ftwdt01... |
39 |
bool has_irq; |
eca10ae60 watchdog: add dri... |
40 41 42 |
}; static inline |
766a2aad6 watchdog: gemini/... |
43 |
struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd) |
eca10ae60 watchdog: add dri... |
44 |
{ |
766a2aad6 watchdog: gemini/... |
45 |
return container_of(wdd, struct ftwdt010_wdt, wdd); |
eca10ae60 watchdog: add dri... |
46 |
} |
766a2aad6 watchdog: gemini/... |
47 |
static int ftwdt010_wdt_start(struct watchdog_device *wdd) |
eca10ae60 watchdog: add dri... |
48 |
{ |
766a2aad6 watchdog: gemini/... |
49 |
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); |
d5433fd60 watchdog: ftwdt01... |
50 |
u32 enable; |
eca10ae60 watchdog: add dri... |
51 |
|
766a2aad6 watchdog: gemini/... |
52 53 |
writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD); writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); |
eca10ae60 watchdog: add dri... |
54 |
/* set clock before enabling */ |
d5433fd60 watchdog: ftwdt01... |
55 56 57 58 59 60 |
enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST; writel(enable, gwdt->base + FTWDT010_WDCR); if (gwdt->has_irq) enable |= WDCR_WDINTR; enable |= WDCR_ENABLE; writel(enable, gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
61 62 63 |
return 0; } |
766a2aad6 watchdog: gemini/... |
64 |
static int ftwdt010_wdt_stop(struct watchdog_device *wdd) |
eca10ae60 watchdog: add dri... |
65 |
{ |
766a2aad6 watchdog: gemini/... |
66 |
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); |
eca10ae60 watchdog: add dri... |
67 |
|
766a2aad6 watchdog: gemini/... |
68 |
writel(0, gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
69 70 71 |
return 0; } |
766a2aad6 watchdog: gemini/... |
72 |
static int ftwdt010_wdt_ping(struct watchdog_device *wdd) |
eca10ae60 watchdog: add dri... |
73 |
{ |
766a2aad6 watchdog: gemini/... |
74 |
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd); |
eca10ae60 watchdog: add dri... |
75 |
|
766a2aad6 watchdog: gemini/... |
76 |
writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART); |
eca10ae60 watchdog: add dri... |
77 78 79 |
return 0; } |
766a2aad6 watchdog: gemini/... |
80 |
static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd, |
eca10ae60 watchdog: add dri... |
81 82 83 84 |
unsigned int timeout) { wdd->timeout = timeout; if (watchdog_active(wdd)) |
766a2aad6 watchdog: gemini/... |
85 |
ftwdt010_wdt_start(wdd); |
eca10ae60 watchdog: add dri... |
86 87 88 |
return 0; } |
766a2aad6 watchdog: gemini/... |
89 |
static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data) |
eca10ae60 watchdog: add dri... |
90 |
{ |
766a2aad6 watchdog: gemini/... |
91 |
struct ftwdt010_wdt *gwdt = data; |
eca10ae60 watchdog: add dri... |
92 93 94 95 96 |
watchdog_notify_pretimeout(&gwdt->wdd); return IRQ_HANDLED; } |
766a2aad6 watchdog: gemini/... |
97 98 99 100 101 |
static const struct watchdog_ops ftwdt010_wdt_ops = { .start = ftwdt010_wdt_start, .stop = ftwdt010_wdt_stop, .ping = ftwdt010_wdt_ping, .set_timeout = ftwdt010_wdt_set_timeout, |
eca10ae60 watchdog: add dri... |
102 103 |
.owner = THIS_MODULE, }; |
766a2aad6 watchdog: gemini/... |
104 |
static const struct watchdog_info ftwdt010_wdt_info = { |
eca10ae60 watchdog: add dri... |
105 106 107 108 109 |
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT, .identity = KBUILD_MODNAME, }; |
766a2aad6 watchdog: gemini/... |
110 |
static int ftwdt010_wdt_probe(struct platform_device *pdev) |
eca10ae60 watchdog: add dri... |
111 112 |
{ struct device *dev = &pdev->dev; |
766a2aad6 watchdog: gemini/... |
113 |
struct ftwdt010_wdt *gwdt; |
eca10ae60 watchdog: add dri... |
114 115 116 117 118 119 120 |
unsigned int reg; int irq; int ret; gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); if (!gwdt) return -ENOMEM; |
0f0a6a285 watchdog: Convert... |
121 |
gwdt->base = devm_platform_ioremap_resource(pdev, 0); |
eca10ae60 watchdog: add dri... |
122 123 |
if (IS_ERR(gwdt->base)) return PTR_ERR(gwdt->base); |
eca10ae60 watchdog: add dri... |
124 |
gwdt->dev = dev; |
766a2aad6 watchdog: gemini/... |
125 126 |
gwdt->wdd.info = &ftwdt010_wdt_info; gwdt->wdd.ops = &ftwdt010_wdt_ops; |
eca10ae60 watchdog: add dri... |
127 128 129 130 131 132 133 134 135 136 |
gwdt->wdd.min_timeout = 1; gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; gwdt->wdd.parent = dev; /* * If 'timeout-sec' unspecified in devicetree, assume a 13 second * default. */ gwdt->wdd.timeout = 13U; watchdog_init_timeout(&gwdt->wdd, 0, dev); |
766a2aad6 watchdog: gemini/... |
137 |
reg = readw(gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
138 139 140 |
if (reg & WDCR_ENABLE) { /* Watchdog was enabled by the bootloader, disable it. */ reg &= ~WDCR_ENABLE; |
766a2aad6 watchdog: gemini/... |
141 |
writel(reg, gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
142 |
} |
d5433fd60 watchdog: ftwdt01... |
143 144 145 146 147 148 149 150 |
irq = platform_get_irq(pdev, 0); if (irq) { ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0, "watchdog bark", gwdt); if (ret) return ret; gwdt->has_irq = true; } |
eca10ae60 watchdog: add dri... |
151 152 |
ret = devm_watchdog_register_device(dev, &gwdt->wdd); |
2d065d2e9 watchdog: ftwdt01... |
153 |
if (ret) |
eca10ae60 watchdog: add dri... |
154 |
return ret; |
eca10ae60 watchdog: add dri... |
155 156 157 |
/* Set up platform driver data */ platform_set_drvdata(pdev, gwdt); |
766a2aad6 watchdog: gemini/... |
158 159 |
dev_info(dev, "FTWDT010 watchdog driver enabled "); |
eca10ae60 watchdog: add dri... |
160 161 162 |
return 0; } |
766a2aad6 watchdog: gemini/... |
163 |
static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev) |
eca10ae60 watchdog: add dri... |
164 |
{ |
766a2aad6 watchdog: gemini/... |
165 |
struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev); |
eca10ae60 watchdog: add dri... |
166 |
unsigned int reg; |
766a2aad6 watchdog: gemini/... |
167 |
reg = readw(gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
168 |
reg &= ~WDCR_ENABLE; |
766a2aad6 watchdog: gemini/... |
169 |
writel(reg, gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
170 171 172 |
return 0; } |
766a2aad6 watchdog: gemini/... |
173 |
static int __maybe_unused ftwdt010_wdt_resume(struct device *dev) |
eca10ae60 watchdog: add dri... |
174 |
{ |
766a2aad6 watchdog: gemini/... |
175 |
struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev); |
eca10ae60 watchdog: add dri... |
176 177 178 |
unsigned int reg; if (watchdog_active(&gwdt->wdd)) { |
766a2aad6 watchdog: gemini/... |
179 |
reg = readw(gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
180 |
reg |= WDCR_ENABLE; |
766a2aad6 watchdog: gemini/... |
181 |
writel(reg, gwdt->base + FTWDT010_WDCR); |
eca10ae60 watchdog: add dri... |
182 183 184 185 |
} return 0; } |
766a2aad6 watchdog: gemini/... |
186 187 188 |
static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend, ftwdt010_wdt_resume) |
eca10ae60 watchdog: add dri... |
189 190 191 |
}; #ifdef CONFIG_OF |
766a2aad6 watchdog: gemini/... |
192 193 |
static const struct of_device_id ftwdt010_wdt_match[] = { { .compatible = "faraday,ftwdt010" }, |
eca10ae60 watchdog: add dri... |
194 195 196 |
{ .compatible = "cortina,gemini-watchdog" }, {}, }; |
766a2aad6 watchdog: gemini/... |
197 |
MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match); |
eca10ae60 watchdog: add dri... |
198 |
#endif |
766a2aad6 watchdog: gemini/... |
199 200 |
static struct platform_driver ftwdt010_wdt_driver = { .probe = ftwdt010_wdt_probe, |
eca10ae60 watchdog: add dri... |
201 |
.driver = { |
766a2aad6 watchdog: gemini/... |
202 203 204 |
.name = "ftwdt010-wdt", .of_match_table = of_match_ptr(ftwdt010_wdt_match), .pm = &ftwdt010_wdt_dev_pm_ops, |
eca10ae60 watchdog: add dri... |
205 206 |
}, }; |
766a2aad6 watchdog: gemini/... |
207 |
module_platform_driver(ftwdt010_wdt_driver); |
eca10ae60 watchdog: add dri... |
208 |
MODULE_AUTHOR("Linus Walleij"); |
766a2aad6 watchdog: gemini/... |
209 |
MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010"); |
eca10ae60 watchdog: add dri... |
210 |
MODULE_LICENSE("GPL"); |