xref: /openbsd/sys/dev/fdt/sxidog.c (revision 0dffc04f)
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