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