ip32-irq.c 12.9 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
/*
 * Code to handle IP32 IRQs
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2000 Harald Koerfgen
 * Copyright (C) 2001 Keith M Wesolowski
 */
#include <linux/init.h>
#include <linux/kernel_stat.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/random.h>
#include <linux/sched.h>

23
#include <asm/irq_cpu.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33 34
#include <asm/mipsregs.h>
#include <asm/signal.h>
#include <asm/system.h>
#include <asm/time.h>
#include <asm/ip32/crime.h>
#include <asm/ip32/mace.h>
#include <asm/ip32/ip32_ints.h>

/* issue a PIO read to make sure no PIO writes are pending */
static void inline flush_crime_bus(void)
{
R
Ralf Baechle 已提交
35
	crime->control;
L
Linus Torvalds 已提交
36 37 38 39
}

static void inline flush_mace_bus(void)
{
R
Ralf Baechle 已提交
40
	mace->perif.ctrl.misc;
L
Linus Torvalds 已提交
41 42
}

43 44
/*
 * O2 irq map
L
Linus Torvalds 已提交
45 46 47 48 49 50 51 52
 *
 * IP0 -> software (ignored)
 * IP1 -> software (ignored)
 * IP2 -> (irq0) C crime 1.1 all interrupts; crime 1.5 ???
 * IP3 -> (irq1) X unknown
 * IP4 -> (irq2) X unknown
 * IP5 -> (irq3) X unknown
 * IP6 -> (irq4) X unknown
53
 * IP7 -> (irq5) 7 CPU count/compare timer (system timer)
L
Linus Torvalds 已提交
54 55 56 57 58
 *
 * crime: (C)
 *
 * CRIME_INT_STAT 31:0:
 *
59 60 61 62
 * 0  ->  8  Video in 1
 * 1  ->  9 Video in 2
 * 2  -> 10  Video out
 * 3  -> 11  Mace ethernet
L
Linus Torvalds 已提交
63 64 65
 * 4  -> S  SuperIO sub-interrupt
 * 5  -> M  Miscellaneous sub-interrupt
 * 6  -> A  Audio sub-interrupt
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
 * 7  -> 15  PCI bridge errors
 * 8  -> 16  PCI SCSI aic7xxx 0
 * 9  -> 17 PCI SCSI aic7xxx 1
 * 10 -> 18 PCI slot 0
 * 11 -> 19 unused (PCI slot 1)
 * 12 -> 20 unused (PCI slot 2)
 * 13 -> 21 unused (PCI shared 0)
 * 14 -> 22 unused (PCI shared 1)
 * 15 -> 23 unused (PCI shared 2)
 * 16 -> 24 GBE0 (E)
 * 17 -> 25 GBE1 (E)
 * 18 -> 26 GBE2 (E)
 * 19 -> 27 GBE3 (E)
 * 20 -> 28 CPU errors
 * 21 -> 29 Memory errors
 * 22 -> 30 RE empty edge (E)
 * 23 -> 31 RE full edge (E)
 * 24 -> 32 RE idle edge (E)
 * 25 -> 33 RE empty level
 * 26 -> 34 RE full level
 * 27 -> 35 RE idle level
 * 28 -> 36 unused (software 0) (E)
 * 29 -> 37 unused (software 1) (E)
 * 30 -> 38 unused (software 2) - crime 1.5 CPU SysCorError (E)
 * 31 -> 39 VICE
L
Linus Torvalds 已提交
91 92 93 94
 *
 * S, M, A: Use the MACE ISA interrupt register
 * MACE_ISA_INT_STAT 31:0
 *
95 96 97
 * 0-7 -> 40-47 Audio
 * 8 -> 48 RTC
 * 9 -> 49 Keyboard
L
Linus Torvalds 已提交
98
 * 10 -> X Keyboard polled
99
 * 11 -> 51 Mouse
L
Linus Torvalds 已提交
100
 * 12 -> X Mouse polled
101 102 103 104
 * 13-15 -> 53-55 Count/compare timers
 * 16-19 -> 56-59 Parallel (16 E)
 * 20-25 -> 60-62 Serial 1 (22 E)
 * 26-31 -> 66-71 Serial 2 (28 E)
L
Linus Torvalds 已提交
105
 *
106
 * Note that this means IRQs 12-14, 50, and 52 do not exist.  This is a
L
Linus Torvalds 已提交
107 108 109 110 111
 * different IRQ map than IRIX uses, but that's OK as Linux irq handling
 * is quite different anyway.
 */

