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