1 /*-
2 * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
4 * Copyright (c) 2022 Julien Cassette <julien.cassette@gmail.com>
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 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/eventhandler.h>
33 #include <sys/systm.h>
34 #include <sys/watchdog.h>
35 #include <sys/reboot.h>
36 #include <sys/bus.h>
37 #include <sys/kernel.h>
38 #include <sys/lock.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41 #include <sys/rman.h>
42
43 #include <dev/ofw/openfirm.h>
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46
47 #include <machine/bus.h>
48
49 #include <arm/allwinner/aw_wdog.h>
50
51 #define READ(_sc, _r) bus_read_4((_sc)->res, (_r))
52 #define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v))
53
54 #define A10_WDOG_CTRL 0x00
55 #define A31_WDOG_CTRL 0x10
56 #define D1_WDOG_CTRL 0x10
57 #define WDOG_CTRL_RESTART (1 << 0)
58 #define A31_WDOG_CTRL_KEY (0xa57 << 1)
59 #define D1_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 D1_WDOG_MODE_KEY (0x16AA << 16)
64 #define A10_WDOG_MODE_INTVL_SHIFT 3
65 #define A31_WDOG_MODE_INTVL_SHIFT 4
66 #define D1_WDOG_MODE_INTVL_SHIFT 4
67 #define A10_WDOG_MODE_RST_EN (1 << 1)
68 #define WDOG_MODE_EN (1 << 0)
69 #define A31_WDOG_CONFIG 0x14
70 #define D1_WDOG_CONFIG 0x14
71 #define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0)
72 #define A31_WDOG_CONFIG_RST_EN_INT (2 << 0)
73 #define D1_WDOG_CONFIG_KEY (0x16AA << 16)
74 #define D1_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0)
75 #define D1_WDOG_CONFIG_RST_EN_INT (2 << 0)
76
77 struct aw_wdog_interval {
78 uint64_t milliseconds;
79 unsigned int value;
80 };
81
82 struct aw_wdog_interval wd_intervals[] = {
83 { 500, 0 },
84 { 1000, 1 },
85 { 2000, 2 },
86 { 3000, 3 },
87 { 4000, 4 },
88 { 5000, 5 },
89 { 6000, 6 },
90 { 8000, 7 },
91 { 10000, 8 },
92 { 12000, 9 },
93 { 14000, 10 },
94 { 16000, 11 },
95 { 0, 0 } /* sentinel */
96 };
97
98 static struct aw_wdog_softc *aw_wdog_sc = NULL;
99
100 struct aw_wdog_softc {
101 device_t dev;
102 struct resource * res;
103 struct mtx mtx;
104 uint8_t wdog_ctrl;
105 uint32_t wdog_ctrl_key;
106 uint8_t wdog_mode;
107 uint32_t wdog_mode_key;
108 uint8_t wdog_mode_intvl_shift;
109 uint8_t wdog_mode_en;
110 uint8_t wdog_config;
111 uint32_t wdog_config_value;
112 };
113
114 #define A10_WATCHDOG 1
115 #define A31_WATCHDOG 2
116 #define D1_WATCHDOG 3
117
118 static struct ofw_compat_data compat_data[] = {
119 {"allwinner,sun4i-a10-wdt", A10_WATCHDOG},
120 {"allwinner,sun6i-a31-wdt", A31_WATCHDOG},
121 {"allwinner,sun20i-d1-wdt", D1_WATCHDOG},
122 {NULL, 0}
123 };
124
125 static void aw_wdog_watchdog_fn(void *, u_int, int *);
126 static void aw_wdog_shutdown_fn(void *, int);
127
128 static int
129 aw_wdog_probe(device_t dev)
130 {
131
132 if (!ofw_bus_status_okay(dev))
133 return (ENXIO);
134 switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
135 case A10_WATCHDOG:
136 device_set_desc(dev, "Allwinner A10 Watchdog");
137 return (BUS_PROBE_DEFAULT);
138 case A31_WATCHDOG:
139 device_set_desc(dev, "Allwinner A31 Watchdog");
140 return (BUS_PROBE_DEFAULT);
141 case D1_WATCHDOG:
142 device_set_desc(dev, "Allwinner D1 Watchdog");
143 return (BUS_PROBE_DEFAULT);
144 }
145 return (ENXIO);
146 }
147
148 static int
149 aw_wdog_attach(device_t dev)
150 {
151 struct aw_wdog_softc *sc;
152 int rid;
153
154 if (aw_wdog_sc != NULL)
155 return (ENXIO);
156
157 sc = device_get_softc(dev);
158 sc->dev = dev;
159
160 rid = 0;
161 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
162 if (sc->res == NULL) {
163 device_printf(dev, "could not allocate memory resource\n");
164 return (ENXIO);
165 }
166
167 aw_wdog_sc = sc;
168
169 switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
170 case A10_WATCHDOG:
171 sc->wdog_ctrl = A10_WDOG_CTRL;
172 sc->wdog_mode = A10_WDOG_MODE;
173 sc->wdog_mode_key = 0;
174 sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT;
175 sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN;
176 break;
177 case A31_WATCHDOG:
178 sc->wdog_ctrl = A31_WDOG_CTRL;
179 sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY;
180 sc->wdog_mode = A31_WDOG_MODE;
181 sc->wdog_mode_key = 0;
182 sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT;
183 sc->wdog_mode_en = WDOG_MODE_EN;
184 sc->wdog_config = A31_WDOG_CONFIG;
185 sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM;
186 break;
187 case D1_WATCHDOG:
188 sc->wdog_ctrl = D1_WDOG_CTRL;
189 sc->wdog_ctrl_key = D1_WDOG_CTRL_KEY;
190 sc->wdog_mode = D1_WDOG_MODE;
191 sc->wdog_mode_key = D1_WDOG_MODE_KEY;
192 sc->wdog_mode_intvl_shift = D1_WDOG_MODE_INTVL_SHIFT;
193 sc->wdog_mode_en = WDOG_MODE_EN;
194 sc->wdog_config = D1_WDOG_CONFIG;
195 sc->wdog_config_value = D1_WDOG_CONFIG_KEY | D1_WDOG_CONFIG_RST_EN_SYSTEM;
196 break;
197 default:
198 bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res);
199 return (ENXIO);
200 }
201
202 mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF);
203 EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0);
204 EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc,
205 SHUTDOWN_PRI_LAST - 1);
206
207 return (0);
208 }
209
210 static void
211 aw_wdog_watchdog_fn(void *private, u_int cmd, int *error)
212 {
213 struct aw_wdog_softc *sc;
214 uint64_t ms;
215 int i;
216
217 sc = private;
218 mtx_lock(&sc->mtx);
219
220 cmd &= WD_INTERVAL;
221
222 if (cmd > 0) {
223 ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000;
224 i = 0;
225 while (wd_intervals[i].milliseconds &&
226 (ms > wd_intervals[i].milliseconds))
227 i++;
228 if (wd_intervals[i].milliseconds) {
229 WRITE(sc, sc->wdog_mode, sc->wdog_mode_key |
230 (wd_intervals[i].value << sc->wdog_mode_intvl_shift) |
231 sc->wdog_mode_en);
232 WRITE(sc, sc->wdog_ctrl,
233 WDOG_CTRL_RESTART | sc->wdog_ctrl_key);
234 if (sc->wdog_config)
235 WRITE(sc, sc->wdog_config,
236 sc->wdog_config_value);
237 *error = 0;
238 }
239 else {
240 /*
241 * Can't arm
242 * disable watchdog as watchdog(9) requires
243 */
244 device_printf(sc->dev,
245 "Can't arm, timeout is more than 16 sec\n");
246 mtx_unlock(&sc->mtx);
247 WRITE(sc, sc->wdog_mode, sc->wdog_mode_key);
248 return;
249 }
250 }
251 else
252 WRITE(sc, sc->wdog_mode, sc->wdog_mode_key);
253
254 mtx_unlock(&sc->mtx);
255 }
256
257 static void
258 aw_wdog_shutdown_fn(void *private, int howto)
259 {
260 if ((howto & (RB_POWEROFF|RB_HALT)) == 0)
261 aw_wdog_watchdog_reset();
262 }
263
264 void
265 aw_wdog_watchdog_reset(void)
266 {
267
268 if (aw_wdog_sc == NULL) {
269 printf("Reset: watchdog device has not been initialized\n");
270 return;
271 }
272
273 WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode, aw_wdog_sc->wdog_mode_key |
274 (wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) |
275 aw_wdog_sc->wdog_mode_en);
276 if (aw_wdog_sc->wdog_config)
277 WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config,
278 aw_wdog_sc->wdog_config_value);
279 WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl,
280 WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key);
281 while(1)
282 ;
283
284 }
285
286 static device_method_t aw_wdog_methods[] = {
287 DEVMETHOD(device_probe, aw_wdog_probe),
288 DEVMETHOD(device_attach, aw_wdog_attach),
289
290 DEVMETHOD_END
291 };
292
293 static driver_t aw_wdog_driver = {
294 "aw_wdog",
295 aw_wdog_methods,
296 sizeof(struct aw_wdog_softc),
297 };
298 static devclass_t aw_wdog_devclass;
299
300 DRIVER_MODULE(aw_wdog, simplebus, aw_wdog_driver, aw_wdog_devclass, 0, 0);
Cache object: 15683a5fe628154680e76ef52386aabd
|