xref: /openbsd/sys/dev/fdt/sxidog.c (revision 09467b48)
1 /* $OpenBSD: sxidog.c,v 1.2 2019/10/17 22:26:32 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 };
59 
60 struct sxidog_softc *sxidog_sc = NULL;	/* for sxidog_reset() */
61 
62 int sxidog_match(struct device *, void *, void *);
63 void sxidog_attach(struct device *, struct device *, void *);
64 int sxidog_activate(struct device *, int);
65 int sxidog_callback(void *, int);
66 void sxidog_reset(void);
67 
68 struct cfattach	sxidog_ca = {
69 	sizeof (struct sxidog_softc), sxidog_match, sxidog_attach,
70 	NULL, sxidog_activate
71 };
72 
73 struct cfdriver sxidog_cd = {
74 	NULL, "sxidog", DV_DULL
75 };
76 
77 int
78 sxidog_match(struct device *parent, void *match, void *aux)
79 {
80 	struct fdt_attach_args *faa = aux;
81 
82 	return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-wdt") ||
83 	    OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-wdt"));
84 }
85 
86 void
87 sxidog_attach(struct device *parent, struct device *self, void *aux)
88 {
89 	struct sxidog_softc *sc = (struct sxidog_softc *)self;
90 	struct fdt_attach_args *faa = aux;
91 
92 	if (faa->fa_nreg < 1)
93 		return;
94 
95 	sc->sc_iot = faa->fa_iot;
96 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
97 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
98 		panic("sxidog_attach: bus_space_map failed!");
99 
100 	if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-wdt")) {
101 		SXIWRITE4(sc, WDOG0_CFG_REG, WDOG0_RST_EN);
102 		sc->sc_type = SXIDOG_A31;
103 	} else
104 		sc->sc_type = SXIDOG_A10;
105 
106 	sxidog_sc = sc;
107 	cpuresetfn = sxidog_reset;
108 
109 #ifndef SMALL_KERNEL
110 	wdog_register(sxidog_callback, sc);
111 #endif
112 
113 	printf("\n");
114 }
115 
116 int
117 sxidog_activate(struct device *self, int act)
118 {
119 	switch (act) {
120 	case DVACT_POWERDOWN:
121 #ifndef SMALL_KERNEL
122 		wdog_shutdown(self);
123 #endif
124 		break;
125 	}
126 
127 	return (0);
128 }
129 
130 int
131 sxidog_callback(void *arg, int period)
132 {
133 	struct sxidog_softc *sc = (struct sxidog_softc *)arg;
134 	int enable;
135 
136 	if (period > 16)
137 		period = 16;
138 	else if (period < 0)
139 		period = 0;
140 
141 	/* Convert to register encoding. */
142 	if (period > 6)
143 		period = 6 + (period - 5) / 2;
144 
145 	switch (sc->sc_type) {
146 	case SXIDOG_A10:
147 		enable = (period > 0) ? WDOG_RST_EN : 0;
148 		SXIWRITE4(sc, WDOG_MODE_REG,
149 		    enable | WDOG_EN | WDOG_INTV_VALUE(period));
150 		SXIWRITE4(sc, WDOG_CTRL_REG, WDOG_KEY | WDOG_RSTART);
151 		break;
152 	case SXIDOG_A31:
153 		enable = (period > 0) ? WDOG0_EN : 0;
154 		SXIWRITE4(sc, WDOG0_MODE_REG,
155 		    enable | WDOG0_INTV_VALUE(period));
156 		SXIWRITE4(sc, WDOG0_CTRL_REG, WDOG0_KEY | WDOG0_RSTART);
157 		break;
158 	}
159 
160 	/* Convert back to seconds. */
161 	if (period > 6)
162 		period = 6 + (period - 6) * 2;
163 
164 	return period;
165 }
166 
167 void
168 sxidog_reset(void)
169 {
170 	if (sxidog_sc == NULL)
171 		return;
172 
173 	sxidog_callback(sxidog_sc, 1);
174 	delay(1500000);
175 }
176