Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/allwinner/aw_wdog.c
108036 views
1
/*-
2
* Copyright (c) 2013 Oleksandr Tymoshenko <[email protected]>
3
* Copyright (c) 2016 Emmanuel Vadot <[email protected]>
4
* Copyright (c) 2022 Julien Cassette <[email protected]>
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/eventhandler.h>
31
#include <sys/systm.h>
32
#include <sys/watchdog.h>
33
#include <sys/reboot.h>
34
#include <sys/bus.h>
35
#include <sys/kernel.h>
36
#include <sys/lock.h>
37
#include <sys/module.h>
38
#include <sys/mutex.h>
39
#include <sys/rman.h>
40
41
#include <dev/ofw/openfirm.h>
42
#include <dev/ofw/ofw_bus.h>
43
#include <dev/ofw/ofw_bus_subr.h>
44
45
#include <machine/bus.h>
46
47
#include <arm/allwinner/aw_wdog.h>
48
49
#define READ(_sc, _r) bus_read_4((_sc)->res, (_r))
50
#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v))
51
52
#define A10_WDOG_CTRL 0x00
53
#define A31_WDOG_CTRL 0x10
54
#define D1_WDOG_CTRL 0x10
55
#define H616_WDOG_CTRL 0xB0
56
#define WDOG_CTRL_RESTART (1 << 0)
57
#define A31_WDOG_CTRL_KEY (0xa57 << 1)
58
#define D1_WDOG_CTRL_KEY (0xa57 << 1)
59
#define H616_WDOG_CTRL_KEY (0xa57 << 1)
60
#define A10_WDOG_MODE 0x04
61
#define A31_WDOG_MODE 0x18
62
#define D1_WDOG_MODE 0x18
63
#define H616_WDOG_MODE 0xB8
64
#define D1_WDOG_MODE_KEY (0x16AA << 16)
65
#define A10_WDOG_MODE_INTVL_SHIFT 3
66
#define A31_WDOG_MODE_INTVL_SHIFT 4
67
#define D1_WDOG_MODE_INTVL_SHIFT 4
68
#define H616_WDOG_MODE_INTVL_SHIFT 4
69
#define A10_WDOG_MODE_RST_EN (1 << 1)
70
#define WDOG_MODE_EN (1 << 0)
71
#define A31_WDOG_CONFIG 0x14
72
#define D1_WDOG_CONFIG 0x14
73
#define H616_WDOG_CONFIG 0xB4
74
#define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0)
75
#define A31_WDOG_CONFIG_RST_EN_INT (2 << 0)
76
#define D1_WDOG_CONFIG_KEY (0x16AA << 16)
77
#define D1_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0)
78
#define D1_WDOG_CONFIG_RST_EN_INT (2 << 0)
79
#define H616_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0)
80
#define H616_WDOG_CONFIG_RST_EN_INT (2 << 0)
81
82
struct aw_wdog_interval {
83
uint64_t milliseconds;
84
unsigned int value;
85
};
86
87
struct aw_wdog_interval wd_intervals[] = {
88
{ 500, 0 },
89
{ 1000, 1 },
90
{ 2000, 2 },
91
{ 3000, 3 },
92
{ 4000, 4 },
93
{ 5000, 5 },
94
{ 6000, 6 },
95
{ 8000, 7 },
96
{ 10000, 8 },
97
{ 12000, 9 },
98
{ 14000, 10 },
99
{ 16000, 11 },
100
{ 0, 0 } /* sentinel */
101
};
102
103
static struct aw_wdog_softc *aw_wdog_sc = NULL;
104
105
struct aw_wdog_softc {
106
device_t dev;
107
struct resource * res;
108
struct mtx mtx;
109
uint8_t wdog_ctrl;
110
uint32_t wdog_ctrl_key;
111
uint8_t wdog_mode;
112
uint32_t wdog_mode_key;
113
uint8_t wdog_mode_intvl_shift;
114
uint8_t wdog_mode_en;
115
uint8_t wdog_config;
116
uint32_t wdog_config_value;
117
};
118
119
enum wdog_type {
120
A10_WATCHDOG = 1,
121
A31_WATCHDOG,
122
D1_WATCHDOG,
123
H616_WATCHDOG,
124
};
125
126
static struct ofw_compat_data compat_data[] = {
127
{"allwinner,sun4i-a10-wdt", A10_WATCHDOG},
128
{"allwinner,sun6i-a31-wdt", A31_WATCHDOG},
129
{"allwinner,sun20i-d1-wdt", D1_WATCHDOG},
130
{"allwinner,sun50i-h616-wdt", H616_WATCHDOG},
131
{NULL, 0}
132
};
133
134
static void aw_wdog_watchdog_fn(void *, u_int, int *);
135
static void aw_wdog_shutdown_fn(void *, int);
136
137
static int
138
aw_wdog_probe(device_t dev)
139
{
140
141
if (!ofw_bus_status_okay(dev))
142
return (ENXIO);
143
switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
144
case A10_WATCHDOG:
145
device_set_desc(dev, "Allwinner A10 Watchdog");
146
return (BUS_PROBE_DEFAULT);
147
case A31_WATCHDOG:
148
device_set_desc(dev, "Allwinner A31 Watchdog");
149
return (BUS_PROBE_DEFAULT);
150
case D1_WATCHDOG:
151
device_set_desc(dev, "Allwinner D1 Watchdog");
152
return (BUS_PROBE_DEFAULT);
153
case H616_WATCHDOG:
154
device_set_desc(dev, "Allwinner H616 Watchdog");
155
return (BUS_PROBE_DEFAULT);
156
}
157
return (ENXIO);
158
}
159
160
static int
161
aw_wdog_attach(device_t dev)
162
{
163
struct aw_wdog_softc *sc;
164
int rid;
165
166
if (aw_wdog_sc != NULL)
167
return (ENXIO);
168
169
sc = device_get_softc(dev);
170
sc->dev = dev;
171
172
rid = 0;
173
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
174
if (sc->res == NULL) {
175
device_printf(dev, "could not allocate memory resource\n");
176
return (ENXIO);
177
}
178
179
aw_wdog_sc = sc;
180
181
switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
182
case A10_WATCHDOG:
183
sc->wdog_ctrl = A10_WDOG_CTRL;
184
sc->wdog_mode = A10_WDOG_MODE;
185
sc->wdog_mode_key = 0;
186
sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT;
187
sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN;
188
break;
189
case A31_WATCHDOG:
190
sc->wdog_ctrl = A31_WDOG_CTRL;
191
sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY;
192
sc->wdog_mode = A31_WDOG_MODE;
193
sc->wdog_mode_key = 0;
194
sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT;
195
sc->wdog_mode_en = WDOG_MODE_EN;
196
sc->wdog_config = A31_WDOG_CONFIG;
197
sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM;
198
break;
199
case D1_WATCHDOG:
200
sc->wdog_ctrl = D1_WDOG_CTRL;
201
sc->wdog_ctrl_key = D1_WDOG_CTRL_KEY;
202
sc->wdog_mode = D1_WDOG_MODE;
203
sc->wdog_mode_key = D1_WDOG_MODE_KEY;
204
sc->wdog_mode_intvl_shift = D1_WDOG_MODE_INTVL_SHIFT;
205
sc->wdog_mode_en = WDOG_MODE_EN;
206
sc->wdog_config = D1_WDOG_CONFIG;
207
sc->wdog_config_value = D1_WDOG_CONFIG_KEY | D1_WDOG_CONFIG_RST_EN_SYSTEM;
208
break;
209
case H616_WATCHDOG:
210
sc->wdog_ctrl = H616_WDOG_CTRL;
211
sc->wdog_ctrl_key = H616_WDOG_CTRL_KEY;
212
sc->wdog_mode = H616_WDOG_MODE;
213
sc->wdog_mode_key = 0;
214
sc->wdog_mode_intvl_shift = H616_WDOG_MODE_INTVL_SHIFT;
215
sc->wdog_mode_en = WDOG_MODE_EN;
216
sc->wdog_config = H616_WDOG_CONFIG;
217
sc->wdog_config_value = H616_WDOG_CONFIG_RST_EN_SYSTEM;
218
break;
219
default:
220
bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res);
221
return (ENXIO);
222
}
223
224
mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF);
225
EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0);
226
EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc,
227
SHUTDOWN_PRI_LAST - 1);
228
229
/* Disable watchdog for now. */
230
WRITE(sc, sc->wdog_mode, sc->wdog_mode_key);
231
232
return (0);
233
}
234
235
static void
236
aw_wdog_watchdog_fn(void *private, u_int cmd, int *error)
237
{
238
struct aw_wdog_softc *sc;
239
uint64_t ms;
240
int i;
241
242
sc = private;
243
mtx_lock(&sc->mtx);
244
245
cmd &= WD_INTERVAL;
246
247
if (cmd > 0) {
248
ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000;
249
i = 0;
250
while (wd_intervals[i].milliseconds &&
251
(ms > wd_intervals[i].milliseconds))
252
i++;
253
if (wd_intervals[i].milliseconds) {
254
WRITE(sc, sc->wdog_mode, sc->wdog_mode_key |
255
(wd_intervals[i].value << sc->wdog_mode_intvl_shift) |
256
sc->wdog_mode_en);
257
WRITE(sc, sc->wdog_ctrl,
258
WDOG_CTRL_RESTART | sc->wdog_ctrl_key);
259
if (sc->wdog_config)
260
WRITE(sc, sc->wdog_config,
261
sc->wdog_config_value);
262
*error = 0;
263
}
264
else {
265
/*
266
* Can't arm
267
* disable watchdog as watchdog(9) requires
268
*/
269
device_printf(sc->dev,
270
"Can't arm, timeout is more than 16 sec\n");
271
mtx_unlock(&sc->mtx);
272
WRITE(sc, sc->wdog_mode, sc->wdog_mode_key);
273
return;
274
}
275
}
276
else
277
WRITE(sc, sc->wdog_mode, sc->wdog_mode_key);
278
279
mtx_unlock(&sc->mtx);
280
}
281
282
static void
283
aw_wdog_shutdown_fn(void *private, int howto)
284
{
285
if ((howto & (RB_POWEROFF|RB_HALT)) == 0)
286
aw_wdog_watchdog_reset();
287
}
288
289
void
290
aw_wdog_watchdog_reset(void)
291
{
292
293
if (aw_wdog_sc == NULL) {
294
printf("Reset: watchdog device has not been initialized\n");
295
return;
296
}
297
298
WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode, aw_wdog_sc->wdog_mode_key |
299
(wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) |
300
aw_wdog_sc->wdog_mode_en);
301
if (aw_wdog_sc->wdog_config)
302
WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config,
303
aw_wdog_sc->wdog_config_value);
304
WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl,
305
WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key);
306
while(1)
307
;
308
309
}
310
311
static device_method_t aw_wdog_methods[] = {
312
DEVMETHOD(device_probe, aw_wdog_probe),
313
DEVMETHOD(device_attach, aw_wdog_attach),
314
315
DEVMETHOD_END
316
};
317
318
static driver_t aw_wdog_driver = {
319
"aw_wdog",
320
aw_wdog_methods,
321
sizeof(struct aw_wdog_softc),
322
};
323
324
DRIVER_MODULE(aw_wdog, simplebus, aw_wdog_driver, 0, 0);
325
326