162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com
762306a36Sopenharmony_ci * Copyright (C) 2000, 2001, 2004 MIPS Technologies, Inc.
862306a36Sopenharmony_ci * Copyright (C) 2001 Ralf Baechle
962306a36Sopenharmony_ci * Copyright (C) 2013 Imagination Technologies Ltd.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Routines for generic manipulation of the interrupts found on the MIPS
1262306a36Sopenharmony_ci * Malta board. The interrupt controller is located in the South Bridge
1362306a36Sopenharmony_ci * a PIIX4 device with two internal 82C95 interrupt controllers.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/irq.h>
1762306a36Sopenharmony_ci#include <linux/irqchip.h>
1862306a36Sopenharmony_ci#include <linux/sched.h>
1962306a36Sopenharmony_ci#include <linux/smp.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/io.h>
2262306a36Sopenharmony_ci#include <linux/of_irq.h>
2362306a36Sopenharmony_ci#include <linux/kernel_stat.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/random.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <asm/traps.h>
2862306a36Sopenharmony_ci#include <asm/i8259.h>
2962306a36Sopenharmony_ci#include <asm/irq_cpu.h>
3062306a36Sopenharmony_ci#include <asm/irq_regs.h>
3162306a36Sopenharmony_ci#include <asm/mips-boards/malta.h>
3262306a36Sopenharmony_ci#include <asm/mips-boards/maltaint.h>
3362306a36Sopenharmony_ci#include <asm/mips-cps.h>
3462306a36Sopenharmony_ci#include <asm/gt64120.h>
3562306a36Sopenharmony_ci#include <asm/mips-boards/generic.h>
3662306a36Sopenharmony_ci#include <asm/mips-boards/msc01_pci.h>
3762306a36Sopenharmony_ci#include <asm/msc01_ic.h>
3862306a36Sopenharmony_ci#include <asm/setup.h>
3962306a36Sopenharmony_ci#include <asm/rtlx.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic inline int mips_pcibios_iack(void)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	int irq;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * Determine highest priority pending interrupt by performing
4762306a36Sopenharmony_ci	 * a PCI Interrupt Acknowledge cycle.
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	switch (mips_revision_sconid) {
5062306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
5162306a36Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
5262306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
5362306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
5462306a36Sopenharmony_ci		MSC_READ(MSC01_PCI_IACK, irq);
5562306a36Sopenharmony_ci		irq &= 0xff;
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	case MIPS_REVISION_SCON_GT64120:
5862306a36Sopenharmony_ci		irq = GT_READ(GT_PCI0_IACK_OFS);
5962306a36Sopenharmony_ci		irq &= 0xff;
6062306a36Sopenharmony_ci		break;
6162306a36Sopenharmony_ci	case MIPS_REVISION_SCON_BONITO:
6262306a36Sopenharmony_ci		/* The following will generate a PCI IACK cycle on the
6362306a36Sopenharmony_ci		 * Bonito controller. It's a little bit kludgy, but it
6462306a36Sopenharmony_ci		 * was the easiest way to implement it in hardware at
6562306a36Sopenharmony_ci		 * the given time.
6662306a36Sopenharmony_ci		 */
6762306a36Sopenharmony_ci		BONITO_PCIMAP_CFG = 0x20000;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		/* Flush Bonito register block */
7062306a36Sopenharmony_ci		(void) BONITO_PCIMAP_CFG;
7162306a36Sopenharmony_ci		iob();	  /* sync */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		irq = __raw_readl((u32 *)_pcictrl_bonito_pcicfg);
7462306a36Sopenharmony_ci		iob();	  /* sync */
7562306a36Sopenharmony_ci		irq &= 0xff;
7662306a36Sopenharmony_ci		BONITO_PCIMAP_CFG = 0;
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	default:
7962306a36Sopenharmony_ci		pr_emerg("Unknown system controller.\n");
8062306a36Sopenharmony_ci		return -1;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	return irq;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void corehi_irqdispatch(void)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	unsigned int intedge, intsteer, pcicmd, pcibadaddr;
8862306a36Sopenharmony_ci	unsigned int pcimstat, intisr, inten, intpol;
8962306a36Sopenharmony_ci	unsigned int intrcause, datalo, datahi;
9062306a36Sopenharmony_ci	struct pt_regs *regs = get_irq_regs();
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	pr_emerg("CoreHI interrupt, shouldn't happen, we die here!\n");
9362306a36Sopenharmony_ci	pr_emerg("epc	 : %08lx\nStatus: %08lx\n"
9462306a36Sopenharmony_ci		 "Cause : %08lx\nbadVaddr : %08lx\n",
9562306a36Sopenharmony_ci		 regs->cp0_epc, regs->cp0_status,
9662306a36Sopenharmony_ci		 regs->cp0_cause, regs->cp0_badvaddr);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Read all the registers and then print them as there is a
9962306a36Sopenharmony_ci	   problem with interspersed printk's upsetting the Bonito controller.
10062306a36Sopenharmony_ci	   Do it for the others too.
10162306a36Sopenharmony_ci	*/
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	switch (mips_revision_sconid) {
10462306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
10562306a36Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
10662306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
10762306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
10862306a36Sopenharmony_ci		ll_msc_irq();
10962306a36Sopenharmony_ci		break;
11062306a36Sopenharmony_ci	case MIPS_REVISION_SCON_GT64120:
11162306a36Sopenharmony_ci		intrcause = GT_READ(GT_INTRCAUSE_OFS);
11262306a36Sopenharmony_ci		datalo = GT_READ(GT_CPUERR_ADDRLO_OFS);
11362306a36Sopenharmony_ci		datahi = GT_READ(GT_CPUERR_ADDRHI_OFS);
11462306a36Sopenharmony_ci		pr_emerg("GT_INTRCAUSE = %08x\n", intrcause);
11562306a36Sopenharmony_ci		pr_emerg("GT_CPUERR_ADDR = %02x%08x\n",
11662306a36Sopenharmony_ci				datahi, datalo);
11762306a36Sopenharmony_ci		break;
11862306a36Sopenharmony_ci	case MIPS_REVISION_SCON_BONITO:
11962306a36Sopenharmony_ci		pcibadaddr = BONITO_PCIBADADDR;
12062306a36Sopenharmony_ci		pcimstat = BONITO_PCIMSTAT;
12162306a36Sopenharmony_ci		intisr = BONITO_INTISR;
12262306a36Sopenharmony_ci		inten = BONITO_INTEN;
12362306a36Sopenharmony_ci		intpol = BONITO_INTPOL;
12462306a36Sopenharmony_ci		intedge = BONITO_INTEDGE;
12562306a36Sopenharmony_ci		intsteer = BONITO_INTSTEER;
12662306a36Sopenharmony_ci		pcicmd = BONITO_PCICMD;
12762306a36Sopenharmony_ci		pr_emerg("BONITO_INTISR = %08x\n", intisr);
12862306a36Sopenharmony_ci		pr_emerg("BONITO_INTEN = %08x\n", inten);
12962306a36Sopenharmony_ci		pr_emerg("BONITO_INTPOL = %08x\n", intpol);
13062306a36Sopenharmony_ci		pr_emerg("BONITO_INTEDGE = %08x\n", intedge);
13162306a36Sopenharmony_ci		pr_emerg("BONITO_INTSTEER = %08x\n", intsteer);
13262306a36Sopenharmony_ci		pr_emerg("BONITO_PCICMD = %08x\n", pcicmd);
13362306a36Sopenharmony_ci		pr_emerg("BONITO_PCIBADADDR = %08x\n", pcibadaddr);
13462306a36Sopenharmony_ci		pr_emerg("BONITO_PCIMSTAT = %08x\n", pcimstat);
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	die("CoreHi interrupt", regs);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic irqreturn_t corehi_handler(int irq, void *dev_id)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	corehi_irqdispatch();
14462306a36Sopenharmony_ci	return IRQ_HANDLED;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic msc_irqmap_t msc_irqmap[] __initdata = {
14862306a36Sopenharmony_ci	{MSC01C_INT_TMR,		MSC01_IRQ_EDGE, 0},
14962306a36Sopenharmony_ci	{MSC01C_INT_PCI,		MSC01_IRQ_LEVEL, 0},
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_cistatic int msc_nr_irqs __initdata = ARRAY_SIZE(msc_irqmap);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic msc_irqmap_t msc_eicirqmap[] __initdata = {
15462306a36Sopenharmony_ci	{MSC01E_INT_SW0,		MSC01_IRQ_LEVEL, 0},
15562306a36Sopenharmony_ci	{MSC01E_INT_SW1,		MSC01_IRQ_LEVEL, 0},
15662306a36Sopenharmony_ci	{MSC01E_INT_I8259A,		MSC01_IRQ_LEVEL, 0},
15762306a36Sopenharmony_ci	{MSC01E_INT_SMI,		MSC01_IRQ_LEVEL, 0},
15862306a36Sopenharmony_ci	{MSC01E_INT_COREHI,		MSC01_IRQ_LEVEL, 0},
15962306a36Sopenharmony_ci	{MSC01E_INT_CORELO,		MSC01_IRQ_LEVEL, 0},
16062306a36Sopenharmony_ci	{MSC01E_INT_TMR,		MSC01_IRQ_EDGE, 0},
16162306a36Sopenharmony_ci	{MSC01E_INT_PCI,		MSC01_IRQ_LEVEL, 0},
16262306a36Sopenharmony_ci	{MSC01E_INT_PERFCTR,		MSC01_IRQ_LEVEL, 0},
16362306a36Sopenharmony_ci	{MSC01E_INT_CPUCTR,		MSC01_IRQ_LEVEL, 0}
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int msc_nr_eicirqs __initdata = ARRAY_SIZE(msc_eicirqmap);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_civoid __init arch_init_irq(void)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	int corehi_irq;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * Preallocate the i8259's expected virq's here. Since irqchip_init()
17462306a36Sopenharmony_ci	 * will probe the irqchips in hierarchial order, i8259 is probed last.
17562306a36Sopenharmony_ci	 * If anything allocates a virq before the i8259 is probed, it will
17662306a36Sopenharmony_ci	 * be given one of the i8259's expected range and consequently setup
17762306a36Sopenharmony_ci	 * of the i8259 will fail.
17862306a36Sopenharmony_ci	 */
17962306a36Sopenharmony_ci	WARN(irq_alloc_descs(I8259A_IRQ_BASE, I8259A_IRQ_BASE,
18062306a36Sopenharmony_ci			    16, numa_node_id()) < 0,
18162306a36Sopenharmony_ci		"Cannot reserve i8259 virqs at IRQ%d\n", I8259A_IRQ_BASE);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	i8259_set_poll(mips_pcibios_iack);
18462306a36Sopenharmony_ci	irqchip_init();
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	switch (mips_revision_sconid) {
18762306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
18862306a36Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
18962306a36Sopenharmony_ci		if (cpu_has_veic)
19062306a36Sopenharmony_ci			init_msc_irqs(MIPS_MSC01_IC_REG_BASE,
19162306a36Sopenharmony_ci					MSC01E_INT_BASE, msc_eicirqmap,
19262306a36Sopenharmony_ci					msc_nr_eicirqs);
19362306a36Sopenharmony_ci		else
19462306a36Sopenharmony_ci			init_msc_irqs(MIPS_MSC01_IC_REG_BASE,
19562306a36Sopenharmony_ci					MSC01C_INT_BASE, msc_irqmap,
19662306a36Sopenharmony_ci					msc_nr_irqs);
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
20062306a36Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
20162306a36Sopenharmony_ci		if (cpu_has_veic)
20262306a36Sopenharmony_ci			init_msc_irqs(MIPS_SOCITSC_IC_REG_BASE,
20362306a36Sopenharmony_ci					MSC01E_INT_BASE, msc_eicirqmap,
20462306a36Sopenharmony_ci					msc_nr_eicirqs);
20562306a36Sopenharmony_ci		else
20662306a36Sopenharmony_ci			init_msc_irqs(MIPS_SOCITSC_IC_REG_BASE,
20762306a36Sopenharmony_ci					MSC01C_INT_BASE, msc_irqmap,
20862306a36Sopenharmony_ci					msc_nr_irqs);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (mips_gic_present()) {
21262306a36Sopenharmony_ci		corehi_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_COREHI;
21362306a36Sopenharmony_ci	} else if (cpu_has_veic) {
21462306a36Sopenharmony_ci		set_vi_handler(MSC01E_INT_COREHI, corehi_irqdispatch);
21562306a36Sopenharmony_ci		corehi_irq = MSC01E_INT_BASE + MSC01E_INT_COREHI;
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		corehi_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_COREHI;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (request_irq(corehi_irq, corehi_handler, IRQF_NO_THREAD, "CoreHi",
22162306a36Sopenharmony_ci			NULL))
22262306a36Sopenharmony_ci		pr_err("Failed to request irq %d (CoreHi)\n", corehi_irq);
22362306a36Sopenharmony_ci}
224