1/* ********************************************************************* 2 * Broadcom Common Firmware Environment (CFE) 3 * 4 * Polled interrupt dispatch routines File: sb1250_irq.c 5 * 6 ********************************************************************* 7 * 8 * Copyright 2000,2001,2002,2003 9 * Broadcom Corporation. All rights reserved. 10 * 11 * This software is furnished under license and may be used and 12 * copied only in accordance with the following terms and 13 * conditions. Subject to these conditions, you may download, 14 * copy, install, use, modify and distribute modified or unmodified 15 * copies of this software in source and/or binary form. No title 16 * or ownership is transferred hereby. 17 * 18 * 1) Any source code used, modified or distributed must reproduce 19 * and retain this copyright notice and list of conditions 20 * as they appear in the source file. 21 * 22 * 2) No right is granted to use any trade name, trademark, or 23 * logo of Broadcom Corporation. The "Broadcom Corporation" 24 * name may not be used to endorse or promote products derived 25 * from this software without the prior written permission of 26 * Broadcom Corporation. 27 * 28 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR 29 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED 30 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 31 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT 32 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN 33 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, 34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 36 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 37 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 38 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 39 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF 40 * THE POSSIBILITY OF SUCH DAMAGE. 41 ********************************************************************* */ 42 43/* ********************************************************************* 44 * This module provides an interface for associating service 45 * routines with SB-1250 system and LDT interrupt sources. The 46 * various interrupt mapper registers are periodically polled 47 * and the requested service routine is invoked when a 48 * corresponding interrupt request is active and enabled. 49 * 50 * The interface is loosely based on irq.c from Linux. 51 * 52 * This is not a full-fledged interrupt handler. 53 * 54 * If CFG_INTERRUPTS == 0, it operates synchronously with the 55 * main polling loop and is never invoked directly by the 56 * hardware exception handler. If CFG_INTERRUPTS == 1, certain 57 * interrupt sources can be handled asynchronously as exceptions. 58 * 59 * For now, only CPU0 polls interrupts, via its interrupt mapper. 60 * 61 ********************************************************************* */ 62 63#include "lib_types.h" 64#include "lib_printf.h" 65#include "lib_malloc.h" 66 67#include "sbmips.h" 68#include "sb1250_regs.h" 69#include "sb1250_int.h" 70extern void sb1250_update_sr(uint32_t clear, uint32_t set); 71 72#include "exception.h" 73#include "cfe_irq.h" 74extern void sb1250_irq_arm(void); 75 76 77/* First level dispatching (MIPS IP level). */ 78 79#define IP_LEVELS 8 80 81/* Shared variables that must be protected in non-interrupt code. */ 82static ip_handler_t ip_handler[IP_LEVELS] = {NULL}; 83 84void 85cfe_irq_setvector(int index, ip_handler_t handler) 86{ 87 if (index >= 0 && index < IP_LEVELS) { 88 uint32_t set, clear; 89 90 ip_handler[index] = NULL; /* disable: see demux */ 91 if (handler == NULL) { 92 clear = _MM_MAKEMASK1(S_SR_IMMASK + index); 93 set = 0; 94 } 95 else { 96 clear = 0; 97 set = _MM_MAKEMASK1(S_SR_IMMASK + index); 98 } 99 sb1250_update_sr(clear, set); 100 ip_handler[index] = handler; 101 } 102} 103 104 105/* 106 * Dispatch function called from the exception handler for 107 * asynchronous (non-polled) interrupts. 108 * info is a pointer to the saved register block. 109 * 110 * At entry, interrupts will be masked. 111 */ 112 113static void 114sb1250_irq_demux(int code, mips_reg_t *info) 115{ 116 uint32_t pending; 117 118 pending = (uint32_t)(info[XCP0_CAUSE] & info[XCP0_SR]); 119 120 /* For now, we handle IP7 (internal timers) and IP2 (mapper) only */ 121 122 if (pending & M_CAUSE_IP7) { 123 if (ip_handler[7] != NULL) { 124 (*(ip_handler[7]))(7); 125 } 126 else { 127 /* mask off IP7, else we're caught here forever */ 128 sb1250_update_sr(M_SR_IM7, 0); 129 } 130 } 131 132 if (pending & M_CAUSE_IP2) { 133 if (ip_handler[2] != NULL) { 134 (*(ip_handler[2]))(2); 135 } 136 else { 137 /* mask off IP2, else we're caught here forever */ 138 sb1250_update_sr(M_SR_IM2, 0); 139 } 140 } 141} 142 143 144/* 145 * Initialize the MIPS level dispatch vector. 146 * This function should be called with interrupts disabled. 147 */ 148 149static void 150sb1250_irq_vectorinit(void) 151{ 152 int i; 153 154 for (i = 0; i < IP_LEVELS; i++) { 155 ip_handler[i] = NULL; 156 } 157 _exc_setvector(XTYPE_INTERRUPT, (void *) sb1250_irq_demux); 158 sb1250_irq_arm(); 159} 160 161 162/* Second level dispatching (sb1250 interrupt source level). */ 163 164#define NR_IRQS K_INT_SOURCES 165 166#define IMR_POINTER(cpu,reg) \ 167 ((volatile uint64_t *)(PHYS_TO_K1(A_IMR_REGISTER(cpu,reg)))) 168 169typedef struct irq_action_s irq_action_t; 170struct irq_action_s { 171 void (*handler)(void *arg); 172 void *arg; 173 unsigned long flags; 174 int device; /* to distinguish actions for shared interrupts */ 175 irq_action_t *next; 176}; 177 178typedef struct irq_desc_s { 179 irq_action_t *actions; 180 int depth; 181 int status; 182} irq_desc_t; 183 184/* status flags */ 185#define IRQ_DISABLED 0x0001 186 187/* Shared variables that must be protected in non-interrupt code. */ 188static irq_desc_t irq_desc[NR_IRQS]; 189 190/* 191 * cfe_irq_init is called early in the boot sequence. It is 192 * responsible for setting up the interrupt mapper and initializing 193 * the handler table that will be used for dispatching pending 194 * interrupts. If hard interrupts are used, it then enables hardware 195 * interrupts (initially all masked). 196 * 197 * This function should be called before interrupts are enabled. 198 */ 199 200void 201cfe_irq_init(void) 202{ 203 int i; 204 irq_desc_t *p; 205 206 for (i = 0, p = irq_desc; i < NR_IRQS; i++, p++) { 207 p->actions = NULL; 208 p->depth = 0; 209 p->status = IRQ_DISABLED; 210 } 211 212 /* initially, all interrupts are masked */ 213 *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) = ~((uint64_t)0); 214 *IMR_POINTER(1, R_IMR_INTERRUPT_MASK) = ~((uint64_t)0); 215 216 *IMR_POINTER(0, R_IMR_INTERRUPT_DIAG) = 0; 217 *IMR_POINTER(1, R_IMR_INTERRUPT_DIAG) = 0; 218 219 for (i = 0; i < R_IMR_INTERRUPT_MAP_COUNT; i++) { 220 *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE + 8*i) = 0; 221 *IMR_POINTER(1, R_IMR_INTERRUPT_MAP_BASE + 8*i) = 0; 222 } 223 224 sb1250_irq_vectorinit(); 225 226#if CFG_INTERRUPTS 227 cfe_irq_setvector(2, (ip_handler_t)cfe_irq_poll); 228#endif 229} 230 231 232/* cfe_mask_irq() is called to mask an interrupt at the hw level */ 233void 234cfe_mask_irq(int cpu, unsigned int irq) 235{ 236 int sr; 237 238 if (irq >= NR_IRQS) 239 return; 240 241 sr = cfe_irq_disable(); 242 *IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK) |= ((uint64_t)1 << irq); 243 __asm__ __volatile__ ("sync" : : : "memory"); 244 cfe_irq_enable(sr); 245} 246 247 248/* cfe_unmask_irq() is called to unmask an interrupt at the hw level */ 249void 250cfe_unmask_irq(int cpu, unsigned int irq) 251{ 252 int sr; 253 254 if (irq >= NR_IRQS) 255 return; 256 257 sr = cfe_irq_disable(); 258 *IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK) &=~ ((uint64_t)1 << irq); 259 cfe_irq_enable(sr); 260} 261 262 263/* If depth is 0, unmask the interrupt. Increment depth. */ 264void 265cfe_enable_irq(unsigned int irq) 266{ 267 int sr; 268 irq_desc_t *desc; 269 270 if (irq >= NR_IRQS) 271 return; 272 273 desc = &irq_desc[irq]; 274 /* The following code must be atomic */ 275 sr = cfe_irq_disable(); 276 if (desc->depth == 0) { 277 *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) &=~ ((uint64_t)1 << irq); 278 desc->status &=~ IRQ_DISABLED; 279 } 280 desc->depth++; 281 cfe_irq_enable(sr); 282} 283 284 285/* Decrement depth. If depth is 0, mask the interrupt. */ 286void 287cfe_disable_irq(unsigned int irq) 288{ 289 int sr; 290 irq_desc_t *desc; 291 292 if (irq >= NR_IRQS) 293 return; 294 295 desc = &irq_desc[irq]; 296 /* The following code must be atomic */ 297 sr = cfe_irq_disable(); 298 desc->depth--; 299 if (desc->depth == 0) { 300 *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) |= ((uint64_t)1 << irq); 301 desc->status |= IRQ_DISABLED; 302 __asm__ __volatile__ ("sync" : : : "memory"); 303 } 304 cfe_irq_enable(sr); 305} 306 307 308/* 309 * cfe_request_irq() is called by drivers to request addition to the 310 * chain of handlers called for a given interrupt. 311 */ 312int 313cfe_request_irq(unsigned int irq, 314 void (*handler)(void *), void *arg, 315 unsigned long irqflags, int device) 316{ 317 int sr; 318 irq_action_t *action; 319 irq_desc_t *desc; 320 irq_action_t *p; 321 322 if (irq >= NR_IRQS) { 323 return -1; 324 } 325 if (handler == NULL) { 326 return -1; 327 } 328 329 action = (irq_action_t *) KMALLOC(sizeof(irq_action_t), 0); 330 if (action == NULL) { 331 return -1; 332 } 333 334 action->handler = handler; 335 action->arg = arg; 336 action->flags = irqflags; 337 action->device = device; 338 action->next = NULL; 339 340 desc = &irq_desc[irq]; 341 342 /* The following block of code has to be executed atomically */ 343 sr = cfe_irq_disable(); 344 p = desc->actions; 345 if (p == NULL) { 346 desc->actions = action; 347 desc->depth = 0; 348 desc->status |= IRQ_DISABLED; 349 350 /* always direct to IP[2] for now */ 351 *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE + 8*irq) = 0; 352 __asm__ __volatile__ ("sync" : : : "memory"); 353 } 354 else { 355 /* Can't share interrupts unless both agree to */ 356 if ((p->flags & action->flags & CFE_IRQ_FLAGS_SHARED) == 0) { 357 cfe_enable_irq(irq); 358 KFREE(action); 359 xprintf("cfe_request_irq: conflicting unsharable interrupts.\n"); 360 return -1; 361 } 362 while (p->next != NULL) { 363 p = p->next; 364 } 365 p->next = action; 366 } 367 368 cfe_enable_irq(irq); 369 cfe_irq_enable(sr); 370 return 0; 371} 372 373 374/* 375 * free_irq() releases a handler set up by request_irq() 376 */ 377void 378cfe_free_irq(unsigned int irq, int device) 379{ 380 int sr; 381 irq_desc_t *desc; 382 irq_action_t *p, *q; 383 384 if (irq >= NR_IRQS) 385 return; 386 387 desc = &irq_desc[irq]; 388 389 /* The following block of code has to be executed atomically */ 390 sr = cfe_irq_disable(); 391 p = desc->actions; 392 q = NULL; 393 while (p != NULL) { 394 if (p->device == device) { 395 cfe_disable_irq(irq); 396 if (q == NULL) { 397 desc->actions = p->next; 398 if (desc->actions == NULL) 399 desc->status |= IRQ_DISABLED; 400 } 401 else 402 q->next = p->next; 403 break; 404 } 405 else { 406 q = p; 407 p = p->next; 408 } 409 } 410 cfe_irq_enable(sr); 411 if (p != NULL) 412 KFREE(p); 413} 414 415 416/* The interrupt polling code calls this routine to dispatch each 417 pending, unmasked interrupt. 418 419 For asynchronous interrupt processing, it is entered with 420 interrupts disabled. */ 421 422void sb1250_dispatch_irq(unsigned int irq); 423void 424sb1250_dispatch_irq(unsigned int irq) 425{ 426 irq_action_t *action; 427 irq_desc_t *desc = &irq_desc[irq]; 428 429 for (action = desc->actions; action != NULL; action = action->next) { 430 if (action->handler != NULL) { 431 (*action->handler)(action->arg); 432 } 433 } 434} 435