1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Pengutronix, Ahmad Fatoum <a.fatoum@pengutronix.de>
*/
#include <common.h>
#include <init.h>
#include <io.h>
#include <watchdog.h>
#include <linux/clk.h>
#include <mach/at91_wdt.h>
#define MIN_WDT_TIMEOUT 1
#define MAX_WDT_TIMEOUT 16
#define SECS_TO_WDOG_TICKS(s) ((s) ? (((s) << 8) - 1) : 0)
struct at91sam9x_wdt {
struct watchdog wdd;
void __iomem *base;
};
static inline void at91sam9x_wdt_ping(struct at91sam9x_wdt *wdt)
{
writel(AT91_WDT_WDRSTT | AT91_WDT_KEY, wdt->base + AT91_WDT_CR);
}
static int at91sam9x_wdt_set_timeout(struct watchdog *wdd, unsigned timeout)
{
struct at91sam9x_wdt *wdt = container_of(wdd, struct at91sam9x_wdt, wdd);
u32 mr_old, mr_new;
mr_old = readl(wdt->base + AT91_WDT_MR);
if (!timeout) {
mr_new = mr_old | AT91_WDT_WDDIS;
writel(mr_new, wdt->base + AT91_WDT_MR);
return 0;
}
mr_new = AT91_WDT_WDRSTEN
| AT91_WDT_WDDBGHLT | AT91_WDT_WDIDLEHLT
| AT91_WDT_WDD
| (SECS_TO_WDOG_TICKS(timeout) & AT91_WDT_WDV);
if (mr_new != mr_old)
writel(mr_new, wdt->base + AT91_WDT_MR);
at91sam9x_wdt_ping(wdt);
return 0;
}
static inline bool at91sam9x_wdt_is_disabled(struct at91sam9x_wdt *wdt)
{
return readl(wdt->base + AT91_WDT_MR) & AT91_WDT_WDDIS;
}
static int at91sam9x_wdt_probe(struct device_d *dev)
{
struct at91sam9x_wdt *wdt;
struct resource *iores;
struct clk *clk;
int ret;
wdt = xzalloc(sizeof(*wdt));
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores)) {
dev_err(dev, "could not get watchdog memory region\n");
return PTR_ERR(iores);
}
wdt->base = IOMEM(iores->start);
clk = clk_get(dev, NULL);
if (WARN_ON(IS_ERR(clk)))
return PTR_ERR(clk);
clk_enable(clk);
wdt->wdd.set_timeout = at91sam9x_wdt_set_timeout;
wdt->wdd.timeout_max = MAX_WDT_TIMEOUT;
wdt->wdd.hwdev = dev;
if (at91sam9x_wdt_is_disabled(wdt))
wdt->wdd.running = WDOG_HW_NOT_RUNNING;
else
wdt->wdd.running = WDOG_HW_RUNNING;
ret = watchdog_register(&wdt->wdd);
if (ret)
free(wdt);
return ret;
}
static const __maybe_unused struct of_device_id at91sam9x_wdt_dt_ids[] = {
{ .compatible = "atmel,at91sam9260-wdt", },
{ .compatible = "atmel,sama5d4-wdt", },
{ /* sentinel */ },
};
static struct driver_d at91sam9x_wdt_driver = {
.name = "at91sam9x-wdt",
.of_compatible = DRV_OF_COMPAT(at91sam9x_wdt_dt_ids),
.probe = at91sam9x_wdt_probe,
};
device_platform_driver(at91sam9x_wdt_driver);
|