xref: /openbsd-src/sys/arch/arm64/dev/aplpmgr.c (revision 01830dbc52845975b04b8c79024da727208930fa)
1 /*	$OpenBSD: aplpmgr.c,v 1.5 2023/07/20 20:40:44 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 
70 const struct cfattach aplpmgr_ca = {
71 	sizeof (struct aplpmgr_softc), aplpmgr_match, aplpmgr_attach
72 };
73 
74 struct cfdriver aplpmgr_cd = {
75 	NULL, "aplpmgr", DV_DULL
76 };
77 
78 void	aplpmgr_enable(void *, uint32_t *, int);
79 void	aplpmgr_reset(void *, uint32_t *, int);
80 
81 int
aplpmgr_match(struct device * parent,void * match,void * aux)82 aplpmgr_match(struct device *parent, void *match, void *aux)
83 {
84 	struct fdt_attach_args *faa = aux;
85 
86 	if (OF_is_compatible(faa->fa_node, "apple,pmgr"))
87 		return 10;	/* Must beat syscon(4). */
88 
89 	return 0;
90 }
91 
92 void
aplpmgr_attach(struct device * parent,struct device * self,void * aux)93 aplpmgr_attach(struct device *parent, struct device *self, void *aux)
94 {
95 	struct aplpmgr_softc *sc = (struct aplpmgr_softc *)self;
96 	struct fdt_attach_args *faa = aux;
97 	struct aplpmgr_pwrstate *ps;
98 	uint32_t reg[2];
99 	int node;
100 
101 	if (faa->fa_nreg < 1) {
102 		printf(": no registers\n");
103 		return;
104 	}
105 
106 	sc->sc_iot = faa->fa_iot;
107 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
108 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
109 		printf(": can't map registers\n");
110 		return;
111 	}
112 
113 	printf("\n");
114 
115 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
116 		if (OF_is_compatible(node, "apple,pmgr-pwrstate"))
117 			sc->sc_npwrstate++;
118 	}
119 
120 	sc->sc_pwrstate = mallocarray(sc->sc_npwrstate,
121 	    sizeof(*sc->sc_pwrstate), M_DEVBUF, M_WAITOK | M_ZERO);
122 
123 	ps = sc->sc_pwrstate;
124 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
125 		if (!OF_is_compatible(node, "apple,pmgr-pwrstate"))
126 			continue;
127 
128 		if (OF_getpropintarray(node, "reg", reg,
129 		    sizeof(reg)) != sizeof(reg)) {
130 			printf("%s: invalid reg property\n",
131 			    sc->sc_dev.dv_xname);
132 			continue;
133 		}
134 
135 		ps->ps_sc = sc;
136 		ps->ps_offset = reg[0];
137 		if (OF_getpropbool(node, "apple,always-on"))
138 			ps->ps_enablecount = 1;
139 
140 		ps->ps_pd.pd_node = node;
141 		ps->ps_pd.pd_cookie = ps;
142 		ps->ps_pd.pd_enable = aplpmgr_enable;
143 		power_domain_register(&ps->ps_pd);
144 
145 		ps->ps_rd.rd_node = node;
146 		ps->ps_rd.rd_cookie = ps;
147 		ps->ps_rd.rd_reset = aplpmgr_reset;
148 		reset_register(&ps->ps_rd);
149 
150 		ps++;
151 	}
152 }
153 
154 void
aplpmgr_enable(void * cookie,uint32_t * cells,int on)155 aplpmgr_enable(void *cookie, uint32_t *cells, int on)
156 {
157 	struct aplpmgr_pwrstate *ps = cookie;
158 	struct aplpmgr_softc *sc = ps->ps_sc;
159 	uint32_t pstate = on ? PMGR_PS_ACTIVE : PMGR_PS_PWRGATE;
160 	uint32_t val;
161 	int timo;
162 
163 	KASSERT(on || ps->ps_enablecount > 0);
164 	KASSERT(!on || ps->ps_enablecount < INT_MAX);
165 
166 	if (on && ps->ps_enablecount > 0) {
167 		power_domain_enable_all(ps->ps_pd.pd_node);
168 		ps->ps_enablecount++;
169 		return;
170 	}
171 	if (!on && ps->ps_enablecount > 1) {
172 		power_domain_disable_all(ps->ps_pd.pd_node);
173 		ps->ps_enablecount--;
174 		return;
175 	}
176 
177 	/* Enable parents before enabling ourselves. */
178 	if (on) {
179 		power_domain_enable_all(ps->ps_pd.pd_node);
180 		ps->ps_enablecount++;
181 	}
182 
183 	val = HREAD4(sc, ps->ps_offset);
184 	val &= ~PMGR_PS_TARGET_MASK;
185 	val |= (pstate << PMGR_PS_TARGET_SHIFT);
186 	HWRITE4(sc, ps->ps_offset, val);
187 
188 	for (timo = 0; timo < 100; timo++) {
189 		val = HREAD4(sc, ps->ps_offset);
190 		val &= PMGR_PS_ACTUAL_MASK;
191 		if ((val >> PMGR_PS_ACTUAL_SHIFT) == pstate)
192 			break;
193 		delay(1);
194 	}
195 
196 	/* Disable parents after disabling ourselves. */
197 	if (!on) {
198 		power_domain_disable_all(ps->ps_pd.pd_node);
199 		ps->ps_enablecount--;
200 	}
201 }
202 
203 void
aplpmgr_reset(void * cookie,uint32_t * cells,int on)204 aplpmgr_reset(void *cookie, uint32_t *cells, int on)
205 {
206 	struct aplpmgr_pwrstate *ps = cookie;
207 	struct aplpmgr_softc *sc = ps->ps_sc;
208 	uint32_t val;
209 
210 	if (on) {
211 		val = HREAD4(sc, ps->ps_offset);
212 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
213 		HWRITE4(sc, ps->ps_offset, val | PMGR_DEV_DISABLE);
214 		val = HREAD4(sc, ps->ps_offset);
215 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
216 		HWRITE4(sc, ps->ps_offset, val | PMGR_RESET);
217 	} else {
218 		val = HREAD4(sc, ps->ps_offset);
219 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
220 		HWRITE4(sc, ps->ps_offset, val & ~PMGR_RESET);
221 		val = HREAD4(sc, ps->ps_offset);
222 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
223 		HWRITE4(sc, ps->ps_offset, val & ~PMGR_DEV_DISABLE);
224 	}
225 }
226