162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 Lemote Inc. 462306a36Sopenharmony_ci * Author: Fuxin Zhang, zhangfx@lemote.com 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/export.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/irq_cpu.h> 1262306a36Sopenharmony_ci#include <asm/i8259.h> 1362306a36Sopenharmony_ci#include <asm/mipsregs.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <loongson.h> 1662306a36Sopenharmony_ci#include <machine.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define LOONGSON_TIMER_IRQ (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */ 1962306a36Sopenharmony_ci#define LOONGSON_NORTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 6) /* bonito */ 2062306a36Sopenharmony_ci#define LOONGSON_UART_IRQ (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */ 2162306a36Sopenharmony_ci#define LOONGSON_SOUTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 2) /* i8259 */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define LOONGSON_INT_BIT_INT0 (1 << 11) 2462306a36Sopenharmony_ci#define LOONGSON_INT_BIT_INT1 (1 << 12) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * The generic i8259_irq() make the kernel hang on booting. Since we cannot 2862306a36Sopenharmony_ci * get the irq via the IRR directly, we access the ISR instead. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ciint mach_i8259_irq(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int irq, isr; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci irq = -1; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if ((LOONGSON_INTISR & LOONGSON_INTEN) & LOONGSON_INT_BIT_INT0) { 3762306a36Sopenharmony_ci raw_spin_lock(&i8259A_lock); 3862306a36Sopenharmony_ci isr = inb(PIC_MASTER_CMD) & 3962306a36Sopenharmony_ci ~inb(PIC_MASTER_IMR) & ~(1 << PIC_CASCADE_IR); 4062306a36Sopenharmony_ci if (!isr) 4162306a36Sopenharmony_ci isr = (inb(PIC_SLAVE_CMD) & ~inb(PIC_SLAVE_IMR)) << 8; 4262306a36Sopenharmony_ci irq = ffs(isr) - 1; 4362306a36Sopenharmony_ci if (unlikely(irq == 7)) { 4462306a36Sopenharmony_ci /* 4562306a36Sopenharmony_ci * This may be a spurious interrupt. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Read the interrupt status register (ISR). If the most 4862306a36Sopenharmony_ci * significant bit is not set then there is no valid 4962306a36Sopenharmony_ci * interrupt. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci outb(0x0B, PIC_MASTER_ISR); /* ISR register */ 5262306a36Sopenharmony_ci if (~inb(PIC_MASTER_ISR) & 0x80) 5362306a36Sopenharmony_ci irq = -1; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci raw_spin_unlock(&i8259A_lock); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return irq; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ciEXPORT_SYMBOL(mach_i8259_irq); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void i8259_irqdispatch(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci int irq; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci irq = mach_i8259_irq(); 6762306a36Sopenharmony_ci if (irq >= 0) 6862306a36Sopenharmony_ci do_IRQ(irq); 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci spurious_interrupt(); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_civoid mach_irq_dispatch(unsigned int pending) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci if (pending & CAUSEF_IP7) 7662306a36Sopenharmony_ci do_IRQ(LOONGSON_TIMER_IRQ); 7762306a36Sopenharmony_ci else if (pending & CAUSEF_IP6) { /* North Bridge, Perf counter */ 7862306a36Sopenharmony_ci bonito_irqdispatch(); 7962306a36Sopenharmony_ci } else if (pending & CAUSEF_IP3) /* CPU UART */ 8062306a36Sopenharmony_ci do_IRQ(LOONGSON_UART_IRQ); 8162306a36Sopenharmony_ci else if (pending & CAUSEF_IP2) /* South Bridge */ 8262306a36Sopenharmony_ci i8259_irqdispatch(); 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci spurious_interrupt(); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic irqreturn_t ip6_action(int cpl, void *dev_id) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci return IRQ_HANDLED; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid __init mach_init_irq(void) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci /* init all controller 9562306a36Sopenharmony_ci * 0-15 ------> i8259 interrupt 9662306a36Sopenharmony_ci * 16-23 ------> mips cpu interrupt 9762306a36Sopenharmony_ci * 32-63 ------> bonito irq 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* setup cs5536 as high level trigger */ 10162306a36Sopenharmony_ci LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1; 10262306a36Sopenharmony_ci LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Sets the first-level interrupt dispatcher. */ 10562306a36Sopenharmony_ci mips_cpu_irq_init(); 10662306a36Sopenharmony_ci init_i8259_irqs(); 10762306a36Sopenharmony_ci bonito_irq_init(); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* setup north bridge irq (bonito) */ 11062306a36Sopenharmony_ci if (request_irq(LOONGSON_NORTH_BRIDGE_IRQ, ip6_action, 11162306a36Sopenharmony_ci IRQF_SHARED | IRQF_NO_THREAD, "cascade", ip6_action)) 11262306a36Sopenharmony_ci pr_err("Failed to register north bridge cascade interrupt\n"); 11362306a36Sopenharmony_ci /* setup source bridge irq (i8259) */ 11462306a36Sopenharmony_ci if (request_irq(LOONGSON_SOUTH_BRIDGE_IRQ, no_action, 11562306a36Sopenharmony_ci IRQF_NO_THREAD | IRQF_NO_SUSPEND, "cascade", NULL)) 11662306a36Sopenharmony_ci pr_err("Failed to register south bridge cascade interrupt\n"); 11762306a36Sopenharmony_ci} 118