.. | .. |
---|
7 | 7 | * Copyright (C) 1999 - 2001 Kanoj Sarcar |
---|
8 | 8 | */ |
---|
9 | 9 | |
---|
10 | | -#undef DEBUG |
---|
11 | | - |
---|
12 | | -#include <linux/init.h> |
---|
13 | | -#include <linux/irq.h> |
---|
14 | | -#include <linux/errno.h> |
---|
15 | | -#include <linux/signal.h> |
---|
16 | | -#include <linux/sched.h> |
---|
17 | | -#include <linux/types.h> |
---|
18 | 10 | #include <linux/interrupt.h> |
---|
| 11 | +#include <linux/irq.h> |
---|
19 | 12 | #include <linux/ioport.h> |
---|
20 | | -#include <linux/timex.h> |
---|
21 | | -#include <linux/smp.h> |
---|
22 | | -#include <linux/random.h> |
---|
23 | 13 | #include <linux/kernel.h> |
---|
24 | | -#include <linux/kernel_stat.h> |
---|
25 | | -#include <linux/delay.h> |
---|
26 | 14 | #include <linux/bitops.h> |
---|
| 15 | +#include <linux/sched.h> |
---|
27 | 16 | |
---|
28 | | -#include <asm/bootinfo.h> |
---|
29 | 17 | #include <asm/io.h> |
---|
30 | | -#include <asm/mipsregs.h> |
---|
31 | | - |
---|
32 | | -#include <asm/processor.h> |
---|
| 18 | +#include <asm/irq_cpu.h> |
---|
33 | 19 | #include <asm/sn/addrs.h> |
---|
34 | 20 | #include <asm/sn/agent.h> |
---|
35 | 21 | #include <asm/sn/arch.h> |
---|
36 | | -#include <asm/sn/hub.h> |
---|
37 | 22 | #include <asm/sn/intr.h> |
---|
| 23 | +#include <asm/sn/irq_alloc.h> |
---|
38 | 24 | |
---|
39 | | -/* |
---|
40 | | - * Linux has a controller-independent x86 interrupt architecture. |
---|
41 | | - * every controller has a 'controller-template', that is used |
---|
42 | | - * by the main code to do the right thing. Each driver-visible |
---|
43 | | - * interrupt source is transparently wired to the appropriate |
---|
44 | | - * controller. Thus drivers need not be aware of the |
---|
45 | | - * interrupt-controller. |
---|
46 | | - * |
---|
47 | | - * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, |
---|
48 | | - * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. |
---|
49 | | - * (IO-APICs assumed to be messaging to Pentium local-APICs) |
---|
50 | | - * |
---|
51 | | - * the code is designed to be easily extended with new/different |
---|
52 | | - * interrupt controllers, without having to do assembly magic. |
---|
53 | | - */ |
---|
| 25 | +struct hub_irq_data { |
---|
| 26 | + u64 *irq_mask[2]; |
---|
| 27 | + cpuid_t cpu; |
---|
| 28 | +}; |
---|
54 | 29 | |
---|
55 | | -extern asmlinkage void ip27_irq(void); |
---|
| 30 | +static DECLARE_BITMAP(hub_irq_map, IP27_HUB_IRQ_COUNT); |
---|
56 | 31 | |
---|
57 | | -/* |
---|
58 | | - * Find first bit set |
---|
59 | | - */ |
---|
60 | | -static int ms1bit(unsigned long x) |
---|
| 32 | +static DEFINE_PER_CPU(unsigned long [2], irq_enable_mask); |
---|
| 33 | + |
---|
| 34 | +static inline int alloc_level(void) |
---|
61 | 35 | { |
---|
62 | | - int b = 0, s; |
---|
| 36 | + int level; |
---|
63 | 37 | |
---|
64 | | - s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s; |
---|
65 | | - s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s; |
---|
66 | | - s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s; |
---|
67 | | - s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s; |
---|
68 | | - s = 1; if (x >> 1 == 0) s = 0; b += s; |
---|
| 38 | +again: |
---|
| 39 | + level = find_first_zero_bit(hub_irq_map, IP27_HUB_IRQ_COUNT); |
---|
| 40 | + if (level >= IP27_HUB_IRQ_COUNT) |
---|
| 41 | + return -ENOSPC; |
---|
69 | 42 | |
---|
70 | | - return b; |
---|
| 43 | + if (test_and_set_bit(level, hub_irq_map)) |
---|
| 44 | + goto again; |
---|
| 45 | + |
---|
| 46 | + return level; |
---|
71 | 47 | } |
---|
| 48 | + |
---|
| 49 | +static void enable_hub_irq(struct irq_data *d) |
---|
| 50 | +{ |
---|
| 51 | + struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); |
---|
| 52 | + unsigned long *mask = per_cpu(irq_enable_mask, hd->cpu); |
---|
| 53 | + |
---|
| 54 | + set_bit(d->hwirq, mask); |
---|
| 55 | + __raw_writeq(mask[0], hd->irq_mask[0]); |
---|
| 56 | + __raw_writeq(mask[1], hd->irq_mask[1]); |
---|
| 57 | +} |
---|
| 58 | + |
---|
| 59 | +static void disable_hub_irq(struct irq_data *d) |
---|
| 60 | +{ |
---|
| 61 | + struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); |
---|
| 62 | + unsigned long *mask = per_cpu(irq_enable_mask, hd->cpu); |
---|
| 63 | + |
---|
| 64 | + clear_bit(d->hwirq, mask); |
---|
| 65 | + __raw_writeq(mask[0], hd->irq_mask[0]); |
---|
| 66 | + __raw_writeq(mask[1], hd->irq_mask[1]); |
---|
| 67 | +} |
---|
| 68 | + |
---|
| 69 | +static void setup_hub_mask(struct hub_irq_data *hd, const struct cpumask *mask) |
---|
| 70 | +{ |
---|
| 71 | + nasid_t nasid; |
---|
| 72 | + int cpu; |
---|
| 73 | + |
---|
| 74 | + cpu = cpumask_first_and(mask, cpu_online_mask); |
---|
| 75 | + if (cpu >= nr_cpu_ids) |
---|
| 76 | + cpu = cpumask_any(cpu_online_mask); |
---|
| 77 | + |
---|
| 78 | + nasid = cpu_to_node(cpu); |
---|
| 79 | + hd->cpu = cpu; |
---|
| 80 | + if (!cputoslice(cpu)) { |
---|
| 81 | + hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_A); |
---|
| 82 | + hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_A); |
---|
| 83 | + } else { |
---|
| 84 | + hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_B); |
---|
| 85 | + hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_B); |
---|
| 86 | + } |
---|
| 87 | +} |
---|
| 88 | + |
---|
| 89 | +static int set_affinity_hub_irq(struct irq_data *d, const struct cpumask *mask, |
---|
| 90 | + bool force) |
---|
| 91 | +{ |
---|
| 92 | + struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); |
---|
| 93 | + |
---|
| 94 | + if (!hd) |
---|
| 95 | + return -EINVAL; |
---|
| 96 | + |
---|
| 97 | + if (irqd_is_started(d)) |
---|
| 98 | + disable_hub_irq(d); |
---|
| 99 | + |
---|
| 100 | + setup_hub_mask(hd, mask); |
---|
| 101 | + |
---|
| 102 | + if (irqd_is_started(d)) |
---|
| 103 | + enable_hub_irq(d); |
---|
| 104 | + |
---|
| 105 | + irq_data_update_effective_affinity(d, cpumask_of(hd->cpu)); |
---|
| 106 | + |
---|
| 107 | + return 0; |
---|
| 108 | +} |
---|
| 109 | + |
---|
| 110 | +static struct irq_chip hub_irq_type = { |
---|
| 111 | + .name = "HUB", |
---|
| 112 | + .irq_mask = disable_hub_irq, |
---|
| 113 | + .irq_unmask = enable_hub_irq, |
---|
| 114 | + .irq_set_affinity = set_affinity_hub_irq, |
---|
| 115 | +}; |
---|
| 116 | + |
---|
| 117 | +static int hub_domain_alloc(struct irq_domain *domain, unsigned int virq, |
---|
| 118 | + unsigned int nr_irqs, void *arg) |
---|
| 119 | +{ |
---|
| 120 | + struct irq_alloc_info *info = arg; |
---|
| 121 | + struct hub_irq_data *hd; |
---|
| 122 | + struct hub_data *hub; |
---|
| 123 | + struct irq_desc *desc; |
---|
| 124 | + int swlevel; |
---|
| 125 | + |
---|
| 126 | + if (nr_irqs > 1 || !info) |
---|
| 127 | + return -EINVAL; |
---|
| 128 | + |
---|
| 129 | + hd = kzalloc(sizeof(*hd), GFP_KERNEL); |
---|
| 130 | + if (!hd) |
---|
| 131 | + return -ENOMEM; |
---|
| 132 | + |
---|
| 133 | + swlevel = alloc_level(); |
---|
| 134 | + if (unlikely(swlevel < 0)) { |
---|
| 135 | + kfree(hd); |
---|
| 136 | + return -EAGAIN; |
---|
| 137 | + } |
---|
| 138 | + irq_domain_set_info(domain, virq, swlevel, &hub_irq_type, hd, |
---|
| 139 | + handle_level_irq, NULL, NULL); |
---|
| 140 | + |
---|
| 141 | + /* use CPU connected to nearest hub */ |
---|
| 142 | + hub = hub_data(info->nasid); |
---|
| 143 | + setup_hub_mask(hd, &hub->h_cpus); |
---|
| 144 | + info->nasid = cpu_to_node(hd->cpu); |
---|
| 145 | + |
---|
| 146 | + /* Make sure it's not already pending when we connect it. */ |
---|
| 147 | + REMOTE_HUB_CLR_INTR(info->nasid, swlevel); |
---|
| 148 | + |
---|
| 149 | + desc = irq_to_desc(virq); |
---|
| 150 | + desc->irq_common_data.node = info->nasid; |
---|
| 151 | + cpumask_copy(desc->irq_common_data.affinity, &hub->h_cpus); |
---|
| 152 | + |
---|
| 153 | + return 0; |
---|
| 154 | +} |
---|
| 155 | + |
---|
| 156 | +static void hub_domain_free(struct irq_domain *domain, |
---|
| 157 | + unsigned int virq, unsigned int nr_irqs) |
---|
| 158 | +{ |
---|
| 159 | + struct irq_data *irqd; |
---|
| 160 | + |
---|
| 161 | + if (nr_irqs > 1) |
---|
| 162 | + return; |
---|
| 163 | + |
---|
| 164 | + irqd = irq_domain_get_irq_data(domain, virq); |
---|
| 165 | + if (irqd && irqd->chip_data) |
---|
| 166 | + kfree(irqd->chip_data); |
---|
| 167 | +} |
---|
| 168 | + |
---|
| 169 | +static const struct irq_domain_ops hub_domain_ops = { |
---|
| 170 | + .alloc = hub_domain_alloc, |
---|
| 171 | + .free = hub_domain_free, |
---|
| 172 | +}; |
---|
72 | 173 | |
---|
73 | 174 | /* |
---|
74 | 175 | * This code is unnecessarily complex, because we do |
---|
.. | .. |
---|
82 | 183 | * Kanoj 05.13.00 |
---|
83 | 184 | */ |
---|
84 | 185 | |
---|
85 | | -static void ip27_do_irq_mask0(void) |
---|
| 186 | +static void ip27_do_irq_mask0(struct irq_desc *desc) |
---|
86 | 187 | { |
---|
87 | | - int irq, swlevel; |
---|
88 | | - hubreg_t pend0, mask0; |
---|
89 | 188 | cpuid_t cpu = smp_processor_id(); |
---|
90 | | - int pi_int_mask0 = |
---|
91 | | - (cputoslice(cpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B; |
---|
| 189 | + unsigned long *mask = per_cpu(irq_enable_mask, cpu); |
---|
| 190 | + struct irq_domain *domain; |
---|
| 191 | + u64 pend0; |
---|
| 192 | + int irq; |
---|
92 | 193 | |
---|
93 | 194 | /* copied from Irix intpend0() */ |
---|
94 | 195 | pend0 = LOCAL_HUB_L(PI_INT_PEND0); |
---|
95 | | - mask0 = LOCAL_HUB_L(pi_int_mask0); |
---|
96 | 196 | |
---|
97 | | - pend0 &= mask0; /* Pick intrs we should look at */ |
---|
| 197 | + pend0 &= mask[0]; /* Pick intrs we should look at */ |
---|
98 | 198 | if (!pend0) |
---|
99 | 199 | return; |
---|
100 | 200 | |
---|
101 | | - swlevel = ms1bit(pend0); |
---|
102 | 201 | #ifdef CONFIG_SMP |
---|
103 | 202 | if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { |
---|
104 | 203 | LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ); |
---|
.. | .. |
---|
108 | 207 | scheduler_ipi(); |
---|
109 | 208 | } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) { |
---|
110 | 209 | LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); |
---|
111 | | - irq_enter(); |
---|
112 | 210 | generic_smp_call_function_interrupt(); |
---|
113 | | - irq_exit(); |
---|
114 | 211 | } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) { |
---|
115 | 212 | LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); |
---|
116 | | - irq_enter(); |
---|
117 | 213 | generic_smp_call_function_interrupt(); |
---|
118 | | - irq_exit(); |
---|
119 | 214 | } else |
---|
120 | 215 | #endif |
---|
121 | 216 | { |
---|
122 | | - /* "map" swlevel to irq */ |
---|
123 | | - struct slice_data *si = cpu_data[cpu].data; |
---|
124 | | - |
---|
125 | | - irq = si->level_to_irq[swlevel]; |
---|
126 | | - do_IRQ(irq); |
---|
| 217 | + domain = irq_desc_get_handler_data(desc); |
---|
| 218 | + irq = irq_linear_revmap(domain, __ffs(pend0)); |
---|
| 219 | + if (irq) |
---|
| 220 | + generic_handle_irq(irq); |
---|
| 221 | + else |
---|
| 222 | + spurious_interrupt(); |
---|
127 | 223 | } |
---|
128 | 224 | |
---|
129 | 225 | LOCAL_HUB_L(PI_INT_PEND0); |
---|
130 | 226 | } |
---|
131 | 227 | |
---|
132 | | -static void ip27_do_irq_mask1(void) |
---|
| 228 | +static void ip27_do_irq_mask1(struct irq_desc *desc) |
---|
133 | 229 | { |
---|
134 | | - int irq, swlevel; |
---|
135 | | - hubreg_t pend1, mask1; |
---|
136 | 230 | cpuid_t cpu = smp_processor_id(); |
---|
137 | | - int pi_int_mask1 = (cputoslice(cpu) == 0) ? PI_INT_MASK1_A : PI_INT_MASK1_B; |
---|
138 | | - struct slice_data *si = cpu_data[cpu].data; |
---|
| 231 | + unsigned long *mask = per_cpu(irq_enable_mask, cpu); |
---|
| 232 | + struct irq_domain *domain; |
---|
| 233 | + u64 pend1; |
---|
| 234 | + int irq; |
---|
139 | 235 | |
---|
140 | 236 | /* copied from Irix intpend0() */ |
---|
141 | 237 | pend1 = LOCAL_HUB_L(PI_INT_PEND1); |
---|
142 | | - mask1 = LOCAL_HUB_L(pi_int_mask1); |
---|
143 | 238 | |
---|
144 | | - pend1 &= mask1; /* Pick intrs we should look at */ |
---|
| 239 | + pend1 &= mask[1]; /* Pick intrs we should look at */ |
---|
145 | 240 | if (!pend1) |
---|
146 | 241 | return; |
---|
147 | 242 | |
---|
148 | | - swlevel = ms1bit(pend1); |
---|
149 | | - /* "map" swlevel to irq */ |
---|
150 | | - irq = si->level_to_irq[swlevel]; |
---|
151 | | - LOCAL_HUB_CLR_INTR(swlevel); |
---|
152 | | - do_IRQ(irq); |
---|
| 243 | + domain = irq_desc_get_handler_data(desc); |
---|
| 244 | + irq = irq_linear_revmap(domain, __ffs(pend1) + 64); |
---|
| 245 | + if (irq) |
---|
| 246 | + generic_handle_irq(irq); |
---|
| 247 | + else |
---|
| 248 | + spurious_interrupt(); |
---|
153 | 249 | |
---|
154 | 250 | LOCAL_HUB_L(PI_INT_PEND1); |
---|
155 | 251 | } |
---|
156 | 252 | |
---|
157 | | -static void ip27_prof_timer(void) |
---|
| 253 | +void install_ipi(void) |
---|
158 | 254 | { |
---|
159 | | - panic("CPU %d got a profiling interrupt", smp_processor_id()); |
---|
160 | | -} |
---|
| 255 | + int cpu = smp_processor_id(); |
---|
| 256 | + unsigned long *mask = per_cpu(irq_enable_mask, cpu); |
---|
| 257 | + int slice = LOCAL_HUB_L(PI_CPU_NUM); |
---|
| 258 | + int resched, call; |
---|
161 | 259 | |
---|
162 | | -static void ip27_hub_error(void) |
---|
163 | | -{ |
---|
164 | | - panic("CPU %d got a hub error interrupt", smp_processor_id()); |
---|
165 | | -} |
---|
| 260 | + resched = CPU_RESCHED_A_IRQ + slice; |
---|
| 261 | + set_bit(resched, mask); |
---|
| 262 | + LOCAL_HUB_CLR_INTR(resched); |
---|
166 | 263 | |
---|
167 | | -asmlinkage void plat_irq_dispatch(void) |
---|
168 | | -{ |
---|
169 | | - unsigned long pending = read_c0_cause() & read_c0_status(); |
---|
170 | | - extern unsigned int rt_timer_irq; |
---|
| 264 | + call = CPU_CALL_A_IRQ + slice; |
---|
| 265 | + set_bit(call, mask); |
---|
| 266 | + LOCAL_HUB_CLR_INTR(call); |
---|
171 | 267 | |
---|
172 | | - if (pending & CAUSEF_IP4) |
---|
173 | | - do_IRQ(rt_timer_irq); |
---|
174 | | - else if (pending & CAUSEF_IP2) /* PI_INT_PEND_0 or CC_PEND_{A|B} */ |
---|
175 | | - ip27_do_irq_mask0(); |
---|
176 | | - else if (pending & CAUSEF_IP3) /* PI_INT_PEND_1 */ |
---|
177 | | - ip27_do_irq_mask1(); |
---|
178 | | - else if (pending & CAUSEF_IP5) |
---|
179 | | - ip27_prof_timer(); |
---|
180 | | - else if (pending & CAUSEF_IP6) |
---|
181 | | - ip27_hub_error(); |
---|
| 268 | + if (slice == 0) { |
---|
| 269 | + LOCAL_HUB_S(PI_INT_MASK0_A, mask[0]); |
---|
| 270 | + LOCAL_HUB_S(PI_INT_MASK1_A, mask[1]); |
---|
| 271 | + } else { |
---|
| 272 | + LOCAL_HUB_S(PI_INT_MASK0_B, mask[0]); |
---|
| 273 | + LOCAL_HUB_S(PI_INT_MASK1_B, mask[1]); |
---|
| 274 | + } |
---|
182 | 275 | } |
---|
183 | 276 | |
---|
184 | 277 | void __init arch_init_irq(void) |
---|
185 | 278 | { |
---|
186 | | -} |
---|
| 279 | + struct irq_domain *domain; |
---|
| 280 | + struct fwnode_handle *fn; |
---|
| 281 | + int i; |
---|
187 | 282 | |
---|
188 | | -void install_ipi(void) |
---|
189 | | -{ |
---|
190 | | - int slice = LOCAL_HUB_L(PI_CPU_NUM); |
---|
191 | | - int cpu = smp_processor_id(); |
---|
192 | | - struct slice_data *si = cpu_data[cpu].data; |
---|
193 | | - struct hub_data *hub = hub_data(cpu_to_node(cpu)); |
---|
194 | | - int resched, call; |
---|
| 283 | + mips_cpu_irq_init(); |
---|
195 | 284 | |
---|
196 | | - resched = CPU_RESCHED_A_IRQ + slice; |
---|
197 | | - __set_bit(resched, hub->irq_alloc_mask); |
---|
198 | | - __set_bit(resched, si->irq_enable_mask); |
---|
199 | | - LOCAL_HUB_CLR_INTR(resched); |
---|
| 285 | + /* |
---|
| 286 | + * Some interrupts are reserved by hardware or by software convention. |
---|
| 287 | + * Mark these as reserved right away so they won't be used accidentally |
---|
| 288 | + * later. |
---|
| 289 | + */ |
---|
| 290 | + for (i = 0; i <= CPU_CALL_B_IRQ; i++) |
---|
| 291 | + set_bit(i, hub_irq_map); |
---|
200 | 292 | |
---|
201 | | - call = CPU_CALL_A_IRQ + slice; |
---|
202 | | - __set_bit(call, hub->irq_alloc_mask); |
---|
203 | | - __set_bit(call, si->irq_enable_mask); |
---|
204 | | - LOCAL_HUB_CLR_INTR(call); |
---|
| 293 | + for (i = NI_BRDCAST_ERR_A; i <= MSC_PANIC_INTR; i++) |
---|
| 294 | + set_bit(i, hub_irq_map); |
---|
205 | 295 | |
---|
206 | | - if (slice == 0) { |
---|
207 | | - LOCAL_HUB_S(PI_INT_MASK0_A, si->irq_enable_mask[0]); |
---|
208 | | - LOCAL_HUB_S(PI_INT_MASK1_A, si->irq_enable_mask[1]); |
---|
209 | | - } else { |
---|
210 | | - LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]); |
---|
211 | | - LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]); |
---|
212 | | - } |
---|
| 296 | + fn = irq_domain_alloc_named_fwnode("HUB"); |
---|
| 297 | + WARN_ON(fn == NULL); |
---|
| 298 | + if (!fn) |
---|
| 299 | + return; |
---|
| 300 | + domain = irq_domain_create_linear(fn, IP27_HUB_IRQ_COUNT, |
---|
| 301 | + &hub_domain_ops, NULL); |
---|
| 302 | + WARN_ON(domain == NULL); |
---|
| 303 | + if (!domain) |
---|
| 304 | + return; |
---|
| 305 | + |
---|
| 306 | + irq_set_default_host(domain); |
---|
| 307 | + |
---|
| 308 | + irq_set_percpu_devid(IP27_HUB_PEND0_IRQ); |
---|
| 309 | + irq_set_chained_handler_and_data(IP27_HUB_PEND0_IRQ, ip27_do_irq_mask0, |
---|
| 310 | + domain); |
---|
| 311 | + irq_set_percpu_devid(IP27_HUB_PEND1_IRQ); |
---|
| 312 | + irq_set_chained_handler_and_data(IP27_HUB_PEND1_IRQ, ip27_do_irq_mask1, |
---|
| 313 | + domain); |
---|
213 | 314 | } |
---|