1 1.15 thorpej /* $NetBSD: becc_icu.c,v 1.15 2020/11/20 18:49:45 thorpej Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 2002 Wasabi Systems, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed for the NetBSD Project by 20 1.1 thorpej * Wasabi Systems, Inc. 21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 1.1 thorpej * or promote products derived from this software without specific prior 23 1.1 thorpej * written permission. 24 1.1 thorpej * 25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej */ 37 1.1 thorpej 38 1.1 thorpej /* 39 1.1 thorpej * Interrupt support for the ADI Engineering Big Endian Companion Chip. 40 1.1 thorpej */ 41 1.3 lukem 42 1.3 lukem #include <sys/cdefs.h> 43 1.15 thorpej __KERNEL_RCSID(0, "$NetBSD: becc_icu.c,v 1.15 2020/11/20 18:49:45 thorpej Exp $"); 44 1.1 thorpej 45 1.1 thorpej #ifndef EVBARM_SPL_NOINLINE 46 1.1 thorpej #define EVBARM_SPL_NOINLINE 47 1.1 thorpej #endif 48 1.1 thorpej 49 1.1 thorpej #include <sys/param.h> 50 1.1 thorpej #include <sys/systm.h> 51 1.15 thorpej #include <sys/kmem.h> 52 1.8 ad #include <sys/bus.h> 53 1.8 ad #include <sys/intr.h> 54 1.1 thorpej 55 1.1 thorpej #include <uvm/uvm_extern.h> 56 1.1 thorpej 57 1.1 thorpej #include <arm/cpufunc.h> 58 1.1 thorpej 59 1.1 thorpej #include <arm/xscale/beccreg.h> 60 1.1 thorpej #include <arm/xscale/beccvar.h> 61 1.1 thorpej 62 1.1 thorpej #include <arm/xscale/i80200reg.h> 63 1.1 thorpej #include <arm/xscale/i80200var.h> 64 1.1 thorpej 65 1.1 thorpej /* Interrupt handler queues. */ 66 1.1 thorpej struct intrq intrq[NIRQ]; 67 1.1 thorpej 68 1.1 thorpej /* Interrupts to mask at each level. */ 69 1.1 thorpej uint32_t becc_imask[NIPL]; 70 1.1 thorpej 71 1.1 thorpej /* Interrupts pending. */ 72 1.6 perry volatile uint32_t becc_ipending; 73 1.6 perry volatile uint32_t becc_sipending; 74 1.1 thorpej 75 1.1 thorpej /* Software copy of the IRQs we have enabled. */ 76 1.6 perry volatile uint32_t intr_enabled; 77 1.1 thorpej 78 1.1 thorpej /* Mask if interrupts steered to FIQs. */ 79 1.1 thorpej uint32_t intr_steer; 80 1.1 thorpej 81 1.1 thorpej /* 82 1.1 thorpej * Interrupt bit names. 83 1.1 thorpej * XXX Some of these are BRH-centric. 84 1.1 thorpej */ 85 1.11 matt const char * const becc_irqnames[] = { 86 1.1 thorpej "soft", 87 1.1 thorpej "timer A", 88 1.1 thorpej "timer B", 89 1.1 thorpej "irq 3", 90 1.1 thorpej "irq 4", 91 1.1 thorpej "irq 5", 92 1.1 thorpej "irq 6", 93 1.1 thorpej "diagerr", 94 1.1 thorpej "DMA EOT", 95 1.1 thorpej "DMA PERR", 96 1.1 thorpej "DMA TABT", 97 1.1 thorpej "DMA MABT", 98 1.1 thorpej "irq 12", 99 1.1 thorpej "irq 13", 100 1.1 thorpej "irq 14", 101 1.1 thorpej "irq 15", 102 1.1 thorpej "PCI PERR", 103 1.1 thorpej "irq 17", 104 1.1 thorpej "irq 18", 105 1.1 thorpej "PCI SERR", 106 1.1 thorpej "PCI OAPE", 107 1.1 thorpej "PCI OATA", 108 1.1 thorpej "PCI OAMA", 109 1.1 thorpej "irq 23", 110 1.1 thorpej "irq 24", 111 1.1 thorpej "irq 25", 112 1.1 thorpej "irq 26", /* PCI INTA */ 113 1.1 thorpej "irq 27", /* PCI INTB */ 114 1.1 thorpej "irq 28", /* PCI INTC */ 115 1.1 thorpej "irq 29", /* PCI INTD */ 116 1.1 thorpej "pushbutton", 117 1.1 thorpej "irq 31", 118 1.1 thorpej }; 119 1.1 thorpej 120 1.13 skrll void becc_intr_dispatch(struct trapframe *frame); 121 1.1 thorpej 122 1.6 perry static inline uint32_t 123 1.1 thorpej becc_icsr_read(void) 124 1.1 thorpej { 125 1.1 thorpej uint32_t icsr; 126 1.1 thorpej 127 1.1 thorpej icsr = BECC_CSR_READ(BECC_ICSR); 128 1.1 thorpej 129 1.1 thorpej /* 130 1.1 thorpej * The ICSR register shows bits that are active even if they are 131 1.1 thorpej * masked in ICMR, so we have to mask them off with the interrupts 132 1.1 thorpej * we consider enabled. 133 1.1 thorpej */ 134 1.1 thorpej return (icsr & intr_enabled); 135 1.1 thorpej } 136 1.1 thorpej 137 1.6 perry static inline void 138 1.1 thorpej becc_set_intrsteer(void) 139 1.1 thorpej { 140 1.1 thorpej 141 1.1 thorpej BECC_CSR_WRITE(BECC_ICSTR, intr_steer & ICU_VALID_MASK); 142 1.1 thorpej (void) BECC_CSR_READ(BECC_ICSTR); 143 1.1 thorpej } 144 1.1 thorpej 145 1.6 perry static inline void 146 1.1 thorpej becc_enable_irq(int irq) 147 1.1 thorpej { 148 1.1 thorpej 149 1.1 thorpej intr_enabled |= (1U << irq); 150 1.1 thorpej becc_set_intrmask(); 151 1.1 thorpej } 152 1.1 thorpej 153 1.6 perry static inline void 154 1.1 thorpej becc_disable_irq(int irq) 155 1.1 thorpej { 156 1.1 thorpej 157 1.1 thorpej intr_enabled &= ~(1U << irq); 158 1.1 thorpej becc_set_intrmask(); 159 1.1 thorpej } 160 1.1 thorpej 161 1.1 thorpej /* 162 1.1 thorpej * NOTE: This routine must be called with interrupts disabled in the CPSR. 163 1.1 thorpej */ 164 1.1 thorpej static void 165 1.1 thorpej becc_intr_calculate_masks(void) 166 1.1 thorpej { 167 1.1 thorpej struct intrq *iq; 168 1.1 thorpej struct intrhand *ih; 169 1.1 thorpej int irq, ipl; 170 1.1 thorpej 171 1.1 thorpej /* First, figure out which IPLs each IRQ has. */ 172 1.1 thorpej for (irq = 0; irq < NIRQ; irq++) { 173 1.1 thorpej int levels = 0; 174 1.1 thorpej iq = &intrq[irq]; 175 1.1 thorpej becc_disable_irq(irq); 176 1.1 thorpej for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL; 177 1.1 thorpej ih = TAILQ_NEXT(ih, ih_list)) 178 1.1 thorpej levels |= (1U << ih->ih_ipl); 179 1.1 thorpej iq->iq_levels = levels; 180 1.1 thorpej } 181 1.1 thorpej 182 1.1 thorpej /* Next, figure out which IRQs are used by each IPL. */ 183 1.1 thorpej for (ipl = 0; ipl < NIPL; ipl++) { 184 1.1 thorpej int irqs = 0; 185 1.1 thorpej for (irq = 0; irq < NIRQ; irq++) { 186 1.1 thorpej if (intrq[irq].iq_levels & (1U << ipl)) 187 1.1 thorpej irqs |= (1U << irq); 188 1.1 thorpej } 189 1.1 thorpej becc_imask[ipl] = irqs; 190 1.1 thorpej } 191 1.1 thorpej 192 1.1 thorpej becc_imask[IPL_NONE] = 0; 193 1.1 thorpej 194 1.1 thorpej /* 195 1.8 ad * Enforce a hierarchy that gives "slow" device (or devices with 196 1.8 ad * limited input buffer space/"real-time" requirements) a better 197 1.8 ad * chance at not dropping data. 198 1.1 thorpej */ 199 1.8 ad becc_imask[IPL_VM] |= becc_imask[IPL_SOFTSERIAL]; 200 1.8 ad becc_imask[IPL_SCHED] |= becc_imask[IPL_VM]; 201 1.8 ad becc_imask[IPL_HIGH] |= becc_imask[IPL_SCHED]; 202 1.1 thorpej 203 1.1 thorpej /* 204 1.1 thorpej * Now compute which IRQs must be blocked when servicing any 205 1.1 thorpej * given IRQ. 206 1.1 thorpej */ 207 1.1 thorpej for (irq = 0; irq < NIRQ; irq++) { 208 1.1 thorpej int irqs = (1U << irq); 209 1.1 thorpej iq = &intrq[irq]; 210 1.1 thorpej if (TAILQ_FIRST(&iq->iq_list) != NULL) 211 1.1 thorpej becc_enable_irq(irq); 212 1.1 thorpej for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL; 213 1.1 thorpej ih = TAILQ_NEXT(ih, ih_list)) 214 1.1 thorpej irqs |= becc_imask[ih->ih_ipl]; 215 1.1 thorpej iq->iq_mask = irqs; 216 1.1 thorpej } 217 1.1 thorpej } 218 1.1 thorpej 219 1.1 thorpej void 220 1.1 thorpej splx(int new) 221 1.1 thorpej { 222 1.1 thorpej becc_splx(new); 223 1.1 thorpej } 224 1.1 thorpej 225 1.1 thorpej int 226 1.1 thorpej _spllower(int ipl) 227 1.1 thorpej { 228 1.1 thorpej return (becc_spllower(ipl)); 229 1.1 thorpej } 230 1.1 thorpej 231 1.1 thorpej int 232 1.1 thorpej _splraise(int ipl) 233 1.1 thorpej { 234 1.1 thorpej return (becc_splraise(ipl)); 235 1.1 thorpej } 236 1.1 thorpej 237 1.1 thorpej /* 238 1.1 thorpej * becc_icu_init: 239 1.1 thorpej * 240 1.1 thorpej * Initialize the BECC ICU. Called early in bootstrap 241 1.1 thorpej * to make sure the ICU is in a pristine state. 242 1.1 thorpej */ 243 1.1 thorpej void 244 1.1 thorpej becc_icu_init(void) 245 1.1 thorpej { 246 1.1 thorpej 247 1.1 thorpej intr_enabled = 0; /* All interrupts disabled */ 248 1.1 thorpej becc_set_intrmask(); 249 1.1 thorpej 250 1.1 thorpej intr_steer = 0; /* All interrupts steered to IRQ */ 251 1.1 thorpej becc_set_intrsteer(); 252 1.1 thorpej 253 1.1 thorpej i80200_extirq_dispatch = becc_intr_dispatch; 254 1.1 thorpej 255 1.1 thorpej i80200_intr_enable(INTCTL_IM); 256 1.1 thorpej } 257 1.1 thorpej 258 1.1 thorpej /* 259 1.1 thorpej * becc_intr_init: 260 1.1 thorpej * 261 1.1 thorpej * Initialize the rest of the interrupt subsystem, making it 262 1.1 thorpej * ready to handle interrupts from devices. 263 1.1 thorpej */ 264 1.1 thorpej void 265 1.1 thorpej becc_intr_init(void) 266 1.1 thorpej { 267 1.1 thorpej struct intrq *iq; 268 1.1 thorpej int i; 269 1.1 thorpej 270 1.1 thorpej intr_enabled = 0; 271 1.1 thorpej 272 1.1 thorpej for (i = 0; i < NIRQ; i++) { 273 1.1 thorpej iq = &intrq[i]; 274 1.1 thorpej TAILQ_INIT(&iq->iq_list); 275 1.1 thorpej 276 1.1 thorpej evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR, 277 1.1 thorpej NULL, "becc", becc_irqnames[i]); 278 1.1 thorpej } 279 1.1 thorpej 280 1.1 thorpej becc_intr_calculate_masks(); 281 1.1 thorpej 282 1.1 thorpej /* Enable IRQs (don't yet use FIQs). */ 283 1.1 thorpej enable_interrupts(I32_bit); 284 1.1 thorpej } 285 1.1 thorpej 286 1.1 thorpej void * 287 1.1 thorpej becc_intr_establish(int irq, int ipl, int (*func)(void *), void *arg) 288 1.1 thorpej { 289 1.1 thorpej struct intrq *iq; 290 1.1 thorpej struct intrhand *ih; 291 1.1 thorpej uint32_t oldirqstate; 292 1.1 thorpej 293 1.1 thorpej if (irq < 0 || irq > NIRQ) 294 1.1 thorpej panic("becc_intr_establish: IRQ %d out of range", irq); 295 1.1 thorpej 296 1.15 thorpej ih = kmem_alloc(sizeof(*ih), KM_SLEEP); 297 1.1 thorpej ih->ih_func = func; 298 1.1 thorpej ih->ih_arg = arg; 299 1.1 thorpej ih->ih_ipl = ipl; 300 1.1 thorpej ih->ih_irq = irq; 301 1.1 thorpej 302 1.1 thorpej iq = &intrq[irq]; 303 1.1 thorpej 304 1.1 thorpej /* All BECC interrupts are level-triggered. */ 305 1.1 thorpej iq->iq_ist = IST_LEVEL; 306 1.1 thorpej 307 1.1 thorpej oldirqstate = disable_interrupts(I32_bit); 308 1.1 thorpej 309 1.1 thorpej TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); 310 1.1 thorpej 311 1.1 thorpej becc_intr_calculate_masks(); 312 1.1 thorpej 313 1.1 thorpej restore_interrupts(oldirqstate); 314 1.1 thorpej 315 1.1 thorpej return (ih); 316 1.1 thorpej } 317 1.1 thorpej 318 1.1 thorpej void 319 1.1 thorpej becc_intr_disestablish(void *cookie) 320 1.1 thorpej { 321 1.1 thorpej struct intrhand *ih = cookie; 322 1.1 thorpej struct intrq *iq = &intrq[ih->ih_irq]; 323 1.1 thorpej uint32_t oldirqstate; 324 1.1 thorpej 325 1.1 thorpej oldirqstate = disable_interrupts(I32_bit); 326 1.1 thorpej 327 1.1 thorpej TAILQ_REMOVE(&iq->iq_list, ih, ih_list); 328 1.1 thorpej 329 1.1 thorpej becc_intr_calculate_masks(); 330 1.1 thorpej 331 1.1 thorpej restore_interrupts(oldirqstate); 332 1.1 thorpej } 333 1.1 thorpej 334 1.1 thorpej void 335 1.13 skrll becc_intr_dispatch(struct trapframe *frame) 336 1.1 thorpej { 337 1.1 thorpej struct intrq *iq; 338 1.1 thorpej struct intrhand *ih; 339 1.11 matt uint32_t oldirqstate, irq, ibit, hwpend; 340 1.11 matt struct cpu_info * const ci = curcpu(); 341 1.11 matt const int ppl = ci->ci_cpl; 342 1.11 matt const uint32_t imask = becc_imask[ppl]; 343 1.1 thorpej 344 1.1 thorpej hwpend = becc_icsr_read(); 345 1.1 thorpej 346 1.1 thorpej /* 347 1.1 thorpej * Disable all the interrupts that are pending. We will 348 1.1 thorpej * reenable them once they are processed and not masked. 349 1.1 thorpej */ 350 1.1 thorpej intr_enabled &= ~hwpend; 351 1.1 thorpej becc_set_intrmask(); 352 1.1 thorpej 353 1.1 thorpej while (hwpend != 0) { 354 1.1 thorpej irq = ffs(hwpend) - 1; 355 1.1 thorpej ibit = (1U << irq); 356 1.1 thorpej 357 1.1 thorpej hwpend &= ~ibit; 358 1.1 thorpej 359 1.11 matt if (imask & ibit) { 360 1.1 thorpej /* 361 1.1 thorpej * IRQ is masked; mark it as pending and check 362 1.1 thorpej * the next one. Note: the IRQ is already disabled. 363 1.1 thorpej */ 364 1.1 thorpej becc_ipending |= ibit; 365 1.1 thorpej continue; 366 1.1 thorpej } 367 1.1 thorpej 368 1.1 thorpej becc_ipending &= ~ibit; 369 1.1 thorpej 370 1.1 thorpej iq = &intrq[irq]; 371 1.1 thorpej iq->iq_ev.ev_count++; 372 1.12 matt ci->ci_data.cpu_nintr++; 373 1.11 matt TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 374 1.11 matt ci->ci_cpl = ih->ih_ipl; 375 1.11 matt oldirqstate = enable_interrupts(I32_bit); 376 1.1 thorpej (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame); 377 1.11 matt restore_interrupts(oldirqstate); 378 1.1 thorpej } 379 1.1 thorpej 380 1.11 matt ci->ci_cpl = ppl; 381 1.1 thorpej 382 1.1 thorpej /* Re-enable this interrupt now that's it's cleared. */ 383 1.1 thorpej intr_enabled |= ibit; 384 1.1 thorpej becc_set_intrmask(); 385 1.1 thorpej } 386 1.1 thorpej 387 1.11 matt if (becc_ipending & ~imask) { 388 1.11 matt intr_enabled |= (becc_ipending & ~imask); 389 1.1 thorpej becc_set_intrmask(); 390 1.1 thorpej } 391 1.11 matt 392 1.11 matt #ifdef __HAVE_FAST_SOFTINTS 393 1.11 matt cpu_dosoftints(); 394 1.11 matt #endif 395 1.1 thorpej } 396