1 /* 2 * Copyright (c) 2000-2004 Opsycon AB (www.opsycon.se) 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 */ 26 27 /* 28 * Interrupt support for Octeon Processor. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/conf.h> 35 #include <sys/malloc.h> 36 #include <sys/device.h> 37 #include <sys/proc.h> 38 39 #include <mips64/archtype.h> 40 41 #include <machine/autoconf.h> 42 #include <machine/atomic.h> 43 #include <machine/intr.h> 44 45 #include <octeon/dev/octeonreg.h> 46 #include <octeon/dev/iobusvar.h> 47 48 extern bus_space_handle_t iobus_h; 49 50 #define OCTEON_NINTS 64 51 52 void octeon_intr_makemasks(void); 53 void octeon_splx(int); 54 uint32_t octeon_iointr(uint32_t, struct trap_frame *); 55 uint32_t octeon_aux(uint32_t, struct trap_frame *); 56 int octeon_iointr_skip(struct intrhand *, uint64_t, uint64_t); 57 void octeon_setintrmask(int); 58 59 struct intrhand *octeon_intrhand[OCTEON_NINTS]; 60 61 #define INTPRI_CIU_0 (INTPRI_CLOCK + 1) 62 63 uint64_t octeon_intem[MAXCPUS]; 64 uint64_t octeon_imask[MAXCPUS][NIPLS]; 65 66 void 67 octeon_intr_init(void) 68 { 69 int cpuid = cpu_number(); 70 bus_space_write_8(&iobus_tag, iobus_h, CIU_IP2_EN0(cpuid), 0); 71 bus_space_write_8(&iobus_tag, iobus_h, CIU_IP3_EN0(cpuid), 0); 72 bus_space_write_8(&iobus_tag, iobus_h, CIU_IP2_EN1(cpuid), 0); 73 bus_space_write_8(&iobus_tag, iobus_h, CIU_IP3_EN1(cpuid), 0); 74 75 set_intr(INTPRI_CIU_0, CR_INT_0, octeon_iointr); 76 register_splx_handler(octeon_splx); 77 } 78 79 /* 80 * Establish an interrupt handler called from the dispatcher. 81 * The interrupt function established should return zero if there was nothing 82 * to serve (no int) and non-zero when an interrupt was serviced. 83 * 84 * Interrupts are numbered from 1 and up where 1 maps to HW int 0. 85 * XXX There is no reason to keep this... except for hardcoded interrupts 86 * XXX in kernel configuration files... 87 */ 88 void * 89 octeon_intr_establish(int irq, int level, 90 int (*ih_fun)(void *), void *ih_arg, const char *ih_what) 91 { 92 int cpuid = cpu_number(); 93 struct intrhand **p, *q, *ih; 94 int s; 95 96 #ifdef DIAGNOSTIC 97 if (irq >= OCTEON_NINTS || irq < 0) 98 panic("intr_establish: illegal irq %d", irq); 99 #endif 100 101 ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT); 102 if (ih == NULL) 103 return NULL; 104 105 ih->ih_next = NULL; 106 ih->ih_fun = ih_fun; 107 ih->ih_arg = ih_arg; 108 ih->ih_level = level; 109 ih->ih_irq = irq; 110 evcount_attach(&ih->ih_count, ih_what, (void *)&ih->ih_irq); 111 112 s = splhigh(); 113 114 /* 115 * Figure out where to put the handler. 116 * This is O(N^2), but we want to preserve the order, and N is 117 * generally small. 118 */ 119 for (p = &octeon_intrhand[irq]; (q = *p) != NULL; 120 p = (struct intrhand **)&q->ih_next) 121 ; 122 *p = ih; 123 124 octeon_intem[cpuid] |= 1UL << irq; 125 octeon_intr_makemasks(); 126 127 splx(s); /* causes hw mask update */ 128 129 return (ih); 130 } 131 132 void 133 octeon_intr_disestablish(void *ih) 134 { 135 /* XXX */ 136 panic("%s not implemented", __func__); 137 } 138 139 void 140 octeon_splx(int newipl) 141 { 142 struct cpu_info *ci = curcpu(); 143 144 /* Update masks to new ipl. Order highly important! */ 145 __asm__ (".set noreorder\n"); 146 ci->ci_ipl = newipl; 147 __asm__ ("sync\n\t.set reorder\n"); 148 if (CPU_IS_PRIMARY(ci)) 149 octeon_setintrmask(newipl); 150 /* If we still have softints pending trigger processing. */ 151 if (ci->ci_softpending != 0 && newipl < IPL_SOFTINT) 152 setsoftintr0(); 153 } 154 155 /* 156 * Recompute interrupt masks. 157 */ 158 void 159 octeon_intr_makemasks() 160 { 161 int cpuid = cpu_number(); 162 int irq, level; 163 struct intrhand *q; 164 uint intrlevel[OCTEON_NINTS]; 165 166 /* First, figure out which levels each IRQ uses. */ 167 for (irq = 0; irq < OCTEON_NINTS; irq++) { 168 uint levels = 0; 169 for (q = (struct intrhand *)octeon_intrhand[irq]; q != NULL; 170 q = q->ih_next) 171 levels |= 1 << q->ih_level; 172 intrlevel[irq] = levels; 173 } 174 175 /* 176 * Then figure out which IRQs use each level. 177 * Note that we make sure never to overwrite imask[IPL_HIGH], in 178 * case an interrupt occurs during intr_disestablish() and causes 179 * an unfortunate splx() while we are here recomputing the masks. 180 */ 181 for (level = IPL_NONE; level < NIPLS; level++) { 182 uint64_t irqs = 0; 183 for (irq = 0; irq < OCTEON_NINTS; irq++) 184 if (intrlevel[irq] & (1 << level)) 185 irqs |= 1UL << irq; 186 octeon_imask[cpuid][level] = irqs; 187 } 188 /* 189 * There are tty, network and disk drivers that use free() at interrupt 190 * time, so vm > (tty | net | bio). 191 * 192 * Enforce a hierarchy that gives slow devices a better chance at not 193 * dropping data. 194 */ 195 octeon_imask[cpuid][IPL_NET] |= octeon_imask[cpuid][IPL_BIO]; 196 octeon_imask[cpuid][IPL_TTY] |= octeon_imask[cpuid][IPL_NET]; 197 octeon_imask[cpuid][IPL_VM] |= octeon_imask[cpuid][IPL_TTY]; 198 octeon_imask[cpuid][IPL_CLOCK] |= octeon_imask[cpuid][IPL_VM]; 199 octeon_imask[cpuid][IPL_HIGH] |= octeon_imask[cpuid][IPL_CLOCK]; 200 octeon_imask[cpuid][IPL_IPI] |= octeon_imask[cpuid][IPL_HIGH]; 201 202 /* 203 * These are pseudo-levels. 204 */ 205 octeon_imask[cpuid][IPL_NONE] = 0; 206 } 207 208 /* 209 * Interrupt dispatcher. 210 */ 211 uint32_t 212 octeon_iointr(uint32_t hwpend, struct trap_frame *frame) 213 { 214 struct cpu_info *ci = curcpu(); 215 int cpuid = cpu_number(); 216 uint64_t imr, isr, mask; 217 int ipl; 218 int bit; 219 struct intrhand *ih; 220 int rc; 221 uint64_t sum0 = CIU_IP2_SUM0(cpuid); 222 uint64_t en0 = CIU_IP2_EN0(cpuid); 223 224 isr = bus_space_read_8(&iobus_tag, iobus_h, sum0); 225 imr = bus_space_read_8(&iobus_tag, iobus_h, en0); 226 bit = 63; 227 228 isr &= imr; 229 if (isr == 0) 230 return 0; /* not for us */ 231 232 /* 233 * Mask all pending interrupts. 234 */ 235 bus_space_write_8(&iobus_tag, iobus_h, en0, imr & ~isr); 236 237 /* 238 * If interrupts are spl-masked, mask them and wait for splx() 239 * to reenable them when necessary. 240 */ 241 if ((mask = isr & octeon_imask[cpuid][frame->ipl]) != 0) { 242 isr &= ~mask; 243 imr &= ~mask; 244 } 245 246 /* 247 * Now process allowed interrupts. 248 */ 249 if (isr != 0) { 250 int lvl, bitno; 251 uint64_t tmpisr; 252 253 __asm__ (".set noreorder\n"); 254 ipl = ci->ci_ipl; 255 __asm__ ("sync\n\t.set reorder\n"); 256 257 /* Service higher level interrupts first */ 258 for (lvl = NIPLS - 1; lvl != IPL_NONE; lvl--) { 259 tmpisr = isr & (octeon_imask[cpuid][lvl] ^ octeon_imask[cpuid][lvl - 1]); 260 if (tmpisr == 0) 261 continue; 262 for (bitno = bit, mask = 1UL << bitno; mask != 0; 263 bitno--, mask >>= 1) { 264 if ((tmpisr & mask) == 0) 265 continue; 266 267 rc = 0; 268 for (ih = (struct intrhand *)octeon_intrhand[bitno]; 269 ih != NULL; 270 ih = ih->ih_next) { 271 #ifdef MULTIPROCESSOR 272 u_int32_t sr; 273 #endif 274 splraise(ih->ih_level); 275 #ifdef MULTIPROCESSOR 276 if (ih->ih_level < IPL_IPI) { 277 sr = getsr(); 278 ENABLEIPI(); 279 if (ipl < IPL_SCHED) 280 __mp_lock(&kernel_lock); 281 } 282 #endif 283 if ((*ih->ih_fun)(ih->ih_arg) != 0) { 284 rc = 1; 285 atomic_add_uint64(&ih->ih_count.ec_count, 1); 286 } 287 #ifdef MULTIPROCESSOR 288 if (ih->ih_level < IPL_IPI) { 289 if (ipl < IPL_SCHED) 290 __mp_unlock(&kernel_lock); 291 setsr(sr); 292 } 293 #endif 294 __asm__ (".set noreorder\n"); 295 ci->ci_ipl = ipl; 296 __asm__ ("sync\n\t.set reorder\n"); 297 } 298 if (rc == 0) 299 printf("spurious crime interrupt %d\n", bitno); 300 301 isr ^= mask; 302 if ((tmpisr ^= mask) == 0) 303 break; 304 } 305 } 306 307 /* 308 * Reenable interrupts which have been serviced. 309 */ 310 bus_space_write_8(&iobus_tag, iobus_h, en0, imr); 311 } 312 313 return hwpend; 314 } 315 316 void 317 octeon_setintrmask(int level) 318 { 319 int cpuid = cpu_number(); 320 321 bus_space_write_8(&iobus_tag, iobus_h, CIU_IP2_EN0(cpuid), 322 octeon_intem[cpuid] & ~octeon_imask[cpuid][level]); 323 } 324