1 /* $OpenBSD: octcib.c,v 1.2 2017/07/31 14:46:14 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2017 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/malloc.h> 27 #include <sys/queue.h> 28 29 #include <dev/ofw/fdt.h> 30 #include <dev/ofw/openfirm.h> 31 32 #include <machine/fdt.h> 33 34 #define CIB_HIGHIPL IPL_BIO 35 #define CIB_MAXBITS 64 36 #define CIB_IRQNUM(sc, bit) (256 + (sc)->sc_dev.dv_unit * CIB_MAXBITS + \ 37 (bit)) 38 39 #define CIB_EN_RD(sc) \ 40 bus_space_read_8((sc)->sc_iot, (sc)->sc_en_ioh, 0) 41 #define CIB_EN_WR(sc, val) \ 42 bus_space_write_8((sc)->sc_iot, (sc)->sc_en_ioh, 0, (val)) 43 #define CIB_RAW_RD(sc) \ 44 bus_space_read_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0) 45 #define CIB_RAW_WR(sc, val) \ 46 bus_space_write_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0, (val)) 47 48 struct octcib_softc; 49 50 struct octcib_intrhand { 51 LIST_ENTRY(octcib_intrhand) cih_list; 52 int (*cih_func)(void *); 53 void *cih_arg; 54 uint32_t cih_bit; 55 uint32_t cih_flags; 56 #define CIH_MPSAFE 0x01 57 #define CIH_EDGE 0x02 /* edge-triggered */ 58 struct evcount cih_count; 59 unsigned int cih_irq; /* for cih_count */ 60 struct octcib_softc *cih_sc; 61 }; 62 63 struct octcib_softc { 64 struct device sc_dev; 65 void *sc_ih; 66 bus_space_tag_t sc_iot; 67 bus_space_handle_t sc_en_ioh; 68 bus_space_handle_t sc_raw_ioh; 69 70 LIST_HEAD(, octcib_intrhand) sc_bits[CIB_MAXBITS]; 71 uint32_t sc_maxbits; 72 73 struct intr_controller sc_ic; 74 }; 75 76 int octcib_match(struct device *, void *, void *); 77 void octcib_attach(struct device *, struct device *, void *); 78 79 void *octcib_establish(void *, int, int, int, int (*func)(void *), 80 void *, const char *); 81 void octcib_disestablish(void *); 82 int octcib_intr(void *); 83 84 const struct cfattach octcib_ca = { 85 sizeof(struct octcib_softc), octcib_match, octcib_attach 86 }; 87 88 struct cfdriver octcib_cd = { 89 NULL, "octcib", DV_DULL 90 }; 91 92 int 93 octcib_match(struct device *parent, void *match, void *aux) 94 { 95 struct fdt_attach_args *faa = aux; 96 97 return OF_is_compatible(faa->fa_node, "cavium,octeon-7130-cib"); 98 } 99 100 void 101 octcib_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct fdt_attach_args *faa = aux; 104 struct octcib_softc *sc = (struct octcib_softc *)self; 105 unsigned int i; 106 107 if (faa->fa_nreg != 2) { 108 printf(": expected 2 IO spaces, got %d\n", faa->fa_nreg); 109 return; 110 } 111 112 sc->sc_iot = faa->fa_iot; 113 sc->sc_maxbits = OF_getpropint(faa->fa_node, "cavium,max-bits", 0); 114 115 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 116 0, &sc->sc_raw_ioh)) { 117 printf(": could not map RAW\n"); 118 goto error; 119 } 120 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size, 121 0, &sc->sc_en_ioh)) { 122 printf(": could not map EN\n"); 123 goto error; 124 } 125 126 /* Disable all interrupts. */ 127 CIB_EN_WR(sc, 0); 128 /* Acknowledge any pending interrupts. */ 129 CIB_RAW_WR(sc, ~0ul); 130 131 sc->sc_ih = octeon_intr_establish_fdt(faa->fa_node, 132 CIB_HIGHIPL | IPL_MPSAFE, octcib_intr, sc, sc->sc_dev.dv_xname); 133 if (sc->sc_ih == NULL) { 134 printf(": failed to register interrupt\n"); 135 goto error; 136 } 137 138 printf(": max-bits %u\n", sc->sc_maxbits); 139 140 for (i = 0; i < CIB_MAXBITS; i++) 141 LIST_INIT(&sc->sc_bits[i]); 142 143 sc->sc_ic.ic_cookie = sc; 144 sc->sc_ic.ic_node = faa->fa_node; 145 sc->sc_ic.ic_establish_fdt_idx = octcib_establish; 146 sc->sc_ic.ic_disestablish = octcib_disestablish; 147 octeon_intr_register(&sc->sc_ic); 148 return; 149 150 error: 151 if (sc->sc_en_ioh != 0) 152 bus_space_unmap(sc->sc_iot, sc->sc_en_ioh, 153 faa->fa_reg[1].size); 154 if (sc->sc_raw_ioh != 0) 155 bus_space_unmap(sc->sc_iot, sc->sc_raw_ioh, 156 faa->fa_reg[0].size); 157 } 158 159 void * 160 octcib_establish(void *cookie, int node, int idx, int level, 161 int (*func)(void *), void *arg, const char *name) 162 { 163 struct octcib_intrhand *cih; 164 struct octcib_softc *sc = cookie; 165 uint64_t en; 166 uint32_t *cells; 167 uint32_t bit, type; 168 int flags, len, s; 169 170 flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0; 171 level &= ~IPL_MPSAFE; 172 173 if (level > CIB_HIGHIPL) 174 return NULL; 175 176 len = OF_getproplen(node, "interrupts"); 177 if (len / (sizeof(uint32_t) * 2) <= idx || 178 len % (sizeof(uint32_t) * 2) != 0) 179 return NULL; 180 181 cells = malloc(len, M_TEMP, M_NOWAIT); 182 if (cells == NULL) 183 return NULL; 184 OF_getpropintarray(node, "interrupts", cells, len); 185 bit = cells[idx * 2]; 186 type = cells[idx * 2 + 1]; 187 free(cells, M_TEMP, len); 188 189 if (bit >= sc->sc_maxbits) 190 return NULL; 191 if (type != 4) 192 flags |= CIH_EDGE; 193 194 cih = malloc(sizeof(*cih), M_DEVBUF, M_NOWAIT); 195 if (cih == NULL) 196 return NULL; 197 cih->cih_func = func; 198 cih->cih_arg = arg; 199 cih->cih_bit = bit; 200 cih->cih_flags = flags; 201 cih->cih_irq = CIB_IRQNUM(sc, bit); 202 cih->cih_sc = sc; 203 204 s = splhigh(); 205 206 evcount_attach(&cih->cih_count, name, &cih->cih_irq); 207 LIST_INSERT_HEAD(&sc->sc_bits[bit], cih, cih_list); 208 209 /* Enable the interrupt. */ 210 en = CIB_EN_RD(sc); 211 en |= 1ul << bit; 212 CIB_EN_WR(sc, en); 213 214 splx(s); 215 216 return cih; 217 } 218 219 void 220 octcib_disestablish(void *cookie) 221 { 222 struct octcib_intrhand *cih = cookie; 223 struct octcib_softc *sc = cih->cih_sc; 224 uint64_t val; 225 uint32_t bit = cih->cih_bit; 226 int s; 227 #ifdef DIAGNOSTIC 228 struct octcib_intrhand *tmp; 229 int found; 230 #endif 231 232 s = splhigh(); 233 234 #ifdef DIAGNOSTIC 235 found = 0; 236 LIST_FOREACH(tmp, &sc->sc_bits[bit], cih_list) { 237 if (tmp == cih) { 238 found = 1; 239 break; 240 } 241 } 242 if (found == 0) 243 panic("%s: intrhand %p not registered", __func__, cih); 244 #endif 245 246 LIST_REMOVE(cih, cih_list); 247 evcount_detach(&cih->cih_count); 248 249 if (LIST_EMPTY(&sc->sc_bits[bit])) { 250 /* Disable the interrupt. */ 251 val = CIB_EN_RD(sc); 252 val &= ~(1ul << bit); 253 CIB_EN_WR(sc, val); 254 } 255 256 splx(s); 257 258 free(cih, M_DEVBUF, sizeof(*cih)); 259 } 260 261 int 262 octcib_intr(void *arg) 263 { 264 struct octcib_intrhand *cih; 265 struct octcib_softc *sc = arg; 266 uint64_t en, isr, mask; 267 uint32_t bit; 268 int handled = 0; 269 #ifdef MULTIPROCESSOR 270 int need_lock; 271 #endif 272 273 en = CIB_EN_RD(sc); 274 isr = CIB_RAW_RD(sc); 275 isr &= en; 276 277 for (bit = 0; isr != 0 && bit < sc->sc_maxbits; bit++) { 278 mask = 1ul << bit; 279 280 if ((isr & mask) == 0) 281 continue; 282 isr &= ~mask; 283 284 handled = 0; 285 LIST_FOREACH(cih, &sc->sc_bits[bit], cih_list) { 286 /* Acknowledge the interrupt. */ 287 if (ISSET(cih->cih_flags, CIH_EDGE)) 288 CIB_RAW_WR(sc, mask); 289 290 #ifdef MULTIPROCESSOR 291 if (!ISSET(cih->cih_flags, CIH_MPSAFE)) 292 need_lock = 1; 293 else 294 need_lock = 0; 295 if (need_lock) 296 __mp_lock(&kernel_lock); 297 #endif 298 if (cih->cih_func(cih->cih_arg)) { 299 handled = 1; 300 cih->cih_count.ec_count++; 301 } 302 #ifdef MULTIPROCESSOR 303 if (need_lock) 304 __mp_unlock(&kernel_lock); 305 #endif 306 } 307 308 if (handled == 0) 309 printf("%s: spurious interrupt %u (bit %u)\n", 310 sc->sc_dev.dv_xname, CIB_IRQNUM(sc, bit), bit); 311 } 312 313 return 1; 314 } 315