162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC 462306a36Sopenharmony_ci * found on INDY and Indigo2 workstations. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1996 David S. Miller (davem@davemloft.net) 762306a36Sopenharmony_ci * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org) 862306a36Sopenharmony_ci * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) 962306a36Sopenharmony_ci * - Indigo2 changes 1062306a36Sopenharmony_ci * - Interrupt handling fixes 1162306a36Sopenharmony_ci * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org) 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/ftrace.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/irq_cpu.h> 2062306a36Sopenharmony_ci#include <asm/sgi/hpc3.h> 2162306a36Sopenharmony_ci#include <asm/sgi/ip22.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* So far nothing hangs here */ 2462306a36Sopenharmony_ci#undef USE_LIO3_IRQ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct sgint_regs *sgint; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic char lc0msk_to_irqnr[256]; 2962306a36Sopenharmony_cistatic char lc1msk_to_irqnr[256]; 3062306a36Sopenharmony_cistatic char lc2msk_to_irqnr[256]; 3162306a36Sopenharmony_cistatic char lc3msk_to_irqnr[256]; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciextern int ip22_eisa_init(void); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void enable_local0_irq(struct irq_data *d) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci /* don't allow mappable interrupt to be enabled from setup_irq, 3862306a36Sopenharmony_ci * we have our own way to do so */ 3962306a36Sopenharmony_ci if (d->irq != SGI_MAP_0_IRQ) 4062306a36Sopenharmony_ci sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0)); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void disable_local0_irq(struct irq_data *d) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct irq_chip ip22_local0_irq_type = { 4962306a36Sopenharmony_ci .name = "IP22 local 0", 5062306a36Sopenharmony_ci .irq_mask = disable_local0_irq, 5162306a36Sopenharmony_ci .irq_unmask = enable_local0_irq, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void enable_local1_irq(struct irq_data *d) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci /* don't allow mappable interrupt to be enabled from setup_irq, 5762306a36Sopenharmony_ci * we have our own way to do so */ 5862306a36Sopenharmony_ci if (d->irq != SGI_MAP_1_IRQ) 5962306a36Sopenharmony_ci sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1)); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void disable_local1_irq(struct irq_data *d) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1)); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic struct irq_chip ip22_local1_irq_type = { 6862306a36Sopenharmony_ci .name = "IP22 local 1", 6962306a36Sopenharmony_ci .irq_mask = disable_local1_irq, 7062306a36Sopenharmony_ci .irq_unmask = enable_local1_irq, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void enable_local2_irq(struct irq_data *d) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); 7662306a36Sopenharmony_ci sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2)); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void disable_local2_irq(struct irq_data *d) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2)); 8262306a36Sopenharmony_ci if (!sgint->cmeimask0) 8362306a36Sopenharmony_ci sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic struct irq_chip ip22_local2_irq_type = { 8762306a36Sopenharmony_ci .name = "IP22 local 2", 8862306a36Sopenharmony_ci .irq_mask = disable_local2_irq, 8962306a36Sopenharmony_ci .irq_unmask = enable_local2_irq, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void enable_local3_irq(struct irq_data *d) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); 9562306a36Sopenharmony_ci sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3)); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void disable_local3_irq(struct irq_data *d) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3)); 10162306a36Sopenharmony_ci if (!sgint->cmeimask1) 10262306a36Sopenharmony_ci sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct irq_chip ip22_local3_irq_type = { 10662306a36Sopenharmony_ci .name = "IP22 local 3", 10762306a36Sopenharmony_ci .irq_mask = disable_local3_irq, 10862306a36Sopenharmony_ci .irq_unmask = enable_local3_irq, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void indy_local0_irqdispatch(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u8 mask = sgint->istat0 & sgint->imask0; 11462306a36Sopenharmony_ci u8 mask2; 11562306a36Sopenharmony_ci int irq; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (mask & SGINT_ISTAT0_LIO2) { 11862306a36Sopenharmony_ci mask2 = sgint->vmeistat & sgint->cmeimask0; 11962306a36Sopenharmony_ci irq = lc2msk_to_irqnr[mask2]; 12062306a36Sopenharmony_ci } else 12162306a36Sopenharmony_ci irq = lc0msk_to_irqnr[mask]; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * workaround for INT2 bug; if irq == 0, INT2 has seen a fifo full 12562306a36Sopenharmony_ci * irq, but failed to latch it into status register 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci if (irq) 12862306a36Sopenharmony_ci do_IRQ(irq); 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci do_IRQ(SGINT_LOCAL0 + 0); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void indy_local1_irqdispatch(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci u8 mask = sgint->istat1 & sgint->imask1; 13662306a36Sopenharmony_ci u8 mask2; 13762306a36Sopenharmony_ci int irq; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (mask & SGINT_ISTAT1_LIO3) { 14062306a36Sopenharmony_ci mask2 = sgint->vmeistat & sgint->cmeimask1; 14162306a36Sopenharmony_ci irq = lc3msk_to_irqnr[mask2]; 14262306a36Sopenharmony_ci } else 14362306a36Sopenharmony_ci irq = lc1msk_to_irqnr[mask]; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* if irq == 0, then the interrupt has already been cleared */ 14662306a36Sopenharmony_ci if (irq) 14762306a36Sopenharmony_ci do_IRQ(irq); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciextern void ip22_be_interrupt(int irq); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void __irq_entry indy_buserror_irq(void) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int irq = SGI_BUSERR_IRQ; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci irq_enter(); 15762306a36Sopenharmony_ci kstat_incr_irq_this_cpu(irq); 15862306a36Sopenharmony_ci ip22_be_interrupt(irq); 15962306a36Sopenharmony_ci irq_exit(); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#ifdef USE_LIO3_IRQ 16362306a36Sopenharmony_ci#define SGI_INTERRUPTS SGINT_END 16462306a36Sopenharmony_ci#else 16562306a36Sopenharmony_ci#define SGI_INTERRUPTS SGINT_LOCAL3 16662306a36Sopenharmony_ci#endif 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciextern void indy_8254timer_irq(void); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * IRQs on the INDY look basically (barring software IRQs which we don't use 17262306a36Sopenharmony_ci * at all) like: 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * MIPS IRQ Source 17562306a36Sopenharmony_ci * -------- ------ 17662306a36Sopenharmony_ci * 0 Software (ignored) 17762306a36Sopenharmony_ci * 1 Software (ignored) 17862306a36Sopenharmony_ci * 2 Local IRQ level zero 17962306a36Sopenharmony_ci * 3 Local IRQ level one 18062306a36Sopenharmony_ci * 4 8254 Timer zero 18162306a36Sopenharmony_ci * 5 8254 Timer one 18262306a36Sopenharmony_ci * 6 Bus Error 18362306a36Sopenharmony_ci * 7 R4k timer (what we use) 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * We handle the IRQ according to _our_ priority which is: 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * Highest ---- R4k Timer 18862306a36Sopenharmony_ci * Local IRQ zero 18962306a36Sopenharmony_ci * Local IRQ one 19062306a36Sopenharmony_ci * Bus Error 19162306a36Sopenharmony_ci * 8254 Timer zero 19262306a36Sopenharmony_ci * Lowest ---- 8254 Timer one 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * then we just return, if multiple IRQs are pending then we will just take 19562306a36Sopenharmony_ci * another exception, big deal. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci unsigned int pending = read_c0_status() & read_c0_cause(); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * First we check for r4k counter/timer IRQ. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci if (pending & CAUSEF_IP7) 20662306a36Sopenharmony_ci do_IRQ(SGI_TIMER_IRQ); 20762306a36Sopenharmony_ci else if (pending & CAUSEF_IP2) 20862306a36Sopenharmony_ci indy_local0_irqdispatch(); 20962306a36Sopenharmony_ci else if (pending & CAUSEF_IP3) 21062306a36Sopenharmony_ci indy_local1_irqdispatch(); 21162306a36Sopenharmony_ci else if (pending & CAUSEF_IP6) 21262306a36Sopenharmony_ci indy_buserror_irq(); 21362306a36Sopenharmony_ci else if (pending & (CAUSEF_IP4 | CAUSEF_IP5)) 21462306a36Sopenharmony_ci indy_8254timer_irq(); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_civoid __init arch_init_irq(void) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int i; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Init local mask --> irq tables. */ 22262306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 22362306a36Sopenharmony_ci if (i & 0x80) { 22462306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7; 22562306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7; 22662306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7; 22762306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7; 22862306a36Sopenharmony_ci } else if (i & 0x40) { 22962306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6; 23062306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6; 23162306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6; 23262306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6; 23362306a36Sopenharmony_ci } else if (i & 0x20) { 23462306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5; 23562306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5; 23662306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5; 23762306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5; 23862306a36Sopenharmony_ci } else if (i & 0x10) { 23962306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4; 24062306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4; 24162306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4; 24262306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4; 24362306a36Sopenharmony_ci } else if (i & 0x08) { 24462306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3; 24562306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3; 24662306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3; 24762306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3; 24862306a36Sopenharmony_ci } else if (i & 0x04) { 24962306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2; 25062306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2; 25162306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2; 25262306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2; 25362306a36Sopenharmony_ci } else if (i & 0x02) { 25462306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1; 25562306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1; 25662306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1; 25762306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1; 25862306a36Sopenharmony_ci } else if (i & 0x01) { 25962306a36Sopenharmony_ci lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0; 26062306a36Sopenharmony_ci lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0; 26162306a36Sopenharmony_ci lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0; 26262306a36Sopenharmony_ci lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0; 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci lc0msk_to_irqnr[i] = 0; 26562306a36Sopenharmony_ci lc1msk_to_irqnr[i] = 0; 26662306a36Sopenharmony_ci lc2msk_to_irqnr[i] = 0; 26762306a36Sopenharmony_ci lc3msk_to_irqnr[i] = 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Mask out all interrupts. */ 27262306a36Sopenharmony_ci sgint->imask0 = 0; 27362306a36Sopenharmony_ci sgint->imask1 = 0; 27462306a36Sopenharmony_ci sgint->cmeimask0 = 0; 27562306a36Sopenharmony_ci sgint->cmeimask1 = 0; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* init CPU irqs */ 27862306a36Sopenharmony_ci mips_cpu_irq_init(); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) { 28162306a36Sopenharmony_ci struct irq_chip *handler; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (i < SGINT_LOCAL1) 28462306a36Sopenharmony_ci handler = &ip22_local0_irq_type; 28562306a36Sopenharmony_ci else if (i < SGINT_LOCAL2) 28662306a36Sopenharmony_ci handler = &ip22_local1_irq_type; 28762306a36Sopenharmony_ci else if (i < SGINT_LOCAL3) 28862306a36Sopenharmony_ci handler = &ip22_local2_irq_type; 28962306a36Sopenharmony_ci else 29062306a36Sopenharmony_ci handler = &ip22_local3_irq_type; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci irq_set_chip_and_handler(i, handler, handle_level_irq); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* vector handler. this register the IRQ as non-sharable */ 29662306a36Sopenharmony_ci if (request_irq(SGI_LOCAL_0_IRQ, no_action, IRQF_NO_THREAD, 29762306a36Sopenharmony_ci "local0 cascade", NULL)) 29862306a36Sopenharmony_ci pr_err("Failed to register local0 cascade interrupt\n"); 29962306a36Sopenharmony_ci if (request_irq(SGI_LOCAL_1_IRQ, no_action, IRQF_NO_THREAD, 30062306a36Sopenharmony_ci "local1 cascade", NULL)) 30162306a36Sopenharmony_ci pr_err("Failed to register local1 cascade interrupt\n"); 30262306a36Sopenharmony_ci if (request_irq(SGI_BUSERR_IRQ, no_action, IRQF_NO_THREAD, 30362306a36Sopenharmony_ci "Bus Error", NULL)) 30462306a36Sopenharmony_ci pr_err("Failed to register Bus Error interrupt\n"); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* cascade in cascade. i love Indy ;-) */ 30762306a36Sopenharmony_ci if (request_irq(SGI_MAP_0_IRQ, no_action, IRQF_NO_THREAD, 30862306a36Sopenharmony_ci "mapable0 cascade", NULL)) 30962306a36Sopenharmony_ci pr_err("Failed to register mapable0 cascade interrupt\n"); 31062306a36Sopenharmony_ci#ifdef USE_LIO3_IRQ 31162306a36Sopenharmony_ci if (request_irq(SGI_MAP_1_IRQ, no_action, IRQF_NO_THREAD, 31262306a36Sopenharmony_ci "mapable1 cascade", NULL)) 31362306a36Sopenharmony_ci pr_err("Failed to register mapable1 cascade interrupt\n"); 31462306a36Sopenharmony_ci#endif 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci#ifdef CONFIG_EISA 31762306a36Sopenharmony_ci if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */ 31862306a36Sopenharmony_ci ip22_eisa_init(); 31962306a36Sopenharmony_ci#endif 32062306a36Sopenharmony_ci} 321