1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/arch/alpha/kernel/irq_i8259.c |
4 | * |
5 | * This is the 'legacy' 8259A Programmable Interrupt Controller, |
6 | * present in the majority of PC/AT boxes. |
7 | * |
8 | * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c. |
9 | */ |
10 | |
11 | #include <linux/init.h> |
12 | #include <linux/cache.h> |
13 | #include <linux/sched.h> |
14 | #include <linux/irq.h> |
15 | #include <linux/interrupt.h> |
16 | |
17 | #include <asm/io.h> |
18 | |
19 | #include "proto.h" |
20 | #include "irq_impl.h" |
21 | |
22 | |
23 | /* Note mask bit is true for DISABLED irqs. */ |
24 | static unsigned int cached_irq_mask = 0xffff; |
25 | static DEFINE_SPINLOCK(i8259_irq_lock); |
26 | |
27 | static inline void |
28 | i8259_update_irq_hw(unsigned int irq, unsigned long mask) |
29 | { |
30 | int port = 0x21; |
31 | if (irq & 8) mask >>= 8; |
32 | if (irq & 8) port = 0xA1; |
33 | outb(value: mask, port); |
34 | } |
35 | |
36 | inline void |
37 | i8259a_enable_irq(struct irq_data *d) |
38 | { |
39 | spin_lock(lock: &i8259_irq_lock); |
40 | i8259_update_irq_hw(irq: d->irq, mask: cached_irq_mask &= ~(1 << d->irq)); |
41 | spin_unlock(lock: &i8259_irq_lock); |
42 | } |
43 | |
44 | static inline void |
45 | __i8259a_disable_irq(unsigned int irq) |
46 | { |
47 | i8259_update_irq_hw(irq, mask: cached_irq_mask |= 1 << irq); |
48 | } |
49 | |
50 | void |
51 | i8259a_disable_irq(struct irq_data *d) |
52 | { |
53 | spin_lock(lock: &i8259_irq_lock); |
54 | __i8259a_disable_irq(irq: d->irq); |
55 | spin_unlock(lock: &i8259_irq_lock); |
56 | } |
57 | |
58 | void |
59 | i8259a_mask_and_ack_irq(struct irq_data *d) |
60 | { |
61 | unsigned int irq = d->irq; |
62 | |
63 | spin_lock(lock: &i8259_irq_lock); |
64 | __i8259a_disable_irq(irq); |
65 | |
66 | /* Ack the interrupt making it the lowest priority. */ |
67 | if (irq >= 8) { |
68 | outb(value: 0xE0 | (irq - 8), port: 0xa0); /* ack the slave */ |
69 | irq = 2; |
70 | } |
71 | outb(value: 0xE0 | irq, port: 0x20); /* ack the master */ |
72 | spin_unlock(lock: &i8259_irq_lock); |
73 | } |
74 | |
75 | struct irq_chip i8259a_irq_type = { |
76 | .name = "XT-PIC" , |
77 | .irq_unmask = i8259a_enable_irq, |
78 | .irq_mask = i8259a_disable_irq, |
79 | .irq_mask_ack = i8259a_mask_and_ack_irq, |
80 | }; |
81 | |
82 | void __init |
83 | init_i8259a_irqs(void) |
84 | { |
85 | long i; |
86 | |
87 | outb(value: 0xff, port: 0x21); /* mask all of 8259A-1 */ |
88 | outb(value: 0xff, port: 0xA1); /* mask all of 8259A-2 */ |
89 | |
90 | for (i = 0; i < 16; i++) { |
91 | irq_set_chip_and_handler(irq: i, chip: &i8259a_irq_type, handle: handle_level_irq); |
92 | } |
93 | |
94 | if (request_irq(irq: 2, handler: no_action, flags: 0, name: "cascade" , NULL)) |
95 | pr_err("Failed to request irq 2 (cascade)\n" ); |
96 | } |
97 | |
98 | |
99 | #if defined(CONFIG_ALPHA_GENERIC) |
100 | # define IACK_SC alpha_mv.iack_sc |
101 | #elif defined(CONFIG_ALPHA_APECS) |
102 | # define IACK_SC APECS_IACK_SC |
103 | #elif defined(CONFIG_ALPHA_LCA) |
104 | # define IACK_SC LCA_IACK_SC |
105 | #elif defined(CONFIG_ALPHA_CIA) |
106 | # define IACK_SC CIA_IACK_SC |
107 | #elif defined(CONFIG_ALPHA_PYXIS) |
108 | # define IACK_SC PYXIS_IACK_SC |
109 | #elif defined(CONFIG_ALPHA_TITAN) |
110 | # define IACK_SC TITAN_IACK_SC |
111 | #elif defined(CONFIG_ALPHA_TSUNAMI) |
112 | # define IACK_SC TSUNAMI_IACK_SC |
113 | #elif defined(CONFIG_ALPHA_IRONGATE) |
114 | # define IACK_SC IRONGATE_IACK_SC |
115 | #endif |
116 | /* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since |
117 | sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ |
118 | |
119 | #if defined(IACK_SC) |
120 | void |
121 | isa_device_interrupt(unsigned long vector) |
122 | { |
123 | /* |
124 | * Generate a PCI interrupt acknowledge cycle. The PIC will |
125 | * respond with the interrupt vector of the highest priority |
126 | * interrupt that is pending. The PALcode sets up the |
127 | * interrupts vectors such that irq level L generates vector L. |
128 | */ |
129 | int j = *(vuip) IACK_SC; |
130 | j &= 0xff; |
131 | handle_irq(j); |
132 | } |
133 | #endif |
134 | |
135 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) |
136 | void |
137 | isa_no_iack_sc_device_interrupt(unsigned long vector) |
138 | { |
139 | unsigned long pic; |
140 | |
141 | /* |
142 | * It seems to me that the probability of two or more *device* |
143 | * interrupts occurring at almost exactly the same time is |
144 | * pretty low. So why pay the price of checking for |
145 | * additional interrupts here if the common case can be |
146 | * handled so much easier? |
147 | */ |
148 | /* |
149 | * The first read of gives you *all* interrupting lines. |
150 | * Therefore, read the mask register and and out those lines |
151 | * not enabled. Note that some documentation has 21 and a1 |
152 | * write only. This is not true. |
153 | */ |
154 | pic = inb(port: 0x20) | (inb(port: 0xA0) << 8); /* read isr */ |
155 | pic &= 0xFFFB; /* mask out cascade & hibits */ |
156 | |
157 | while (pic) { |
158 | int j = ffz(~pic); |
159 | pic &= pic - 1; |
160 | handle_irq(irq: j); |
161 | } |
162 | } |
163 | #endif |
164 | |