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