malta_int.c 10.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * Carsten Langgaard, carstenl@mips.com
 * Copyright (C) 2000, 2001, 2004 MIPS Technologies, Inc.
 * Copyright (C) 2001 Ralf Baechle
 *
 *  This program is free software; you can distribute it and/or modify it
 *  under the terms of the GNU General Public License (Version 2) as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
 *
 * Routines for generic manipulation of the interrupts found on the MIPS
 * Malta board.
 * The interrupt controller is located in the South Bridge a PIIX4 device
 * with two internal 82C95 interrupt controllers.
 */
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
30
#include <linux/kernel.h>
L
Linus Torvalds 已提交
31 32 33
#include <linux/random.h>

#include <asm/i8259.h>
34
#include <asm/irq_cpu.h>
L
Linus Torvalds 已提交
35
#include <asm/io.h>
36
#include <asm/irq_regs.h>
L
Linus Torvalds 已提交
37 38 39 40 41 42
#include <asm/mips-boards/malta.h>
#include <asm/mips-boards/maltaint.h>
#include <asm/mips-boards/piix4.h>
#include <asm/gt64120.h>
#include <asm/mips-boards/generic.h>
#include <asm/mips-boards/msc01_pci.h>
43
#include <asm/msc01_ic.h>
L
Linus Torvalds 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

static DEFINE_SPINLOCK(mips_irq_lock);

static inline int mips_pcibios_iack(void)
{
	int irq;
        u32 dummy;

	/*
	 * Determine highest priority pending interrupt by performing
	 * a PCI Interrupt Acknowledge cycle.
	 */
	switch(mips_revision_corid) {
	case MIPS_REVISION_CORID_CORE_MSC:
	case MIPS_REVISION_CORID_CORE_FPGA2:
R
Ralf Baechle 已提交
59
	case MIPS_REVISION_CORID_CORE_FPGA3:
C
Chris Dearman 已提交
60
	case MIPS_REVISION_CORID_CORE_24K:
L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	case MIPS_REVISION_CORID_CORE_EMUL_MSC:
	        MSC_READ(MSC01_PCI_IACK, irq);
		irq &= 0xff;
		break;
	case MIPS_REVISION_CORID_QED_RM5261:
	case MIPS_REVISION_CORID_CORE_LV:
	case MIPS_REVISION_CORID_CORE_FPGA:
	case MIPS_REVISION_CORID_CORE_FPGAR2:
		irq = GT_READ(GT_PCI0_IACK_OFS);
		irq &= 0xff;
		break;
	case MIPS_REVISION_CORID_BONITO64:
	case MIPS_REVISION_CORID_CORE_20K:
	case MIPS_REVISION_CORID_CORE_EMUL_BON:
		/* The following will generate a PCI IACK cycle on the
		 * Bonito controller. It's a little bit kludgy, but it
		 * was the easiest way to implement it in hardware at
		 * the given time.
		 */
		BONITO_PCIMAP_CFG = 0x20000;

		/* Flush Bonito register block */
		dummy = BONITO_PCIMAP_CFG;
		iob();    /* sync */

86
		irq = readl((u32 *)_pcictrl_bonito_pcicfg);
L
Linus Torvalds 已提交
87 88 89 90 91 92 93 94 95 96 97
		iob();    /* sync */
		irq &= 0xff;
		BONITO_PCIMAP_CFG = 0;
		break;
	default:
	        printk("Unknown Core card, don't know the system controller.\n");
		return -1;
	}
	return irq;
}

98
static inline int get_int(void)
L
Linus Torvalds 已提交
99 100
{
	unsigned long flags;
101
	int irq;
L
Linus Torvalds 已提交
102 103
	spin_lock_irqsave(&mips_irq_lock, flags);

104
	irq = mips_pcibios_iack();
L
Linus Torvalds 已提交
105 106

	/*
R
Ralf Baechle 已提交
107 108 109
	 * The only way we can decide if an interrupt is spurious
	 * is by checking the 8259 registers.  This needs a spinlock
	 * on an SMP system,  so leave it up to the generic code...
L
Linus Torvalds 已提交
110 111 112 113
	 */

	spin_unlock_irqrestore(&mips_irq_lock, flags);

114
	return irq;
L
Linus Torvalds 已提交
115 116
}

117
static void malta_hw0_irqdispatch(void)
L
Linus Torvalds 已提交
118 119 120
{
	int irq;

121
	irq = get_int();
122
	if (irq < 0) {
123
		return;  /* interrupt has already been cleared */
124
	}
L
Linus Torvalds 已提交
125

126
	do_IRQ(MALTA_INT_BASE + irq);
L
Linus Torvalds 已提交
127 128
}

