18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com
78c2ecf20Sopenharmony_ci * Copyright (C) 2000, 2001, 2004 MIPS Technologies, Inc.
88c2ecf20Sopenharmony_ci * Copyright (C) 2001 Ralf Baechle
98c2ecf20Sopenharmony_ci * Copyright (C) 2013 Imagination Technologies Ltd.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Routines for generic manipulation of the interrupts found on the MIPS
128c2ecf20Sopenharmony_ci * Malta board. The interrupt controller is located in the South Bridge
138c2ecf20Sopenharmony_ci * a PIIX4 device with two internal 82C95 interrupt controllers.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
188c2ecf20Sopenharmony_ci#include <linux/sched.h>
198c2ecf20Sopenharmony_ci#include <linux/smp.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h>
248c2ecf20Sopenharmony_ci#include <linux/kernel.h>
258c2ecf20Sopenharmony_ci#include <linux/random.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <asm/traps.h>
288c2ecf20Sopenharmony_ci#include <asm/i8259.h>
298c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h>
308c2ecf20Sopenharmony_ci#include <asm/irq_regs.h>
318c2ecf20Sopenharmony_ci#include <asm/mips-boards/malta.h>
328c2ecf20Sopenharmony_ci#include <asm/mips-boards/maltaint.h>
338c2ecf20Sopenharmony_ci#include <asm/mips-cps.h>
348c2ecf20Sopenharmony_ci#include <asm/gt64120.h>
358c2ecf20Sopenharmony_ci#include <asm/mips-boards/generic.h>
368c2ecf20Sopenharmony_ci#include <asm/mips-boards/msc01_pci.h>
378c2ecf20Sopenharmony_ci#include <asm/msc01_ic.h>
388c2ecf20Sopenharmony_ci#include <asm/setup.h>
398c2ecf20Sopenharmony_ci#include <asm/rtlx.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline int mips_pcibios_iack(void)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	int irq;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * Determine highest priority pending interrupt by performing
478c2ecf20Sopenharmony_ci	 * a PCI Interrupt Acknowledge cycle.
488c2ecf20Sopenharmony_ci	 */
498c2ecf20Sopenharmony_ci	switch (mips_revision_sconid) {
508c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
518c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
528c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
538c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
548c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_IACK, irq);
558c2ecf20Sopenharmony_ci		irq &= 0xff;
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_GT64120:
588c2ecf20Sopenharmony_ci		irq = GT_READ(GT_PCI0_IACK_OFS);
598c2ecf20Sopenharmony_ci		irq &= 0xff;
608c2ecf20Sopenharmony_ci		break;
618c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_BONITO:
628c2ecf20Sopenharmony_ci		/* The following will generate a PCI IACK cycle on the
638c2ecf20Sopenharmony_ci		 * Bonito controller. It's a little bit kludgy, but it
648c2ecf20Sopenharmony_ci		 * was the easiest way to implement it in hardware at
658c2ecf20Sopenharmony_ci		 * the given time.
668c2ecf20Sopenharmony_ci		 */
678c2ecf20Sopenharmony_ci		BONITO_PCIMAP_CFG = 0x20000;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		/* Flush Bonito register block */
708c2ecf20Sopenharmony_ci		(void) BONITO_PCIMAP_CFG;
718c2ecf20Sopenharmony_ci		iob();	  /* sync */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		irq = __raw_readl((u32 *)_pcictrl_bonito_pcicfg);
748c2ecf20Sopenharmony_ci		iob();	  /* sync */
758c2ecf20Sopenharmony_ci		irq &= 0xff;
768c2ecf20Sopenharmony_ci		BONITO_PCIMAP_CFG = 0;
778c2ecf20Sopenharmony_ci		break;
788c2ecf20Sopenharmony_ci	default:
798c2ecf20Sopenharmony_ci		pr_emerg("Unknown system controller.\n");
808c2ecf20Sopenharmony_ci		return -1;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci	return irq;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void corehi_irqdispatch(void)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	unsigned int intedge, intsteer, pcicmd, pcibadaddr;
888c2ecf20Sopenharmony_ci	unsigned int pcimstat, intisr, inten, intpol;
898c2ecf20Sopenharmony_ci	unsigned int intrcause, datalo, datahi;
908c2ecf20Sopenharmony_ci	struct pt_regs *regs = get_irq_regs();
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	pr_emerg("CoreHI interrupt, shouldn't happen, we die here!\n");
938c2ecf20Sopenharmony_ci	pr_emerg("epc	 : %08lx\nStatus: %08lx\n"
948c2ecf20Sopenharmony_ci		 "Cause : %08lx\nbadVaddr : %08lx\n",
958c2ecf20Sopenharmony_ci		 regs->cp0_epc, regs->cp0_status,
968c2ecf20Sopenharmony_ci		 regs->cp0_cause, regs->cp0_badvaddr);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* Read all the registers and then print them as there is a
998c2ecf20Sopenharmony_ci	   problem with interspersed printk's upsetting the Bonito controller.
1008c2ecf20Sopenharmony_ci	   Do it for the others too.
1018c2ecf20Sopenharmony_ci	*/
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	switch (mips_revision_sconid) {
1048c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
1058c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
1068c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
1078c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
1088c2ecf20Sopenharmony_ci		ll_msc_irq();
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_GT64120:
1118c2ecf20Sopenharmony_ci		intrcause = GT_READ(GT_INTRCAUSE_OFS);
1128c2ecf20Sopenharmony_ci		datalo = GT_READ(GT_CPUERR_ADDRLO_OFS);
1138c2ecf20Sopenharmony_ci		datahi = GT_READ(GT_CPUERR_ADDRHI_OFS);
1148c2ecf20Sopenharmony_ci		pr_emerg("GT_INTRCAUSE = %08x\n", intrcause);
1158c2ecf20Sopenharmony_ci		pr_emerg("GT_CPUERR_ADDR = %02x%08x\n",
1168c2ecf20Sopenharmony_ci				datahi, datalo);
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_BONITO:
1198c2ecf20Sopenharmony_ci		pcibadaddr = BONITO_PCIBADADDR;
1208c2ecf20Sopenharmony_ci		pcimstat = BONITO_PCIMSTAT;
1218c2ecf20Sopenharmony_ci		intisr = BONITO_INTISR;
1228c2ecf20Sopenharmony_ci		inten = BONITO_INTEN;
1238c2ecf20Sopenharmony_ci		intpol = BONITO_INTPOL;
1248c2ecf20Sopenharmony_ci		intedge = BONITO_INTEDGE;
1258c2ecf20Sopenharmony_ci		intsteer = BONITO_INTSTEER;
1268c2ecf20Sopenharmony_ci		pcicmd = BONITO_PCICMD;
1278c2ecf20Sopenharmony_ci		pr_emerg("BONITO_INTISR = %08x\n", intisr);
1288c2ecf20Sopenharmony_ci		pr_emerg("BONITO_INTEN = %08x\n", inten);
1298c2ecf20Sopenharmony_ci		pr_emerg("BONITO_INTPOL = %08x\n", intpol);
1308c2ecf20Sopenharmony_ci		pr_emerg("BONITO_INTEDGE = %08x\n", intedge);
1318c2ecf20Sopenharmony_ci		pr_emerg("BONITO_INTSTEER = %08x\n", intsteer);
1328c2ecf20Sopenharmony_ci		pr_emerg("BONITO_PCICMD = %08x\n", pcicmd);
1338c2ecf20Sopenharmony_ci		pr_emerg("BONITO_PCIBADADDR = %08x\n", pcibadaddr);
1348c2ecf20Sopenharmony_ci		pr_emerg("BONITO_PCIMSTAT = %08x\n", pcimstat);
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	die("CoreHi interrupt", regs);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic irqreturn_t corehi_handler(int irq, void *dev_id)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	corehi_irqdispatch();
1448c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic msc_irqmap_t msc_irqmap[] __initdata = {
1488c2ecf20Sopenharmony_ci	{MSC01C_INT_TMR,		MSC01_IRQ_EDGE, 0},
1498c2ecf20Sopenharmony_ci	{MSC01C_INT_PCI,		MSC01_IRQ_LEVEL, 0},
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_cistatic int msc_nr_irqs __initdata = ARRAY_SIZE(msc_irqmap);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic msc_irqmap_t msc_eicirqmap[] __initdata = {
1548c2ecf20Sopenharmony_ci	{MSC01E_INT_SW0,		MSC01_IRQ_LEVEL, 0},
1558c2ecf20Sopenharmony_ci	{MSC01E_INT_SW1,		MSC01_IRQ_LEVEL, 0},
1568c2ecf20Sopenharmony_ci	{MSC01E_INT_I8259A,		MSC01_IRQ_LEVEL, 0},
1578c2ecf20Sopenharmony_ci	{MSC01E_INT_SMI,		MSC01_IRQ_LEVEL, 0},
1588c2ecf20Sopenharmony_ci	{MSC01E_INT_COREHI,		MSC01_IRQ_LEVEL, 0},
1598c2ecf20Sopenharmony_ci	{MSC01E_INT_CORELO,		MSC01_IRQ_LEVEL, 0},
1608c2ecf20Sopenharmony_ci	{MSC01E_INT_TMR,		MSC01_IRQ_EDGE, 0},
1618c2ecf20Sopenharmony_ci	{MSC01E_INT_PCI,		MSC01_IRQ_LEVEL, 0},
1628c2ecf20Sopenharmony_ci	{MSC01E_INT_PERFCTR,		MSC01_IRQ_LEVEL, 0},
1638c2ecf20Sopenharmony_ci	{MSC01E_INT_CPUCTR,		MSC01_IRQ_LEVEL, 0}
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int msc_nr_eicirqs __initdata = ARRAY_SIZE(msc_eicirqmap);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_civoid __init arch_init_irq(void)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int corehi_irq;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * Preallocate the i8259's expected virq's here. Since irqchip_init()
1748c2ecf20Sopenharmony_ci	 * will probe the irqchips in hierarchial order, i8259 is probed last.
1758c2ecf20Sopenharmony_ci	 * If anything allocates a virq before the i8259 is probed, it will
1768c2ecf20Sopenharmony_ci	 * be given one of the i8259's expected range and consequently setup
1778c2ecf20Sopenharmony_ci	 * of the i8259 will fail.
1788c2ecf20Sopenharmony_ci	 */
1798c2ecf20Sopenharmony_ci	WARN(irq_alloc_descs(I8259A_IRQ_BASE, I8259A_IRQ_BASE,
1808c2ecf20Sopenharmony_ci			    16, numa_node_id()) < 0,
1818c2ecf20Sopenharmony_ci		"Cannot reserve i8259 virqs at IRQ%d\n", I8259A_IRQ_BASE);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	i8259_set_poll(mips_pcibios_iack);
1848c2ecf20Sopenharmony_ci	irqchip_init();
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	switch (mips_revision_sconid) {
1878c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
1888c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
1898c2ecf20Sopenharmony_ci		if (cpu_has_veic)
1908c2ecf20Sopenharmony_ci			init_msc_irqs(MIPS_MSC01_IC_REG_BASE,
1918c2ecf20Sopenharmony_ci					MSC01E_INT_BASE, msc_eicirqmap,
1928c2ecf20Sopenharmony_ci					msc_nr_eicirqs);
1938c2ecf20Sopenharmony_ci		else
1948c2ecf20Sopenharmony_ci			init_msc_irqs(MIPS_MSC01_IC_REG_BASE,
1958c2ecf20Sopenharmony_ci					MSC01C_INT_BASE, msc_irqmap,
1968c2ecf20Sopenharmony_ci					msc_nr_irqs);
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
2008c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
2018c2ecf20Sopenharmony_ci		if (cpu_has_veic)
2028c2ecf20Sopenharmony_ci			init_msc_irqs(MIPS_SOCITSC_IC_REG_BASE,
2038c2ecf20Sopenharmony_ci					MSC01E_INT_BASE, msc_eicirqmap,
2048c2ecf20Sopenharmony_ci					msc_nr_eicirqs);
2058c2ecf20Sopenharmony_ci		else
2068c2ecf20Sopenharmony_ci			init_msc_irqs(MIPS_SOCITSC_IC_REG_BASE,
2078c2ecf20Sopenharmony_ci					MSC01C_INT_BASE, msc_irqmap,
2088c2ecf20Sopenharmony_ci					msc_nr_irqs);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (mips_gic_present()) {
2128c2ecf20Sopenharmony_ci		corehi_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_COREHI;
2138c2ecf20Sopenharmony_ci	} else if (cpu_has_veic) {
2148c2ecf20Sopenharmony_ci		set_vi_handler(MSC01E_INT_COREHI, corehi_irqdispatch);
2158c2ecf20Sopenharmony_ci		corehi_irq = MSC01E_INT_BASE + MSC01E_INT_COREHI;
2168c2ecf20Sopenharmony_ci	} else {
2178c2ecf20Sopenharmony_ci		corehi_irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_COREHI;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (request_irq(corehi_irq, corehi_handler, IRQF_NO_THREAD, "CoreHi",
2218c2ecf20Sopenharmony_ci			NULL))
2228c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (CoreHi)\n", corehi_irq);
2238c2ecf20Sopenharmony_ci}
224