/* Some initial interrupts to set up */
112 113
extern irqreturn_t crime_memerr_intr(int irq, void *dev_id);
extern irqreturn_t crime_cpuerr_intr(int irq, void *dev_id);
L
Linus Torvalds 已提交
114

115 116 117 118 119 120
struct irqaction memerr_irq = {
	.handler = crime_memerr_intr,
	.flags = IRQF_DISABLED,
	.mask = CPU_MASK_NONE,
	.name = "CRIME memory error",
};
121

122 123 124 125 126 127
struct irqaction cpuerr_irq = {
	.handler = crime_cpuerr_intr,
	.flags = IRQF_DISABLED,
	.mask = CPU_MASK_NONE,
	.name = "CRIME CPU error",
};
L
Linus Torvalds 已提交
128 129 130 131 132 133 134 135

/*
 * This is for pure CRIME interrupts - ie not MACE.  The advantage?
 * We get to split the register in half and do faster lookups.
 */

static uint64_t crime_mask;

136
static inline void crime_enable_irq(unsigned int irq)
L
Linus Torvalds 已提交
137
{
138 139 140
	unsigned int bit = irq - CRIME_IRQ_BASE;

	crime_mask |= 1 << bit;
L
Linus Torvalds 已提交
141 142 143
	crime->imask = crime_mask;
}

144
static inline void crime_disable_irq(unsigned int irq)
L
Linus Torvalds 已提交
145
{
146 147 148
	unsigned int bit = irq - CRIME_IRQ_BASE;

	crime_mask &= ~(1 << bit);
L
Linus Torvalds 已提交
149 150 151 152
	crime->imask = crime_mask;
	flush_crime_bus();
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
static void crime_level_mask_and_ack_irq(unsigned int irq)
{
	crime_disable_irq(irq);
}

static void crime_level_end_irq(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
		crime_enable_irq(irq);
}

static struct irq_chip crime_level_interrupt = {
	.name		= "IP32 CRIME",
	.ack		= crime_level_mask_and_ack_irq,
	.mask		= crime_disable_irq,
	.mask_ack	= crime_level_mask_and_ack_irq,
	.unmask		= crime_enable_irq,
	.end		= crime_level_end_irq,
};

static void crime_edge_mask_and_ack_irq(unsigned int irq)
L
Linus Torvalds 已提交
174
{
175 176 177
	unsigned int bit = irq - CRIME_IRQ_BASE;
	uint64_t crime_int;

L
Linus Torvalds 已提交
178
	/* Edge triggered interrupts must be cleared. */
179 180 181 182 183 184

	crime_int = crime->hard_int;
	crime_int &= ~(1 << bit);
	crime->hard_int = crime_int;

	crime_disable_irq(irq);
L
Linus Torvalds 已提交
185 186
}

187
static void crime_edge_end_irq(unsigned int irq)
L
Linus Torvalds 已提交
188 189
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
190
		crime_enable_irq(irq);
L
Linus Torvalds 已提交
191 192
}

193 194 195 196 197 198 199
static struct irq_chip crime_edge_interrupt = {
	.name		= "IP32 CRIME",
	.ack		= crime_edge_mask_and_ack_irq,
	.mask		= crime_disable_irq,
	.mask_ack	= crime_edge_mask_and_ack_irq,
	.unmask		= crime_enable_irq,
	.end		= crime_edge_end_irq,
L
Linus Torvalds 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
};

/*
 * This is for MACE PCI interrupts.  We can decrease bus traffic by masking
 * as close to the source as possible.  This also means we can take the
 * next chunk of the CRIME register in one piece.
 */

static unsigned long macepci_mask;

static void enable_macepci_irq(unsigned int irq)
{
	macepci_mask |= MACEPCI_CONTROL_INT(irq - 9);
	mace->pci.control = macepci_mask;
	crime_mask |= 1 << (irq - 1);
	crime->imask = crime_mask;
}

static void disable_macepci_irq(unsigned int irq)
{
	crime_mask &= ~(1 << (irq - 1));
	crime->imask = crime_mask;
	flush_crime_bus();
	macepci_mask &= ~MACEPCI_CONTROL_INT(irq - 9);
	mace->pci.control = macepci_mask;
	flush_mace_bus();
}

static void end_macepci_irq(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
		enable_macepci_irq(irq);
}