129
static void corehi_irqdispatch(void)
L
Linus Torvalds 已提交
130
{
131 132
	unsigned int intedge, intsteer, pcicmd, pcibadaddr;
        unsigned int pcimstat, intisr, inten, intpol;
133
	unsigned int intrcause,datalo,datahi;
134
	struct pt_regs *regs = get_irq_regs();
L
Linus Torvalds 已提交
135 136

        printk("CoreHI interrupt, shouldn't happen, so we die here!!!\n");
137 138 139 140
        printk("epc   : %08lx\nStatus: %08lx\n"
	       "Cause : %08lx\nbadVaddr : %08lx\n",
	       regs->cp0_epc, regs->cp0_status,
	       regs->cp0_cause, regs->cp0_badvaddr);
141 142 143 144 145 146

	/* Read all the registers and then print them as there is a
	   problem with interspersed printk's upsetting the Bonito controller.
	   Do it for the others too.
	*/

L
Linus Torvalds 已提交
147 148 149
        switch(mips_revision_corid) {
        case MIPS_REVISION_CORID_CORE_MSC:
        case MIPS_REVISION_CORID_CORE_FPGA2:
R
Ralf Baechle 已提交
150
        case MIPS_REVISION_CORID_CORE_FPGA3:
C
Chris Dearman 已提交
151
        case MIPS_REVISION_CORID_CORE_24K:
152
        case MIPS_REVISION_CORID_CORE_EMUL_MSC:
153
                ll_msc_irq();
L
Linus Torvalds 已提交
154 155 156 157 158
                break;
        case MIPS_REVISION_CORID_QED_RM5261:
        case MIPS_REVISION_CORID_CORE_LV:
        case MIPS_REVISION_CORID_CORE_FPGA:
        case MIPS_REVISION_CORID_CORE_FPGAR2:
159 160
                intrcause = GT_READ(GT_INTRCAUSE_OFS);
                datalo = GT_READ(GT_CPUERR_ADDRLO_OFS);
L
Linus Torvalds 已提交
161
                datahi = GT_READ(GT_CPUERR_ADDRHI_OFS);
162 163
                printk("GT_INTRCAUSE = %08x\n", intrcause);
                printk("GT_CPUERR_ADDR = %02x%08x\n", datahi, datalo);
L
Linus Torvalds 已提交
164 165 166 167
                break;
        case MIPS_REVISION_CORID_BONITO64:
        case MIPS_REVISION_CORID_CORE_20K:
        case MIPS_REVISION_CORID_CORE_EMUL_BON:
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
                pcibadaddr = BONITO_PCIBADADDR;
                pcimstat = BONITO_PCIMSTAT;
                intisr = BONITO_INTISR;
                inten = BONITO_INTEN;
                intpol = BONITO_INTPOL;
                intedge = BONITO_INTEDGE;
                intsteer = BONITO_INTSTEER;
                pcicmd = BONITO_PCICMD;
                printk("BONITO_INTISR = %08x\n", intisr);
                printk("BONITO_INTEN = %08x\n", inten);
                printk("BONITO_INTPOL = %08x\n", intpol);
                printk("BONITO_INTEDGE = %08x\n", intedge);
                printk("BONITO_INTSTEER = %08x\n", intsteer);
                printk("BONITO_PCICMD = %08x\n", pcicmd);
                printk("BONITO_PCIBADADDR = %08x\n", pcibadaddr);
                printk("BONITO_PCIMSTAT = %08x\n", pcimstat);
L
Linus Torvalds 已提交
184 185 186 187 188 189 190
                break;
        }

        /* We die here*/
        die("CoreHi interrupt", regs);
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
static inline int clz(unsigned long x)
{
	__asm__ (
	"	.set	push					\n"
	"	.set	mips32					\n"
	"	clz	%0, %1					\n"
	"	.set	pop					\n"
	: "=r" (x)
	: "r" (x));

	return x;
}

/*
 * Version of ffs that only looks at bits 12..15.
 */
static inline unsigned int irq_ffs(unsigned int pending)
{
#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64)
	return -clz(pending) + 31 - CAUSEB_IP;
#else
	unsigned int a0 = 7;
	unsigned int t0;

215
	t0 = pending & 0xf000;
216 217 218
	t0 = t0 < 1;
	t0 = t0 << 2;
	a0 = a0 - t0;
219
	pending = pending << t0;
220

221
	t0 = pending & 0xc000;
222 223 224
	t0 = t0 < 1;
	t0 = t0 << 1;
	a0 = a0 - t0;
225
	pending = pending << t0;
226

227
	t0 = pending & 0x8000;
228 229 230
	t0 = t0 < 1;
	//t0 = t0 << 2;
	a0 = a0 - t0;
231
	//pending = pending << t0;
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

	return a0;
#endif
}

/*
 * IRQs on the Malta board look basically (barring software IRQs which we
 * don't use at all and all external interrupt sources are combined together
 * on hardware interrupt 0 (MIPS IRQ 2)) like:
 *
 *	MIPS IRQ	Source
 *      --------        ------
 *             0	Software (ignored)
 *             1        Software (ignored)
 *             2        Combined hardware interrupt (hw0)
 *             3        Hardware (ignored)
 *             4        Hardware (ignored)
 *             5        Hardware (ignored)
 *             6        Hardware (ignored)
 *             7        R4k timer (what we use)
 *
 * We handle the IRQ according to _our_ priority which is:
 *
 * Highest ----     R4k Timer
 * Lowest  ----     Combined hardware interrupt
 *
 * then we just return, if multiple IRQs are pending then we will just take
 * another exception, big deal.
 */

262
asmlinkage void plat_irq_dispatch(void)
263 264 265 266 267 268 269
{
	unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM;
	int irq;

	irq = irq_ffs(pending);

	if (irq == MIPSCPU_INT_I8259A)
270
		malta_hw0_irqdispatch();
271
	else if (irq > 0)
272
		do_IRQ(MIPSCPU_INT_BASE + irq);
273
	else
274
		spurious_interrupt();
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290
static struct irqaction i8259irq = {
	.handler = no_action,
	.name = "XT-PIC cascade"
};

static struct irqaction corehi_irqaction = {
	.handler = no_action,
	.name = "CoreHi"
};

msc_irqmap_t __initdata msc_irqmap[] = {
	{MSC01C_INT_TMR,		MSC01_IRQ_EDGE, 0},
	{MSC01C_INT_PCI,		MSC01_IRQ_LEVEL, 0},
};
291
int __initdata msc_nr_irqs = ARRAY_SIZE(msc_irqmap);
292 293 294 295 296 297 298 299 300 301 302 303 304

msc_irqmap_t __initdata msc_eicirqmap[] = {
	{MSC01E_INT_SW0,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_SW1,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_I8259A,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_SMI,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_COREHI,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_CORELO,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_TMR,		MSC01_IRQ_EDGE, 0},
	{MSC01E_INT_PCI,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_PERFCTR,		MSC01_IRQ_LEVEL, 0},
	{MSC01E_INT_CPUCTR,		MSC01_IRQ_LEVEL, 0}
};
305
int __initdata msc_nr_eicirqs = ARRAY_SIZE(msc_eicirqmap);
306

L
Linus Torvalds 已提交
307 308 309
void __init arch_init_irq(void)
{
	init_i8259_irqs();
310 311

	if (!cpu_has_veic)
312
		mips_cpu_irq_init();
313 314 315 316

        switch(mips_revision_corid) {
        case MIPS_REVISION_CORID_CORE_MSC:
        case MIPS_REVISION_CORID_CORE_FPGA2:
R
Ralf Baechle 已提交
317
        case MIPS_REVISION_CORID_CORE_FPGA3:
C
Chris Dearman 已提交
318
        case MIPS_REVISION_CORID_CORE_24K:
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
        case MIPS_REVISION_CORID_CORE_EMUL_MSC:
		if (cpu_has_veic)
			init_msc_irqs (MSC01E_INT_BASE, msc_eicirqmap, msc_nr_eicirqs);
		else
			init_msc_irqs (MSC01C_INT_BASE, msc_irqmap, msc_nr_irqs);
	}

	if (cpu_has_veic) {
		set_vi_handler (MSC01E_INT_I8259A, malta_hw0_irqdispatch);
		set_vi_handler (MSC01E_INT_COREHI, corehi_irqdispatch);
		setup_irq (MSC01E_INT_BASE+MSC01E_INT_I8259A, &i8259irq);
		setup_irq (MSC01E_INT_BASE+MSC01E_INT_COREHI, &corehi_irqaction);
	}
	else if (cpu_has_vint) {
		set_vi_handler (MIPSCPU_INT_I8259A, malta_hw0_irqdispatch);
		set_vi_handler (MIPSCPU_INT_COREHI, corehi_irqdispatch);
335 336 337 338 339 340
#ifdef CONFIG_MIPS_MT_SMTC
		setup_irq_smtc (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq,
			(0x100 << MIPSCPU_INT_I8259A));
		setup_irq_smtc (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI,
			&corehi_irqaction, (0x100 << MIPSCPU_INT_COREHI));
#else /* Not SMTC */
341 342
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq);
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction);
343
#endif /* CONFIG_MIPS_MT_SMTC */
344 345 346 347 348
	}
	else {
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq);
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction);
	}
L
Linus Torvalds 已提交
349
}