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