1/* $NetBSD: ixp425_intr.c,v 1.28 2020/11/20 18:49:45 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2003 5 * Ichiro FUKUHARA <ichiro@ichiro.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29/* 30 * Copyright (c) 2001, 2002 Wasabi Systems, Inc. 31 * All rights reserved. 32 * 33 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. All advertising materials mentioning features or use of this software 44 * must display the following acknowledgement: 45 * This product includes software developed for the NetBSD Project by 46 * Wasabi Systems, Inc. 47 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 48 * or promote products derived from this software without specific prior 49 * written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 53 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 54 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 55 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 56 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 57 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 58 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 59 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 60 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 61 * POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64#include <sys/cdefs.h> 65__KERNEL_RCSID(0, "$NetBSD: ixp425_intr.c,v 1.28 2020/11/20 18:49:45 thorpej Exp $"); 66 67#ifndef EVBARM_SPL_NOINLINE 68#define EVBARM_SPL_NOINLINE 69#endif 70 71/* 72 * Interrupt support for the Intel IXP425 NetworkProcessor. 73 */ 74 75#include <sys/param.h> 76#include <sys/systm.h> 77#include <sys/kmem.h> 78 79#include <sys/bus.h> 80#include <machine/intr.h> 81 82#include <arm/cpufunc.h> 83 84#include <arm/xscale/ixp425reg.h> 85#include <arm/xscale/ixp425var.h> 86 87/* Interrupt handler queues. */ 88struct intrq intrq[NIRQ]; 89 90/* Interrupts to mask at each level. */ 91int ixp425_imask[NIPL]; 92 93/* Interrupts pending. */ 94volatile int ixp425_ipending; 95 96/* Software copy of the IRQs we have enabled. */ 97volatile uint32_t intr_enabled; 98 99/* Mask if interrupts steered to FIQs. */ 100uint32_t intr_steer; 101 102#ifdef __HAVE_FAST_SOFTINTS 103/* 104 * Map a software interrupt queue index 105 * 106 * XXX: !NOTE! :XXX 107 * We 'borrow' bits from the interrupt status register for interrupt sources 108 * which are not used by the current IXP425 port. Should any of the following 109 * interrupt sources be used at some future time, this must be revisited. 110 * 111 * Bit#31: SW Interrupt 1 112 * Bit#30: SW Interrupt 0 113 * Bit#14: Timestamp Timer 114 * Bit#11: General-purpose Timer 1 115 */ 116static const uint32_t si_to_irqbit[SI_NQUEUES] = { 117 IXP425_INT_bit31, /* SI_SOFT */ 118 IXP425_INT_bit30, /* SI_SOFTCLOCK */ 119 IXP425_INT_bit14, /* SI_SOFTNET */ 120 IXP425_INT_bit11, /* SI_SOFTSERIAL */ 121}; 122 123#define SI_TO_IRQBIT(si) (1U << si_to_irqbit[(si)]) 124 125/* 126 * Map a software interrupt queue to an interrupt priority level. 127 */ 128static const int si_to_ipl[] = { 129 [SI_SOFTCLOCK] = IPL_SOFTCLOCK, 130 [SI_SOFTBIO] = IPL_SOFTBIO, 131 [SI_SOFTNET] = IPL_SOFTNET, 132 [SI_SOFTSERIAL] = IPL_SOFTSERIAL, 133}; 134#endif /* __HAVE_FAST_SOFTINTS */ 135void ixp425_intr_dispatch(struct clockframe *frame); 136 137static inline uint32_t 138ixp425_irq_read(void) 139{ 140 return IXPREG(IXP425_INT_STATUS) & intr_enabled; 141} 142 143static inline void 144ixp425_set_intrsteer(void) 145{ 146 IXPREG(IXP425_INT_SELECT) = intr_steer & IXP425_INT_HWMASK; 147} 148 149static inline void 150ixp425_enable_irq(int irq) 151{ 152 153 intr_enabled |= (1U << irq); 154 ixp425_set_intrmask(); 155} 156 157static inline void 158ixp425_disable_irq(int irq) 159{ 160 161 intr_enabled &= ~(1U << irq); 162 ixp425_set_intrmask(); 163} 164 165static inline uint32_t 166ixp425_irq2gpio_bit(int irq) 167{ 168 169 static const uint8_t int2gpio[32] __attribute__ ((aligned(32))) = { 170 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#0 -> INT#5 */ 171 0x00, 0x01, /* GPIO#0 -> GPIO#1 */ 172 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#8 -> INT#13 */ 173 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#14 -> INT#18 */ 174 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* GPIO#2 -> GPIO#7 */ 175 0x08, 0x09, 0x0a, 0x0b, 0x0c, /* GPIO#8 -> GPIO#12 */ 176 0xff, 0xff /* INT#30 -> INT#31 */ 177 }; 178 179#ifdef DEBUG 180 if (int2gpio[irq] == 0xff) 181 panic("ixp425_irq2gpio_bit: bad GPIO irq: %d\n", irq); 182#endif 183 return (1U << int2gpio[irq]); 184} 185 186/* 187 * NOTE: This routine must be called with interrupts disabled in the CPSR. 188 */ 189static void 190ixp425_intr_calculate_masks(void) 191{ 192 struct intrq *iq; 193 struct intrhand *ih; 194 int irq, ipl; 195 196 /* First, figure out which IPLs each IRQ has. */ 197 for (irq = 0; irq < NIRQ; irq++) { 198 int levels = 0; 199 iq = &intrq[irq]; 200 ixp425_disable_irq(irq); 201 for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL; 202 ih = TAILQ_NEXT(ih, ih_list)) 203 levels |= (1U << ih->ih_ipl); 204 iq->iq_levels = levels; 205 } 206 207 /* Next, figure out which IRQs are used by each IPL. */ 208 for (ipl = 0; ipl < NIPL; ipl++) { 209 int irqs = 0; 210 for (irq = 0; irq < NIRQ; irq++) { 211 if (intrq[irq].iq_levels & (1U << ipl)) 212 irqs |= (1U << irq); 213 } 214 ixp425_imask[ipl] = irqs; 215 } 216 217 KASSERT(ixp425_imask[IPL_NONE] == 0); 218 219#ifdef __HAVE_FAST_SOFTINTS 220 /* 221 * Initialize the soft interrupt masks to block themselves. 222 */ 223 ixp425_imask[IPL_SOFTCLOCK] = SI_TO_IRQBIT(SI_SOFTCLOCK); 224 ixp425_imask[IPL_SOFTBIO] = SI_TO_IRQBIT(SI_SOFTBIO); 225 ixp425_imask[IPL_SOFTNET] = SI_TO_IRQBIT(SI_SOFTNET); 226 ixp425_imask[IPL_SOFTSERIAL] = SI_TO_IRQBIT(SI_SOFTSERIAL); 227#else 228 KASSERT(ixp425_imask[IPL_SOFTCLOCK] == 0); 229 KASSERT(ixp425_imask[IPL_SOFTBIO] == 0); 230 KASSERT(ixp425_imask[IPL_SOFTNET] == 0); 231 KASSERT(ixp425_imask[IPL_SOFTSERIAL] == 0); 232#endif 233 234 /* 235 * Enforce a hierarchy that gives "slow" device (or devices with 236 * limited input buffer space/"real-time" requirements) a better 237 * chance at not dropping data. 238 */ 239 ixp425_imask[IPL_SCHED] |= ixp425_imask[IPL_VM]; 240 ixp425_imask[IPL_HIGH] |= ixp425_imask[IPL_SCHED]; 241 242 /* 243 * Now compute which IRQs must be blocked when servicing any 244 * given IRQ. 245 */ 246 for (irq = 0; irq < NIRQ; irq++) { 247 int irqs = (1U << irq); 248 iq = &intrq[irq]; 249 if (TAILQ_FIRST(&iq->iq_list) != NULL) 250 ixp425_enable_irq(irq); 251 for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL; 252 ih = TAILQ_NEXT(ih, ih_list)) 253 irqs |= ixp425_imask[ih->ih_ipl]; 254 iq->iq_mask = irqs; 255 } 256} 257 258void 259splx(int new) 260{ 261 ixp425_splx(new); 262} 263 264int 265_spllower(int ipl) 266{ 267 return (ixp425_spllower(ipl)); 268} 269 270int 271_splraise(int ipl) 272{ 273 return (ixp425_splraise(ipl)); 274} 275 276/* 277 * ixp425_icu_init: 278 * 279 * Called early in bootstrap to make clear interrupt register 280 */ 281void 282ixp425_icu_init(void) 283{ 284 285 intr_enabled = 0; /* All interrupts disabled */ 286 ixp425_set_intrmask(); 287 288 intr_steer = 0; /* All interrupts steered to IRQ */ 289 ixp425_set_intrsteer(); 290} 291 292/* 293 * ixp425_intr_init: 294 * 295 * Initialize the rest of the interrupt subsystem, making it 296 * ready to handle interrupts from devices. 297 */ 298void 299ixp425_intr_init(void) 300{ 301 struct intrq *iq; 302 int i; 303 304 intr_enabled = 0; 305 306 for (i = 0; i < NIRQ; i++) { 307 iq = &intrq[i]; 308 TAILQ_INIT(&iq->iq_list); 309 310 snprintf(iq->iq_name, sizeof(iq->iq_name), "irq %d", i); 311 } 312 313 ixp425_intr_calculate_masks(); 314 315 /* Enable IRQs (don't yet use FIQs). */ 316 enable_interrupts(I32_bit); 317} 318 319void 320ixp425_intr_evcnt_attach(void) 321{ 322 struct intrq *iq; 323 int i; 324 325 for (i = 0; i < NIRQ; i++) { 326 iq = &intrq[i]; 327 evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR, 328 NULL, "ixp425", iq->iq_name); 329 } 330} 331 332void * 333ixp425_intr_establish(int irq, int ipl, int (*func)(void *), void *arg) 334{ 335 struct intrq *iq; 336 struct intrhand *ih; 337 u_int oldirqstate; 338 339 if (irq < 0 || irq > NIRQ) 340 panic("ixp425_intr_establish: IRQ %d out of range", irq); 341#ifdef DEBUG 342 printf("ixp425_intr_establish(irq=%d, ipl=%d, func=%08x, arg=%08x)\n", 343 irq, ipl, (uint32_t) func, (uint32_t) arg); 344#endif 345 346 ih = kmem_alloc(sizeof(*ih), KM_SLEEP); 347 ih->ih_func = func; 348 ih->ih_arg = arg; 349 ih->ih_ipl = ipl; 350 ih->ih_irq = irq; 351 352 iq = &intrq[irq]; 353 354 /* All IXP425 interrupts are level-triggered. */ 355 iq->iq_ist = IST_LEVEL; /* XXX */ 356 357 oldirqstate = disable_interrupts(I32_bit); 358 359 TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); 360 361 ixp425_intr_calculate_masks(); 362 363 restore_interrupts(oldirqstate); 364 365 return (ih); 366} 367 368void 369ixp425_intr_disestablish(void *cookie) 370{ 371 struct intrhand *ih = cookie; 372 struct intrq *iq = &intrq[ih->ih_irq]; 373 int oldirqstate; 374 375 oldirqstate = disable_interrupts(I32_bit); 376 377 TAILQ_REMOVE(&iq->iq_list, ih, ih_list); 378 379 ixp425_intr_calculate_masks(); 380 381 restore_interrupts(oldirqstate); 382} 383 384void 385ixp425_intr_dispatch(struct clockframe *frame) 386{ 387 struct intrq *iq; 388 struct intrhand *ih; 389 int oldirqstate, irq, ibit, hwpend; 390 struct cpu_info * const ci = curcpu(); 391 const int ppl = ci->ci_cpl; 392 const uint32_t imask = ixp425_imask[ppl]; 393 394 hwpend = ixp425_irq_read(); 395 396 /* 397 * Disable all the interrupts that are pending. We will 398 * reenable them once they are processed and not masked. 399 */ 400 intr_enabled &= ~hwpend; 401 ixp425_set_intrmask(); 402 403 while (hwpend != 0) { 404 irq = ffs(hwpend) - 1; 405 ibit = (1U << irq); 406 407 hwpend &= ~ibit; 408 409 if (imask & ibit) { 410 /* 411 * IRQ is masked; mark it as pending and check 412 * the next one. Note: the IRQ is already disabled. 413 */ 414 ixp425_ipending |= ibit; 415 continue; 416 } 417 418 ixp425_ipending &= ~ibit; 419 420 iq = &intrq[irq]; 421 iq->iq_ev.ev_count++; 422 ci->ci_data.cpu_nintr++; 423 424 /* Clear down non-level triggered GPIO interrupts now */ 425 if ((ibit & IXP425_INT_GPIOMASK) && iq->iq_ist != IST_LEVEL) { 426 IXPREG(IXP425_GPIO_VBASE + IXP425_GPIO_GPISR) = 427 ixp425_irq2gpio_bit(irq); 428 } 429 430 TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 431 ci->ci_cpl = ih->ih_ipl; 432 oldirqstate = enable_interrupts(I32_bit); 433 (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame); 434 restore_interrupts(oldirqstate); 435 } 436 437 /* Clear down level triggered GPIO interrupts now */ 438 if ((ibit & IXP425_INT_GPIOMASK) && iq->iq_ist == IST_LEVEL) { 439 IXPREG(IXP425_GPIO_VBASE + IXP425_GPIO_GPISR) = 440 ixp425_irq2gpio_bit(irq); 441 } 442 443 ci->ci_cpl = ppl; 444 445 /* Re-enable this interrupt now that's it's cleared. */ 446 intr_enabled |= ibit; 447 ixp425_set_intrmask(); 448 449 /* 450 * Don't forget to include interrupts which may have 451 * arrived in the meantime. 452 */ 453 hwpend |= ((ixp425_ipending & IXP425_INT_HWMASK) & ~imask); 454 } 455 456#ifdef __HAVE_FAST_SOFTINTS 457 cpu_dosoftints(); 458#endif 459} 460