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 48struct aplpmgr_softc; 49 50struct 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 58struct 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 67int aplpmgr_match(struct device *, void *, void *); 68void aplpmgr_attach(struct device *, struct device *, void *); 69 70const struct cfattach aplpmgr_ca = { 71 sizeof (struct aplpmgr_softc), aplpmgr_match, aplpmgr_attach 72}; 73 74struct cfdriver aplpmgr_cd = { 75 NULL, "aplpmgr", DV_DULL 76}; 77 78void aplpmgr_enable(void *, uint32_t *, int); 79void aplpmgr_reset(void *, uint32_t *, int); 80 81int 82aplpmgr_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 92void 93aplpmgr_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 154void 155aplpmgr_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 203void 204aplpmgr_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