1
2
3
4
5
6
7
8#include <linux/delay.h>
9#include <linux/io.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/platform_device.h>
14#include <linux/watchdog.h>
15
16struct aspeed_wdt {
17 struct watchdog_device wdd;
18 void __iomem *base;
19 u32 ctrl;
20};
21
22struct aspeed_wdt_config {
23 u32 ext_pulse_width_mask;
24};
25
26static const struct aspeed_wdt_config ast2400_config = {
27 .ext_pulse_width_mask = 0xff,
28};
29
30static const struct aspeed_wdt_config ast2500_config = {
31 .ext_pulse_width_mask = 0xfffff,
32};
33
34static const struct of_device_id aspeed_wdt_of_table[] = {
35 { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
36 { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
37 { .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
38 { },
39};
40MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
41
42#define WDT_STATUS 0x00
43#define WDT_RELOAD_VALUE 0x04
44#define WDT_RESTART 0x08
45#define WDT_CTRL 0x0C
46#define WDT_CTRL_BOOT_SECONDARY BIT(7)
47#define WDT_CTRL_RESET_MODE_SOC (0x00 << 5)
48#define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
49#define WDT_CTRL_RESET_MODE_ARM_CPU (0x10 << 5)
50#define WDT_CTRL_1MHZ_CLK BIT(4)
51#define WDT_CTRL_WDT_EXT BIT(3)
52#define WDT_CTRL_WDT_INTR BIT(2)
53#define WDT_CTRL_RESET_SYSTEM BIT(1)
54#define WDT_CTRL_ENABLE BIT(0)
55#define WDT_TIMEOUT_STATUS 0x10
56#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
57#define WDT_CLEAR_TIMEOUT_STATUS 0x14
58#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84#define WDT_RESET_WIDTH 0x18
85#define WDT_RESET_WIDTH_ACTIVE_HIGH BIT(31)
86#define WDT_ACTIVE_HIGH_MAGIC (0xA5 << 24)
87#define WDT_ACTIVE_LOW_MAGIC (0x5A << 24)
88#define WDT_RESET_WIDTH_PUSH_PULL BIT(30)
89#define WDT_PUSH_PULL_MAGIC (0xA8 << 24)
90#define WDT_OPEN_DRAIN_MAGIC (0x8A << 24)
91
92#define WDT_RESTART_MAGIC 0x4755
93
94
95#define WDT_MAX_TIMEOUT_MS 4294967
96#define WDT_DEFAULT_TIMEOUT 30
97#define WDT_RATE_1MHZ 1000000
98
99static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd)
100{
101 return container_of(wdd, struct aspeed_wdt, wdd);
102}
103
104static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count)
105{
106 wdt->ctrl |= WDT_CTRL_ENABLE;
107
108 writel(0, wdt->base + WDT_CTRL);
109 writel(count, wdt->base + WDT_RELOAD_VALUE);
110 writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
111 writel(wdt->ctrl, wdt->base + WDT_CTRL);
112}
113
114static int aspeed_wdt_start(struct watchdog_device *wdd)
115{
116 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
117
118 aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ);
119
120 return 0;
121}
122
123static int aspeed_wdt_stop(struct watchdog_device *wdd)
124{
125 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
126
127 wdt->ctrl &= ~WDT_CTRL_ENABLE;
128 writel(wdt->ctrl, wdt->base + WDT_CTRL);
129
130 return 0;
131}
132
133static int aspeed_wdt_ping(struct watchdog_device *wdd)
134{
135 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
136
137 writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
138
139 return 0;
140}
141
142static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
143 unsigned int timeout)
144{
145 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
146 u32 actual;
147
148 wdd->timeout = timeout;
149
150 actual = min(timeout, wdd->max_hw_heartbeat_ms * 1000);
151
152 writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE);
153 writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
154
155 return 0;
156}
157
158static int aspeed_wdt_restart(struct watchdog_device *wdd,
159 unsigned long action, void *data)
160{
161 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
162
163 wdt->ctrl &= ~WDT_CTRL_BOOT_SECONDARY;
164 aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000);
165
166 mdelay(1000);
167
168 return 0;
169}
170
171
172static ssize_t access_cs0_show(struct device *dev,
173 struct device_attribute *attr, char *buf)
174{
175 struct aspeed_wdt *wdt = dev_get_drvdata(dev);
176 u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
177
178 return sprintf(buf, "%u\n",
179 !(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
180}
181
182static ssize_t access_cs0_store(struct device *dev,
183 struct device_attribute *attr, const char *buf,
184 size_t size)
185{
186 struct aspeed_wdt *wdt = dev_get_drvdata(dev);
187 unsigned long val;
188
189 if (kstrtoul(buf, 10, &val))
190 return -EINVAL;
191
192 if (val)
193 writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION,
194 wdt->base + WDT_CLEAR_TIMEOUT_STATUS);
195
196 return size;
197}
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217static DEVICE_ATTR_RW(access_cs0);
218
219static struct attribute *bswitch_attrs[] = {
220 &dev_attr_access_cs0.attr,
221 NULL
222};
223ATTRIBUTE_GROUPS(bswitch);
224
225static const struct watchdog_ops aspeed_wdt_ops = {
226 .start = aspeed_wdt_start,
227 .stop = aspeed_wdt_stop,
228 .ping = aspeed_wdt_ping,
229 .set_timeout = aspeed_wdt_set_timeout,
230 .restart = aspeed_wdt_restart,
231 .owner = THIS_MODULE,
232};
233
234static const struct watchdog_info aspeed_wdt_info = {
235 .options = WDIOF_KEEPALIVEPING
236 | WDIOF_MAGICCLOSE
237 | WDIOF_SETTIMEOUT,
238 .identity = KBUILD_MODNAME,
239};
240
241static int aspeed_wdt_probe(struct platform_device *pdev)
242{
243 struct device *dev = &pdev->dev;
244 const struct aspeed_wdt_config *config;
245 const struct of_device_id *ofdid;
246 struct aspeed_wdt *wdt;
247 struct device_node *np;
248 const char *reset_type;
249 u32 duration;
250 u32 status;
251 int ret;
252
253 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
254 if (!wdt)
255 return -ENOMEM;
256
257 wdt->base = devm_platform_ioremap_resource(pdev, 0);
258 if (IS_ERR(wdt->base))
259 return PTR_ERR(wdt->base);
260
261
262
263
264
265
266 wdt->wdd.info = &aspeed_wdt_info;
267 wdt->wdd.ops = &aspeed_wdt_ops;
268 wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
269 wdt->wdd.parent = dev;
270
271 wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
272 watchdog_init_timeout(&wdt->wdd, 0, dev);
273
274 np = dev->of_node;
275
276 ofdid = of_match_node(aspeed_wdt_of_table, np);
277 if (!ofdid)
278 return -EINVAL;
279 config = ofdid->data;
280
281 wdt->ctrl = WDT_CTRL_1MHZ_CLK;
282
283
284
285
286
287 ret = of_property_read_string(np, "aspeed,reset-type", &reset_type);
288 if (ret) {
289 wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM;
290 } else {
291 if (!strcmp(reset_type, "cpu"))
292 wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU |
293 WDT_CTRL_RESET_SYSTEM;
294 else if (!strcmp(reset_type, "soc"))
295 wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC |
296 WDT_CTRL_RESET_SYSTEM;
297 else if (!strcmp(reset_type, "system"))
298 wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP |
299 WDT_CTRL_RESET_SYSTEM;
300 else if (strcmp(reset_type, "none"))
301 return -EINVAL;
302 }
303 if (of_property_read_bool(np, "aspeed,external-signal"))
304 wdt->ctrl |= WDT_CTRL_WDT_EXT;
305 if (of_property_read_bool(np, "aspeed,alt-boot"))
306 wdt->ctrl |= WDT_CTRL_BOOT_SECONDARY;
307
308 if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) {
309
310
311
312
313
314
315 aspeed_wdt_start(&wdt->wdd);
316 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
317 }
318
319 if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
320 (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
321 u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
322
323 reg &= config->ext_pulse_width_mask;
324 if (of_property_read_bool(np, "aspeed,ext-push-pull"))
325 reg |= WDT_PUSH_PULL_MAGIC;
326 else
327 reg |= WDT_OPEN_DRAIN_MAGIC;
328
329 writel(reg, wdt->base + WDT_RESET_WIDTH);
330
331 reg &= config->ext_pulse_width_mask;
332 if (of_property_read_bool(np, "aspeed,ext-active-high"))
333 reg |= WDT_ACTIVE_HIGH_MAGIC;
334 else
335 reg |= WDT_ACTIVE_LOW_MAGIC;
336
337 writel(reg, wdt->base + WDT_RESET_WIDTH);
338 }
339
340 if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) {
341 u32 max_duration = config->ext_pulse_width_mask + 1;
342
343 if (duration == 0 || duration > max_duration) {
344 dev_err(dev, "Invalid pulse duration: %uus\n",
345 duration);
346 duration = max(1U, min(max_duration, duration));
347 dev_info(dev, "Pulse duration set to %uus\n",
348 duration);
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362
363 writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
364 }
365
366 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
367 if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
368 wdt->wdd.bootstatus = WDIOF_CARDRESET;
369
370 if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
371 of_device_is_compatible(np, "aspeed,ast2500-wdt"))
372 wdt->wdd.groups = bswitch_groups;
373 }
374
375 dev_set_drvdata(dev, wdt);
376
377 return devm_watchdog_register_device(dev, &wdt->wdd);
378}
379
380static struct platform_driver aspeed_watchdog_driver = {
381 .probe = aspeed_wdt_probe,
382 .driver = {
383 .name = KBUILD_MODNAME,
384 .of_match_table = of_match_ptr(aspeed_wdt_of_table),
385 },
386};
387
388static int __init aspeed_wdt_init(void)
389{
390 return platform_driver_register(&aspeed_watchdog_driver);
391}
392arch_initcall(aspeed_wdt_init);
393
394static void __exit aspeed_wdt_exit(void)
395{
396 platform_driver_unregister(&aspeed_watchdog_driver);
397}
398module_exit(aspeed_wdt_exit);
399
400MODULE_DESCRIPTION("Aspeed Watchdog Driver");
401MODULE_LICENSE("GPL");
402