234
static struct irq_chip ip32_macepci_interrupt = {
235
	.name = "IP32 MACE PCI",
A
Atsushi Nemoto 已提交
236 237 238 239
	.ack = disable_macepci_irq,
	.mask = disable_macepci_irq,
	.mask_ack = disable_macepci_irq,
	.unmask = enable_macepci_irq,
240
	.end = end_macepci_irq,
L
Linus Torvalds 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
};

/* This is used for MACE ISA interrupts.  That means bits 4-6 in the
 * CRIME register.
 */

#define MACEISA_AUDIO_INT	(MACEISA_AUDIO_SW_INT |		\
				 MACEISA_AUDIO_SC_INT |		\
				 MACEISA_AUDIO1_DMAT_INT |	\
				 MACEISA_AUDIO1_OF_INT |	\
				 MACEISA_AUDIO2_DMAT_INT |	\
				 MACEISA_AUDIO2_MERR_INT |	\
				 MACEISA_AUDIO3_DMAT_INT |	\
				 MACEISA_AUDIO3_MERR_INT)
#define MACEISA_MISC_INT	(MACEISA_RTC_INT |		\
				 MACEISA_KEYB_INT |		\
				 MACEISA_KEYB_POLL_INT |	\
				 MACEISA_MOUSE_INT |		\
				 MACEISA_MOUSE_POLL_INT |	\
260 261 262
				 MACEISA_TIMER0_INT |		\
				 MACEISA_TIMER1_INT |		\
				 MACEISA_TIMER2_INT)
L
Linus Torvalds 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
#define MACEISA_SUPERIO_INT	(MACEISA_PARALLEL_INT |		\
				 MACEISA_PAR_CTXA_INT |		\
				 MACEISA_PAR_CTXB_INT |		\
				 MACEISA_PAR_MERR_INT |		\
				 MACEISA_SERIAL1_INT |		\
				 MACEISA_SERIAL1_TDMAT_INT |	\
				 MACEISA_SERIAL1_TDMAPR_INT |	\
				 MACEISA_SERIAL1_TDMAME_INT |	\
				 MACEISA_SERIAL1_RDMAT_INT |	\
				 MACEISA_SERIAL1_RDMAOR_INT |	\
				 MACEISA_SERIAL2_INT |		\
				 MACEISA_SERIAL2_TDMAT_INT |	\
				 MACEISA_SERIAL2_TDMAPR_INT |	\
				 MACEISA_SERIAL2_TDMAME_INT |	\
				 MACEISA_SERIAL2_RDMAT_INT |	\
				 MACEISA_SERIAL2_RDMAOR_INT)

static unsigned long maceisa_mask;

282
static void enable_maceisa_irq(unsigned int irq)
L
Linus Torvalds 已提交
283 284 285
{
	unsigned int crime_int = 0;

286
	pr_debug("maceisa enable: %u\n", irq);
L
Linus Torvalds 已提交
287 288 289 290 291

	switch (irq) {
	case MACEISA_AUDIO_SW_IRQ ... MACEISA_AUDIO3_MERR_IRQ:
		crime_int = MACE_AUDIO_INT;
		break;
292
	case MACEISA_RTC_IRQ ... MACEISA_TIMER2_IRQ:
L
Linus Torvalds 已提交
293 294 295 296 297 298
		crime_int = MACE_MISC_INT;
		break;
	case MACEISA_PARALLEL_IRQ ... MACEISA_SERIAL2_RDMAOR_IRQ:
		crime_int = MACE_SUPERIO_INT;
		break;
	}
299
	pr_debug("crime_int %08x enabled\n", crime_int);
L
Linus Torvalds 已提交
300 301 302 303 304 305 306 307 308 309 310
	crime_mask |= crime_int;
	crime->imask = crime_mask;
	maceisa_mask |= 1 << (irq - 33);
	mace->perif.ctrl.imask = maceisa_mask;
}

static void disable_maceisa_irq(unsigned int irq)
{
	unsigned int crime_int = 0;

	maceisa_mask &= ~(1 << (irq - 33));
311
        if (!(maceisa_mask & MACEISA_AUDIO_INT))
L
Linus Torvalds 已提交
312
		crime_int |= MACE_AUDIO_INT;
313
        if (!(maceisa_mask & MACEISA_MISC_INT))
L
Linus Torvalds 已提交
314
		crime_int |= MACE_MISC_INT;
315
        if (!(maceisa_mask & MACEISA_SUPERIO_INT))
L
Linus Torvalds 已提交
316 317 318 319 320 321 322 323 324 325
		crime_int |= MACE_SUPERIO_INT;
	crime_mask &= ~crime_int;
	crime->imask = crime_mask;
	flush_crime_bus();
	mace->perif.ctrl.imask = maceisa_mask;
	flush_mace_bus();
}

