1 /* $OpenBSD: apic.c,v 1.7 2007/10/06 23:50:54 krw Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Michael Shalayeff 5 * Copyright (c) 2007 Mark Kettenis 6 * All rights reserved. 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN 17 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 18 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/evcount.h> 24 #include <sys/malloc.h> 25 26 #include <machine/autoconf.h> 27 #include <machine/pdc.h> 28 29 #include <dev/pci/pcireg.h> 30 #include <dev/pci/pcivar.h> 31 #include <dev/pci/pcidevs.h> 32 33 #include <hppa/dev/elroyreg.h> 34 #include <hppa/dev/elroyvar.h> 35 36 #define APIC_INT_LINE_MASK 0x0000ff00 37 #define APIC_INT_LINE_SHIFT 8 38 #define APIC_INT_IRQ_MASK 0x0000001f 39 40 #define APIC_INT_LINE(x) (((x) & APIC_INT_LINE_MASK) >> APIC_INT_LINE_SHIFT) 41 #define APIC_INT_IRQ(x) ((x) & APIC_INT_IRQ_MASK) 42 43 /* 44 * Interrupt types match the Intel MP Specification. 45 */ 46 47 #define MPS_INTPO_DEF 0 48 #define MPS_INTPO_ACTHI 1 49 #define MPS_INTPO_ACTLO 3 50 #define MPS_INTPO_SHIFT 0 51 #define MPS_INTPO_MASK 3 52 53 #define MPS_INTTR_DEF 0 54 #define MPS_INTTR_EDGE 1 55 #define MPS_INTTR_LEVEL 3 56 #define MPS_INTTR_SHIFT 2 57 #define MPS_INTTR_MASK 3 58 59 #define MPS_INT(p,t) \ 60 ((((p) & MPS_INTPO_MASK) << MPS_INTPO_SHIFT) | \ 61 (((t) & MPS_INTTR_MASK) << MPS_INTTR_SHIFT)) 62 63 struct apic_iv { 64 struct elroy_softc *sc; 65 pci_intr_handle_t ih; 66 int (*handler)(void *); 67 void *arg; 68 struct apic_iv *next; 69 struct evcount *cnt; 70 }; 71 72 struct apic_iv *apic_intr_list[CPU_NINTS]; 73 74 void apic_get_int_tbl(struct elroy_softc *); 75 u_int32_t apic_get_int_ent0(struct elroy_softc *, int); 76 #ifdef DEBUG 77 void apic_dump(struct elroy_softc *); 78 #endif 79 80 void 81 apic_write(volatile struct elroy_regs *r, u_int32_t reg, u_int32_t val) 82 { 83 elroy_write32(&r->apic_addr, htole32(reg)); 84 elroy_write32(&r->apic_data, htole32(val)); 85 elroy_read32(&r->apic_data); 86 } 87 88 u_int32_t 89 apic_read(volatile struct elroy_regs *r, u_int32_t reg) 90 { 91 elroy_write32(&r->apic_addr, htole32(reg)); 92 return letoh32(elroy_read32(&r->apic_data)); 93 } 94 95 void 96 apic_attach(struct elroy_softc *sc) 97 { 98 volatile struct elroy_regs *r = sc->sc_regs; 99 u_int32_t data; 100 101 data = apic_read(r, APIC_VERSION); 102 sc->sc_nints = (data & APIC_VERSION_NENT) >> APIC_VERSION_NENT_SHIFT; 103 printf(" APIC ver %x, %d pins", 104 data & APIC_VERSION_MASK, sc->sc_nints); 105 106 sc->sc_irq = malloc(sc->sc_nints * sizeof(int), M_DEVBUF, 107 M_NOWAIT | M_ZERO); 108 if (sc->sc_irq == NULL) 109 panic("apic_attach: cannot allocate irq table\n"); 110 111 apic_get_int_tbl(sc); 112 113 #ifdef DEBUG 114 apic_dump(sc); 115 #endif 116 } 117 118 int 119 apic_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) 120 { 121 struct elroy_softc *sc = pa->pa_pc->_cookie; 122 pci_chipset_tag_t pc = pa->pa_pc; 123 pcitag_t tag = pa->pa_tag; 124 pcireg_t reg; 125 int line; 126 127 reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); 128 #ifdef DEBUG 129 printf(" pin=%d line=%d ", PCI_INTERRUPT_PIN(reg), 130 PCI_INTERRUPT_LINE(reg)); 131 #endif 132 line = PCI_INTERRUPT_LINE(reg); 133 if (sc->sc_irq[line] == 0) 134 sc->sc_irq[line] = cpu_intr_findirq(); 135 *ihp = (line << APIC_INT_LINE_SHIFT) | sc->sc_irq[line]; 136 return (APIC_INT_IRQ(*ihp) == 0); 137 } 138 139 const char * 140 apic_intr_string(void *v, pci_intr_handle_t ih) 141 { 142 static char buf[32]; 143 144 snprintf(buf, 32, "line %ld irq %ld", 145 APIC_INT_LINE(ih), APIC_INT_IRQ(ih)); 146 147 return (buf); 148 } 149 150 void * 151 apic_intr_establish(void *v, pci_intr_handle_t ih, 152 int pri, int (*handler)(void *), void *arg, char *name) 153 { 154 struct elroy_softc *sc = v; 155 volatile struct elroy_regs *r = sc->sc_regs; 156 hppa_hpa_t hpa = cpu_gethpa(0); 157 struct evcount *cnt; 158 struct apic_iv *aiv, *biv; 159 void *iv; 160 int irq = APIC_INT_IRQ(ih); 161 int line = APIC_INT_LINE(ih); 162 u_int32_t ent0; 163 164 /* no mapping or bogus */ 165 if (irq <= 0 || irq > 31) 166 return (NULL); 167 168 aiv = malloc(sizeof(struct apic_iv), M_DEVBUF, M_NOWAIT); 169 if (aiv == NULL) { 170 free(cnt, M_DEVBUF); 171 return NULL; 172 } 173 174 aiv->sc = sc; 175 aiv->ih = ih; 176 aiv->handler = handler; 177 aiv->arg = arg; 178 aiv->next = NULL; 179 aiv->cnt = NULL; 180 if (apic_intr_list[irq]) { 181 cnt = malloc(sizeof(struct evcount), M_DEVBUF, M_NOWAIT); 182 if (!cnt) { 183 free(aiv, M_DEVBUF); 184 return (NULL); 185 } 186 187 evcount_attach(cnt, name, NULL, &evcount_intr); 188 biv = apic_intr_list[irq]; 189 while (biv->next) 190 biv = biv->next; 191 biv->next = aiv; 192 aiv->cnt = cnt; 193 return (arg); 194 } 195 196 if ((iv = cpu_intr_establish(pri, irq, apic_intr, aiv, name))) { 197 ent0 = (31 - irq) & APIC_ENT0_VEC; 198 ent0 |= apic_get_int_ent0(sc, line); 199 #if 0 200 if (cold) { 201 sc->sc_imr |= (1 << irq); 202 ent0 |= APIC_ENT0_MASK; 203 } 204 #endif 205 apic_write(sc->sc_regs, APIC_ENT0(line), APIC_ENT0_MASK); 206 apic_write(sc->sc_regs, APIC_ENT1(line), 207 ((hpa & 0x0ff00000) >> 4) | ((hpa & 0x000ff000) << 12)); 208 apic_write(sc->sc_regs, APIC_ENT0(line), ent0); 209 210 /* Signal EOI. */ 211 elroy_write32(&r->apic_eoi, 212 htole32((31 - irq) & APIC_ENT0_VEC)); 213 214 apic_intr_list[irq] = aiv; 215 } 216 217 return (arg); 218 } 219 220 void 221 apic_intr_disestablish(void *v, void *cookie) 222 { 223 } 224 225 int 226 apic_intr(void *v) 227 { 228 struct apic_iv *iv = v; 229 struct elroy_softc *sc = iv->sc; 230 volatile struct elroy_regs *r = sc->sc_regs; 231 int claimed = 0; 232 233 while (iv) { 234 if (iv->handler(iv->arg)) { 235 if (iv->cnt) 236 iv->cnt->ec_count++; 237 else 238 claimed = 1; 239 } 240 iv = iv->next; 241 } 242 243 /* Signal EOI. */ 244 elroy_write32(&r->apic_eoi, 245 htole32((31 - APIC_INT_IRQ(iv->ih)) & APIC_ENT0_VEC)); 246 247 return (claimed); 248 } 249 250 /* Maximum number of supported interrupt routing entries. */ 251 #define MAX_INT_TBL_SZ 16 252 253 void 254 apic_get_int_tbl(struct elroy_softc *sc) 255 { 256 struct pdc_pat_io_num int_tbl_sz PDC_ALIGNMENT; 257 struct pdc_pat_pci_rt int_tbl[MAX_INT_TBL_SZ] PDC_ALIGNMENT; 258 size_t size; 259 260 /* 261 * XXX int_tbl should not be allocated on the stack, but we need a 262 * 1:1 mapping, and malloc doesn't provide that. 263 */ 264 265 if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SZ, 266 &int_tbl_sz, 0, 0, 0, 0, 0)) 267 return; 268 269 if (int_tbl_sz.num > MAX_INT_TBL_SZ) 270 panic("interrupt routing table too big (%d entries)", 271 int_tbl_sz.num); 272 273 size = int_tbl_sz.num * sizeof(struct pdc_pat_pci_rt); 274 sc->sc_int_tbl_sz = int_tbl_sz.num; 275 sc->sc_int_tbl = malloc(size, M_DEVBUF, M_NOWAIT); 276 if (sc->sc_int_tbl == NULL) 277 return; 278 279 if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL, 280 &int_tbl_sz, 0, &int_tbl, 0, 0, 0)) 281 return; 282 283 memcpy(sc->sc_int_tbl, int_tbl, size); 284 } 285 286 u_int32_t 287 apic_get_int_ent0(struct elroy_softc *sc, int line) 288 { 289 volatile struct elroy_regs *r = sc->sc_regs; 290 int trigger = MPS_INT(MPS_INTPO_DEF, MPS_INTTR_DEF); 291 u_int32_t ent0 = APIC_ENT0_LOW | APIC_ENT0_LEV; 292 int bus, mpspo, mpstr; 293 int i; 294 295 bus = letoh32(elroy_read32(&r->busnum)) & 0xff; 296 for (i = 0; i < sc->sc_int_tbl_sz; i++) { 297 if (bus == sc->sc_int_tbl[i].bus && 298 line == sc->sc_int_tbl[i].line) 299 trigger = sc->sc_int_tbl[i].trigger; 300 } 301 302 mpspo = (trigger >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK; 303 mpstr = (trigger >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK; 304 305 switch (mpspo) { 306 case MPS_INTPO_DEF: 307 break; 308 case MPS_INTPO_ACTHI: 309 ent0 &= ~APIC_ENT0_LOW; 310 break; 311 case MPS_INTPO_ACTLO: 312 ent0 |= APIC_ENT0_LOW; 313 break; 314 default: 315 panic("unknown MPS interrupt polarity %d", mpspo); 316 } 317 318 switch(mpstr) { 319 case MPS_INTTR_DEF: 320 break; 321 case MPS_INTTR_LEVEL: 322 ent0 |= APIC_ENT0_LEV; 323 break; 324 case MPS_INTTR_EDGE: 325 ent0 &= ~APIC_ENT0_LEV; 326 break; 327 default: 328 panic("unknown MPS interrupt trigger %d", mpstr); 329 } 330 331 return ent0; 332 } 333 334 #ifdef DEBUG 335 void 336 apic_dump(struct elroy_softc *sc) 337 { 338 int i; 339 340 for (i = 0; i < sc->sc_nints; i++) 341 printf("0x%04x 0x%04x\n", apic_read(sc->sc_regs, APIC_ENT0(i)), 342 apic_read(sc->sc_regs, APIC_ENT1(i))); 343 344 for (i = 0; i < sc->sc_int_tbl_sz; i++) { 345 printf("type=%x ", sc->sc_int_tbl[i].type); 346 printf("len=%d ", sc->sc_int_tbl[i].len); 347 printf("itype=%d ", sc->sc_int_tbl[i].itype); 348 printf("trigger=%x ", sc->sc_int_tbl[i].trigger); 349 printf("pin=%x ", sc->sc_int_tbl[i].pin); 350 printf("bus=%d ", sc->sc_int_tbl[i].bus); 351 printf("line=%d ", sc->sc_int_tbl[i].line); 352 printf("addr=%x\n", sc->sc_int_tbl[i].addr); 353 } 354 } 355 #endif 356