1/* 2 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 * 16 * Broadcom HND BCM47xx chips interrupt dispatcher. 17 * Derived from ../generic/irq.c 18 * 19 * MIPS IRQ Source 20 * -------- ------------------ 21 * 0 Software 22 * 1 Software 23 * 2 Hardware (shared) 24 * 3 Hardware 25 * 4 Hardware 26 * 5 Hardware 27 * 6 Hardware 28 * 7 Hardware (r4k timer) 29 * 30 * MIPS IRQ Linux IRQ 31 * -------- ----------- 32 * 0 - 1 0 - 1 33 * 2 8 and above 34 * 3 - 7 3 - 7 35 * 36 * MIPS has 8 IRQs as indicated and assigned above. SB cores 37 * that use dedicated MIPS IRQ3 to IRQ6 are 1-to-1 mapped to 38 * linux IRQ3 to IRQ6. SB cores sharing MIPS IRQ2 are mapped 39 * to linux IRQ8 and above as virtual IRQs using the following 40 * mapping: 41 * 42 * <linux-IRQ#> = <SB-core-flag> + <base-IRQ> + 2 43 * 44 * where <base-IRQ> is specified in setup.c when calling 45 * sb_mips_init(), 2 is to offset the two software IRQs. 46 * 47 * $Id: irq.c,v 1.11 2010-01-07 06:40:35 $ 48 */ 49 50#include <linux/version.h> 51 52#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) 53#include <linux/config.h> 54#endif 55 56#ifdef CONFIG_SMP 57/* 58 * This code is designed to work on Uniprocessor only. 59 * 60 * To support SMP we must know: 61 * - interrupt architecture 62 * - interrupt distribution machenism 63 */ 64#error "This implementation does not support SMP" 65#endif 66 67#include <linux/init.h> 68#include <linux/irq.h> 69#include <linux/kernel_stat.h> 70 71#include <asm/mipsregs.h> 72 73#include <typedefs.h> 74#include <osl.h> 75#include <bcmutils.h> 76#include <hndsoc.h> 77#include <siutils.h> 78#include <hndcpu.h> 79#include <hndsoc.h> 80#include <mips74k_core.h> 81#include "bcm947xx.h" 82 83/* cp0 SR register IM field */ 84#define SR_IM(irq) (1 << ((irq) + STATUSB_IP0)) 85 86/* cp0 CR register IP field */ 87#define CR_IP(irq) (1 << ((irq) + CAUSEB_IP0)) 88 89/* other local constants */ 90#define NUM_IRQS 32 91 92/* local variables and functions */ 93static sbconfig_t *ccsbr = NULL; /* Chipc core SB config regs */ 94static sbconfig_t *mipssbr = NULL; /* MIPS core SB config regs */ 95static int mipsirq = -1; /* MIPS core virtual IRQ number */ 96static uint32 shints = 0; /* Set of shared interrupts */ 97static int irq2en = 0; /* MIPS IRQ2 enable count */ 98static uint *mips_corereg = NULL; 99 100/* global variables and functions */ 101extern si_t *bcm947xx_sih; /* defined in setup.c */ 102 103extern asmlinkage void brcmIRQ(void); 104 105/* Control functions for MIPS IRQ0 to IRQ7 */ 106static INLINE void 107enable_brcm_irq(unsigned int irq) 108{ 109 set_c0_status(SR_IM(irq)); 110 irq_enable_hazard(); 111} 112 113static INLINE void 114disable_brcm_irq(unsigned int irq) 115{ 116 clear_c0_status(SR_IM(irq)); 117 irq_disable_hazard(); 118} 119 120static void 121ack_brcm_irq(unsigned int irq) 122{ 123 /* Done in plat_irq_dispatch()! */ 124} 125 126static void 127end_brcm_irq(unsigned int irq) 128{ 129 /* Done in plat_irq_dispatch()! */ 130} 131 132/* Control functions for linux IRQ8 and above */ 133static INLINE void 134enable_brcm_irq2(unsigned int irq) 135{ 136 ASSERT(irq2en >= 0); 137 if (irq2en++) 138 return; 139 enable_brcm_irq(2); 140} 141 142static INLINE void 143disable_brcm_irq2(unsigned int irq) 144{ 145 ASSERT(irq2en > 0); 146 if (--irq2en) 147 return; 148 disable_brcm_irq(2); 149} 150 151static void 152ack_brcm_irq2(unsigned int irq) 153{ 154 /* Already done in plat_irq_dispatch()! */ 155} 156 157static void 158end_brcm_irq2(unsigned int irq) 159{ 160 /* Already done in plat_irq_dispatch()! */ 161} 162 163/* 164 * Route interrupts to ISR(s). 165 * 166 * This function is entered with the IE disabled. It can be 167 * re-entered as soon as the IE is re-enabled in function 168 * handle_IRQ_envet(). 169 */ 170void BCMFASTPATH 171plat_irq_dispatch(struct pt_regs *regs) 172{ 173 u32 pending, ipvec; 174 unsigned long flags = 0; 175 int irq; 176 177 /* Disable MIPS IRQs with pending interrupts */ 178 pending = read_c0_cause() & CAUSEF_IP; 179 pending &= read_c0_status(); 180 clear_c0_status(pending); 181 irq_disable_hazard(); 182 183 /* Handle MIPS timer interrupt. Re-enable MIPS IRQ7 184 * immediately after servicing the interrupt so that 185 * we can take this kind of interrupt again later 186 * while servicing other interrupts. 187 */ 188 if (pending & CAUSEF_IP7) { 189 do_IRQ(7); 190 pending &= ~CAUSEF_IP7; 191 set_c0_status(STATUSF_IP7); 192 irq_enable_hazard(); 193 } 194 195 /* Build bitvec for pending interrupts. Start with 196 * MIPS IRQ2 and add linux IRQs to higher bits to 197 * make the interrupt processing uniform. 198 */ 199 ipvec = pending >> CAUSEB_IP2; 200 if (pending & CAUSEF_IP2) { 201 if (ccsbr) 202 flags = R_REG(NULL, &ccsbr->sbflagst); 203 204 /* Read intstatus */ 205 if (mips_corereg) 206 flags = R_REG(NULL, &((mips74kregs_t *)mips_corereg)->intstatus); 207 208 flags &= shints; 209 ipvec |= flags << SBMIPS_VIRTIRQ_BASE; 210 } 211 212#ifdef CONFIG_HND_BMIPS3300_PROF 213 /* Handle MIPS core interrupt. Re-enable the MIPS IRQ that 214 * MIPS core is assigned to immediately after servicing the 215 * interrupt so that we can take this kind of interrupt again 216 * later while servicing other interrupts. 217 * 218 * mipsirq < 0 indicates MIPS core IRQ # is unknown. 219 */ 220 if (mipsirq >= 0 && (ipvec & (1 << mipsirq))) { 221 /* MIPS core raised the interrupt on the shared MIPS IRQ2. 222 * Make sure MIPS core is the only interrupt source before 223 * re-enabling the IRQ. 224 */ 225 if (mipsirq >= SBMIPS_VIRTIRQ_BASE) { 226 if (flags == (1 << (mipsirq-SBMIPS_VIRTIRQ_BASE))) { 227 irq = mipsirq + 2; 228 do_IRQ(irq); 229 ipvec &= ~(1 << mipsirq); 230 pending &= ~CAUSEF_IP2; 231 set_c0_status(STATUSF_IP2); 232 irq_enable_hazard(); 233 } 234 } 235 /* MIPS core raised the interrupt on a dedicated MIPS IRQ. 236 * Re-enable the IRQ immediately. 237 */ 238 else { 239 irq = mipsirq + 2; 240 do_IRQ(irq); 241 ipvec &= ~(1 << mipsirq); 242 pending &= ~CR_IP(irq); 243 set_c0_status(SR_IM(irq)); 244 irq_enable_hazard(); 245 } 246 } 247#endif /* CONFIG_HND_BMIPS3300_PROF */ 248 249 /* Shared interrupt bits are shifted to respective bit positions in 250 * ipvec above. IP2 (bit 0) is of no significance, hence shifting the 251 * bit map by 1 to the right. 252 */ 253 ipvec >>= 1; 254 255 /* Handle all other interrupts. Re-enable disabled MIPS IRQs 256 * after processing all pending interrupts. 257 */ 258 for (irq = 3; ipvec != 0; irq++) { 259 if (ipvec & 1) 260 do_IRQ(irq); 261 ipvec >>= 1; 262 } 263 set_c0_status(pending); 264 irq_enable_hazard(); 265 266 /* Process any pending softirqs (tasklets, softirqs ...) */ 267 local_irq_save(flags); 268 if (local_softirq_pending() && !in_interrupt()) 269 __do_softirq(); 270 local_irq_restore(flags); 271} 272 273/* MIPS IRQ0 to IRQ7 interrupt controller */ 274static struct irq_chip brcm_irq_type = { 275 .name = "MIPS", 276 .ack = ack_brcm_irq, 277 .mask = disable_brcm_irq, 278 .mask_ack = disable_brcm_irq, 279 .unmask = enable_brcm_irq, 280 .end = end_brcm_irq 281}; 282 283/* linux IRQ8 and above interrupt controller */ 284static struct irq_chip brcm_irq2_type = { 285 .name = "IRQ2", 286 .ack = ack_brcm_irq2, 287 .mask = disable_brcm_irq2, 288 .mask_ack = disable_brcm_irq2, 289 .unmask = enable_brcm_irq2, 290 .end = end_brcm_irq2 291}; 292 293/* 294 * We utilize chipcommon configuration register SBFlagSt to implement a 295 * smart shared IRQ handling machenism through which only ISRs registered 296 * for the SB cores that raised the interrupt are invoked. This machenism 297 * relies on the SBFlagSt register's reliable recording of the SB cores 298 * that raised the interrupt. 299 */ 300void __init 301arch_init_irq(void) 302{ 303 int i; 304 uint32 coreidx, mips_core_id; 305 void *regs; 306 307 if (BCM330X(current_cpu_data.processor_id)) 308 mips_core_id = MIPS33_CORE_ID; 309 else if (MIPS74K(current_cpu_data.processor_id)) 310 mips_core_id = MIPS74K_CORE_ID; 311 else { 312 printk(KERN_ERR "MIPS CPU type %x unknown", current_cpu_data.processor_id); 313 return; 314 } 315 316 /* Cache chipc and mips33 config registers */ 317 ASSERT(bcm947xx_sih); 318 coreidx = si_coreidx(bcm947xx_sih); 319 regs = si_setcore(bcm947xx_sih, mips_core_id, 0); 320 mipsirq = si_irq(bcm947xx_sih); 321 if (bcm947xx_sih->socitype == SOCI_SB) { 322 if (regs) 323 mipssbr = (sbconfig_t *)((ulong)regs + SBCONFIGOFF); 324 325 if ((regs = si_setcore(bcm947xx_sih, CC_CORE_ID, 0))) 326 ccsbr = (sbconfig_t *)((ulong)regs + SBCONFIGOFF); 327 } 328 si_setcoreidx(bcm947xx_sih, coreidx); 329 330 if (BCM330X(current_cpu_data.processor_id)) { 331 /* Cache mips33 sbintvec register */ 332 if (mipssbr) 333 shints = R_REG(NULL, &mipssbr->sbintvec); 334 } else { 335 uint32 *intmask; 336 337 /* Use intmask5 register to route the timer interrupt */ 338 intmask = (uint32 *) &((mips74kregs_t *)regs)->intmask[5]; 339 W_REG(NULL, intmask, 1 << 31); 340 341 intmask = (uint32 *) &((mips74kregs_t *)regs)->intmask[0]; 342 shints = R_REG(NULL, intmask); 343 344 /* Save the pointer to mips core registers */ 345 mips_corereg = regs; 346 } 347 348 /* Install interrupt controllers */ 349 for (i = 0; i < NR_IRQS; i++) { 350 set_irq_chip(i, (i < SBMIPS_NUMIRQS ? &brcm_irq_type : &brcm_irq2_type)); 351 } 352} 353