Source
1
+
// SPDX-License-Identifier: GPL-2.0+
2
+
/*
3
+
* Marvell Armada 37xx SoC Watchdog Driver
4
+
*
5
+
* Marek Behun <marek.behun@nic.cz>
6
+
*/
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
DECLARE_GLOBAL_DATA_PTR;
16
+
17
+
struct a37xx_wdt {
18
+
void __iomem *sel_reg;
19
+
void __iomem *reg;
20
+
ulong clk_rate;
21
+
u64 timeout;
22
+
};
23
+
24
+
/*
25
+
* We use Counter 1 for watchdog timer, because so does Marvell's Linux by
26
+
* default.
27
+
*/
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
static void set_counter_value(struct a37xx_wdt *priv)
42
+
{
43
+
writel(priv->timeout & 0xffffffff, priv->reg + CNTR_COUNT_LOW);
44
+
writel(priv->timeout >> 32, priv->reg + CNTR_COUNT_HIGH);
45
+
}
46
+
47
+
static void a37xx_wdt_enable(struct a37xx_wdt *priv)
48
+
{
49
+
u32 reg = readl(priv->reg + CNTR_CTRL);
50
+
51
+
reg |= CNTR_CTRL_ENABLE;
52
+
writel(reg, priv->reg + CNTR_CTRL);
53
+
}
54
+
55
+
static void a37xx_wdt_disable(struct a37xx_wdt *priv)
56
+
{
57
+
u32 reg = readl(priv->reg + CNTR_CTRL);
58
+
59
+
reg &= ~CNTR_CTRL_ENABLE;
60
+
writel(reg, priv->reg + CNTR_CTRL);
61
+
}
62
+
63
+
static int a37xx_wdt_reset(struct udevice *dev)
64
+
{
65
+
struct a37xx_wdt *priv = dev_get_priv(dev);
66
+
67
+
if (!priv->timeout)
68
+
return -EINVAL;
69
+
70
+
a37xx_wdt_disable(priv);
71
+
set_counter_value(priv);
72
+
a37xx_wdt_enable(priv);
73
+
74
+
return 0;
75
+
}
76
+
77
+
static int a37xx_wdt_expire_now(struct udevice *dev, ulong flags)
78
+
{
79
+
struct a37xx_wdt *priv = dev_get_priv(dev);
80
+
81
+
a37xx_wdt_disable(priv);
82
+
priv->timeout = 0;
83
+
set_counter_value(priv);
84
+
a37xx_wdt_enable(priv);
85
+
86
+
return 0;
87
+
}
88
+
89
+
static int a37xx_wdt_start(struct udevice *dev, u64 ms, ulong flags)
90
+
{
91
+
struct a37xx_wdt *priv = dev_get_priv(dev);
92
+
u32 reg;
93
+
94
+
reg = readl(priv->reg + CNTR_CTRL);
95
+
96
+
if (reg & CNTR_CTRL_ACTIVE)
97
+
return -EBUSY;
98
+
99
+
/* set mode */
100
+
reg = (reg & ~CNTR_CTRL_MODE_MASK) | CNTR_CTRL_MODE_ONESHOT;
101
+
102
+
/* set prescaler to the min value */
103
+
reg &= ~CNTR_CTRL_PRESCALE_MASK;
104
+
reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
105
+
106
+
priv->timeout = ms * priv->clk_rate / 1000 / CNTR_CTRL_PRESCALE_MIN;
107
+
108
+
writel(reg, priv->reg + CNTR_CTRL);
109
+
110
+
set_counter_value(priv);
111
+
a37xx_wdt_enable(priv);
112
+
113
+
return 0;
114
+
}
115
+
116
+
static int a37xx_wdt_stop(struct udevice *dev)
117
+
{
118
+
struct a37xx_wdt *priv = dev_get_priv(dev);
119
+
120
+
a37xx_wdt_disable(priv);
121
+
122
+
return 0;
123
+
}
124
+
125
+
static int a37xx_wdt_probe(struct udevice *dev)
126
+
{
127
+
struct a37xx_wdt *priv = dev_get_priv(dev);
128
+
fdt_addr_t addr;
129
+
130
+
addr = dev_read_addr_index(dev, 0);
131
+
if (addr == FDT_ADDR_T_NONE)
132
+
goto err;
133
+
priv->sel_reg = (void __iomem *)addr;
134
+
135
+
addr = dev_read_addr_index(dev, 1);
136
+
if (addr == FDT_ADDR_T_NONE)
137
+
goto err;
138
+
priv->reg = (void __iomem *)addr;
139
+
140
+
priv->clk_rate = (ulong)get_ref_clk() * 1000000;
141
+
142
+
a37xx_wdt_disable(priv);
143
+
144
+
/*
145
+
* We use timer 1 as watchdog timer (because Marvell's Linux uses that
146
+
* timer as default), therefore we only set bit TIMER1_IS_WCHDOG_TIMER.
147
+
*/
148
+
writel(1 << 1, priv->sel_reg);
149
+
150
+
return 0;
151
+
err:
152
+
dev_err(dev, "no io address\n");
153
+
return -ENODEV;
154
+
}
155
+
156
+
static const struct wdt_ops a37xx_wdt_ops = {
157
+
.start = a37xx_wdt_start,
158
+
.reset = a37xx_wdt_reset,
159
+
.stop = a37xx_wdt_stop,
160
+
.expire_now = a37xx_wdt_expire_now,
161
+
};
162
+
163
+
static const struct udevice_id a37xx_wdt_ids[] = {
164
+
{ .compatible = "marvell,armada-3700-wdt" },
165
+
{}
166
+
};
167
+
168
+
U_BOOT_DRIVER(a37xx_wdt) = {
169
+
.name = "armada_37xx_wdt",
170
+
.id = UCLASS_WDT,
171
+
.of_match = a37xx_wdt_ids,
172
+
.probe = a37xx_wdt_probe,
173
+
.priv_auto_alloc_size = sizeof(struct a37xx_wdt),
174
+
.ops = &a37xx_wdt_ops,
175
+
};