malta_int.c 9.7 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

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.
	 */
56 57 58 59 60
	switch (mips_revision_sconid) {
	case MIPS_REVISION_SCON_SOCIT:
	case MIPS_REVISION_SCON_ROCIT:
	case MIPS_REVISION_SCON_SOCITSC:
	case MIPS_REVISION_SCON_SOCITSCP:
L
Linus Torvalds 已提交
61 62 63
	        MSC_READ(MSC01_PCI_IACK, irq);
		irq &= 0xff;
		break;
64
	case MIPS_REVISION_SCON_GT64120:
L
Linus Torvalds 已提交
65 66 67
		irq = GT_READ(GT_PCI0_IACK_OFS);
		irq &= 0xff;
		break;
68
	case MIPS_REVISION_SCON_BONITO:
L
Linus Torvalds 已提交
69 70 71 72 73 74 75 76 77 78 79
		/* 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 */

80
		irq = readl((u32 *)_pcictrl_bonito_pcicfg);
L
Linus Torvalds 已提交
81 82 83 84 85
		iob();    /* sync */
		irq &= 0xff;
		BONITO_PCIMAP_CFG = 0;
		break;
	default:
86
	        printk("Unknown system controller.\n");
L
Linus Torvalds 已提交
87 88 89 90 91
		return -1;
	}
	return irq;
}

92
static inline int get_int(void)
L
Linus Torvalds 已提交
93 94
{
	unsigned long flags;
95
	int irq;
L
Linus Torvalds 已提交
96 97
	spin_lock_irqsave(&mips_irq_lock, flags);

98
	irq = mips_pcibios_iack();
L
Linus Torvalds 已提交
99 100

	/*
R
Ralf Baechle 已提交
101 102 103
	 * 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 已提交
104 105 106 107
	 */

	spin_unlock_irqrestore(&mips_irq_lock, flags);

108
	return irq;
L
Linus Torvalds 已提交
109 110
}

111
static void malta_hw0_irqdispatch(void)
L
Linus Torvalds 已提交
112 113 114
{
	int irq;

115
	irq = get_int();
116
	if (irq < 0) {
117
		return;  /* interrupt has already been cleared */
118
	}
L
Linus Torvalds 已提交
119

120
	do_IRQ(MALTA_INT_BASE + irq);
L
Linus Torvalds 已提交
121 122
}

123
static void corehi_irqdispatch(void)
L
Linus Torvalds 已提交
124
{
125 126
	unsigned int intedge, intsteer, pcicmd, pcibadaddr;
        unsigned int pcimstat, intisr, inten, intpol;
127
	unsigned int intrcause,datalo,datahi;
128
	struct pt_regs *regs = get_irq_regs();
L
Linus Torvalds 已提交
129 130

        printk("CoreHI interrupt, shouldn't happen, so we die here!!!\n");
131 132 133 134
        printk("epc   : %08lx\nStatus: %08lx\n"
	       "Cause : %08lx\nbadVaddr : %08lx\n",
	       regs->cp0_epc, regs->cp0_status,
	       regs->cp0_cause, regs->cp0_badvaddr);
135 136 137 138 139 140

	/* 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.
	*/

141 142 143 144 145
	switch (mips_revision_sconid) {
        case MIPS_REVISION_SCON_SOCIT:
	case MIPS_REVISION_SCON_ROCIT:
	case MIPS_REVISION_SCON_SOCITSC:
	case MIPS_REVISION_SCON_SOCITSCP:
146
                ll_msc_irq();
L
Linus Torvalds 已提交
147
                break;
148
        case MIPS_REVISION_SCON_GT64120:
149 150
                intrcause = GT_READ(GT_INTRCAUSE_OFS);
                datalo = GT_READ(GT_CPUERR_ADDRLO_OFS);
L
Linus Torvalds 已提交
151
                datahi = GT_READ(GT_CPUERR_ADDRHI_OFS);
152 153
                printk("GT_INTRCAUSE = %08x\n", intrcause);
                printk("GT_CPUERR_ADDR = %02x%08x\n", datahi, datalo);
L
Linus Torvalds 已提交
154
                break;
155
        case MIPS_REVISION_SCON_BONITO:
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
                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 已提交
172 173 174 175 176 177 178
                break;
        }

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

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
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;

203
	t0 = pending & 0xf000;
204 205 206
	t0 = t0 < 1;
	t0 = t0 << 2;
	a0 = a0 - t0;
207
	pending = pending << t0;
208

209
	t0 = pending & 0xc000;
210 211 212
	t0 = t0 < 1;
	t0 = t0 << 1;
	a0 = a0 - t0;
213
	pending = pending << t0;
214

215
	t0 = pending & 0x8000;
216 217 218
	t0 = t0 < 1;
	//t0 = t0 << 2;
	a0 = a0 - t0;
219
	//pending = pending << t0;
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

	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.
 */

250
asmlinkage void plat_irq_dispatch(void)
251 252 253 254 255 256 257
{
	unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM;
	int irq;

	irq = irq_ffs(pending);

	if (irq == MIPSCPU_INT_I8259A)
258
		malta_hw0_irqdispatch();
259
	else if (irq > 0)
260
		do_IRQ(MIPSCPU_INT_BASE + irq);
261
	else
262
		spurious_interrupt();
263 264
}

265 266 267 268 269 270 271 272 273 274 275 276 277 278
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},
};
279
int __initdata msc_nr_irqs = ARRAY_SIZE(msc_irqmap);
280 281 282 283 284 285 286 287 288 289 290 291 292

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}
};
293
int __initdata msc_nr_eicirqs = ARRAY_SIZE(msc_eicirqmap);
294

L
Linus Torvalds 已提交
295 296 297
void __init arch_init_irq(void)
{
	init_i8259_irqs();
298 299

	if (!cpu_has_veic)
300
		mips_cpu_irq_init();
301

302 303 304 305 306 307 308 309 310 311 312
        switch(mips_revision_sconid) {
        case MIPS_REVISION_SCON_SOCIT:
        case MIPS_REVISION_SCON_ROCIT:
		if (cpu_has_veic)
			init_msc_irqs (MIPS_MSC01_IC_REG_BASE, MSC01E_INT_BASE, msc_eicirqmap, msc_nr_eicirqs);
		else
			init_msc_irqs (MIPS_MSC01_IC_REG_BASE, MSC01C_INT_BASE, msc_irqmap, msc_nr_irqs);
		break;

        case MIPS_REVISION_SCON_SOCITSC:
        case MIPS_REVISION_SCON_SOCITSCP:
313
		if (cpu_has_veic)
314
			init_msc_irqs (MIPS_SOCITSC_IC_REG_BASE, MSC01E_INT_BASE, msc_eicirqmap, msc_nr_eicirqs);
315
		else
316
			init_msc_irqs (MIPS_SOCITSC_IC_REG_BASE, MSC01C_INT_BASE, msc_irqmap, msc_nr_irqs);
317 318 319 320 321 322 323 324 325 326 327
	}

	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);
328 329 330 331 332 333
#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 */
334 335
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq);
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction);
336
#endif /* CONFIG_MIPS_MT_SMTC */
337 338 339 340 341
	}
	else {
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_I8259A, &i8259irq);
		setup_irq (MIPSCPU_INT_BASE+MIPSCPU_INT_COREHI, &corehi_irqaction);
	}
L
Linus Torvalds 已提交
342
}