diff options
Diffstat (limited to 'arch/ia64/kernel/irq_ia64.c')
-rw-r--r-- | arch/ia64/kernel/irq_ia64.c | 317 |
1 files changed, 282 insertions, 35 deletions
diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c index bc47049f060f..91797c111162 100644 --- a/arch/ia64/kernel/irq_ia64.c +++ b/arch/ia64/kernel/irq_ia64.c | |||
@@ -46,6 +46,12 @@ | |||
46 | 46 | ||
47 | #define IRQ_DEBUG 0 | 47 | #define IRQ_DEBUG 0 |
48 | 48 | ||
49 | #define IRQ_VECTOR_UNASSIGNED (0) | ||
50 | |||
51 | #define IRQ_UNUSED (0) | ||
52 | #define IRQ_USED (1) | ||
53 | #define IRQ_RSVD (2) | ||
54 | |||
49 | /* These can be overridden in platform_irq_init */ | 55 | /* These can be overridden in platform_irq_init */ |
50 | int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR; | 56 | int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR; |
51 | int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR; | 57 | int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR; |
@@ -54,6 +60,8 @@ int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR; | |||
54 | void __iomem *ipi_base_addr = ((void __iomem *) | 60 | void __iomem *ipi_base_addr = ((void __iomem *) |
55 | (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR)); | 61 | (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR)); |
56 | 62 | ||
63 | static cpumask_t vector_allocation_domain(int cpu); | ||
64 | |||
57 | /* | 65 | /* |
58 | * Legacy IRQ to IA-64 vector translation table. | 66 | * Legacy IRQ to IA-64 vector translation table. |
59 | */ | 67 | */ |
@@ -64,46 +72,269 @@ __u8 isa_irq_to_vector_map[16] = { | |||
64 | }; | 72 | }; |
65 | EXPORT_SYMBOL(isa_irq_to_vector_map); | 73 | EXPORT_SYMBOL(isa_irq_to_vector_map); |
66 | 74 | ||
67 | static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_MAX_DEVICE_VECTORS)]; | 75 | DEFINE_SPINLOCK(vector_lock); |
76 | |||
77 | struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = { | ||
78 | [0 ... NR_IRQS - 1] = { | ||
79 | .vector = IRQ_VECTOR_UNASSIGNED, | ||
80 | .domain = CPU_MASK_NONE | ||
81 | } | ||
82 | }; | ||
83 | |||
84 | DEFINE_PER_CPU(int[IA64_NUM_VECTORS], vector_irq) = { | ||
85 | [0 ... IA64_NUM_VECTORS - 1] = IA64_SPURIOUS_INT_VECTOR | ||
86 | }; | ||
87 | |||
88 | static cpumask_t vector_table[IA64_MAX_DEVICE_VECTORS] = { | ||
89 | [0 ... IA64_MAX_DEVICE_VECTORS - 1] = CPU_MASK_NONE | ||
90 | }; | ||
91 | |||
92 | static int irq_status[NR_IRQS] = { | ||
93 | [0 ... NR_IRQS -1] = IRQ_UNUSED | ||
94 | }; | ||
95 | |||
96 | int check_irq_used(int irq) | ||
97 | { | ||
98 | if (irq_status[irq] == IRQ_USED) | ||
99 | return 1; | ||
100 | |||
101 | return -1; | ||
102 | } | ||
103 | |||
104 | static void reserve_irq(unsigned int irq) | ||
105 | { | ||
106 | unsigned long flags; | ||
107 | |||
108 | spin_lock_irqsave(&vector_lock, flags); | ||
109 | irq_status[irq] = IRQ_RSVD; | ||
110 | spin_unlock_irqrestore(&vector_lock, flags); | ||
111 | } | ||
112 | |||
113 | static inline int find_unassigned_irq(void) | ||
114 | { | ||
115 | int irq; | ||
116 | |||
117 | for (irq = IA64_FIRST_DEVICE_VECTOR; irq < NR_IRQS; irq++) | ||
118 | if (irq_status[irq] == IRQ_UNUSED) | ||
119 | return irq; | ||
120 | return -ENOSPC; | ||
121 | } | ||
122 | |||
123 | static inline int find_unassigned_vector(cpumask_t domain) | ||
124 | { | ||
125 | cpumask_t mask; | ||
126 | int pos; | ||
127 | |||
128 | cpus_and(mask, domain, cpu_online_map); | ||
129 | if (cpus_empty(mask)) | ||
130 | return -EINVAL; | ||
131 | |||
132 | for (pos = 0; pos < IA64_NUM_DEVICE_VECTORS; pos++) { | ||
133 | cpus_and(mask, domain, vector_table[pos]); | ||
134 | if (!cpus_empty(mask)) | ||
135 | continue; | ||
136 | return IA64_FIRST_DEVICE_VECTOR + pos; | ||
137 | } | ||
138 | return -ENOSPC; | ||
139 | } | ||
140 | |||
141 | static int __bind_irq_vector(int irq, int vector, cpumask_t domain) | ||
142 | { | ||
143 | cpumask_t mask; | ||
144 | int cpu, pos; | ||
145 | struct irq_cfg *cfg = &irq_cfg[irq]; | ||
146 | |||
147 | cpus_and(mask, domain, cpu_online_map); | ||
148 | if (cpus_empty(mask)) | ||
149 | return -EINVAL; | ||
150 | if ((cfg->vector == vector) && cpus_equal(cfg->domain, domain)) | ||
151 | return 0; | ||
152 | if (cfg->vector != IRQ_VECTOR_UNASSIGNED) | ||
153 | return -EBUSY; | ||
154 | for_each_cpu_mask(cpu, mask) | ||
155 | per_cpu(vector_irq, cpu)[vector] = irq; | ||
156 | cfg->vector = vector; | ||
157 | cfg->domain = domain; | ||
158 | irq_status[irq] = IRQ_USED; | ||
159 | pos = vector - IA64_FIRST_DEVICE_VECTOR; | ||
160 | cpus_or(vector_table[pos], vector_table[pos], domain); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | int bind_irq_vector(int irq, int vector, cpumask_t domain) | ||
165 | { | ||
166 | unsigned long flags; | ||
167 | int ret; | ||
168 | |||
169 | spin_lock_irqsave(&vector_lock, flags); | ||
170 | ret = __bind_irq_vector(irq, vector, domain); | ||
171 | spin_unlock_irqrestore(&vector_lock, flags); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static void __clear_irq_vector(int irq) | ||
176 | { | ||
177 | int vector, cpu, pos; | ||
178 | cpumask_t mask; | ||
179 | cpumask_t domain; | ||
180 | struct irq_cfg *cfg = &irq_cfg[irq]; | ||
181 | |||
182 | BUG_ON((unsigned)irq >= NR_IRQS); | ||
183 | BUG_ON(cfg->vector == IRQ_VECTOR_UNASSIGNED); | ||
184 | vector = cfg->vector; | ||
185 | domain = cfg->domain; | ||
186 | cpus_and(mask, cfg->domain, cpu_online_map); | ||
187 | for_each_cpu_mask(cpu, mask) | ||
188 | per_cpu(vector_irq, cpu)[vector] = IA64_SPURIOUS_INT_VECTOR; | ||
189 | cfg->vector = IRQ_VECTOR_UNASSIGNED; | ||
190 | cfg->domain = CPU_MASK_NONE; | ||
191 | irq_status[irq] = IRQ_UNUSED; | ||
192 | pos = vector - IA64_FIRST_DEVICE_VECTOR; | ||
193 | cpus_andnot(vector_table[pos], vector_table[pos], domain); | ||
194 | } | ||
195 | |||
196 | static void clear_irq_vector(int irq) | ||
197 | { | ||
198 | unsigned long flags; | ||
199 | |||
200 | spin_lock_irqsave(&vector_lock, flags); | ||
201 | __clear_irq_vector(irq); | ||
202 | spin_unlock_irqrestore(&vector_lock, flags); | ||
203 | } | ||
68 | 204 | ||
69 | int | 205 | int |
70 | assign_irq_vector (int irq) | 206 | assign_irq_vector (int irq) |
71 | { | 207 | { |
72 | int pos, vector; | 208 | unsigned long flags; |
73 | again: | 209 | int vector, cpu; |
74 | pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS); | 210 | cpumask_t domain; |
75 | vector = IA64_FIRST_DEVICE_VECTOR + pos; | 211 | |
76 | if (vector > IA64_LAST_DEVICE_VECTOR) | 212 | vector = -ENOSPC; |
77 | return -ENOSPC; | 213 | |
78 | if (test_and_set_bit(pos, ia64_vector_mask)) | 214 | spin_lock_irqsave(&vector_lock, flags); |
79 | goto again; | 215 | if (irq < 0) { |
216 | goto out; | ||
217 | } | ||
218 | for_each_online_cpu(cpu) { | ||
219 | domain = vector_allocation_domain(cpu); | ||
220 | vector = find_unassigned_vector(domain); | ||
221 | if (vector >= 0) | ||
222 | break; | ||
223 | } | ||
224 | if (vector < 0) | ||
225 | goto out; | ||
226 | BUG_ON(__bind_irq_vector(irq, vector, domain)); | ||
227 | out: | ||
228 | spin_unlock_irqrestore(&vector_lock, flags); | ||
80 | return vector; | 229 | return vector; |
81 | } | 230 | } |
82 | 231 | ||
83 | void | 232 | void |
84 | free_irq_vector (int vector) | 233 | free_irq_vector (int vector) |
85 | { | 234 | { |
86 | int pos; | 235 | if (vector < IA64_FIRST_DEVICE_VECTOR || |
87 | 236 | vector > IA64_LAST_DEVICE_VECTOR) | |
88 | if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR) | ||
89 | return; | 237 | return; |
90 | 238 | clear_irq_vector(vector); | |
91 | pos = vector - IA64_FIRST_DEVICE_VECTOR; | ||
92 | if (!test_and_clear_bit(pos, ia64_vector_mask)) | ||
93 | printk(KERN_WARNING "%s: double free!\n", __FUNCTION__); | ||
94 | } | 239 | } |
95 | 240 | ||
96 | int | 241 | int |
97 | reserve_irq_vector (int vector) | 242 | reserve_irq_vector (int vector) |
98 | { | 243 | { |
99 | int pos; | ||
100 | |||
101 | if (vector < IA64_FIRST_DEVICE_VECTOR || | 244 | if (vector < IA64_FIRST_DEVICE_VECTOR || |
102 | vector > IA64_LAST_DEVICE_VECTOR) | 245 | vector > IA64_LAST_DEVICE_VECTOR) |
103 | return -EINVAL; | 246 | return -EINVAL; |
247 | return !!bind_irq_vector(vector, vector, CPU_MASK_ALL); | ||
248 | } | ||
104 | 249 | ||
105 | pos = vector - IA64_FIRST_DEVICE_VECTOR; | 250 | /* |
106 | return test_and_set_bit(pos, ia64_vector_mask); | 251 | * Initialize vector_irq on a new cpu. This function must be called |
252 | * with vector_lock held. | ||
253 | */ | ||
254 | void __setup_vector_irq(int cpu) | ||
255 | { | ||
256 | int irq, vector; | ||
257 | |||
258 | /* Clear vector_irq */ | ||
259 | for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) | ||
260 | per_cpu(vector_irq, cpu)[vector] = IA64_SPURIOUS_INT_VECTOR; | ||
261 | /* Mark the inuse vectors */ | ||
262 | for (irq = 0; irq < NR_IRQS; ++irq) { | ||
263 | if (!cpu_isset(cpu, irq_cfg[irq].domain)) | ||
264 | continue; | ||
265 | vector = irq_to_vector(irq); | ||
266 | per_cpu(vector_irq, cpu)[vector] = irq; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | #if defined(CONFIG_SMP) && (defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_DIG)) | ||
271 | static enum vector_domain_type { | ||
272 | VECTOR_DOMAIN_NONE, | ||
273 | VECTOR_DOMAIN_PERCPU | ||
274 | } vector_domain_type = VECTOR_DOMAIN_NONE; | ||
275 | |||
276 | static cpumask_t vector_allocation_domain(int cpu) | ||
277 | { | ||
278 | if (vector_domain_type == VECTOR_DOMAIN_PERCPU) | ||
279 | return cpumask_of_cpu(cpu); | ||
280 | return CPU_MASK_ALL; | ||
281 | } | ||
282 | |||
283 | static int __init parse_vector_domain(char *arg) | ||
284 | { | ||
285 | if (!arg) | ||
286 | return -EINVAL; | ||
287 | if (!strcmp(arg, "percpu")) { | ||
288 | vector_domain_type = VECTOR_DOMAIN_PERCPU; | ||
289 | no_int_routing = 1; | ||
290 | } | ||
291 | return 1; | ||
292 | } | ||
293 | early_param("vector", parse_vector_domain); | ||
294 | #else | ||
295 | static cpumask_t vector_allocation_domain(int cpu) | ||
296 | { | ||
297 | return CPU_MASK_ALL; | ||
298 | } | ||
299 | #endif | ||
300 | |||
301 | |||
302 | void destroy_and_reserve_irq(unsigned int irq) | ||
303 | { | ||
304 | dynamic_irq_cleanup(irq); | ||
305 | |||
306 | clear_irq_vector(irq); | ||
307 | reserve_irq(irq); | ||
308 | } | ||
309 | |||
310 | static int __reassign_irq_vector(int irq, int cpu) | ||
311 | { | ||
312 | struct irq_cfg *cfg = &irq_cfg[irq]; | ||
313 | int vector; | ||
314 | cpumask_t domain; | ||
315 | |||
316 | if (cfg->vector == IRQ_VECTOR_UNASSIGNED || !cpu_online(cpu)) | ||
317 | return -EINVAL; | ||
318 | if (cpu_isset(cpu, cfg->domain)) | ||
319 | return 0; | ||
320 | domain = vector_allocation_domain(cpu); | ||
321 | vector = find_unassigned_vector(domain); | ||
322 | if (vector < 0) | ||
323 | return -ENOSPC; | ||
324 | __clear_irq_vector(irq); | ||
325 | BUG_ON(__bind_irq_vector(irq, vector, domain)); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | int reassign_irq_vector(int irq, int cpu) | ||
330 | { | ||
331 | unsigned long flags; | ||
332 | int ret; | ||
333 | |||
334 | spin_lock_irqsave(&vector_lock, flags); | ||
335 | ret = __reassign_irq_vector(irq, cpu); | ||
336 | spin_unlock_irqrestore(&vector_lock, flags); | ||
337 | return ret; | ||
107 | } | 338 | } |
108 | 339 | ||
109 | /* | 340 | /* |
@@ -111,18 +342,35 @@ reserve_irq_vector (int vector) | |||
111 | */ | 342 | */ |
112 | int create_irq(void) | 343 | int create_irq(void) |
113 | { | 344 | { |
114 | int vector = assign_irq_vector(AUTO_ASSIGN); | 345 | unsigned long flags; |
115 | 346 | int irq, vector, cpu; | |
116 | if (vector >= 0) | 347 | cpumask_t domain; |
117 | dynamic_irq_init(vector); | 348 | |
118 | 349 | irq = vector = -ENOSPC; | |
119 | return vector; | 350 | spin_lock_irqsave(&vector_lock, flags); |
351 | for_each_online_cpu(cpu) { | ||
352 | domain = vector_allocation_domain(cpu); | ||
353 | vector = find_unassigned_vector(domain); | ||
354 | if (vector >= 0) | ||
355 | break; | ||
356 | } | ||
357 | if (vector < 0) | ||
358 | goto out; | ||
359 | irq = find_unassigned_irq(); | ||
360 | if (irq < 0) | ||
361 | goto out; | ||
362 | BUG_ON(__bind_irq_vector(irq, vector, domain)); | ||
363 | out: | ||
364 | spin_unlock_irqrestore(&vector_lock, flags); | ||
365 | if (irq >= 0) | ||
366 | dynamic_irq_init(irq); | ||
367 | return irq; | ||
120 | } | 368 | } |
121 | 369 | ||
122 | void destroy_irq(unsigned int irq) | 370 | void destroy_irq(unsigned int irq) |
123 | { | 371 | { |
124 | dynamic_irq_cleanup(irq); | 372 | dynamic_irq_cleanup(irq); |
125 | free_irq_vector(irq); | 373 | clear_irq_vector(irq); |
126 | } | 374 | } |
127 | 375 | ||
128 | #ifdef CONFIG_SMP | 376 | #ifdef CONFIG_SMP |
@@ -301,14 +549,13 @@ register_percpu_irq (ia64_vector vec, struct irqaction *action) | |||
301 | irq_desc_t *desc; | 549 | irq_desc_t *desc; |
302 | unsigned int irq; | 550 | unsigned int irq; |
303 | 551 | ||
304 | for (irq = 0; irq < NR_IRQS; ++irq) | 552 | irq = vec; |
305 | if (irq_to_vector(irq) == vec) { | 553 | BUG_ON(bind_irq_vector(irq, vec, CPU_MASK_ALL)); |
306 | desc = irq_desc + irq; | 554 | desc = irq_desc + irq; |
307 | desc->status |= IRQ_PER_CPU; | 555 | desc->status |= IRQ_PER_CPU; |
308 | desc->chip = &irq_type_ia64_lsapic; | 556 | desc->chip = &irq_type_ia64_lsapic; |
309 | if (action) | 557 | if (action) |
310 | setup_irq(irq, action); | 558 | setup_irq(irq, action); |
311 | } | ||
312 | } | 559 | } |
313 | 560 | ||
314 | void __init | 561 | void __init |