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