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