aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/kernel/irq_ia64.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/kernel/irq_ia64.c')
-rw-r--r--arch/ia64/kernel/irq_ia64.c317
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 */
50int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR; 56int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR;
51int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR; 57int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR;
@@ -54,6 +60,8 @@ int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR;
54void __iomem *ipi_base_addr = ((void __iomem *) 60void __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
63static 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};
65EXPORT_SYMBOL(isa_irq_to_vector_map); 73EXPORT_SYMBOL(isa_irq_to_vector_map);
66 74
67static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_MAX_DEVICE_VECTORS)]; 75DEFINE_SPINLOCK(vector_lock);
76
77struct 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
84DEFINE_PER_CPU(int[IA64_NUM_VECTORS], vector_irq) = {
85 [0 ... IA64_NUM_VECTORS - 1] = IA64_SPURIOUS_INT_VECTOR
86};
87
88static cpumask_t vector_table[IA64_MAX_DEVICE_VECTORS] = {
89 [0 ... IA64_MAX_DEVICE_VECTORS - 1] = CPU_MASK_NONE
90};
91
92static int irq_status[NR_IRQS] = {
93 [0 ... NR_IRQS -1] = IRQ_UNUSED
94};
95
96int check_irq_used(int irq)
97{
98 if (irq_status[irq] == IRQ_USED)
99 return 1;
100
101 return -1;
102}
103
104static 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
113static 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
123static 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
141static 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
164int 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
175static 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
196static 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
69int 205int
70assign_irq_vector (int irq) 206assign_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
83void 232void
84free_irq_vector (int vector) 233free_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
96int 241int
97reserve_irq_vector (int vector) 242reserve_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 */
254void __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))
271static enum vector_domain_type {
272 VECTOR_DOMAIN_NONE,
273 VECTOR_DOMAIN_PERCPU
274} vector_domain_type = VECTOR_DOMAIN_NONE;
275
276static 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
283static 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}
293early_param("vector", parse_vector_domain);
294#else
295static cpumask_t vector_allocation_domain(int cpu)
296{
297 return CPU_MASK_ALL;
298}
299#endif
300
301
302void 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
310static 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
329int 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 */
112int create_irq(void) 343int 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
122void destroy_irq(unsigned int irq) 370void 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
314void __init 561void __init