1 /* $OpenBSD: mvgicp.c,v 1.1 2019/02/03 14:03:36 patrick 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, int (*)(void *), void *, char *); 52 void mvgicp_intr_disestablish(void *); 53 54 struct cfattach mvgicp_ca = { 55 sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach 56 }; 57 58 struct cfdriver mvgicp_cd = { 59 NULL, "mvgicp", DV_DULL 60 }; 61 62 int 63 mvgicp_match(struct device *parent, void *match, void *aux) 64 { 65 struct fdt_attach_args *faa = aux; 66 67 return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp"); 68 } 69 70 void 71 mvgicp_attach(struct device *parent, struct device *self, void *aux) 72 { 73 struct mvgicp_softc *sc = (struct mvgicp_softc *)self; 74 struct fdt_attach_args *faa = aux; 75 struct interrupt_controller *ic; 76 uint32_t phandle; 77 78 if (faa->fa_nreg < 1) { 79 printf(": no registers\n"); 80 return; 81 } 82 83 OF_getpropintarray(faa->fa_node, "marvell,spi-ranges", 84 sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges)); 85 sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3]; 86 sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *), 87 M_DEVBUF, M_WAITOK | M_ZERO); 88 89 sc->sc_iot = faa->fa_iot; 90 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 91 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 92 printf(": can't map registers\n"); 93 return; 94 } 95 96 /* XXX: Hack to retrieve the physical address (from a CPU PoV). */ 97 if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) { 98 printf(": cannot retrieve msi addr\n"); 99 return; 100 } 101 102 extern uint32_t fdt_intr_get_parent(int); 103 phandle = fdt_intr_get_parent(faa->fa_node); 104 extern LIST_HEAD(, interrupt_controller) interrupt_controllers; 105 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 106 if (ic->ic_phandle == phandle) 107 break; 108 } 109 sc->sc_parent_ic = ic; 110 111 sc->sc_ic.ic_node = faa->fa_node; 112 sc->sc_ic.ic_cookie = sc; 113 sc->sc_ic.ic_establish_msi = mvgicp_intr_establish; 114 sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish; 115 fdt_intr_register(&sc->sc_ic); 116 117 printf("\n"); 118 } 119 120 void * 121 mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data, 122 int level, int (*func)(void *), void *arg, char *name) 123 { 124 struct mvgicp_softc *sc = (struct mvgicp_softc *)self; 125 struct interrupt_controller *ic = sc->sc_parent_ic; 126 uint32_t interrupt[3]; 127 uint32_t flags; 128 void *cookie; 129 int i, spi; 130 131 if (ic == NULL) 132 return NULL; 133 134 for (i = 0; i < sc->sc_nspi; i++) { 135 if (sc->sc_spi[i] == NULL) { 136 spi = i; 137 break; 138 } 139 } 140 if (i == sc->sc_nspi) 141 return NULL; 142 143 flags = *data; 144 145 *addr = sc->sc_addr; 146 *data = spi; 147 148 /* Convert to GIC interrupt source. */ 149 for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) { 150 if (spi < sc->sc_spi_ranges[i + 1]) { 151 spi += sc->sc_spi_ranges[i]; 152 break; 153 } 154 spi -= sc->sc_spi_ranges[i + 1]; 155 } 156 if (i == nitems(sc->sc_spi_ranges)) 157 return NULL; 158 159 interrupt[0] = 0; 160 interrupt[1] = spi - 32; 161 interrupt[2] = flags; 162 cookie = ic->ic_establish(ic->ic_cookie, interrupt, level, 163 func, arg, name); 164 if (cookie == NULL) 165 return NULL; 166 167 sc->sc_spi[*data] = cookie; 168 return &sc->sc_spi[*data]; 169 } 170 171 void 172 mvgicp_intr_disestablish(void *cookie) 173 { 174 fdt_intr_disestablish(*(void **)cookie); 175 *(void **)cookie = NULL; 176 } 177