xref: /openbsd-src/sys/arch/arm64/dev/aplpmgr.c (revision 01830dbc52845975b04b8c79024da727208930fa)
1*01830dbcSkettenis /*	$OpenBSD: aplpmgr.c,v 1.5 2023/07/20 20:40:44 kettenis Exp $	*/
29a869755Skettenis /*
39a869755Skettenis  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
49a869755Skettenis  *
59a869755Skettenis  * Permission to use, copy, modify, and distribute this software for any
69a869755Skettenis  * purpose with or without fee is hereby granted, provided that the above
79a869755Skettenis  * copyright notice and this permission notice appear in all copies.
89a869755Skettenis  *
99a869755Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109a869755Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119a869755Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129a869755Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139a869755Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149a869755Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159a869755Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169a869755Skettenis  */
179a869755Skettenis 
189a869755Skettenis #include <sys/param.h>
199a869755Skettenis #include <sys/systm.h>
209a869755Skettenis #include <sys/device.h>
219a869755Skettenis #include <sys/malloc.h>
229a869755Skettenis 
239a869755Skettenis #include <machine/bus.h>
249a869755Skettenis #include <machine/fdt.h>
259a869755Skettenis 
269a869755Skettenis #include <dev/ofw/openfirm.h>
276df2d0b7Skettenis #include <dev/ofw/ofw_clock.h>
289a869755Skettenis #include <dev/ofw/ofw_misc.h>
299a869755Skettenis #include <dev/ofw/ofw_power.h>
309a869755Skettenis #include <dev/ofw/fdt.h>
319a869755Skettenis 
329a869755Skettenis #define PMGR_PS_TARGET_MASK	0x0000000f
339a869755Skettenis #define PMGR_PS_TARGET_SHIFT	0
349a869755Skettenis #define PMGR_PS_ACTUAL_MASK	0x000000f0
359a869755Skettenis #define PMGR_PS_ACTUAL_SHIFT	4
369a869755Skettenis #define  PMGR_PS_ACTIVE		0xf
379a869755Skettenis #define  PMGR_PS_PWRGATE	0x0
386df2d0b7Skettenis #define PMGR_WAS_PWRGATED	0x00000100
396df2d0b7Skettenis #define PMGR_WAS_CLKGATED	0x00000200
406df2d0b7Skettenis #define PMGR_DEV_DISABLE	0x00000400
416df2d0b7Skettenis #define PMGR_RESET		0x80000000
429a869755Skettenis 
439a869755Skettenis #define HREAD4(sc, reg)							\
449a869755Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
459a869755Skettenis #define HWRITE4(sc, reg, val)						\
469a869755Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
479a869755Skettenis 
489a869755Skettenis struct aplpmgr_softc;
499a869755Skettenis 
509a869755Skettenis struct aplpmgr_pwrstate {
519a869755Skettenis 	struct aplpmgr_softc	*ps_sc;
529a869755Skettenis 	bus_size_t		ps_offset;
53edff2dd8Skettenis 	int			ps_enablecount;
546df2d0b7Skettenis 	struct power_domain_device ps_pd;
556df2d0b7Skettenis 	struct reset_device	ps_rd;
569a869755Skettenis };
579a869755Skettenis 
589a869755Skettenis struct aplpmgr_softc {
599a869755Skettenis 	struct device		sc_dev;
609a869755Skettenis 	bus_space_tag_t		sc_iot;
619a869755Skettenis 	bus_space_handle_t	sc_ioh;
629a869755Skettenis 
639a869755Skettenis 	struct aplpmgr_pwrstate	*sc_pwrstate;
649a869755Skettenis 	int			sc_npwrstate;
659a869755Skettenis };
669a869755Skettenis 
679a869755Skettenis int	aplpmgr_match(struct device *, void *, void *);
689a869755Skettenis void	aplpmgr_attach(struct device *, struct device *, void *);
699a869755Skettenis 
709a869755Skettenis const struct cfattach aplpmgr_ca = {
719a869755Skettenis 	sizeof (struct aplpmgr_softc), aplpmgr_match, aplpmgr_attach
729a869755Skettenis };
739a869755Skettenis 
749a869755Skettenis struct cfdriver aplpmgr_cd = {
759a869755Skettenis 	NULL, "aplpmgr", DV_DULL
769a869755Skettenis };
779a869755Skettenis 
789a869755Skettenis void	aplpmgr_enable(void *, uint32_t *, int);
796df2d0b7Skettenis void	aplpmgr_reset(void *, uint32_t *, int);
809a869755Skettenis 
819a869755Skettenis int
aplpmgr_match(struct device * parent,void * match,void * aux)829a869755Skettenis aplpmgr_match(struct device *parent, void *match, void *aux)
839a869755Skettenis {
849a869755Skettenis 	struct fdt_attach_args *faa = aux;
859a869755Skettenis 
869a869755Skettenis 	if (OF_is_compatible(faa->fa_node, "apple,pmgr"))
879a869755Skettenis 		return 10;	/* Must beat syscon(4). */
889a869755Skettenis 
899a869755Skettenis 	return 0;
909a869755Skettenis }
919a869755Skettenis 
929a869755Skettenis void
aplpmgr_attach(struct device * parent,struct device * self,void * aux)939a869755Skettenis aplpmgr_attach(struct device *parent, struct device *self, void *aux)
949a869755Skettenis {
959a869755Skettenis 	struct aplpmgr_softc *sc = (struct aplpmgr_softc *)self;
969a869755Skettenis 	struct fdt_attach_args *faa = aux;
979a869755Skettenis 	struct aplpmgr_pwrstate *ps;
989a869755Skettenis 	uint32_t reg[2];
999a869755Skettenis 	int node;
1009a869755Skettenis 
1019a869755Skettenis 	if (faa->fa_nreg < 1) {
1029a869755Skettenis 		printf(": no registers\n");
1039a869755Skettenis 		return;
1049a869755Skettenis 	}
1059a869755Skettenis 
1069a869755Skettenis 	sc->sc_iot = faa->fa_iot;
1079a869755Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1089a869755Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1099a869755Skettenis 		printf(": can't map registers\n");
1109a869755Skettenis 		return;
1119a869755Skettenis 	}
1129a869755Skettenis 
1139a869755Skettenis 	printf("\n");
1149a869755Skettenis 
1159a869755Skettenis 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
1169a869755Skettenis 		if (OF_is_compatible(node, "apple,pmgr-pwrstate"))
1179a869755Skettenis 			sc->sc_npwrstate++;
1189a869755Skettenis 	}
1199a869755Skettenis 
1209a869755Skettenis 	sc->sc_pwrstate = mallocarray(sc->sc_npwrstate,
1219a869755Skettenis 	    sizeof(*sc->sc_pwrstate), M_DEVBUF, M_WAITOK | M_ZERO);
1229a869755Skettenis 
1239a869755Skettenis 	ps = sc->sc_pwrstate;
1249a869755Skettenis 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
1259a869755Skettenis 		if (!OF_is_compatible(node, "apple,pmgr-pwrstate"))
1269a869755Skettenis 			continue;
1279a869755Skettenis 
1289a869755Skettenis 		if (OF_getpropintarray(node, "reg", reg,
1299a869755Skettenis 		    sizeof(reg)) != sizeof(reg)) {
1309a869755Skettenis 			printf("%s: invalid reg property\n",
1319a869755Skettenis 			    sc->sc_dev.dv_xname);
1329a869755Skettenis 			continue;
1339a869755Skettenis 		}
1349a869755Skettenis 
1359a869755Skettenis 		ps->ps_sc = sc;
1369a869755Skettenis 		ps->ps_offset = reg[0];
137edff2dd8Skettenis 		if (OF_getpropbool(node, "apple,always-on"))
138edff2dd8Skettenis 			ps->ps_enablecount = 1;
1396df2d0b7Skettenis 
1409a869755Skettenis 		ps->ps_pd.pd_node = node;
1419a869755Skettenis 		ps->ps_pd.pd_cookie = ps;
1429a869755Skettenis 		ps->ps_pd.pd_enable = aplpmgr_enable;
1439a869755Skettenis 		power_domain_register(&ps->ps_pd);
1446df2d0b7Skettenis 
1456df2d0b7Skettenis 		ps->ps_rd.rd_node = node;
1466df2d0b7Skettenis 		ps->ps_rd.rd_cookie = ps;
1476df2d0b7Skettenis 		ps->ps_rd.rd_reset = aplpmgr_reset;
1486df2d0b7Skettenis 		reset_register(&ps->ps_rd);
1496df2d0b7Skettenis 
1509a869755Skettenis 		ps++;
1519a869755Skettenis 	}
1529a869755Skettenis }
1539a869755Skettenis 
1549a869755Skettenis void
aplpmgr_enable(void * cookie,uint32_t * cells,int on)1559a869755Skettenis aplpmgr_enable(void *cookie, uint32_t *cells, int on)
1569a869755Skettenis {
1579a869755Skettenis 	struct aplpmgr_pwrstate *ps = cookie;
1589a869755Skettenis 	struct aplpmgr_softc *sc = ps->ps_sc;
1599a869755Skettenis 	uint32_t pstate = on ? PMGR_PS_ACTIVE : PMGR_PS_PWRGATE;
1609a869755Skettenis 	uint32_t val;
1619a869755Skettenis 	int timo;
1629a869755Skettenis 
163edff2dd8Skettenis 	KASSERT(on || ps->ps_enablecount > 0);
164edff2dd8Skettenis 	KASSERT(!on || ps->ps_enablecount < INT_MAX);
165edff2dd8Skettenis 
166edff2dd8Skettenis 	if (on && ps->ps_enablecount > 0) {
1679a869755Skettenis 		power_domain_enable_all(ps->ps_pd.pd_node);
168edff2dd8Skettenis 		ps->ps_enablecount++;
169edff2dd8Skettenis 		return;
170edff2dd8Skettenis 	}
171edff2dd8Skettenis 	if (!on && ps->ps_enablecount > 1) {
172edff2dd8Skettenis 		power_domain_disable_all(ps->ps_pd.pd_node);
173edff2dd8Skettenis 		ps->ps_enablecount--;
174edff2dd8Skettenis 		return;
175edff2dd8Skettenis 	}
176edff2dd8Skettenis 
177edff2dd8Skettenis 	/* Enable parents before enabling ourselves. */
178edff2dd8Skettenis 	if (on) {
179edff2dd8Skettenis 		power_domain_enable_all(ps->ps_pd.pd_node);
180edff2dd8Skettenis 		ps->ps_enablecount++;
181edff2dd8Skettenis 	}
1829a869755Skettenis 
1839a869755Skettenis 	val = HREAD4(sc, ps->ps_offset);
1849a869755Skettenis 	val &= ~PMGR_PS_TARGET_MASK;
1859a869755Skettenis 	val |= (pstate << PMGR_PS_TARGET_SHIFT);
1869a869755Skettenis 	HWRITE4(sc, ps->ps_offset, val);
1879a869755Skettenis 
1889a869755Skettenis 	for (timo = 0; timo < 100; timo++) {
1899a869755Skettenis 		val = HREAD4(sc, ps->ps_offset);
1909a869755Skettenis 		val &= PMGR_PS_ACTUAL_MASK;
1919a869755Skettenis 		if ((val >> PMGR_PS_ACTUAL_SHIFT) == pstate)
1929a869755Skettenis 			break;
1939a869755Skettenis 		delay(1);
1949a869755Skettenis 	}
195edff2dd8Skettenis 
196edff2dd8Skettenis 	/* Disable parents after disabling ourselves. */
197edff2dd8Skettenis 	if (!on) {
198edff2dd8Skettenis 		power_domain_disable_all(ps->ps_pd.pd_node);
199edff2dd8Skettenis 		ps->ps_enablecount--;
200edff2dd8Skettenis 	}
2019a869755Skettenis }
2026df2d0b7Skettenis 
2036df2d0b7Skettenis void
aplpmgr_reset(void * cookie,uint32_t * cells,int on)2046df2d0b7Skettenis aplpmgr_reset(void *cookie, uint32_t *cells, int on)
2056df2d0b7Skettenis {
2066df2d0b7Skettenis 	struct aplpmgr_pwrstate *ps = cookie;
2076df2d0b7Skettenis 	struct aplpmgr_softc *sc = ps->ps_sc;
2086df2d0b7Skettenis 	uint32_t val;
2096df2d0b7Skettenis 
2106df2d0b7Skettenis 	if (on) {
2116df2d0b7Skettenis 		val = HREAD4(sc, ps->ps_offset);
2126df2d0b7Skettenis 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
2136df2d0b7Skettenis 		HWRITE4(sc, ps->ps_offset, val | PMGR_DEV_DISABLE);
2146df2d0b7Skettenis 		val = HREAD4(sc, ps->ps_offset);
2156df2d0b7Skettenis 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
2166df2d0b7Skettenis 		HWRITE4(sc, ps->ps_offset, val | PMGR_RESET);
2176df2d0b7Skettenis 	} else {
2186df2d0b7Skettenis 		val = HREAD4(sc, ps->ps_offset);
2196df2d0b7Skettenis 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
2206df2d0b7Skettenis 		HWRITE4(sc, ps->ps_offset, val & ~PMGR_RESET);
2216df2d0b7Skettenis 		val = HREAD4(sc, ps->ps_offset);
2226df2d0b7Skettenis 		val &= ~(PMGR_WAS_CLKGATED | PMGR_WAS_PWRGATED);
2236df2d0b7Skettenis 		HWRITE4(sc, ps->ps_offset, val & ~PMGR_DEV_DISABLE);
2246df2d0b7Skettenis 	}
2256df2d0b7Skettenis }
226