/*
* linux/arch/alpha/kernel/irq.c
*
* Copyright (C) 1995 Linus Torvalds
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
* instead of just grabbing them. Thus setups with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
*/
#include <linux/config.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/random.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/dma.h>
extern void timer_interrupt(struct pt_regs * regs);
#if NR_IRQS > 64
# error Unable to handle more than 64 irq levels.
#endif
/* Reserved interrupts. These must NEVER be requested by any driver!
*/
#define IS_RESERVED_IRQ(irq) ((irq)==2) /* IRQ 2 used by hw cascade */
/*
* Shadow-copy of masked interrupts.
* The bits are used as follows:
* 0.. 7 first (E)ISA PIC (irq level 0..7)
* 8..15 second (E)ISA PIC (irq level 8..15)
* Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT):
* 16..47 PCI interrupts 0..31 (int at GRU_INT_MASK)
* Mikasa:
* 16..31 PCI interrupts 0..15 (short at I/O port 536)
* Other systems (not Mikasa) with 16 PCI interrupt lines:
* 16..23 PCI interrupts 0.. 7 (char at I/O port 26)
* 24..31 PCI interrupts 8..15 (char at I/O port 27)
* Systems with 17 PCI interrupt lines (e.g., Cabriolet and eb164):
* 16..32 PCI interrupts 0..31 (int at I/O port 804)
*/
static unsigned long irq_mask = ~0UL;
/*
* Update the hardware with the irq mask passed in MASK. The function
* exploits the fact that it is known that only bit IRQ has changed.
*/
static void update_hw(unsigned long irq, unsigned long mask)
{
#ifdef CONFIG_ALPHA_ALCOR
/* always mask out 20..30 (which are unused) */
mask |= 0x7ff00000UL << 16;
#endif
switch (irq) {
#if NR_IRQS == 48
default:
/* note inverted sense of mask bits: */
*(unsigned int *)GRU_INT_MASK = ~(mask >> 16); mb();
break;
#elif NR_IRQS == 33
default:
outl(mask >> 16, 0x804);
break;
#elif defined(CONFIG_ALPHA_MIKASA)
default:
outw(~(mask >> 16), 0x536); /* note invert */
break;
#elif NR_IRQS == 32
case 16 ... 23:
outb(mask >> 16, 0x26);
break;
default:
outb(mask >> 24, 0x27);
break;
#endif
/* handle ISA irqs last---fast devices belong on PCI... */
case 0 ... 7: /* ISA PIC1 */
outb(mask, 0x21);
break;
case 8 ...15: /* ISA PIC2 */
outb(mask >> 8, 0xA1);
break;
}
}
static inline void mask_irq(unsigned long irq)
{
irq_mask |= (1UL << irq);
update_hw(irq, irq_mask);
}
static inline void unmask_irq(unsigned long irq)
{
irq_mask &= ~(1UL << irq);
update_hw(irq, irq_mask);
}
void disable_irq(unsigned int irq_nr)
{
unsigned long flags;
save_flags(flags);
cli();
mask_irq(irq_nr);
restore_flags(flags);
}
void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
save_flags(flags);
cli();
unmask_irq(irq_nr);
restore_flags(flags);
}
/*
* Initial irq handlers.
*/
static struct irqaction *irq_action[NR_IRQS];
int get_irq_list(char *buf)
{
int i, len = 0;
struct irqaction * action;
for (i = 0 ; i < NR_IRQS ; i++) {
action = irq_action[i];
if (!action)
continue;
len += sprintf(buf+len, "%2d: %8d %c %s",
i, kstat.interrupts[i],
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ",%s %s",
(action->flags & SA_INTERRUPT) ? " +" : "",
action->name);
}
len += sprintf(buf+len, "\n");
}
return len;
}
static inline void ack_irq(int irq)
{
if (irq < 16) {
/* ACK the interrupt making it the lowest priority */
/* First the slave .. */
if (irq > 7) {
outb(0xE0 | (irq - 8), 0xa0);
irq = 2;
}
/* .. then the master */
outb(0xE0 | irq, 0x20);
#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
/* on ALCOR/XLT, need to dismiss interrupt via GRU */
*(int *)GRU_INT_CLEAR = 0x80000000; mb();
*(int *)GRU_INT_CLEAR = 0x00000000; mb();
#endif /* ALCOR || XLT */
}
}
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char * devname,
void *dev_id)
{
int shared = 0;
struct irqaction * action, **p;
unsigned long flags;
if (irq >= NR_IRQS)
return -EINVAL;
if (IS_RESERVED_IRQ(irq))
return -EINVAL;
if (!handler)
return -EINVAL;
p = irq_action + irq;
action = *p;
if (action) {
/* Can't share interrupts unless both agree to */
if (!(action->flags & irqflags & SA_SHIRQ))
return -EBUSY;
/* Can't share interrupts unless both are same type */
if ((action->flags ^ irqflags) & SA_INTERRUPT)
return -EBUSY;
/* add new interrupt at end of irq queue */
do {
p = &action->next;
action = *p;
} while (action);
shared = 1;
}
action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
GFP_KERNEL);
if (!action)
return -ENOMEM;
if (irqflags & SA_SAMPLE_RANDOM)
rand_initialize_irq(irq);
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
save_flags(flags);
cli();
*p = action;
if (!shared)
unmask_irq(irq);
restore_flags(flags);
return 0;
}
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action, **p;
unsigned long flags;
if (irq >= NR_IRQS) {
printk("Trying to free IRQ%d\n",irq);
return;
}
if (IS_RESERVED_IRQ(irq)) {
printk("Trying to free reserved IRQ %d\n", irq);
return;
}
for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
/* Found it - now free it */
save_flags(flags);
cli();
*p = action->next;
if (!irq[irq_action])
mask_irq(irq);
restore_flags(flags);
kfree(action);
return;
}
printk("Trying to free free IRQ%d\n",irq);
}
static inline void handle_nmi(struct pt_regs * regs)
{
printk("Whee.. NMI received. Probable hardware error\n");
printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461));
}
static void unexpected_irq(int irq, struct pt_regs * regs)
{
struct irqaction *action;
int i;
printk("IO device interrupt, irq = %d\n", irq);
printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
printk("Expecting: ");
for (i = 0; i < 16; i++)
if ((action = irq_action[i]))
while (action->handler) {
printk("[%s:%d] ", action->name, i);
action = action->next;
}
printk("\n");
#if defined(CONFIG_ALPHA_JENSEN)
printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
outb(0x0c, 0x3fc);
outb(0x0c, 0x2fc);
outb(0,0x61);
outb(0,0x461);
#endif
}
static inline void handle_irq(int irq, struct pt_regs * regs)
{
struct irqaction * action = irq_action[irq];
kstat.interrupts[irq]++;
if (!action) {
unexpected_irq(irq, regs);
return;
}
do {
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
}
static inline void device_interrupt(int irq, int ack, struct pt_regs * regs)
{
struct irqaction * action;
if ((unsigned) irq > NR_IRQS) {
printk("device_interrupt: unexpected interrupt %d\n", irq);
return;
}
kstat.interrupts[irq]++;
action = irq_action[irq];
/*
* For normal interrupts, we mask it out, and then ACK it.
* This way another (more timing-critical) interrupt can
* come through while we're doing this one.
*
* Note! A irq without a handler gets masked and acked, but
* never unmasked. The autoirq stuff depends on this (it looks
* at the masks before and after doing the probing).
*/
mask_irq(ack);
ack_irq(ack);
if (!action)
return;
if (action->flags & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
do {
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
unmask_irq(ack);
}
#ifdef CONFIG_PCI
/*
* Handle ISA interrupt via the PICs.
*/
static inline void isa_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
#if defined(CONFIG_ALPHA_APECS)
# define IACK_SC APECS_IACK_SC
#elif defined(CONFIG_ALPHA_LCA)
# define IACK_SC LCA_IACK_SC
#elif defined(CONFIG_ALPHA_CIA)
# define IACK_SC CIA_IACK_SC
#else
/*
* This is bogus but necessary to get it to compile
* on all platforms. If you try to use this on any
* other than the intended platforms, you'll notice
* real fast...
*/
# define IACK_SC 1L
#endif
int j;
#if 1
/*
* Generate a PCI interrupt acknowledge cycle. The PIC will
* respond with the interrupt vector of the highest priority
* interrupt that is pending. The PALcode sets up the
* interrupts vectors such that irq level L generates vector
* L.
*/
j = *(volatile int *) IACK_SC;
j &= 0xff;
if (j == 7) {
if (!(inb(0x20) & 0x80)) {
/* it's only a passive release... */
return;
}
}
device_interrupt(j, j, regs);
#else
unsigned long pic;
/*
* It seems to me that the probability of two or more *device*
* interrupts occurring at almost exactly the same time is
* pretty low. So why pay the price of checking for
* additional interrupts here if the common case can be
* handled so much easier?
*/
/*
* The first read of gives you *all* interrupting lines.
* Therefore, read the mask register and and out those lines
* not enabled. Note that some documentation has 21 and a1
* write only. This is not true.
*/
pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */
pic &= ~irq_mask; /* apply mask */
pic &= 0xFFFB; /* mask out cascade & hibits */
while (pic) {
j = ffz(~pic);
pic &= pic - 1;
device_interrupt(j, j, regs);
}
#endif
}
#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
/* we have to conditionally compile this because of GRU_xxx symbols */
static inline void alcor_and_xlt_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
save_flags(flags);
cli();
/* read the interrupt summary register of the GRU */
pld = (*(unsigned int *)GRU_INT_REQ) & GRU_INT_REQ_BITS;
#if 0
printk("[0x%08lx/0x%04x]", pld, inb(0x20) | (inb(0xA0) << 8));
#endif
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 31) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(16 + i, 16 + i, regs);
}
}
restore_flags(flags);
}
#endif /* ALCOR || XLT */
static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
save_flags(flags);
cli();
/* read the interrupt summary registers */
pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16);
#if 0
printk("[0x%04X/0x%04X]", pld, inb(0x20) | (inb(0xA0) << 8));
#endif
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 4) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(16 + i, 16 + i, regs);
}
}
restore_flags(flags);
}
static inline void mikasa_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
save_flags(flags);
cli();
/* read the interrupt summary registers */
pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) |
(((unsigned long) inb(0xa0)) << 8) |
((unsigned long) inb(0x20));
#if 0
printk("[0x%08lx]", pld);
#endif
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i < 16) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(i, i, regs);
}
}
restore_flags(flags);
}
static inline void eb66_and_eb64p_device_interrupt(unsigned long vector,
struct pt_regs * regs)
{
unsigned long pld;
unsigned int i;
unsigned long flags;
save_flags(flags);
cli();
/* read the interrupt summary registers */
pld = inb(0x26) | (inb(0x27) << 8);
/*
* Now, for every possible bit set, work through
* them and call the appropriate interrupt handler.
*/
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
if (i == 5) {
isa_device_interrupt(vector, regs);
} else {
device_interrupt(16 + i, 16 + i, regs);
}
}
restore_flags(flags);
}
#endif /* CONFIG_PCI */
/*
* Jensen is special: the vector is 0x8X0 for EISA interrupt X, and
* 0x9X0 for the local motherboard interrupts..
*
* 0x660 - NMI
*
* 0x800 - IRQ0 interval timer (not used, as we use the RTC timer)
* 0x810 - IRQ1 line printer (duh..)
* 0x860 - IRQ6 floppy disk
* 0x8E0 - IRQ14 SCSI controller
*
* 0x900 - COM1
* 0x920 - COM2
* 0x980 - keyboard
* 0x990 - mouse
*
* PCI-based systems are more sane: they don't have the local
* interrupts at all, and have only normal PCI interrupts from
* devices. Happily it's easy enough to do a sane mapping from the
* Jensen.. Note that this means that we may have to do a hardware
* "ack" to a different interrupt than we report to the rest of the
* world.
*/
static inline void srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
{
int irq, ack;
unsigned long flags;
save_flags(flags);
cli();
ack = irq = (vector - 0x800) >> 4;
#ifdef CONFIG_ALPHA_JENSEN
switch (vector) {
case 0x660: handle_nmi(regs); return;
/* local device interrupts: */
case 0x900: handle_irq(4, regs); return; /* com1 -> irq 4 */
case 0x920: handle_irq(3, regs); return; /* com2 -> irq 3 */
case 0x980: handle_irq(1, regs); return; /* kbd -> irq 1 */
case 0x990: handle_irq(9, regs); return; /* mouse -> irq 9 */
default:
if (vector > 0x900) {
printk("Unknown local interrupt %lx\n", vector);
}
}
/* irq1 is supposed to be the keyboard, silly Jensen (is this really needed??) */
if (irq == 1)
irq = 7;
#endif /* CONFIG_ALPHA_JENSEN */
device_interrupt(irq, ack, regs);
restore_flags(flags) ;
}
/*
* Start listening for interrupts..
*/
unsigned long probe_irq_on(void)
{
struct irqaction * action;
unsigned long irqs = 0;
unsigned long delay;
unsigned int i;
for (i = NR_IRQS - 1; i > 0; i--) {
action = irq_action[i];
if (!action) {
enable_irq(i);
irqs |= (1 << i);
}
}
/*
* Wait about 100ms for spurious interrupts to mask themselves
* out again...
*/
for (delay = jiffies + HZ/10; delay > jiffies; )
barrier();
/* now filter out any obviously spurious interrupts */
return irqs & ~irq_mask;
}
/*
* Get the result of the IRQ probe.. A negative result means that
* we have several candidates (but we return the lowest-numbered
* one).
*/
int probe_irq_off(unsigned long irqs)
{
int i;
irqs &= irq_mask & ~1; /* always mask out irq 0---it's the unused timer */
#ifdef CONFIG_ALPHA_P2K
irqs &= ~(1 << 8); /* mask out irq 8 since that's the unused RTC input to PIC */
#endif
if (!irqs)
return 0;
i = ffz(~irqs);
if (irqs != (1UL << i))
i = -i;
return i;
}
static void machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs)
{
#if defined(CONFIG_ALPHA_LCA)
extern void lca_machine_check (unsigned long vector, unsigned long la,
struct pt_regs *regs);
lca_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_APECS)
extern void apecs_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
apecs_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_CIA)
extern void cia_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
cia_machine_check(vector, la, regs);
#else
printk("Machine check\n");
#endif
}
asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
unsigned long a3, unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
switch (type) {
case 0:
printk("Interprocessor interrupt? You must be kidding\n");
break;
case 1:
timer_interrupt(®s);
return;
case 2:
machine_check(vector, la_ptr, ®s);
return;
case 3:
#if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \
defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM)
srm_device_interrupt(vector, ®s);
#elif NR_IRQS == 48
alcor_and_xlt_device_interrupt(vector, ®s);
#elif NR_IRQS == 33
cabriolet_and_eb66p_device_interrupt(vector, ®s);
#elif defined(CONFIG_ALPHA_MIKASA)
mikasa_device_interrupt(vector, ®s);
#elif NR_IRQS == 32
eb66_and_eb64p_device_interrupt(vector, ®s);
#elif NR_IRQS == 16
isa_device_interrupt(vector, ®s);
#endif
return;
case 4:
printk("Performance counter interrupt\n");
break;;
default:
printk("Hardware intr %ld %lx? Huh?\n", type, vector);
}
printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps);
}
extern asmlinkage void entInt(void);
void init_IRQ(void)
{
wrent(entInt, 0);
dma_outb(0, DMA1_RESET_REG);
dma_outb(0, DMA2_RESET_REG);
dma_outb(0, DMA1_CLR_MASK_REG);
dma_outb(0, DMA2_CLR_MASK_REG);
#if NR_IRQS == 48
*(unsigned int *)GRU_INT_MASK = ~(irq_mask >> 16); mb();/* invert */
enable_irq(16 + 31); /* enable (E)ISA PIC cascade */
#elif NR_IRQS == 33
outl(irq_mask >> 16, 0x804);
enable_irq(16 + 4); /* enable SIO cascade */
#elif defined(CONFIG_ALPHA_MIKASA)
outw(~(irq_mask >> 16), 0x536); /* note invert */
#elif NR_IRQS == 32
outb(irq_mask >> 16, 0x26);
outb(irq_mask >> 24, 0x27);
enable_irq(16 + 5); /* enable SIO cascade */
#endif
enable_irq(2); /* enable cascade */
}