static void mask_and_ack_maceisa_irq(unsigned int irq)
{
A
Atsushi Nemoto 已提交
326
	unsigned long mace_int;
L
Linus Torvalds 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

	switch (irq) {
	case MACEISA_PARALLEL_IRQ:
	case MACEISA_SERIAL1_TDMAPR_IRQ:
	case MACEISA_SERIAL2_TDMAPR_IRQ:
		/* edge triggered */
		mace_int = mace->perif.ctrl.istat;
		mace_int &= ~(1 << (irq - 33));
		mace->perif.ctrl.istat = mace_int;
		break;
	}
	disable_maceisa_irq(irq);
}

static void end_maceisa_irq(unsigned irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
		enable_maceisa_irq(irq);
}

347
static struct irq_chip ip32_maceisa_interrupt = {
348 349 350 351 352 353
	.name		= "IP32 MACE ISA",
	.ack		= mask_and_ack_maceisa_irq,
	.mask		= disable_maceisa_irq,
	.mask_ack	= mask_and_ack_maceisa_irq,
	.unmask		= enable_maceisa_irq,
	.end		= end_maceisa_irq,
L
Linus Torvalds 已提交
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
};

/* This is used for regular non-ISA, non-PCI MACE interrupts.  That means
 * bits 0-3 and 7 in the CRIME register.
 */

static void enable_mace_irq(unsigned int irq)
{
	crime_mask |= 1 << (irq - 1);
	crime->imask = crime_mask;
}

static void disable_mace_irq(unsigned int irq)
{
	crime_mask &= ~(1 << (irq - 1));
	crime->imask = crime_mask;
	flush_crime_bus();
}

static void end_mace_irq(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
		enable_mace_irq(irq);
}

379
static struct irq_chip ip32_mace_interrupt = {
380
	.name = "IP32 MACE",
A
Atsushi Nemoto 已提交
381 382 383 384
	.ack = disable_mace_irq,
	.mask = disable_mace_irq,
	.mask_ack = disable_mace_irq,
	.unmask = enable_mace_irq,
385
	.end = end_mace_irq,
L
Linus Torvalds 已提交
386 387
};

388
static void ip32_unknown_interrupt(void)
L
Linus Torvalds 已提交
389
{
390 391 392 393 394 395 396 397 398
	printk("Unknown interrupt occurred!\n");
	printk("cp0_status: %08x\n", read_c0_status());
	printk("cp0_cause: %08x\n", read_c0_cause());
	printk("CRIME intr mask: %016lx\n", crime->imask);
	printk("CRIME intr status: %016lx\n", crime->istat);
	printk("CRIME hardware intr register: %016lx\n", crime->hard_int);
	printk("MACE ISA intr mask: %08lx\n", mace->perif.ctrl.imask);
	printk("MACE ISA intr status: %08lx\n", mace->perif.ctrl.istat);
	printk("MACE PCI control register: %08x\n", mace->pci.control);
L
Linus Torvalds 已提交
399 400

	printk("Register dump:\n");
401
	show_regs(get_irq_regs());
L
Linus Torvalds 已提交
402 403 404 405 406 407 408 409

	printk("Please mail this report to linux-mips@linux-mips.org\n");
	printk("Spinning...");
	while(1) ;
}

/* CRIME 1.1 appears to deliver all interrupts to this one pin. */
/* change this to loop over all edge-triggered irqs, exception masked out ones */
410
static void ip32_irq0(void)
L
Linus Torvalds 已提交
411 412 413 414
{
	uint64_t crime_int;
	int irq = 0;

415 416 417 418 419 420 421 422
	/*
	 * Sanity check interrupt numbering enum.
	 * MACE got 32 interrupts and there are 32 MACE ISA interrupts daisy
	 * chained.
	 */
	BUILD_BUG_ON(CRIME_VICE_IRQ - MACE_VID_IN1_IRQ != 31);
	BUILD_BUG_ON(MACEISA_SERIAL2_RDMAOR_IRQ - MACEISA_AUDIO_SW_IRQ != 31);

L
Linus Torvalds 已提交
423
	crime_int = crime->istat & crime_mask;
424
	irq = MACE_VID_IN1_IRQ + __ffs(crime_int);
425
	crime_int = 1 << irq;
L
Linus Torvalds 已提交
426 427 428

	if (crime_int & CRIME_MACEISA_INT_MASK) {
		unsigned long mace_int = mace->perif.ctrl.istat;
429
		irq = __ffs(mace_int & maceisa_mask) + MACEISA_AUDIO_SW_IRQ;
L
Linus Torvalds 已提交
430
	}
431

432
	pr_debug("*irq %u*\n", irq);
433
	do_IRQ(irq);
L
Linus Torvalds 已提交
434 435
}

