ep93xx_intr.c revision 1.22
1250757Sjkim/* $NetBSD: ep93xx_intr.c,v 1.22 2014/03/26 08:51:59 christos Exp $ */ 2250757Sjkim 3250757Sjkim/* 4250757Sjkim * Copyright (c) 2002 The NetBSD Foundation, Inc. 5250757Sjkim * All rights reserved. 6250757Sjkim * 7250757Sjkim * This code is derived from software contributed to The NetBSD Foundation 8298714Sjkim * by Jesse Off 9250757Sjkim * 10250757Sjkim * This code is derived from software contributed to The NetBSD Foundation 11250757Sjkim * by Ichiro FUKUHARA and Naoto Shimazaki. 12250757Sjkim * 13250757Sjkim * Redistribution and use in source and binary forms, with or without 14250757Sjkim * modification, are permitted provided that the following conditions 15250757Sjkim * are met: 16250757Sjkim * 1. Redistributions of source code must retain the above copyright 17250757Sjkim * notice, this list of conditions and the following disclaimer. 18250757Sjkim * 2. Redistributions in binary form must reproduce the above copyright 19250757Sjkim * notice, this list of conditions and the following disclaimer in the 20250757Sjkim * documentation and/or other materials provided with the distribution. 21250757Sjkim * 22250757Sjkim * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23250757Sjkim * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24250757Sjkim * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25250757Sjkim * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26250757Sjkim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27250757Sjkim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28250757Sjkim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29250757Sjkim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30250757Sjkim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31250757Sjkim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32250757Sjkim * POSSIBILITY OF SUCH DAMAGE. 33250757Sjkim */ 34250757Sjkim 35250757Sjkim#include <sys/cdefs.h> 36250757Sjkim__KERNEL_RCSID(0, "$NetBSD: ep93xx_intr.c,v 1.22 2014/03/26 08:51:59 christos Exp $"); 37250757Sjkim 38250757Sjkim/* 39250757Sjkim * Interrupt support for the Cirrus Logic EP93XX 40250757Sjkim */ 41250757Sjkim 42250757Sjkim#include <sys/param.h> 43250757Sjkim#include <sys/systm.h> 44250838Sjkim#include <sys/malloc.h> 45250838Sjkim#include <sys/termios.h> 46250757Sjkim 47250757Sjkim#include <sys/bus.h> 48250757Sjkim#include <sys/intr.h> 49250757Sjkim 50250757Sjkim#include <arm/locore.h> 51250757Sjkim 52250757Sjkim#include <arm/ep93xx/ep93xxreg.h> 53250757Sjkim#include <arm/ep93xx/ep93xxvar.h> 54250757Sjkim 55250757Sjkim/* Interrupt handler queues. */ 56250757Sjkimstruct intrq intrq[NIRQ]; 57250757Sjkim 58250757Sjkim/* Interrupts to mask at each level. */ 59250757Sjkimstatic uint32_t vic1_imask[NIPL]; 60250757Sjkimstatic uint32_t vic2_imask[NIPL]; 61250757Sjkim 62250757Sjkim/* Current interrupt priority level. */ 63250757Sjkimvolatile int hardware_spl_level; 64250757Sjkim 65250757Sjkim/* Software copy of the IRQs we have enabled. */ 66250757Sjkimvolatile uint32_t vic1_intr_enabled; 67250757Sjkimvolatile uint32_t vic2_intr_enabled; 68250757Sjkim 69250757Sjkimvoid ep93xx_intr_dispatch(struct trapframe *); 70250757Sjkim 71250757Sjkim#define VIC1REG(reg) *((volatile uint32_t*) (EP93XX_AHB_VBASE + \ 72250757Sjkim EP93XX_AHB_VIC1 + (reg))) 73250757Sjkim#define VIC2REG(reg) *((volatile uint32_t*) (EP93XX_AHB_VBASE + \ 74250757Sjkim EP93XX_AHB_VIC2 + (reg))) 75250757Sjkim 76250757Sjkimstatic void 77250757Sjkimep93xx_set_intrmask(uint32_t vic1_irqs, uint32_t vic2_irqs) 78250757Sjkim{ 79250757Sjkim VIC1REG(EP93XX_VIC_IntEnClear) = vic1_irqs; 80250757Sjkim VIC1REG(EP93XX_VIC_IntEnable) = vic1_intr_enabled & ~vic1_irqs; 81250757Sjkim VIC2REG(EP93XX_VIC_IntEnClear) = vic2_irqs; 82250757Sjkim VIC2REG(EP93XX_VIC_IntEnable) = vic2_intr_enabled & ~vic2_irqs; 83250757Sjkim} 84250757Sjkim 85250757Sjkimstatic void 86250757Sjkimep93xx_enable_irq(int irq) 87250757Sjkim{ 88250757Sjkim if (irq < VIC_NIRQ) { 89250757Sjkim vic1_intr_enabled |= (1U << irq); 90250757Sjkim VIC1REG(EP93XX_VIC_IntEnable) = (1U << irq); 91250757Sjkim } else { 92250757Sjkim vic2_intr_enabled |= (1U << (irq - VIC_NIRQ)); 93250757Sjkim VIC2REG(EP93XX_VIC_IntEnable) = (1U << (irq - VIC_NIRQ)); 94250757Sjkim } 95250757Sjkim} 96250757Sjkim 97250757Sjkimstatic inline void 98250757Sjkimep93xx_disable_irq(int irq) 99250757Sjkim{ 100250757Sjkim if (irq < VIC_NIRQ) { 101250757Sjkim vic1_intr_enabled &= ~(1U << irq); 102250757Sjkim VIC1REG(EP93XX_VIC_IntEnClear) = (1U << irq); 103250757Sjkim } else { 104250757Sjkim vic2_intr_enabled &= ~(1U << (irq - VIC_NIRQ)); 105250757Sjkim VIC2REG(EP93XX_VIC_IntEnClear) = (1U << (irq - VIC_NIRQ)); 106250757Sjkim } 107250757Sjkim} 108250757Sjkim 109250757Sjkim/* 110250757Sjkim * NOTE: This routine must be called with interrupts disabled in the CPSR. 111250757Sjkim */ 112250757Sjkimstatic void 113250757Sjkimep93xx_intr_calculate_masks(void) 114250757Sjkim{ 115250757Sjkim struct intrq *iq; 116250757Sjkim struct intrhand *ih; 117250757Sjkim int irq, ipl; 118250757Sjkim 119250757Sjkim /* First, figure out which IPLs each IRQ has. */ 120250757Sjkim for (irq = 0; irq < NIRQ; irq++) { 121250757Sjkim int levels = 0; 122250757Sjkim iq = &intrq[irq]; 123250757Sjkim ep93xx_disable_irq(irq); 124250757Sjkim for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL; 125250757Sjkim ih = TAILQ_NEXT(ih, ih_list)) 126250757Sjkim levels |= (1U << ih->ih_ipl); 127250757Sjkim iq->iq_levels = levels; 128250757Sjkim } 129250757Sjkim 130250757Sjkim /* Next, figure out which IRQs are used by each IPL. */ 131250757Sjkim for (ipl = 0; ipl < NIPL; ipl++) { 132250757Sjkim int vic1_irqs = 0; 133250757Sjkim int vic2_irqs = 0; 134250757Sjkim for (irq = 0; irq < VIC_NIRQ; irq++) { 135250757Sjkim if (intrq[irq].iq_levels & (1U << ipl)) 136250757Sjkim vic1_irqs |= (1U << irq); 137250757Sjkim } 138250757Sjkim vic1_imask[ipl] = vic1_irqs; 139250757Sjkim for (irq = 0; irq < VIC_NIRQ; irq++) { 140250757Sjkim if (intrq[irq + VIC_NIRQ].iq_levels & (1U << ipl)) 141250757Sjkim vic2_irqs |= (1U << irq); 142250757Sjkim } 143250757Sjkim vic2_imask[ipl] = vic2_irqs; 144250757Sjkim } 145250757Sjkim 146250757Sjkim KASSERT(vic1_imask[IPL_NONE] == 0); 147250757Sjkim KASSERT(vic2_imask[IPL_NONE] == 0); 148250757Sjkim KASSERT(vic1_imask[IPL_SOFTCLOCK] == 0); 149250757Sjkim KASSERT(vic2_imask[IPL_SOFTCLOCK] == 0); 150250757Sjkim KASSERT(vic1_imask[IPL_SOFTBIO] == 0); 151250757Sjkim KASSERT(vic2_imask[IPL_SOFTBIO] == 0); 152250757Sjkim KASSERT(vic1_imask[IPL_SOFTNET] == 0); 153250757Sjkim KASSERT(vic2_imask[IPL_SOFTNET] == 0); 154250757Sjkim KASSERT(vic1_imask[IPL_SOFTSERIAL] == 0); 155250757Sjkim KASSERT(vic2_imask[IPL_SOFTSERIAL] == 0); 156250757Sjkim 157250757Sjkim /* 158250757Sjkim * splsched() must block anything that uses the scheduler. 159250757Sjkim */ 160250757Sjkim vic1_imask[IPL_SCHED] |= vic1_imask[IPL_VM]; 161281396Sjkim vic2_imask[IPL_SCHED] |= vic2_imask[IPL_VM]; 162281396Sjkim 163281396Sjkim /* 164281396Sjkim * splhigh() must block "everything". 165281396Sjkim */ 166281396Sjkim vic1_imask[IPL_HIGH] |= vic1_imask[IPL_SCHED]; 167281396Sjkim vic2_imask[IPL_HIGH] |= vic2_imask[IPL_SCHED]; 168281396Sjkim 169281396Sjkim /* 170250757Sjkim * Now compute which IRQs must be blocked when servicing any 171284583Sjkim * given IRQ. 172250757Sjkim */ 173250757Sjkim for (irq = 0; irq < NIRQ; irq++) { 174250757Sjkim int vic1_irqs; 175250757Sjkim int vic2_irqs; 176250757Sjkim 177250757Sjkim if (irq < VIC_NIRQ) { 178250757Sjkim vic1_irqs = (1U << irq); 179250757Sjkim vic2_irqs = 0; 180250757Sjkim } else { 181250757Sjkim vic1_irqs = 0; 182250757Sjkim vic2_irqs = (1U << (irq - VIC_NIRQ)); 183250757Sjkim } 184250757Sjkim iq = &intrq[irq]; 185250757Sjkim if (TAILQ_FIRST(&iq->iq_list) != NULL) 186250757Sjkim ep93xx_enable_irq(irq); 187250757Sjkim for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL; 188250757Sjkim ih = TAILQ_NEXT(ih, ih_list)) { 189250757Sjkim vic1_irqs |= vic1_imask[ih->ih_ipl]; 190250757Sjkim vic2_irqs |= vic2_imask[ih->ih_ipl]; 191250757Sjkim } 192250757Sjkim iq->iq_vic1_mask = vic1_irqs; 193250757Sjkim iq->iq_vic2_mask = vic2_irqs; 194250757Sjkim } 195250757Sjkim} 196250757Sjkim 197250757Sjkiminline void 198250757Sjkimsplx(int new) 199250757Sjkim{ 200250757Sjkim u_int oldirqstate; 201250757Sjkim 202250757Sjkim oldirqstate = disable_interrupts(I32_bit); 203250757Sjkim set_curcpl(new); 204250757Sjkim if (new != hardware_spl_level) { 205250757Sjkim hardware_spl_level = new; 206250757Sjkim ep93xx_set_intrmask(vic1_imask[new], vic2_imask[new]); 207250757Sjkim } 208250757Sjkim restore_interrupts(oldirqstate); 209250757Sjkim 210250757Sjkim#ifdef __HAVE_FAST_SOFTINTS 211250757Sjkim cpu_dosoftints(); 212250757Sjkim#endif 213250757Sjkim} 214250757Sjkim 215250757Sjkimint 216250757Sjkim_splraise(int ipl) 217250757Sjkim{ 218250757Sjkim int old; 219250757Sjkim u_int oldirqstate; 220250757Sjkim 221250757Sjkim oldirqstate = disable_interrupts(I32_bit); 222250757Sjkim old = curcpl(); 223250757Sjkim set_curcpl(ipl); 224250757Sjkim restore_interrupts(oldirqstate); 225250757Sjkim return (old); 226250757Sjkim} 227250757Sjkim 228272444Sjkimint 229272444Sjkim_spllower(int ipl) 230272444Sjkim{ 231272444Sjkim int old = curcpl(); 232272444Sjkim 233272444Sjkim if (old <= ipl) 234272444Sjkim return (old); 235272444Sjkim splx(ipl); 236272444Sjkim return (old); 237272444Sjkim} 238272444Sjkim 239272444Sjkim/* 240272444Sjkim * ep93xx_intr_init: 241272444Sjkim * 242272444Sjkim * Initialize the rest of the interrupt subsystem, making it 243272444Sjkim * ready to handle interrupts from devices. 244272444Sjkim */ 245272444Sjkimvoid 246272444Sjkimep93xx_intr_init(void) 247272444Sjkim{ 248272444Sjkim struct intrq *iq; 249272444Sjkim int i; 250272444Sjkim 251272444Sjkim vic1_intr_enabled = 0; 252272444Sjkim vic2_intr_enabled = 0; 253272444Sjkim 254272444Sjkim for (i = 0; i < NIRQ; i++) { 255272444Sjkim iq = &intrq[i]; 256272444Sjkim TAILQ_INIT(&iq->iq_list); 257272444Sjkim 258272444Sjkim snprintf(iq->iq_name, sizeof(iq->iq_name), "irq %d", i); 259272444Sjkim evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR, 260272444Sjkim NULL, (i < VIC_NIRQ ? "vic1" : "vic2"), 261272444Sjkim iq->iq_name); 262272444Sjkim } 263272444Sjkim curcpu()->ci_intr_depth = 0; 264272444Sjkim set_curcpl(0); 265272444Sjkim hardware_spl_level = 0; 266272444Sjkim 267272444Sjkim /* All interrupts should use IRQ not FIQ */ 268272444Sjkim VIC1REG(EP93XX_VIC_IntSelect) = 0; 269272444Sjkim VIC2REG(EP93XX_VIC_IntSelect) = 0; 270272444Sjkim 271272444Sjkim ep93xx_intr_calculate_masks(); 272272444Sjkim 273272444Sjkim /* Enable IRQs (don't yet use FIQs). */ 274272444Sjkim enable_interrupts(I32_bit); 275272444Sjkim} 276272444Sjkim 277272444Sjkimvoid * 278272444Sjkimep93xx_intr_establish(int irq, int ipl, int (*ih_func)(void *), void *arg) 279272444Sjkim{ 280272444Sjkim struct intrq* iq; 281272444Sjkim struct intrhand* ih; 282272444Sjkim u_int oldirqstate; 283272444Sjkim 284272444Sjkim if (irq < 0 || irq > NIRQ) 285272444Sjkim panic("ep93xx_intr_establish: IRQ %d out of range", irq); 286272444Sjkim if (ipl < 0 || ipl > NIPL) 287272444Sjkim panic("ep93xx_intr_establish: IPL %d out of range", ipl); 288272444Sjkim 289272444Sjkim ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT); 290272444Sjkim if (ih == NULL) 291272444Sjkim return (NULL); 292272444Sjkim 293272444Sjkim ih->ih_func = ih_func; 294272444Sjkim ih->ih_arg = arg; 295272444Sjkim ih->ih_irq = irq; 296272444Sjkim ih->ih_ipl = ipl; 297272444Sjkim 298272444Sjkim iq = &intrq[irq]; 299272444Sjkim 300272444Sjkim oldirqstate = disable_interrupts(I32_bit); 301272444Sjkim TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); 302272444Sjkim ep93xx_intr_calculate_masks(); 303272444Sjkim restore_interrupts(oldirqstate); 304272444Sjkim 305272444Sjkim return (ih); 306272444Sjkim} 307272444Sjkim 308272444Sjkimvoid 309272444Sjkimep93xx_intr_disestablish(void *cookie) 310272444Sjkim{ 311272444Sjkim struct intrhand* ih = cookie; 312272444Sjkim struct intrq* iq = &intrq[ih->ih_irq]; 313272444Sjkim u_int oldirqstate; 314272444Sjkim 315272444Sjkim oldirqstate = disable_interrupts(I32_bit); 316272444Sjkim TAILQ_REMOVE(&iq->iq_list, ih, ih_list); 317272444Sjkim ep93xx_intr_calculate_masks(); 318272444Sjkim restore_interrupts(oldirqstate); 319272444Sjkim} 320272444Sjkim 321272444Sjkimvoid 322272444Sjkimep93xx_intr_dispatch(struct trapframe *frame) 323272444Sjkim{ 324272444Sjkim struct intrq* iq; 325272444Sjkim struct intrhand* ih; 326272444Sjkim u_int oldirqstate; 327272444Sjkim int pcpl; 328272444Sjkim uint32_t vic1_hwpend; 329272444Sjkim uint32_t vic2_hwpend; 330272444Sjkim int irq; 331272444Sjkim 332272444Sjkim pcpl = curcpl(); 333272444Sjkim 334272444Sjkim vic1_hwpend = VIC1REG(EP93XX_VIC_IRQStatus); 335272444Sjkim vic2_hwpend = VIC2REG(EP93XX_VIC_IRQStatus); 336272444Sjkim 337272444Sjkim hardware_spl_level = pcpl; 338272444Sjkim ep93xx_set_intrmask(vic1_imask[pcpl] | vic1_hwpend, 339272444Sjkim vic2_imask[pcpl] | vic2_hwpend); 340272444Sjkim 341272444Sjkim vic1_hwpend &= ~vic1_imask[pcpl]; 342272444Sjkim vic2_hwpend &= ~vic2_imask[pcpl]; 343272444Sjkim 344284583Sjkim if (vic1_hwpend) { 345272444Sjkim irq = ffs(vic1_hwpend) - 1; 346272444Sjkim 347272444Sjkim iq = &intrq[irq]; 348272444Sjkim iq->iq_ev.ev_count++; 349272444Sjkim curcpu()->ci_data.cpu_nintr++; 350272444Sjkim TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 351272444Sjkim set_curcpl(ih->ih_ipl); 352272444Sjkim oldirqstate = enable_interrupts(I32_bit); 353272444Sjkim (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame); 354272444Sjkim restore_interrupts(oldirqstate); 355272444Sjkim } 356272444Sjkim } else if (vic2_hwpend) { 357272444Sjkim irq = ffs(vic2_hwpend) - 1; 358272444Sjkim 359272444Sjkim iq = &intrq[irq + VIC_NIRQ]; 360272444Sjkim iq->iq_ev.ev_count++; 361272444Sjkim curcpu()->ci_data.cpu_nintr++; 362272444Sjkim TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 363 set_curcpl(ih->ih_ipl); 364 oldirqstate = enable_interrupts(I32_bit); 365 (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame); 366 restore_interrupts(oldirqstate); 367 } 368 } 369 370 set_curcpl(pcpl); 371 hardware_spl_level = pcpl; 372 ep93xx_set_intrmask(vic1_imask[pcpl], vic2_imask[pcpl]); 373 374#ifdef __HAVE_FAST_SOFTINTS 375 cpu_dosoftints(); 376#endif 377} 378