11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * linux/arch/alpha/kernel/irq_i8259.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is the 'legacy' 8259A Programmable Interrupt Controller, 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * present in the majority of PC/AT boxes. 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c. 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cache.h> 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h> 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/irq.h> 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "proto.h" 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "irq_impl.h" 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Note mask bit is true for DISABLED irqs. */ 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int cached_irq_mask = 0xffff; 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEFINE_SPINLOCK(i8259_irq_lock); 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsi8259_update_irq_hw(unsigned int irq, unsigned long mask) 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int port = 0x21; 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (irq & 8) mask >>= 8; 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (irq & 8) port = 0xA1; 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(mask, port); 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsinline void 36ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixneri8259a_enable_irq(struct irq_data *d) 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock(&i8259_irq_lock); 39ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner i8259_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq)); 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock(&i8259_irq_lock); 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds__i8259a_disable_irq(unsigned int irq) 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i8259_update_irq_hw(irq, cached_irq_mask |= 1 << irq); 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid 50ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixneri8259a_disable_irq(struct irq_data *d) 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock(&i8259_irq_lock); 53ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner __i8259a_disable_irq(d->irq); 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock(&i8259_irq_lock); 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid 58ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixneri8259a_mask_and_ack_irq(struct irq_data *d) 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 60ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner unsigned int irq = d->irq; 61ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock(&i8259_irq_lock); 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __i8259a_disable_irq(irq); 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ack the interrupt making it the lowest priority. */ 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (irq >= 8) { 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(0xE0 | (irq - 8), 0xa0); /* ack the slave */ 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds irq = 2; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(0xE0 | irq, 0x20); /* ack the master */ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock(&i8259_irq_lock); 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7444377f622ee4f23ea0afc9b83dba5d3ec2d560cdThomas Gleixnerstruct irq_chip i8259a_irq_type = { 758ab1221c20255f35d85664a046549bc6135122c2Thomas Gleixner .name = "XT-PIC", 76ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner .irq_unmask = i8259a_enable_irq, 77ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner .irq_mask = i8259a_disable_irq, 78ff53afe66a3ab5614309a4193df72c82ec3bb984Thomas Gleixner .irq_mask_ack = i8259a_mask_and_ack_irq, 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid __init 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsinit_i8259a_irqs(void) 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static struct irqaction cascade = { 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .handler = no_action, 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cascade", 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds long i; 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(0xff, 0x21); /* mask all of 8259A-1 */ 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(0xff, 0xA1); /* mask all of 8259A-2 */ 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < 16; i++) { 95a9eb076b21425929ce543978db03265d9db210deThomas Gleixner irq_set_chip_and_handler(i, &i8259a_irq_type, handle_level_irq); 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds setup_irq(2, &cascade); 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(CONFIG_ALPHA_GENERIC) 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC alpha_mv.iack_sc 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_APECS) 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC APECS_IACK_SC 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_LCA) 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC LCA_IACK_SC 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_CIA) 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC CIA_IACK_SC 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_PYXIS) 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC PYXIS_IACK_SC 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_TITAN) 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC TITAN_IACK_SC 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_TSUNAMI) 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC TSUNAMI_IACK_SC 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#elif defined(CONFIG_ALPHA_IRONGATE) 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# define IACK_SC IRONGATE_IACK_SC 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(IACK_SC) 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid 1247ca56053b29633ef08b14e5ca16c663363edac36Al Viroisa_device_interrupt(unsigned long vector) 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Generate a PCI interrupt acknowledge cycle. The PIC will 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * respond with the interrupt vector of the highest priority 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * interrupt that is pending. The PALcode sets up the 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * interrupts vectors such that irq level L generates vector L. 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int j = *(vuip) IACK_SC; 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds j &= 0xff; 1343dbb8c62897f96bbf5d4e4fe649e5d3791fc33c5Al Viro handle_irq(j); 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid 1403dbb8c62897f96bbf5d4e4fe649e5d3791fc33c5Al Viroisa_no_iack_sc_device_interrupt(unsigned long vector) 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long pic; 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * It seems to me that the probability of two or more *device* 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * interrupts occurring at almost exactly the same time is 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * pretty low. So why pay the price of checking for 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * additional interrupts here if the common case can be 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * handled so much easier? 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The first read of gives you *all* interrupting lines. 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Therefore, read the mask register and and out those lines 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * not enabled. Note that some documentation has 21 and a1 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * write only. This is not true. 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pic &= 0xFFFB; /* mask out cascade & hibits */ 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (pic) { 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int j = ffz(~pic); 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pic &= pic - 1; 1633dbb8c62897f96bbf5d4e4fe649e5d3791fc33c5Al Viro handle_irq(j); 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 167