1 /* $OpenBSD: mvgicp.c,v 1.5 2021/10/24 17:52:26 mpi Exp $ */ 2 /* 3 * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se> 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 <uvm/uvm_extern.h> 24 25 #include <machine/intr.h> 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_misc.h> 31 #include <dev/ofw/fdt.h> 32 33 struct mvgicp_softc { 34 struct device sc_dev; 35 bus_space_tag_t sc_iot; 36 bus_space_handle_t sc_ioh; 37 paddr_t sc_addr; 38 39 uint32_t sc_spi_ranges[4]; 40 void **sc_spi; 41 uint32_t sc_nspi; 42 43 struct interrupt_controller sc_ic; 44 struct interrupt_controller *sc_parent_ic; 45 }; 46 47 int mvgicp_match(struct device *, void *, void *); 48 void mvgicp_attach(struct device *, struct device *, void *); 49 50 void * mvgicp_intr_establish(void *, uint64_t *, uint64_t *, 51 int, struct cpu_info *, int (*)(void *), void *, char *); 52 void mvgicp_intr_disestablish(void *); 53 void mvgicp_intr_barrier(void *); 54 55 const struct cfattach mvgicp_ca = { 56 sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach 57 }; 58 59 struct cfdriver mvgicp_cd = { 60 NULL, "mvgicp", DV_DULL 61 }; 62 63 int 64 mvgicp_match(struct device *parent, void *match, void *aux) 65 { 66 struct fdt_attach_args *faa = aux; 67 68 return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp"); 69 } 70 71 void 72 mvgicp_attach(struct device *parent, struct device *self, void *aux) 73 { 74 struct mvgicp_softc *sc = (struct mvgicp_softc *)self; 75 struct fdt_attach_args *faa = aux; 76 struct interrupt_controller *ic; 77 uint32_t phandle; 78 79 if (faa->fa_nreg < 1) { 80 printf(": no registers\n"); 81 return; 82 } 83 84 OF_getpropintarray(faa->fa_node, "marvell,spi-ranges", 85 sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges)); 86 sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3]; 87 sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *), 88 M_DEVBUF, M_WAITOK | M_ZERO); 89 90 sc->sc_iot = faa->fa_iot; 91 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 92 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 93 printf(": can't map registers\n"); 94 return; 95 } 96 97 /* XXX: Hack to retrieve the physical address (from a CPU PoV). */ 98 if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) { 99 printf(": cannot retrieve msi addr\n"); 100 return; 101 } 102 103 extern uint32_t fdt_intr_get_parent(int); 104 phandle = fdt_intr_get_parent(faa->fa_node); 105 extern LIST_HEAD(, interrupt_controller) interrupt_controllers; 106 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 107 if (ic->ic_phandle == phandle) 108 break; 109 } 110 sc->sc_parent_ic = ic; 111 112 sc->sc_ic.ic_node = faa->fa_node; 113 sc->sc_ic.ic_cookie = sc; 114 sc->sc_ic.ic_establish_msi = mvgicp_intr_establish; 115 sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish; 116 sc->sc_ic.ic_barrier = mvgicp_intr_barrier; 117 fdt_intr_register(&sc->sc_ic); 118 119 printf("\n"); 120 } 121 122 void * 123 mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data, 124 int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 125 { 126 struct mvgicp_softc *sc = (struct mvgicp_softc *)self; 127 struct interrupt_controller *ic = sc->sc_parent_ic; 128 struct machine_intr_handle *ih; 129 uint32_t interrupt[3]; 130 uint32_t flags; 131 void *cookie; 132 int i, spi; 133 134 if (ic == NULL) 135 return NULL; 136 137 for (i = 0; i < sc->sc_nspi; i++) { 138 if (sc->sc_spi[i] == NULL) { 139 spi = i; 140 break; 141 } 142 } 143 if (i == sc->sc_nspi) 144 return NULL; 145 146 flags = *data; 147 148 *addr = sc->sc_addr; 149 *data = spi; 150 151 /* Convert to GIC interrupt source. */ 152 for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) { 153 if (spi < sc->sc_spi_ranges[i + 1]) { 154 spi += sc->sc_spi_ranges[i]; 155 break; 156 } 157 spi -= sc->sc_spi_ranges[i + 1]; 158 } 159 if (i == nitems(sc->sc_spi_ranges)) 160 return NULL; 161 162 interrupt[0] = 0; 163 interrupt[1] = spi - 32; 164 interrupt[2] = flags; 165 cookie = ic->ic_establish(ic->ic_cookie, interrupt, level, 166 ci, func, arg, name); 167 if (cookie == NULL) 168 return NULL; 169 170 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 171 ih->ih_ic = ic; 172 ih->ih_ih = cookie; 173 174 sc->sc_spi[*data] = ih; 175 return &sc->sc_spi[*data]; 176 } 177 178 void 179 mvgicp_intr_disestablish(void *cookie) 180 { 181 struct machine_intr_handle *ih = *(void **)cookie; 182 struct interrupt_controller *ic = ih->ih_ic; 183 184 ic->ic_disestablish(ih->ih_ih); 185 free(ih, M_DEVBUF, sizeof(*ih)); 186 *(void **)cookie = NULL; 187 } 188 189 void 190 mvgicp_intr_barrier(void *cookie) 191 { 192 struct machine_intr_handle *ih = *(void **)cookie; 193 struct interrupt_controller *ic = ih->ih_ic; 194 195 ic->ic_barrier(ih->ih_ih); 196 } 197