1 /* $OpenBSD: amlpwrc.c,v 1.4 2022/04/06 18:59:28 naddy Exp $ */ 2 /* 3 * Copyright (c) 2020 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 22 #include <machine/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_power.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/fdt.h> 30 31 /* Power domain IDs */ 32 #define PWRC_G12A_ETH_ID 1 33 #define PWRC_SM1_USB_ID 2 34 #define PWRC_SM1_PCIE_ID 3 35 #define PWRC_SM1_ETH_ID 6 36 37 /* Registers */ 38 #define AO_RTI_GEN_PWR_SLEEP0 0x3a 39 #define AO_RTI_GEN_PWR_ISO0 0x3b 40 #define AO_RTI_GEN_PWR_PCIE_MASK (1 << 18) 41 #define AO_RTI_GEN_PWR_USB_MASK (1 << 17) 42 #define HHI_MEM_PD_REG0 0x40 43 #define HHI_MEM_PD_USB_MASK (0x3 << 30) 44 #define HHI_MEM_PD_PCIE_MASK (0xf << 26) 45 #define HHI_MEM_PD_ETH_MASK (0x3 << 2) 46 47 #define HREAD4(sc, reg) \ 48 (regmap_read_4((sc)->sc_rm, (reg) << 2)) 49 #define HWRITE4(sc, reg, val) \ 50 regmap_write_4((sc)->sc_rm, (reg) << 2, (val)) 51 #define HSET4(sc, reg, bits) \ 52 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 53 #define HCLR4(sc, reg, bits) \ 54 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 55 56 struct amlpwrc_softc { 57 struct device sc_dev; 58 struct regmap *sc_rm_hhi; 59 struct regmap *sc_rm_ao; 60 uint32_t sc_ao; 61 int sc_node; 62 63 struct power_domain_device sc_pd; 64 }; 65 66 int amlpwrc_match(struct device *, void *, void *); 67 void amlpwrc_attach(struct device *, struct device *, void *); 68 69 const struct cfattach amlpwrc_ca = { 70 sizeof (struct amlpwrc_softc), amlpwrc_match, amlpwrc_attach 71 }; 72 73 struct cfdriver amlpwrc_cd = { 74 NULL, "amlpwrc", DV_DULL 75 }; 76 77 void amlpwrc_g12a_enable(void *, uint32_t *, int); 78 void amlpwrc_sm1_enable(void *, uint32_t *, int); 79 80 int 81 amlpwrc_match(struct device *parent, void *match, void *aux) 82 { 83 struct fdt_attach_args *faa = aux; 84 85 return (OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-pwrc") || 86 OF_is_compatible(faa->fa_node, "amlogic,meson-sm1-pwrc")); 87 } 88 89 void 90 amlpwrc_attach(struct device *parent, struct device *self, void *aux) 91 { 92 struct amlpwrc_softc *sc = (struct amlpwrc_softc *)self; 93 struct fdt_attach_args *faa = aux; 94 95 /* 96 * We can't lookup the AO regmap at this point since the 97 * syscon(4) instance that provides it attaches after us. 98 */ 99 sc->sc_rm_hhi = regmap_bynode(OF_parent(faa->fa_node)); 100 sc->sc_ao = OF_getpropint(faa->fa_node, "amlogic,ao-sysctrl", 0); 101 if (sc->sc_rm_hhi == NULL || sc->sc_ao == 0) { 102 printf(": no registers\n"); 103 return; 104 } 105 106 sc->sc_node = faa->fa_node; 107 printf("\n"); 108 109 sc->sc_pd.pd_node = faa->fa_node; 110 sc->sc_pd.pd_cookie = sc; 111 if (OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-pwrc")) 112 sc->sc_pd.pd_enable = amlpwrc_g12a_enable; 113 else if (OF_is_compatible(faa->fa_node, "amlogic,meson-sm1-pwrc")) 114 sc->sc_pd.pd_enable = amlpwrc_sm1_enable; 115 power_domain_register(&sc->sc_pd); 116 } 117 118 static inline void 119 amlpwrc_toggle(struct regmap *rm, bus_size_t reg, uint32_t mask, int on) 120 { 121 uint32_t val; 122 123 val = regmap_read_4(rm, reg << 2); 124 if (on) 125 val &= ~mask; 126 else 127 val |= mask; 128 regmap_write_4(rm, reg << 2, val); 129 } 130 131 void 132 amlpwrc_g12a_enable(void *cookie, uint32_t *cells, int on) 133 { 134 struct amlpwrc_softc *sc = cookie; 135 uint32_t idx = cells[0]; 136 137 sc->sc_rm_ao = regmap_byphandle(sc->sc_ao); 138 KASSERT(sc->sc_rm_ao); 139 140 switch (idx) { 141 case PWRC_G12A_ETH_ID: 142 amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, 143 HHI_MEM_PD_ETH_MASK, on); 144 delay(20); 145 return; 146 } 147 148 printf("%s: 0x%08x\n", __func__, idx); 149 } 150 151 void 152 amlpwrc_sm1_enable(void *cookie, uint32_t *cells, int on) 153 { 154 struct amlpwrc_softc *sc = cookie; 155 uint32_t idx = cells[0]; 156 157 sc->sc_rm_ao = regmap_byphandle(sc->sc_ao); 158 KASSERT(sc->sc_rm_ao); 159 160 switch (idx) { 161 case PWRC_SM1_USB_ID: 162 amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_SLEEP0, 163 AO_RTI_GEN_PWR_USB_MASK, on); 164 delay(20); 165 amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, 166 HHI_MEM_PD_USB_MASK, on); 167 delay(20); 168 amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_ISO0, 169 AO_RTI_GEN_PWR_USB_MASK, on); 170 return; 171 case PWRC_SM1_PCIE_ID: 172 amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_SLEEP0, 173 AO_RTI_GEN_PWR_PCIE_MASK, on); 174 delay(20); 175 amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, 176 HHI_MEM_PD_PCIE_MASK, on); 177 delay(20); 178 amlpwrc_toggle(sc->sc_rm_ao, AO_RTI_GEN_PWR_ISO0, 179 AO_RTI_GEN_PWR_PCIE_MASK, on); 180 return; 181 case PWRC_SM1_ETH_ID: 182 amlpwrc_toggle(sc->sc_rm_hhi, HHI_MEM_PD_REG0, 183 HHI_MEM_PD_ETH_MASK, on); 184 delay(20); 185 return; 186 } 187 188 printf("%s: 0x%08x\n", __func__, idx); 189 } 190