xref: /openbsd-src/sys/arch/arm64/dev/aplpmgr.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: aplpmgr.c,v 1.3 2022/11/10 11:44:06 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2021 Mark Kettenis <kettenis@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 #include <sys/malloc.h>
22 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_clock.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/ofw_power.h>
30 #include <dev/ofw/fdt.h>
31 
32 #define PMGR_PS_TARGET_MASK	0x0000000f
33 #define PMGR_PS_TARGET_SHIFT	0
34 #define PMGR_PS_ACTUAL_MASK	0x000000f0
35 #define PMGR_PS_ACTUAL_SHIFT	4
36 #define  PMGR_PS_ACTIVE		0xf
37 #define  PMGR_PS_PWRGATE	0x0
38 #define PMGR_WAS_PWRGATED	0x00000100
39 #define PMGR_WAS_CLKGATED	0x00000200
40 #define PMGR_DEV_DISABLE	0x00000400
41 #define PMGR_RESET		0x80000000
42 
43 #define HREAD4(sc, reg)							\
44 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
45 #define HWRITE4(sc, reg, val)						\
46 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
47 
48 struct aplpmgr_softc;
49 
50 struct aplpmgr_pwrstate {
51 	struct aplpmgr_softc	*ps_sc;
52 	bus_size_t		ps_offset;
53 	int			ps_enablecount;
54 	struct power_domain_device ps_pd;
55 	struct reset_device	ps_rd;
56 };
57 
58 struct aplpmgr_softc {
59 	struct device		sc_dev;
60 	bus_space_tag_t		sc_iot;
61 	bus_space_handle_t	sc_ioh;
62 
63 	struct aplpmgr_pwrstate	*sc_pwrstate;
64 	int			sc_npwrstate;
65 };
66 
67 int	aplpmgr_match(struct device *, void *, void *);
68 void	aplpmgr_attach(struct device *, struct device *, void *);
69 int	aplpmgr_activate(struct device *, int act);
70 
71 const struct cfattach aplpmgr_ca = {
72 	sizeof (struct aplpmgr_softc), aplpmgr_match, aplpmgr_attach
73 };
74 
75 struct cfdriver aplpmgr_cd = {
76 	NULL, "aplpmgr", DV_DULL
77 };
78 
79 void	aplpmgr_enable(void *, uint32_t *, int);
80 void	aplpmgr_reset(void *, uint32_t *, int);
81 
82 int
83 aplpmgr_match(struct device *parent, void *match, void *aux)
84 {
85 	struct fdt_attach_args *faa = aux;
86 
87 	if (OF_is_compatible(faa->fa_node, "apple,pmgr"))
88 		return 10;	/* Must beat syscon(4). */
89 
90 	return 0;
91 }
92 
93 void
94 aplpmgr_attach(struct device *parent, struct device *self, void *aux)
95 {
96 	struct aplpmgr_softc *sc = (struct aplpmgr_softc *)self;
97 	struct fdt_attach_args *faa = aux;
98 	struct aplpmgr_pwrstate *ps;
99 	uint32_t reg[2];
100 	int node;
101 
102 	if (faa->fa_nreg < 1) {
103 		printf(": no registers\n");
104 		return;
105 	}
106 
107 	sc->sc_iot = faa->fa_iot;
108 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
109 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
110 		printf(": can't map registers\n");
111 		return;
112 	}
113 
114 	printf("\n");
115 
116 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
117 		if (OF_is_compatible(node, "apple,pmgr-pwrstate"))
118 			sc->sc_npwrstate++;
119 	}
120 
121 	sc->sc_pwrstate = mallocarray(sc->sc_npwrstate,
122 	    sizeof(*sc->sc_pwrstate), M_DEVBUF, M_WAITOK | M_ZERO);
123 
124 	ps = sc->sc_pwrstate;
125 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
126 		if (!OF_is_compatible(node, "apple,pmgr-pwrstate"))
127 			continue;
128 
129 		if (OF_getpropintarray(node, "reg", reg,
130 		    sizeof(reg)) != sizeof(reg)) {
131 			printf("%s: invalid reg property\n",
132 			    sc->sc_dev.dv_xname);
133 			continue;
134 		}
135 
136 		ps->ps_sc = sc;
137 		ps->ps_offset = reg[0];
138 		if (OF_getpropbool(node, "apple,always-on"))
139 			ps->ps_enablecount = 1;
140 
141 		ps->ps_pd.pd_node = node;
142 		ps->ps_pd.pd_cookie = ps;
143 		ps->ps_pd.pd_enable = aplpmgr_enable;
144 		power_domain_register(&ps->ps_pd);
145 
146 		ps->ps_rd.rd_node = node;
147 		ps->ps_rd.rd_cookie = ps;
148 		ps->ps_rd.rd_reset = aplpmgr_reset;
149 		reset_register(&ps->ps_rd);
150 
151 		ps++;
152 	}
153 }
154 
155 void
156 aplpmgr_enable(void *cookie, uint32_t *cells, int on)
157 {
158 	struct aplpmgr_pwrstate *ps = cookie;
159 	struct aplpmgr_softc *sc = ps->ps_sc;
160 	uint32_t pstate = on ? PMGR_PS_ACTIVE : PMGR_PS_PWRGATE;
161 	uint32_t val;
162 	int timo;
163 
164 	KASSERT(on || ps->ps_enablecount > 0);
165 	KASSERT(!on || ps->ps_enablecount < INT_MAX);
166 
167 	if (on && ps->ps_enablecount > 0) {
168 		power_domain_enable_all(ps->ps_pd.pd_node);
169 		ps->ps_enablecount++;
170 		return;
171 	}
172 	if (!on && ps->ps_enablecount > 1) {
173 		power_domain_disable_all(ps->ps_pd.pd_node);
174 		ps->ps_enablecount--;
175 		return;
176 	}
177 
178 	/* Enable parents before enabling ourselves. */
179 	if (on) {
180 		power_domain_enable_all(ps->ps_pd.pd_node);
181 		ps->ps_enablecount++;
182 	}
183 
184 	val = HREAD4(sc, ps->ps_offset);
185 	val &= ~PMGR_PS_TARGET_MASK;
186 	val |= (pstate << PMGR_PS_TARGET_SHIFT);
187 	HWRITE4(sc, ps->ps_offset, val);
188 
189 	for (timo = 0; timo < 100; timo++) {
190 		val = HREAD4(sc, ps->ps_offset);
191 		val &= PMGR_PS_ACTUAL_MASK;
192 		if ((val >> PMGR_PS_ACTUAL_SHIFT) == pstate)
193 			break;
194 		delay(1);
195 	}
196 
197 	/* Disable parents after disabling ourselves. */
198 	if (!on) {
199 		power_domain_disable_all(ps->ps_pd.pd_node);
200 		ps->ps_enablecount--;
201 	}
202 }
203 
204 void
205 aplpmgr_reset(void *cookie, uint32_t *cells, int on)
206 {
207 	struct aplpmgr_pwrstate *ps = cookie;
208 	struct aplpmgr_softc *sc = ps->ps_sc;
209 	uint32_t val;
210 
211 	if (on) {
212 		val = HREAD4(sc, ps->ps_offset);
213 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
214 		HWRITE4(sc, ps->ps_offset, val | PMGR_DEV_DISABLE);
215 		val = HREAD4(sc, ps->ps_offset);
216 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
217 		HWRITE4(sc, ps->ps_offset, val | PMGR_RESET);
218 	} else {
219 		val = HREAD4(sc, ps->ps_offset);
220 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
221 		HWRITE4(sc, ps->ps_offset, val & ~PMGR_RESET);
222 		val = HREAD4(sc, ps->ps_offset);
223 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
224 		HWRITE4(sc, ps->ps_offset, val & ~PMGR_DEV_DISABLE);
225 	}
226 }
227