1 /* $OpenBSD: sxidog.c,v 1.4 2024/01/26 17:03:45 kettenis Exp $ */
2 /*
3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21
22 #include <machine/bus.h>
23 #include <machine/fdt.h>
24
25 #include <dev/fdt/sunxireg.h>
26
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/fdt.h>
29
30 extern void (*cpuresetfn)(void);
31
32 /* Allwinner A10 registers */
33 #define WDOG_CTRL_REG 0x00
34 #define WDOG_KEY (0x0a57 << 1)
35 #define WDOG_RSTART 0x01
36 #define WDOG_MODE_REG 0x04
37 #define WDOG_EN (1 << 0)
38 #define WDOG_RST_EN (1 << 1) /* system reset */
39 #define WDOG_INTV_VALUE(x) ((x) << 3)
40
41 /* Allwinner A31 registers */
42 #define WDOG0_CTRL_REG 0x10
43 #define WDOG0_KEY (0x0a57 << 1)
44 #define WDOG0_RSTART (1 << 0)
45 #define WDOG0_CFG_REG 0x14
46 #define WDOG0_RST_EN (1 << 0)
47 #define WDOG0_MODE_REG 0x18
48 #define WDOG0_EN (1 << 0)
49 #define WDOG0_INTV_VALUE(x) ((x) << 4)
50
51 struct sxidog_softc {
52 struct device sc_dev;
53 bus_space_tag_t sc_iot;
54 bus_space_handle_t sc_ioh;
55 int sc_type;
56 #define SXIDOG_A10 0
57 #define SXIDOG_A31 1
58 uint32_t sc_key;
59 };
60
61 struct sxidog_softc *sxidog_sc = NULL; /* for sxidog_reset() */
62
63 int sxidog_match(struct device *, void *, void *);
64 void sxidog_attach(struct device *, struct device *, void *);
65 int sxidog_activate(struct device *, int);
66 int sxidog_callback(void *, int);
67 void sxidog_reset(void);
68
69 const struct cfattach sxidog_ca = {
70 sizeof (struct sxidog_softc), sxidog_match, sxidog_attach,
71 NULL, sxidog_activate
72 };
73
74 struct cfdriver sxidog_cd = {
75 NULL, "sxidog", DV_DULL
76 };
77
78 int
sxidog_match(struct device * parent,void * match,void * aux)79 sxidog_match(struct device *parent, void *match, void *aux)
80 {
81 struct fdt_attach_args *faa = aux;
82
83 return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-wdt") ||
84 OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-wdt") ||
85 OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-wdt"));
86 }
87
88 void
sxidog_attach(struct device * parent,struct device * self,void * aux)89 sxidog_attach(struct device *parent, struct device *self, void *aux)
90 {
91 struct sxidog_softc *sc = (struct sxidog_softc *)self;
92 struct fdt_attach_args *faa = aux;
93
94 if (faa->fa_nreg < 1)
95 return;
96
97 sc->sc_iot = faa->fa_iot;
98 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
99 faa->fa_reg[0].size, 0, &sc->sc_ioh))
100 panic("sxidog_attach: bus_space_map failed!");
101
102 if (OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-wdt"))
103 sc->sc_key = 0x16aa0000;
104
105 if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-wdt") ||
106 OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-wdt")) {
107 SXIWRITE4(sc, WDOG0_MODE_REG, sc->sc_key);
108 SXIWRITE4(sc, WDOG0_CFG_REG, WDOG0_RST_EN | sc->sc_key);
109 sc->sc_type = SXIDOG_A31;
110 } else {
111 SXIWRITE4(sc, WDOG_MODE_REG, 0);
112 sc->sc_type = SXIDOG_A10;
113 }
114
115 sxidog_sc = sc;
116 cpuresetfn = sxidog_reset;
117
118 #ifndef SMALL_KERNEL
119 wdog_register(sxidog_callback, sc);
120 #endif
121
122 printf("\n");
123 }
124
125 int
sxidog_activate(struct device * self,int act)126 sxidog_activate(struct device *self, int act)
127 {
128 switch (act) {
129 case DVACT_POWERDOWN:
130 #ifndef SMALL_KERNEL
131 wdog_shutdown(self);
132 #endif
133 break;
134 }
135
136 return (0);
137 }
138
139 int
sxidog_callback(void * arg,int period)140 sxidog_callback(void *arg, int period)
141 {
142 struct sxidog_softc *sc = (struct sxidog_softc *)arg;
143 int enable;
144
145 if (period > 16)
146 period = 16;
147 else if (period < 0)
148 period = 0;
149
150 /* Convert to register encoding. */
151 if (period > 6)
152 period = 6 + (period - 5) / 2;
153
154 switch (sc->sc_type) {
155 case SXIDOG_A10:
156 enable = (period > 0) ? WDOG_RST_EN : 0;
157 SXIWRITE4(sc, WDOG_MODE_REG,
158 enable | WDOG_EN | WDOG_INTV_VALUE(period));
159 SXIWRITE4(sc, WDOG_CTRL_REG, WDOG_KEY | WDOG_RSTART);
160 break;
161 case SXIDOG_A31:
162 enable = (period > 0) ? WDOG0_EN : 0;
163 SXIWRITE4(sc, WDOG0_MODE_REG,
164 enable | WDOG0_INTV_VALUE(period) | sc->sc_key);
165 SXIWRITE4(sc, WDOG0_CTRL_REG, WDOG0_KEY | WDOG0_RSTART);
166 break;
167 }
168
169 /* Convert back to seconds. */
170 if (period > 6)
171 period = 6 + (period - 6) * 2;
172
173 return period;
174 }
175
176 void
sxidog_reset(void)177 sxidog_reset(void)
178 {
179 if (sxidog_sc == NULL)
180 return;
181
182 sxidog_callback(sxidog_sc, 1);
183 delay(1500000);
184 }
185