1 /* $OpenBSD: octcib.c,v 1.5 2019/09/01 12:16:01 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2017, 2019 Visa Hankala 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Driver for Cavium Interrupt Bus (CIB) widget. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <sys/evcount.h> 27 #include <sys/malloc.h> 28 #include <sys/queue.h> 29 30 #include <dev/ofw/fdt.h> 31 #include <dev/ofw/openfirm.h> 32 33 #include <machine/fdt.h> 34 35 #define CIB_HIGHIPL IPL_BIO 36 #define CIB_MAXBITS 64 37 #define CIB_IRQNUM(sc, bit) (256 + (sc)->sc_dev.dv_unit * CIB_MAXBITS + \ 38 (bit)) 39 40 #define CIB_EN_RD(sc) \ 41 bus_space_read_8((sc)->sc_iot, (sc)->sc_en_ioh, 0) 42 #define CIB_EN_WR(sc, val) \ 43 bus_space_write_8((sc)->sc_iot, (sc)->sc_en_ioh, 0, (val)) 44 #define CIB_RAW_RD(sc) \ 45 bus_space_read_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0) 46 #define CIB_RAW_WR(sc, val) \ 47 bus_space_write_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0, (val)) 48 49 struct octcib_softc; 50 51 struct octcib_intrhand { 52 LIST_ENTRY(octcib_intrhand) cih_list; 53 int (*cih_func)(void *); 54 void *cih_arg; 55 uint32_t cih_bit; 56 uint32_t cih_flags; 57 #define CIH_MPSAFE 0x01 58 #define CIH_EDGE 0x02 /* edge-triggered */ 59 struct evcount cih_count; 60 unsigned int cih_irq; /* for cih_count */ 61 struct octcib_softc *cih_sc; 62 }; 63 64 struct octcib_softc { 65 struct device sc_dev; 66 void *sc_ih; 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_en_ioh; 69 bus_space_handle_t sc_raw_ioh; 70 71 LIST_HEAD(, octcib_intrhand) sc_bits[CIB_MAXBITS]; 72 uint32_t sc_maxbits; 73 74 struct intr_controller sc_ic; 75 }; 76 77 int octcib_match(struct device *, void *, void *); 78 void octcib_attach(struct device *, struct device *, void *); 79 80 void *octcib_establish(void *, int, int, int, int (*func)(void *), 81 void *, const char *); 82 void octcib_disestablish(void *); 83 void octcib_intr_barrier(void *); 84 int octcib_intr(void *); 85 86 const struct cfattach octcib_ca = { 87 sizeof(struct octcib_softc), octcib_match, octcib_attach 88 }; 89 90 struct cfdriver octcib_cd = { 91 NULL, "octcib", DV_DULL 92 }; 93 94 int 95 octcib_match(struct device *parent, void *match, void *aux) 96 { 97 struct fdt_attach_args *faa = aux; 98 99 return OF_is_compatible(faa->fa_node, "cavium,octeon-7130-cib"); 100 } 101 102 void 103 octcib_attach(struct device *parent, struct device *self, void *aux) 104 { 105 struct fdt_attach_args *faa = aux; 106 struct octcib_softc *sc = (struct octcib_softc *)self; 107 unsigned int i; 108 109 if (faa->fa_nreg != 2) { 110 printf(": expected 2 IO spaces, got %d\n", faa->fa_nreg); 111 return; 112 } 113 114 sc->sc_iot = faa->fa_iot; 115 sc->sc_maxbits = OF_getpropint(faa->fa_node, "cavium,max-bits", 0); 116 117 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 118 0, &sc->sc_raw_ioh)) { 119 printf(": could not map RAW\n"); 120 goto error; 121 } 122 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size, 123 0, &sc->sc_en_ioh)) { 124 printf(": could not map EN\n"); 125 goto error; 126 } 127 128 /* Disable all interrupts. */ 129 CIB_EN_WR(sc, 0); 130 /* Acknowledge any pending interrupts. */ 131 CIB_RAW_WR(sc, ~0ul); 132 133 sc->sc_ih = octeon_intr_establish_fdt(faa->fa_node, 134 CIB_HIGHIPL | IPL_MPSAFE, octcib_intr, sc, sc->sc_dev.dv_xname); 135 if (sc->sc_ih == NULL) { 136 printf(": failed to register interrupt\n"); 137 goto error; 138 } 139 140 printf(": max-bits %u\n", sc->sc_maxbits); 141 142 for (i = 0; i < CIB_MAXBITS; i++) 143 LIST_INIT(&sc->sc_bits[i]); 144 145 sc->sc_ic.ic_cookie = sc; 146 sc->sc_ic.ic_node = faa->fa_node; 147 sc->sc_ic.ic_establish_fdt_idx = octcib_establish; 148 sc->sc_ic.ic_disestablish = octcib_disestablish; 149 sc->sc_ic.ic_intr_barrier = octcib_intr_barrier; 150 octeon_intr_register(&sc->sc_ic); 151 return; 152 153 error: 154 if (sc->sc_en_ioh != 0) 155 bus_space_unmap(sc->sc_iot, sc->sc_en_ioh, 156 faa->fa_reg[1].size); 157 if (sc->sc_raw_ioh != 0) 158 bus_space_unmap(sc->sc_iot, sc->sc_raw_ioh, 159 faa->fa_reg[0].size); 160 } 161 162 void * 163 octcib_establish(void *cookie, int node, int idx, int level, 164 int (*func)(void *), void *arg, const char *name) 165 { 166 struct octcib_intrhand *cih; 167 struct octcib_softc *sc = cookie; 168 uint64_t en; 169 uint32_t *cells; 170 uint32_t bit, type; 171 int flags, len, s; 172 173 flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0; 174 level &= ~IPL_MPSAFE; 175 176 if (level > CIB_HIGHIPL) 177 return NULL; 178 179 len = OF_getproplen(node, "interrupts"); 180 if (len / (sizeof(uint32_t) * 2) <= idx || 181 len % (sizeof(uint32_t) * 2) != 0) 182 return NULL; 183 184 cells = malloc(len, M_TEMP, M_NOWAIT); 185 if (cells == NULL) 186 return NULL; 187 OF_getpropintarray(node, "interrupts", cells, len); 188 bit = cells[idx * 2]; 189 type = cells[idx * 2 + 1]; 190 free(cells, M_TEMP, len); 191 192 if (bit >= sc->sc_maxbits) 193 return NULL; 194 if (type != 4) 195 flags |= CIH_EDGE; 196 197 cih = malloc(sizeof(*cih), M_DEVBUF, M_NOWAIT); 198 if (cih == NULL) 199 return NULL; 200 cih->cih_func = func; 201 cih->cih_arg = arg; 202 cih->cih_bit = bit; 203 cih->cih_flags = flags; 204 cih->cih_irq = CIB_IRQNUM(sc, bit); 205 cih->cih_sc = sc; 206 207 s = splhigh(); 208 209 evcount_attach(&cih->cih_count, name, &cih->cih_irq); 210 LIST_INSERT_HEAD(&sc->sc_bits[bit], cih, cih_list); 211 212 /* Enable the interrupt. */ 213 en = CIB_EN_RD(sc); 214 en |= 1ul << bit; 215 CIB_EN_WR(sc, en); 216 217 splx(s); 218 219 return cih; 220 } 221 222 void 223 octcib_disestablish(void *cookie) 224 { 225 struct octcib_intrhand *cih = cookie; 226 struct octcib_softc *sc = cih->cih_sc; 227 uint64_t val; 228 uint32_t bit = cih->cih_bit; 229 int s; 230 #ifdef DIAGNOSTIC 231 struct octcib_intrhand *tmp; 232 int found; 233 #endif 234 235 s = splhigh(); 236 237 #ifdef DIAGNOSTIC 238 found = 0; 239 LIST_FOREACH(tmp, &sc->sc_bits[bit], cih_list) { 240 if (tmp == cih) { 241 found = 1; 242 break; 243 } 244 } 245 if (found == 0) 246 panic("%s: intrhand %p not registered", __func__, cih); 247 #endif 248 249 LIST_REMOVE(cih, cih_list); 250 evcount_detach(&cih->cih_count); 251 252 if (LIST_EMPTY(&sc->sc_bits[bit])) { 253 /* Disable the interrupt. */ 254 val = CIB_EN_RD(sc); 255 val &= ~(1ul << bit); 256 CIB_EN_WR(sc, val); 257 } 258 259 splx(s); 260 261 free(cih, M_DEVBUF, sizeof(*cih)); 262 } 263 264 void 265 octcib_intr_barrier(void *cookie) 266 { 267 struct octcib_intrhand *cih = cookie; 268 struct octcib_softc *sc = cih->cih_sc; 269 270 intr_barrier(sc->sc_ih); 271 } 272 273 int 274 octcib_intr(void *arg) 275 { 276 struct octcib_intrhand *cih; 277 struct octcib_softc *sc = arg; 278 uint64_t en, isr, mask; 279 uint32_t bit; 280 int handled = 0; 281 #ifdef MULTIPROCESSOR 282 int need_lock; 283 #endif 284 285 en = CIB_EN_RD(sc); 286 isr = CIB_RAW_RD(sc); 287 isr &= en; 288 289 for (bit = 0; isr != 0 && bit < sc->sc_maxbits; bit++) { 290 mask = 1ul << bit; 291 292 if ((isr & mask) == 0) 293 continue; 294 isr &= ~mask; 295 296 handled = 0; 297 LIST_FOREACH(cih, &sc->sc_bits[bit], cih_list) { 298 /* Acknowledge the interrupt. */ 299 if (ISSET(cih->cih_flags, CIH_EDGE)) 300 CIB_RAW_WR(sc, mask); 301 302 #ifdef MULTIPROCESSOR 303 if (!ISSET(cih->cih_flags, CIH_MPSAFE)) 304 need_lock = 1; 305 else 306 need_lock = 0; 307 if (need_lock) 308 __mp_lock(&kernel_lock); 309 #endif 310 if (cih->cih_func(cih->cih_arg)) { 311 handled = 1; 312 cih->cih_count.ec_count++; 313 } 314 #ifdef MULTIPROCESSOR 315 if (need_lock) 316 __mp_unlock(&kernel_lock); 317 #endif 318 } 319 320 if (handled == 0) 321 printf("%s: spurious interrupt %u (bit %u) " 322 "on cpu %lu\n", 323 sc->sc_dev.dv_xname, CIB_IRQNUM(sc, bit), bit, 324 cpu_number()); 325 } 326 327 return 1; 328 } 329