1 /* $OpenBSD: mvicu.c,v 1.4 2019/02/03 14:03:36 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, int (*)(void *), 97 void *, char *); 98 void mvicu_intr_disestablish(void *); 99 100 int 101 mvicu_match(struct device *parent, void *match, void *aux) 102 { 103 struct fdt_attach_args *faa = aux; 104 105 return OF_is_compatible(faa->fa_node, "marvell,cp110-icu"); 106 } 107 108 void 109 mvicu_attach(struct device *parent, struct device *self, void *aux) 110 { 111 struct mvicu_softc *sc = (struct mvicu_softc *)self; 112 struct fdt_attach_args *faa = aux; 113 int i, node, nchildren; 114 115 if (faa->fa_nreg < 1) { 116 printf(": no registers\n"); 117 return; 118 } 119 120 sc->sc_iot = faa->fa_iot; 121 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 122 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 123 printf(": can't map registers\n"); 124 return; 125 } 126 127 printf("\n"); 128 129 if (OF_child(faa->fa_node) == 0) { 130 sc->sc_legacy = 1; 131 sc->sc_nodes = mallocarray(1, sizeof(*sc->sc_nodes), 132 M_DEVBUF, M_WAITOK | M_ZERO); 133 mvicu_register(sc, faa->fa_node, 0); 134 } else { 135 for (node = OF_child(faa->fa_node), nchildren = 0; 136 node; node = OF_peer(node)) 137 nchildren++; 138 sc->sc_nodes = mallocarray(nchildren, sizeof(*sc->sc_nodes), 139 M_DEVBUF, M_WAITOK | M_ZERO); 140 for (node = OF_child(faa->fa_node), i = 0; node; 141 node = OF_peer(node)) 142 mvicu_register(sc, node, i++); 143 } 144 } 145 146 void 147 mvicu_register(struct mvicu_softc *sc, int node, int idx) 148 { 149 struct mvicu_subnode *sn = &sc->sc_nodes[idx]; 150 struct interrupt_controller *ic; 151 uint32_t phandle = 0; 152 uint32_t group; 153 int i; 154 155 sn->sn_group = -1; 156 if (OF_is_compatible(node, "marvell,cp110-icu") || 157 OF_is_compatible(node, "marvell,cp110-icu-nsr")) 158 sn->sn_group = ICU_GRP_NSR; 159 if (OF_is_compatible(node, "marvell,cp110-icu-sei")) 160 sn->sn_group = ICU_GRP_SEI; 161 162 for (i = 0; i < ICU_DEVICE_NIRQ; i++) { 163 group = HREAD4(sc, ICU_INT_CFG(i)) >> ICU_INT_GROUP_SHIFT; 164 if ((sn->sn_group == ICU_GRP_NSR && group == ICU_GRP_NSR) || 165 (sn->sn_group == ICU_GRP_SEI && group == ICU_GRP_SEI)) 166 HWRITE4(sc, ICU_INT_CFG(i), 0); 167 } 168 169 sn->sn_sc = sc; 170 sn->sn_ic.ic_node = node; 171 sn->sn_ic.ic_cookie = sn; 172 sn->sn_ic.ic_establish = mvicu_intr_establish; 173 sn->sn_ic.ic_disestablish = mvicu_intr_disestablish; 174 175 while (node && !phandle) { 176 phandle = OF_getpropint(node, "msi-parent", 0); 177 node = OF_parent(node); 178 } 179 if (phandle == 0) 180 return; 181 182 extern LIST_HEAD(, interrupt_controller) interrupt_controllers; 183 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 184 if (ic->ic_phandle == phandle) 185 break; 186 } 187 if (ic == NULL) 188 return; 189 190 sn->sn_parent_ic = ic; 191 fdt_intr_register(&sn->sn_ic); 192 } 193 194 void * 195 mvicu_intr_establish(void *cookie, int *cell, int level, 196 int (*func)(void *), void *arg, char *name) 197 { 198 struct mvicu_subnode *sn = cookie; 199 struct mvicu_softc *sc = sn->sn_sc; 200 struct interrupt_controller *ic = sn->sn_parent_ic; 201 uint32_t idx, flags; 202 uint64_t addr, data; 203 int edge = 0; 204 205 if (sc->sc_legacy) { 206 if (cell[0] != ICU_GRP_NSR) 207 return NULL; 208 idx = cell[1]; 209 flags = cell[2]; 210 edge = ((flags & 0xf) == 0x1); 211 } else if (sn->sn_group == ICU_GRP_NSR) { 212 idx = cell[0]; 213 flags = cell[1]; 214 edge = ((flags & 0xf) == 0x1); 215 } else if (sn->sn_group == ICU_GRP_SEI) { 216 idx = cell[0]; 217 flags = cell[1]; 218 edge = 1; 219 } else { 220 return NULL; 221 } 222 223 data = flags; 224 cookie = ic->ic_establish_msi(ic->ic_cookie, &addr, &data, 225 level, func, arg, name); 226 if (cookie == NULL) 227 return NULL; 228 229 if (sn->sn_group == ICU_GRP_NSR && !sc->sc_nsr_addr) { 230 sc->sc_nsr_addr = addr; 231 HWRITE4(sc, ICU_SETSPI_NSR_AL, 232 (addr + GICP_SETSPI_NSR) & 0xffffffff); 233 HWRITE4(sc, ICU_SETSPI_NSR_AH, 234 (addr + GICP_SETSPI_NSR) >> 32); 235 HWRITE4(sc, ICU_CLRSPI_NSR_AL, 236 (addr + GICP_CLRSPI_NSR) & 0xffffffff); 237 HWRITE4(sc, ICU_CLRSPI_NSR_AH, 238 (addr + GICP_CLRSPI_NSR) >> 32); 239 } 240 241 if (sn->sn_group == ICU_GRP_SEI && !sc->sc_sei_addr) { 242 sc->sc_sei_addr = addr; 243 HWRITE4(sc, ICU_SET_SEI_AL, addr & 0xffffffff); 244 HWRITE4(sc, ICU_SET_SEI_AH, addr >> 32); 245 } 246 247 /* Configure ICU. */ 248 HWRITE4(sc, ICU_INT_CFG(idx), data | ICU_INT_ENABLE | 249 (sn->sn_group << ICU_INT_GROUP_SHIFT) | (edge ? ICU_INT_EDGE : 0)); 250 251 /* Need to configure interrupt for both SATA ports. */ 252 if (idx == ICU_DEVICE_SATA0 || idx == ICU_DEVICE_SATA1) { 253 HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA0), data | 254 ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) | 255 (edge ? ICU_INT_EDGE : 0)); 256 HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA1), data | 257 ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) | 258 (edge ? ICU_INT_EDGE : 0)); 259 } 260 261 return cookie; 262 } 263 264 void 265 mvicu_intr_disestablish(void *cookie) 266 { 267 panic("%s", __func__); 268 } 269