xref: /openbmc/linux/arch/powerpc/sysdev/i8259.c (revision 87c2ce3b)
1 /*
2  * i8259 interrupt controller driver.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version
7  * 2 of the License, or (at your option) any later version.
8  */
9 #include <linux/init.h>
10 #include <linux/ioport.h>
11 #include <linux/interrupt.h>
12 #include <asm/io.h>
13 #include <asm/i8259.h>
14 
15 static volatile void __iomem *pci_intack; /* RO, gives us the irq vector */
16 
17 static unsigned char cached_8259[2] = { 0xff, 0xff };
18 #define cached_A1 (cached_8259[0])
19 #define cached_21 (cached_8259[1])
20 
21 static DEFINE_SPINLOCK(i8259_lock);
22 
23 static int i8259_pic_irq_offset;
24 
25 /*
26  * Acknowledge the IRQ using either the PCI host bridge's interrupt
27  * acknowledge feature or poll.  How i8259_init() is called determines
28  * which is called.  It should be noted that polling is broken on some
29  * IBM and Motorola PReP boxes so we must use the int-ack feature on them.
30  */
31 int i8259_irq(struct pt_regs *regs)
32 {
33 	int irq;
34 
35 	spin_lock(&i8259_lock);
36 
37 	/* Either int-ack or poll for the IRQ */
38 	if (pci_intack)
39 		irq = readb(pci_intack);
40 	else {
41 		/* Perform an interrupt acknowledge cycle on controller 1. */
42 		outb(0x0C, 0x20);		/* prepare for poll */
43 		irq = inb(0x20) & 7;
44 		if (irq == 2 ) {
45 			/*
46 			 * Interrupt is cascaded so perform interrupt
47 			 * acknowledge on controller 2.
48 			 */
49 			outb(0x0C, 0xA0);	/* prepare for poll */
50 			irq = (inb(0xA0) & 7) + 8;
51 		}
52 	}
53 
54 	if (irq == 7) {
55 		/*
56 		 * This may be a spurious interrupt.
57 		 *
58 		 * Read the interrupt status register (ISR). If the most
59 		 * significant bit is not set then there is no valid
60 		 * interrupt.
61 		 */
62 		if (!pci_intack)
63 			outb(0x0B, 0x20);	/* ISR register */
64 		if(~inb(0x20) & 0x80)
65 			irq = -1;
66 	}
67 
68 	spin_unlock(&i8259_lock);
69 	return irq + i8259_pic_irq_offset;
70 }
71 
72 int i8259_irq_cascade(struct pt_regs *regs, void *unused)
73 {
74 	return i8259_irq(regs);
75 }
76 
77 static void i8259_mask_and_ack_irq(unsigned int irq_nr)
78 {
79 	unsigned long flags;
80 
81 	spin_lock_irqsave(&i8259_lock, flags);
82 	irq_nr -= i8259_pic_irq_offset;
83 	if (irq_nr > 7) {
84 		cached_A1 |= 1 << (irq_nr-8);
85 		inb(0xA1); 	/* DUMMY */
86 		outb(cached_A1, 0xA1);
87 		outb(0x20, 0xA0);	/* Non-specific EOI */
88 		outb(0x20, 0x20);	/* Non-specific EOI to cascade */
89 	} else {
90 		cached_21 |= 1 << irq_nr;
91 		inb(0x21); 	/* DUMMY */
92 		outb(cached_21, 0x21);
93 		outb(0x20, 0x20);	/* Non-specific EOI */
94 	}
95 	spin_unlock_irqrestore(&i8259_lock, flags);
96 }
97 
98 static void i8259_set_irq_mask(int irq_nr)
99 {
100 	outb(cached_A1,0xA1);
101 	outb(cached_21,0x21);
102 }
103 
104 static void i8259_mask_irq(unsigned int irq_nr)
105 {
106 	unsigned long flags;
107 
108 	spin_lock_irqsave(&i8259_lock, flags);
109 	irq_nr -= i8259_pic_irq_offset;
110 	if (irq_nr < 8)
111 		cached_21 |= 1 << irq_nr;
112 	else
113 		cached_A1 |= 1 << (irq_nr-8);
114 	i8259_set_irq_mask(irq_nr);
115 	spin_unlock_irqrestore(&i8259_lock, flags);
116 }
117 
118 static void i8259_unmask_irq(unsigned int irq_nr)
119 {
120 	unsigned long flags;
121 
122 	spin_lock_irqsave(&i8259_lock, flags);
123 	irq_nr -= i8259_pic_irq_offset;
124 	if (irq_nr < 8)
125 		cached_21 &= ~(1 << irq_nr);
126 	else
127 		cached_A1 &= ~(1 << (irq_nr-8));
128 	i8259_set_irq_mask(irq_nr);
129 	spin_unlock_irqrestore(&i8259_lock, flags);
130 }
131 
132 static void i8259_end_irq(unsigned int irq)
133 {
134 	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))
135 	    && irq_desc[irq].action)
136 		i8259_unmask_irq(irq);
137 }
138 
139 struct hw_interrupt_type i8259_pic = {
140 	.typename = " i8259    ",
141 	.enable = i8259_unmask_irq,
142 	.disable = i8259_mask_irq,
143 	.ack = i8259_mask_and_ack_irq,
144 	.end = i8259_end_irq,
145 };
146 
147 static struct resource pic1_iores = {
148 	.name = "8259 (master)",
149 	.start = 0x20,
150 	.end = 0x21,
151 	.flags = IORESOURCE_BUSY,
152 };
153 
154 static struct resource pic2_iores = {
155 	.name = "8259 (slave)",
156 	.start = 0xa0,
157 	.end = 0xa1,
158 	.flags = IORESOURCE_BUSY,
159 };
160 
161 static struct resource pic_edgectrl_iores = {
162 	.name = "8259 edge control",
163 	.start = 0x4d0,
164 	.end = 0x4d1,
165 	.flags = IORESOURCE_BUSY,
166 };
167 
168 static struct irqaction i8259_irqaction = {
169 	.handler = no_action,
170 	.flags = SA_INTERRUPT,
171 	.mask = CPU_MASK_NONE,
172 	.name = "82c59 secondary cascade",
173 };
174 
175 /*
176  * i8259_init()
177  * intack_addr - PCI interrupt acknowledge (real) address which will return
178  *               the active irq from the 8259
179  */
180 void __init i8259_init(unsigned long intack_addr, int offset)
181 {
182 	unsigned long flags;
183 	int i;
184 
185 	spin_lock_irqsave(&i8259_lock, flags);
186 	i8259_pic_irq_offset = offset;
187 
188 	/* init master interrupt controller */
189 	outb(0x11, 0x20); /* Start init sequence */
190 	outb(0x00, 0x21); /* Vector base */
191 	outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
192 	outb(0x01, 0x21); /* Select 8086 mode */
193 
194 	/* init slave interrupt controller */
195 	outb(0x11, 0xA0); /* Start init sequence */
196 	outb(0x08, 0xA1); /* Vector base */
197 	outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
198 	outb(0x01, 0xA1); /* Select 8086 mode */
199 
200 	/* always read ISR */
201 	outb(0x0B, 0x20);
202 	outb(0x0B, 0xA0);
203 
204 	/* Mask all interrupts */
205 	outb(cached_A1, 0xA1);
206 	outb(cached_21, 0x21);
207 
208 	spin_unlock_irqrestore(&i8259_lock, flags);
209 
210 	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
211 		irq_desc[offset + i].handler = &i8259_pic;
212 
213 	/* reserve our resources */
214 	setup_irq(offset + 2, &i8259_irqaction);
215 	request_resource(&ioport_resource, &pic1_iores);
216 	request_resource(&ioport_resource, &pic2_iores);
217 	request_resource(&ioport_resource, &pic_edgectrl_iores);
218 
219 	if (intack_addr != 0)
220 		pci_intack = ioremap(intack_addr, 1);
221 
222 }
223