1 /* $OpenBSD: mvicu.c,v 1.6 2020/07/17 08:07:34 patrick Exp $ */ 2 /* 3 * Copyright (c) 2018 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/intr.h> 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/fdt.h> 29 30 /* Registers. */ 31 #define ICU_SETSPI_NSR_AL 0x10 32 #define ICU_SETSPI_NSR_AH 0x14 33 #define ICU_CLRSPI_NSR_AL 0x18 34 #define ICU_CLRSPI_NSR_AH 0x1c 35 #define ICU_SET_SEI_AL 0x50 36 #define ICU_SET_SEI_AH 0x54 37 #define ICU_CLR_SEI_AL 0x58 38 #define ICU_CLR_SEI_AH 0x5c 39 #define ICU_INT_CFG(x) (0x100 + (x) * 4) 40 #define ICU_INT_ENABLE (1 << 24) 41 #define ICU_INT_EDGE (1 << 28) 42 #define ICU_INT_GROUP_SHIFT 29 43 #define ICU_INT_MASK 0x3ff 44 45 #define GICP_SETSPI_NSR 0x00 46 #define GICP_CLRSPI_NSR 0x08 47 48 /* Devices */ 49 #define ICU_DEVICE_SATA0 109 50 #define ICU_DEVICE_SATA1 107 51 #define ICU_DEVICE_NIRQ 207 52 53 /* Groups. */ 54 #define ICU_GRP_NSR 0x0 55 #define ICU_GRP_SR 0x1 56 #define ICU_GRP_SEI 0x4 57 #define ICU_GRP_REI 0x5 58 59 #define HREAD4(sc, reg) \ 60 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 61 #define HWRITE4(sc, reg, val) \ 62 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 63 64 struct mvicu_softc; 65 struct mvicu_subnode { 66 struct mvicu_softc *sn_sc; 67 int sn_group; 68 struct interrupt_controller sn_ic; 69 struct interrupt_controller *sn_parent_ic; 70 }; 71 72 struct mvicu_softc { 73 struct device sc_dev; 74 bus_space_tag_t sc_iot; 75 bus_space_handle_t sc_ioh; 76 77 uint64_t sc_nsr_addr; 78 uint64_t sc_sei_addr; 79 80 int sc_legacy; 81 struct mvicu_subnode *sc_nodes; 82 }; 83 84 int mvicu_match(struct device *, void *, void *); 85 void mvicu_attach(struct device *, struct device *, void *); 86 87 struct cfattach mvicu_ca = { 88 sizeof (struct mvicu_softc), mvicu_match, mvicu_attach 89 }; 90 91 struct cfdriver mvicu_cd = { 92 NULL, "mvicu", DV_DULL 93 }; 94 95 void mvicu_register(struct mvicu_softc *, int, int); 96 void *mvicu_intr_establish(void *, int *, int, struct cpu_info *, 97 int (*)(void *), void *, char *); 98 void mvicu_intr_disestablish(void *); 99 void mvicu_intr_barrier(void *); 100 101 int 102 mvicu_match(struct device *parent, void *match, void *aux) 103 { 104 struct fdt_attach_args *faa = aux; 105 106 return OF_is_compatible(faa->fa_node, "marvell,cp110-icu"); 107 } 108 109 void 110 mvicu_attach(struct device *parent, struct device *self, void *aux) 111 { 112 struct mvicu_softc *sc = (struct mvicu_softc *)self; 113 struct fdt_attach_args *faa = aux; 114 int i, node, nchildren; 115 116 if (faa->fa_nreg < 1) { 117 printf(": no registers\n"); 118 return; 119 } 120 121 sc->sc_iot = faa->fa_iot; 122 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 123 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 124 printf(": can't map registers\n"); 125 return; 126 } 127 128 printf("\n"); 129 130 if (OF_child(faa->fa_node) == 0) { 131 sc->sc_legacy = 1; 132 sc->sc_nodes = mallocarray(1, sizeof(*sc->sc_nodes), 133 M_DEVBUF, M_WAITOK | M_ZERO); 134 mvicu_register(sc, faa->fa_node, 0); 135 } else { 136 for (node = OF_child(faa->fa_node), nchildren = 0; 137 node; node = OF_peer(node)) 138 nchildren++; 139 sc->sc_nodes = mallocarray(nchildren, sizeof(*sc->sc_nodes), 140 M_DEVBUF, M_WAITOK | M_ZERO); 141 for (node = OF_child(faa->fa_node), i = 0; node; 142 node = OF_peer(node)) 143 mvicu_register(sc, node, i++); 144 } 145 } 146 147 void 148 mvicu_register(struct mvicu_softc *sc, int node, int idx) 149 { 150 struct mvicu_subnode *sn = &sc->sc_nodes[idx]; 151 struct interrupt_controller *ic; 152 uint32_t phandle = 0; 153 uint32_t group; 154 int i; 155 156 sn->sn_group = -1; 157 if (OF_is_compatible(node, "marvell,cp110-icu") || 158 OF_is_compatible(node, "marvell,cp110-icu-nsr")) 159 sn->sn_group = ICU_GRP_NSR; 160 if (OF_is_compatible(node, "marvell,cp110-icu-sei")) 161 sn->sn_group = ICU_GRP_SEI; 162 163 for (i = 0; i < ICU_DEVICE_NIRQ; i++) { 164 group = HREAD4(sc, ICU_INT_CFG(i)) >> ICU_INT_GROUP_SHIFT; 165 if ((sn->sn_group == ICU_GRP_NSR && group == ICU_GRP_NSR) || 166 (sn->sn_group == ICU_GRP_SEI && group == ICU_GRP_SEI)) 167 HWRITE4(sc, ICU_INT_CFG(i), 0); 168 } 169 170 sn->sn_sc = sc; 171 sn->sn_ic.ic_node = node; 172 sn->sn_ic.ic_cookie = sn; 173 sn->sn_ic.ic_establish = mvicu_intr_establish; 174 sn->sn_ic.ic_disestablish = mvicu_intr_disestablish; 175 sn->sn_ic.ic_barrier = mvicu_intr_barrier; 176 177 while (node && !phandle) { 178 phandle = OF_getpropint(node, "msi-parent", 0); 179 node = OF_parent(node); 180 } 181 if (phandle == 0) 182 return; 183 184 extern LIST_HEAD(, interrupt_controller) interrupt_controllers; 185 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 186 if (ic->ic_phandle == phandle) 187 break; 188 } 189 if (ic == NULL) 190 return; 191 192 sn->sn_parent_ic = ic; 193 fdt_intr_register(&sn->sn_ic); 194 } 195 196 void * 197 mvicu_intr_establish(void *cookie, int *cell, int level, 198 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 199 { 200 struct mvicu_subnode *sn = cookie; 201 struct mvicu_softc *sc = sn->sn_sc; 202 struct interrupt_controller *ic = sn->sn_parent_ic; 203 struct arm_intr_handle *ih; 204 uint32_t idx, flags; 205 uint64_t addr, data; 206 int edge = 0; 207 208 if (sc->sc_legacy) { 209 if (cell[0] != ICU_GRP_NSR) 210 return NULL; 211 idx = cell[1]; 212 flags = cell[2]; 213 edge = ((flags & 0xf) == 0x1); 214 } else if (sn->sn_group == ICU_GRP_NSR) { 215 idx = cell[0]; 216 flags = cell[1]; 217 edge = ((flags & 0xf) == 0x1); 218 } else if (sn->sn_group == ICU_GRP_SEI) { 219 idx = cell[0]; 220 flags = cell[1]; 221 edge = 1; 222 } else { 223 return NULL; 224 } 225 226 data = flags; 227 cookie = ic->ic_establish_msi(ic->ic_cookie, &addr, &data, 228 level, ci, func, arg, name); 229 if (cookie == NULL) 230 return NULL; 231 232 if (sn->sn_group == ICU_GRP_NSR && !sc->sc_nsr_addr) { 233 sc->sc_nsr_addr = addr; 234 HWRITE4(sc, ICU_SETSPI_NSR_AL, 235 (addr + GICP_SETSPI_NSR) & 0xffffffff); 236 HWRITE4(sc, ICU_SETSPI_NSR_AH, 237 (addr + GICP_SETSPI_NSR) >> 32); 238 HWRITE4(sc, ICU_CLRSPI_NSR_AL, 239 (addr + GICP_CLRSPI_NSR) & 0xffffffff); 240 HWRITE4(sc, ICU_CLRSPI_NSR_AH, 241 (addr + GICP_CLRSPI_NSR) >> 32); 242 } 243 244 if (sn->sn_group == ICU_GRP_SEI && !sc->sc_sei_addr) { 245 sc->sc_sei_addr = addr; 246 HWRITE4(sc, ICU_SET_SEI_AL, addr & 0xffffffff); 247 HWRITE4(sc, ICU_SET_SEI_AH, addr >> 32); 248 } 249 250 /* Configure ICU. */ 251 HWRITE4(sc, ICU_INT_CFG(idx), data | ICU_INT_ENABLE | 252 (sn->sn_group << ICU_INT_GROUP_SHIFT) | (edge ? ICU_INT_EDGE : 0)); 253 254 /* Need to configure interrupt for both SATA ports. */ 255 if (idx == ICU_DEVICE_SATA0 || idx == ICU_DEVICE_SATA1) { 256 HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA0), data | 257 ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) | 258 (edge ? ICU_INT_EDGE : 0)); 259 HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA1), data | 260 ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) | 261 (edge ? ICU_INT_EDGE : 0)); 262 } 263 264 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 265 ih->ih_ic = ic; 266 ih->ih_ih = cookie; 267 268 return ih; 269 } 270 271 void 272 mvicu_intr_disestablish(void *cookie) 273 { 274 panic("%s", __func__); 275 } 276 277 void 278 mvicu_intr_barrier(void *cookie) 279 { 280 struct arm_intr_handle *ih = cookie; 281 struct interrupt_controller *ic = ih->ih_ic; 282 283 ic->ic_barrier(ih->ih_ih); 284 } 285