18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Lemote Inc. 48c2ecf20Sopenharmony_ci * Author: Fuxin Zhang, zhangfx@lemote.com 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/export.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h> 128c2ecf20Sopenharmony_ci#include <asm/i8259.h> 138c2ecf20Sopenharmony_ci#include <asm/mipsregs.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <loongson.h> 168c2ecf20Sopenharmony_ci#include <machine.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define LOONGSON_TIMER_IRQ (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */ 198c2ecf20Sopenharmony_ci#define LOONGSON_NORTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 6) /* bonito */ 208c2ecf20Sopenharmony_ci#define LOONGSON_UART_IRQ (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */ 218c2ecf20Sopenharmony_ci#define LOONGSON_SOUTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 2) /* i8259 */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define LOONGSON_INT_BIT_INT0 (1 << 11) 248c2ecf20Sopenharmony_ci#define LOONGSON_INT_BIT_INT1 (1 << 12) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * The generic i8259_irq() make the kernel hang on booting. Since we cannot 288c2ecf20Sopenharmony_ci * get the irq via the IRR directly, we access the ISR instead. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ciint mach_i8259_irq(void) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int irq, isr; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci irq = -1; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if ((LOONGSON_INTISR & LOONGSON_INTEN) & LOONGSON_INT_BIT_INT0) { 378c2ecf20Sopenharmony_ci raw_spin_lock(&i8259A_lock); 388c2ecf20Sopenharmony_ci isr = inb(PIC_MASTER_CMD) & 398c2ecf20Sopenharmony_ci ~inb(PIC_MASTER_IMR) & ~(1 << PIC_CASCADE_IR); 408c2ecf20Sopenharmony_ci if (!isr) 418c2ecf20Sopenharmony_ci isr = (inb(PIC_SLAVE_CMD) & ~inb(PIC_SLAVE_IMR)) << 8; 428c2ecf20Sopenharmony_ci irq = ffs(isr) - 1; 438c2ecf20Sopenharmony_ci if (unlikely(irq == 7)) { 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * This may be a spurious interrupt. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Read the interrupt status register (ISR). If the most 488c2ecf20Sopenharmony_ci * significant bit is not set then there is no valid 498c2ecf20Sopenharmony_ci * interrupt. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci outb(0x0B, PIC_MASTER_ISR); /* ISR register */ 528c2ecf20Sopenharmony_ci if (~inb(PIC_MASTER_ISR) & 0x80) 538c2ecf20Sopenharmony_ci irq = -1; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci raw_spin_unlock(&i8259A_lock); 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return irq; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mach_i8259_irq); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void i8259_irqdispatch(void) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int irq; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci irq = mach_i8259_irq(); 678c2ecf20Sopenharmony_ci if (irq >= 0) 688c2ecf20Sopenharmony_ci do_IRQ(irq); 698c2ecf20Sopenharmony_ci else 708c2ecf20Sopenharmony_ci spurious_interrupt(); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_civoid mach_irq_dispatch(unsigned int pending) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci if (pending & CAUSEF_IP7) 768c2ecf20Sopenharmony_ci do_IRQ(LOONGSON_TIMER_IRQ); 778c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP6) { /* North Bridge, Perf counter */ 788c2ecf20Sopenharmony_ci do_perfcnt_IRQ(); 798c2ecf20Sopenharmony_ci bonito_irqdispatch(); 808c2ecf20Sopenharmony_ci } else if (pending & CAUSEF_IP3) /* CPU UART */ 818c2ecf20Sopenharmony_ci do_IRQ(LOONGSON_UART_IRQ); 828c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP2) /* South Bridge */ 838c2ecf20Sopenharmony_ci i8259_irqdispatch(); 848c2ecf20Sopenharmony_ci else 858c2ecf20Sopenharmony_ci spurious_interrupt(); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic irqreturn_t ip6_action(int cpl, void *dev_id) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_civoid __init mach_init_irq(void) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci /* init all controller 968c2ecf20Sopenharmony_ci * 0-15 ------> i8259 interrupt 978c2ecf20Sopenharmony_ci * 16-23 ------> mips cpu interrupt 988c2ecf20Sopenharmony_ci * 32-63 ------> bonito irq 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* setup cs5536 as high level trigger */ 1028c2ecf20Sopenharmony_ci LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1; 1038c2ecf20Sopenharmony_ci LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Sets the first-level interrupt dispatcher. */ 1068c2ecf20Sopenharmony_ci mips_cpu_irq_init(); 1078c2ecf20Sopenharmony_ci init_i8259_irqs(); 1088c2ecf20Sopenharmony_ci bonito_irq_init(); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* setup north bridge irq (bonito) */ 1118c2ecf20Sopenharmony_ci if (request_irq(LOONGSON_NORTH_BRIDGE_IRQ, ip6_action, 1128c2ecf20Sopenharmony_ci IRQF_SHARED | IRQF_NO_THREAD, "cascade", ip6_action)) 1138c2ecf20Sopenharmony_ci pr_err("Failed to register north bridge cascade interrupt\n"); 1148c2ecf20Sopenharmony_ci /* setup source bridge irq (i8259) */ 1158c2ecf20Sopenharmony_ci if (request_irq(LOONGSON_SOUTH_BRIDGE_IRQ, no_action, 1168c2ecf20Sopenharmony_ci IRQF_NO_THREAD | IRQF_NO_SUSPEND, "cascade", NULL)) 1178c2ecf20Sopenharmony_ci pr_err("Failed to register south bridge cascade interrupt\n"); 1188c2ecf20Sopenharmony_ci} 119