436
static void ip32_irq1(void)
L
Linus Torvalds 已提交
437
{
438
	ip32_unknown_interrupt();
L
Linus Torvalds 已提交
439 440
}

441
static void ip32_irq2(void)
L
Linus Torvalds 已提交
442
{
443
	ip32_unknown_interrupt();
L
Linus Torvalds 已提交
444 445
}

446
static void ip32_irq3(void)
L
Linus Torvalds 已提交
447
{
448
	ip32_unknown_interrupt();
L
Linus Torvalds 已提交
449 450
}

451
static void ip32_irq4(void)
L
Linus Torvalds 已提交
452
{
453
	ip32_unknown_interrupt();
L
Linus Torvalds 已提交
454 455
}

456
static void ip32_irq5(void)
L
Linus Torvalds 已提交
457
{
458
	do_IRQ(MIPS_CPU_IRQ_BASE + 7);
L
Linus Torvalds 已提交
459 460
}

461
asmlinkage void plat_irq_dispatch(void)
462
{
463
	unsigned int pending = read_c0_status() & read_c0_cause();
464 465

	if (likely(pending & IE_IRQ0))
466
		ip32_irq0();
467
	else if (unlikely(pending & IE_IRQ1))
468
		ip32_irq1();
469
	else if (unlikely(pending & IE_IRQ2))
470
		ip32_irq2();
471
	else if (unlikely(pending & IE_IRQ3))
472
		ip32_irq3();
473
	else if (unlikely(pending & IE_IRQ4))
474
		ip32_irq4();
475
	else if (likely(pending & IE_IRQ5))
476
		ip32_irq5();
477 478
}

L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486 487 488 489 490
void __init arch_init_irq(void)
{
	unsigned int irq;

	/* Install our interrupt handler, then clear and disable all
	 * CRIME and MACE interrupts. */
	crime->imask = 0;
	crime->hard_int = 0;
	crime->soft_int = 0;
	mace->perif.ctrl.istat = 0;
	mace->perif.ctrl.imask = 0;

491 492 493 494
	mips_cpu_irq_init();
	for (irq = MIPS_CPU_IRQ_BASE + 8; irq <= IP32_IRQ_MAX; irq++) {
		switch (irq) {
		case MACE_VID_IN1_IRQ ... MACE_PCI_BRIDGE_IRQ:
495
			set_irq_chip(irq, &ip32_mace_interrupt);
496 497
			break;
		case MACEPCI_SCSI0_IRQ ...  MACEPCI_SHARED2_IRQ:
498 499 500 501 502 503 504 505
			set_irq_chip(irq, &ip32_macepci_interrupt);
			break;
		case CRIME_GBE0_IRQ ... CRIME_GBE3_IRQ:
			set_irq_chip(irq, &crime_edge_interrupt);
			break;
		case CRIME_CPUERR_IRQ:
		case CRIME_MEMERR_IRQ:
			set_irq_chip(irq, &crime_level_interrupt);
506
			break;
507 508 509 510 511 512
		case CRIME_RE_EMPTY_E_IRQ ... CRIME_RE_IDLE_E_IRQ:
		case CRIME_SOFT0_IRQ ... CRIME_SOFT2_IRQ:
			set_irq_chip(irq, &crime_edge_interrupt);
			break;
		case CRIME_VICE_IRQ:
			set_irq_chip(irq, &crime_edge_interrupt);
513 514
			break;
		default:
515 516
			set_irq_chip(irq, &ip32_maceisa_interrupt);
			break;
517
		}
L
Linus Torvalds 已提交
518 519 520 521 522 523 524
	}
	setup_irq(CRIME_MEMERR_IRQ, &memerr_irq);
	setup_irq(CRIME_CPUERR_IRQ, &cpuerr_irq);

#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
	change_c0_status(ST0_IM, ALLINTS);
}