xref: /openbmc/linux/arch/alpha/kernel/irq_i8259.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *      linux/arch/alpha/kernel/irq_i8259.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This is the 'legacy' 8259A Programmable Interrupt Controller,
61da177e4SLinus Torvalds  * present in the majority of PC/AT boxes.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c.
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/init.h>
121da177e4SLinus Torvalds #include <linux/cache.h>
131da177e4SLinus Torvalds #include <linux/sched.h>
141da177e4SLinus Torvalds #include <linux/irq.h>
151da177e4SLinus Torvalds #include <linux/interrupt.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <asm/io.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include "proto.h"
201da177e4SLinus Torvalds #include "irq_impl.h"
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds /* Note mask bit is true for DISABLED irqs.  */
241da177e4SLinus Torvalds static unsigned int cached_irq_mask = 0xffff;
251da177e4SLinus Torvalds static DEFINE_SPINLOCK(i8259_irq_lock);
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds static inline void
i8259_update_irq_hw(unsigned int irq,unsigned long mask)281da177e4SLinus Torvalds i8259_update_irq_hw(unsigned int irq, unsigned long mask)
291da177e4SLinus Torvalds {
301da177e4SLinus Torvalds 	int port = 0x21;
311da177e4SLinus Torvalds 	if (irq & 8) mask >>= 8;
321da177e4SLinus Torvalds 	if (irq & 8) port = 0xA1;
331da177e4SLinus Torvalds 	outb(mask, port);
341da177e4SLinus Torvalds }
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds inline void
i8259a_enable_irq(struct irq_data * d)37ff53afe6SThomas Gleixner i8259a_enable_irq(struct irq_data *d)
381da177e4SLinus Torvalds {
391da177e4SLinus Torvalds 	spin_lock(&i8259_irq_lock);
40ff53afe6SThomas Gleixner 	i8259_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq));
411da177e4SLinus Torvalds 	spin_unlock(&i8259_irq_lock);
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds static inline void
__i8259a_disable_irq(unsigned int irq)451da177e4SLinus Torvalds __i8259a_disable_irq(unsigned int irq)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds 	i8259_update_irq_hw(irq, cached_irq_mask |= 1 << irq);
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds void
i8259a_disable_irq(struct irq_data * d)51ff53afe6SThomas Gleixner i8259a_disable_irq(struct irq_data *d)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	spin_lock(&i8259_irq_lock);
54ff53afe6SThomas Gleixner 	__i8259a_disable_irq(d->irq);
551da177e4SLinus Torvalds 	spin_unlock(&i8259_irq_lock);
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds void
i8259a_mask_and_ack_irq(struct irq_data * d)59ff53afe6SThomas Gleixner i8259a_mask_and_ack_irq(struct irq_data *d)
601da177e4SLinus Torvalds {
61ff53afe6SThomas Gleixner 	unsigned int irq = d->irq;
62ff53afe6SThomas Gleixner 
631da177e4SLinus Torvalds 	spin_lock(&i8259_irq_lock);
641da177e4SLinus Torvalds 	__i8259a_disable_irq(irq);
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 	/* Ack the interrupt making it the lowest priority.  */
671da177e4SLinus Torvalds 	if (irq >= 8) {
681da177e4SLinus Torvalds 		outb(0xE0 | (irq - 8), 0xa0);   /* ack the slave */
691da177e4SLinus Torvalds 		irq = 2;
701da177e4SLinus Torvalds 	}
711da177e4SLinus Torvalds 	outb(0xE0 | irq, 0x20);			/* ack the master */
721da177e4SLinus Torvalds 	spin_unlock(&i8259_irq_lock);
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds 
7544377f62SThomas Gleixner struct irq_chip i8259a_irq_type = {
768ab1221cSThomas Gleixner 	.name		= "XT-PIC",
77ff53afe6SThomas Gleixner 	.irq_unmask	= i8259a_enable_irq,
78ff53afe6SThomas Gleixner 	.irq_mask	= i8259a_disable_irq,
79ff53afe6SThomas Gleixner 	.irq_mask_ack	= i8259a_mask_and_ack_irq,
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds void __init
init_i8259a_irqs(void)831da177e4SLinus Torvalds init_i8259a_irqs(void)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	long i;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	outb(0xff, 0x21);	/* mask all of 8259A-1 */
881da177e4SLinus Torvalds 	outb(0xff, 0xA1);	/* mask all of 8259A-2 */
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	for (i = 0; i < 16; i++) {
91a9eb076bSThomas Gleixner 		irq_set_chip_and_handler(i, &i8259a_irq_type, handle_level_irq);
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 
94*82c849ebSafzal mohammed 	if (request_irq(2, no_action, 0, "cascade", NULL))
95*82c849ebSafzal mohammed 		pr_err("Failed to request irq 2 (cascade)\n");
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds #if defined(CONFIG_ALPHA_GENERIC)
1001da177e4SLinus Torvalds # define IACK_SC	alpha_mv.iack_sc
1011da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_APECS)
1021da177e4SLinus Torvalds # define IACK_SC	APECS_IACK_SC
1031da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_LCA)
1041da177e4SLinus Torvalds # define IACK_SC	LCA_IACK_SC
1051da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_CIA)
1061da177e4SLinus Torvalds # define IACK_SC	CIA_IACK_SC
1071da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_PYXIS)
1081da177e4SLinus Torvalds # define IACK_SC	PYXIS_IACK_SC
1091da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_TITAN)
1101da177e4SLinus Torvalds # define IACK_SC	TITAN_IACK_SC
1111da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_TSUNAMI)
1121da177e4SLinus Torvalds # define IACK_SC	TSUNAMI_IACK_SC
1131da177e4SLinus Torvalds #elif defined(CONFIG_ALPHA_IRONGATE)
1141da177e4SLinus Torvalds # define IACK_SC        IRONGATE_IACK_SC
1151da177e4SLinus Torvalds #endif
1161da177e4SLinus Torvalds /* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since
1171da177e4SLinus Torvalds    sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason.  */
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds #if defined(IACK_SC)
1201da177e4SLinus Torvalds void
isa_device_interrupt(unsigned long vector)1217ca56053SAl Viro isa_device_interrupt(unsigned long vector)
1221da177e4SLinus Torvalds {
1231da177e4SLinus Torvalds 	/*
1241da177e4SLinus Torvalds 	 * Generate a PCI interrupt acknowledge cycle.  The PIC will
1251da177e4SLinus Torvalds 	 * respond with the interrupt vector of the highest priority
1261da177e4SLinus Torvalds 	 * interrupt that is pending.  The PALcode sets up the
1271da177e4SLinus Torvalds 	 * interrupts vectors such that irq level L generates vector L.
1281da177e4SLinus Torvalds 	 */
1291da177e4SLinus Torvalds 	int j = *(vuip) IACK_SC;
1301da177e4SLinus Torvalds 	j &= 0xff;
1313dbb8c62SAl Viro 	handle_irq(j);
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds #endif
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds #if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC)
1361da177e4SLinus Torvalds void
isa_no_iack_sc_device_interrupt(unsigned long vector)1373dbb8c62SAl Viro isa_no_iack_sc_device_interrupt(unsigned long vector)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds 	unsigned long pic;
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	/*
1421da177e4SLinus Torvalds 	 * It seems to me that the probability of two or more *device*
1431da177e4SLinus Torvalds 	 * interrupts occurring at almost exactly the same time is
1441da177e4SLinus Torvalds 	 * pretty low.  So why pay the price of checking for
1451da177e4SLinus Torvalds 	 * additional interrupts here if the common case can be
1461da177e4SLinus Torvalds 	 * handled so much easier?
1471da177e4SLinus Torvalds 	 */
1481da177e4SLinus Torvalds 	/*
1491da177e4SLinus Torvalds 	 *  The first read of gives you *all* interrupting lines.
1501da177e4SLinus Torvalds 	 *  Therefore, read the mask register and and out those lines
1511da177e4SLinus Torvalds 	 *  not enabled.  Note that some documentation has 21 and a1
1521da177e4SLinus Torvalds 	 *  write only.  This is not true.
1531da177e4SLinus Torvalds 	 */
1541da177e4SLinus Torvalds 	pic = inb(0x20) | (inb(0xA0) << 8);	/* read isr */
1551da177e4SLinus Torvalds 	pic &= 0xFFFB;				/* mask out cascade & hibits */
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	while (pic) {
1581da177e4SLinus Torvalds 		int j = ffz(~pic);
1591da177e4SLinus Torvalds 		pic &= pic - 1;
1603dbb8c62SAl Viro 		handle_irq(j);
1611da177e4SLinus Torvalds 	}
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds #endif
164