irq.c 23.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/*
 *  Derived from arch/i386/kernel/irq.c
 *    Copyright (C) 1992 Linus Torvalds
 *  Adapted from arch/i386 by Gary Thomas
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
Stephen Rothwell's avatar
Stephen Rothwell committed
6
7
 *  Updated and modified by Cort Dougan <cort@fsmlabs.com>
 *    Copyright (C) 1996-2001 Cort Dougan
Linus Torvalds's avatar
Linus Torvalds committed
8
9
10
 *  Adapted for Power Macintosh by Paul Mackerras
 *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
Stephen Rothwell's avatar
Stephen Rothwell committed
11
 *
Linus Torvalds's avatar
Linus Torvalds committed
12
13
14
15
16
17
18
19
20
21
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * This file contains the code used by various IRQ handling routines:
 * asking for different IRQ's should be done through these routines
 * instead of just grabbing them. Thus setups with different IRQ numbers
 * shouldn't result in any weird surprises, and installing new handlers
 * should be easier.
Stephen Rothwell's avatar
Stephen Rothwell committed
22
23
24
25
26
27
28
29
 *
 * The MPC8xx has an interrupt mask in the SIU.  If a bit is set, the
 * interrupt is _enabled_.  As expected, IRQ0 is bit 0 in the 32-bit
 * mask register (of which only 16 are defined), hence the weird shifting
 * and complement of the cached_irq_mask.  I want to be able to stuff
 * this right into the SIU SMASK register.
 * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx
 * to reduce code space and undefined function references.
Linus Torvalds's avatar
Linus Torvalds committed
30
31
 */

32
33
#undef DEBUG

Linus Torvalds's avatar
Linus Torvalds committed
34
35
36
37
38
#include <linux/module.h>
#include <linux/threads.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
Stephen Rothwell's avatar
Stephen Rothwell committed
39
#include <linux/ptrace.h>
Linus Torvalds's avatar
Linus Torvalds committed
40
41
42
43
44
45
46
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/timex.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/irq.h>
Stephen Rothwell's avatar
Stephen Rothwell committed
47
48
#include <linux/seq_file.h>
#include <linux/cpumask.h>
Linus Torvalds's avatar
Linus Torvalds committed
49
50
#include <linux/profile.h>
#include <linux/bitops.h>
51
52
53
54
#include <linux/list.h>
#include <linux/radix-tree.h>
#include <linux/mutex.h>
#include <linux/bootmem.h>
Jake Moilanen's avatar
Jake Moilanen committed
55
#include <linux/pci.h>
Linus Torvalds's avatar
Linus Torvalds committed
56
57
58
59
60
61
62
63
64
65

#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/cache.h>
#include <asm/prom.h>
#include <asm/ptrace.h>
#include <asm/machdep.h>
66
#include <asm/udbg.h>
67
#ifdef CONFIG_PPC64
Linus Torvalds's avatar
Linus Torvalds committed
68
#include <asm/paca.h>
69
#include <asm/firmware.h>
Stephen Rothwell's avatar
Stephen Rothwell committed
70
#endif
Linus Torvalds's avatar
Linus Torvalds committed
71

72
int __irq_offset_value;
Stephen Rothwell's avatar
Stephen Rothwell committed
73
74
75
static int ppc_spurious_interrupts;

#ifdef CONFIG_PPC32
76
77
EXPORT_SYMBOL(__irq_offset_value);
atomic_t ppc_n_lost_interrupts;
Stephen Rothwell's avatar
Stephen Rothwell committed
78

79
80
#ifndef CONFIG_PPC_MERGE
#define NR_MASK_WORDS	((NR_IRQS + 31) / 32)
Stephen Rothwell's avatar
Stephen Rothwell committed
81
unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
82
#endif
Stephen Rothwell's avatar
Stephen Rothwell committed
83
84
85
86
87

#ifdef CONFIG_TAU_INT
extern int tau_initialized;
extern int tau_interrupts(int);
#endif
88
#endif /* CONFIG_PPC32 */
Stephen Rothwell's avatar
Stephen Rothwell committed
89
90
91
92
93
94
95

#if defined(CONFIG_SMP) && !defined(CONFIG_PPC_MERGE)
extern atomic_t ipi_recv;
extern atomic_t ipi_sent;
#endif

#ifdef CONFIG_PPC64
Linus Torvalds's avatar
Linus Torvalds committed
96
97
98
EXPORT_SYMBOL(irq_desc);

int distribute_irqs = 1;
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
static inline unsigned long get_hard_enabled(void)
{
	unsigned long enabled;

	__asm__ __volatile__("lbz %0,%1(13)"
	: "=r" (enabled) : "i" (offsetof(struct paca_struct, hard_enabled)));

	return enabled;
}

static inline void set_soft_enabled(unsigned long enable)
{
	__asm__ __volatile__("stb %0,%1(13)"
	: : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
}

116
117
void local_irq_restore(unsigned long en)
{
118
119
120
121
122
123
124
125
	/*
	 * get_paca()->soft_enabled = en;
	 * Is it ever valid to use local_irq_restore(0) when soft_enabled is 1?
	 * That was allowed before, and in such a case we do need to take care
	 * that gcc will set soft_enabled directly via r13, not choose to use
	 * an intermediate register, lest we're preempted to a different cpu.
	 */
	set_soft_enabled(en);
126
127
128
129
	if (!en)
		return;

	if (firmware_has_feature(FW_FEATURE_ISERIES)) {
130
131
132
133
134
135
136
137
138
139
140
141
142
143
		/*
		 * Do we need to disable preemption here?  Not really: in the
		 * unlikely event that we're preempted to a different cpu in
		 * between getting r13, loading its lppaca_ptr, and loading
		 * its any_int, we might call iseries_handle_interrupts without
		 * an interrupt pending on the new cpu, but that's no disaster,
		 * is it?  And the business of preempting us off the old cpu
		 * would itself involve a local_irq_restore which handles the
		 * interrupt to that cpu.
		 *
		 * But use "local_paca->lppaca_ptr" instead of "get_lppaca()"
		 * to avoid any preemption checking added into get_paca().
		 */
		if (local_paca->lppaca_ptr->int_dword.any_int)
144
145
146
147
			iseries_handle_interrupts();
		return;
	}

148
149
150
151
152
153
154
	/*
	 * if (get_paca()->hard_enabled) return;
	 * But again we need to take care that gcc gets hard_enabled directly
	 * via r13, not choose to use an intermediate register, lest we're
	 * preempted to a different cpu in between the two instructions.
	 */
	if (get_hard_enabled())
155
		return;
156
157
158
159
160
161
162

	/*
	 * Need to hard-enable interrupts here.  Since currently disabled,
	 * no need to take further asm precautions against preemption; but
	 * use local_paca instead of get_paca() to avoid preemption checking.
	 */
	local_paca->hard_enabled = en;
163
164
165
166
	if ((int)mfspr(SPRN_DEC) < 0)
		mtspr(SPRN_DEC, 1);
	hard_irq_enable();
}
Stephen Rothwell's avatar
Stephen Rothwell committed
167
#endif /* CONFIG_PPC64 */
Linus Torvalds's avatar
Linus Torvalds committed
168
169
170

int show_interrupts(struct seq_file *p, void *v)
{
Stephen Rothwell's avatar
Stephen Rothwell committed
171
172
	int i = *(loff_t *)v, j;
	struct irqaction *action;
Linus Torvalds's avatar
Linus Torvalds committed
173
174
175
176
	irq_desc_t *desc;
	unsigned long flags;

	if (i == 0) {
Stephen Rothwell's avatar
Stephen Rothwell committed
177
178
179
		seq_puts(p, "           ");
		for_each_online_cpu(j)
			seq_printf(p, "CPU%d       ", j);
Linus Torvalds's avatar
Linus Torvalds committed
180
181
182
183
184
185
186
187
188
189
190
		seq_putc(p, '\n');
	}

	if (i < NR_IRQS) {
		desc = get_irq_desc(i);
		spin_lock_irqsave(&desc->lock, flags);
		action = desc->action;
		if (!action || !action->handler)
			goto skip;
		seq_printf(p, "%3d: ", i);
#ifdef CONFIG_SMP
Stephen Rothwell's avatar
Stephen Rothwell committed
191
192
		for_each_online_cpu(j)
			seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
Linus Torvalds's avatar
Linus Torvalds committed
193
194
195
#else
		seq_printf(p, "%10u ", kstat_irqs(i));
#endif /* CONFIG_SMP */
196
197
		if (desc->chip)
			seq_printf(p, " %s ", desc->chip->typename);
Linus Torvalds's avatar
Linus Torvalds committed
198
		else
Stephen Rothwell's avatar
Stephen Rothwell committed
199
			seq_puts(p, "  None      ");
Linus Torvalds's avatar
Linus Torvalds committed
200
		seq_printf(p, "%s", (desc->status & IRQ_LEVEL) ? "Level " : "Edge  ");
Stephen Rothwell's avatar
Stephen Rothwell committed
201
202
		seq_printf(p, "    %s", action->name);
		for (action = action->next; action; action = action->next)
Linus Torvalds's avatar
Linus Torvalds committed
203
204
205
206
			seq_printf(p, ", %s", action->name);
		seq_putc(p, '\n');
skip:
		spin_unlock_irqrestore(&desc->lock, flags);
Stephen Rothwell's avatar
Stephen Rothwell committed
207
208
209
210
211
	} else if (i == NR_IRQS) {
#ifdef CONFIG_PPC32
#ifdef CONFIG_TAU_INT
		if (tau_initialized){
			seq_puts(p, "TAU: ");
212
213
			for_each_online_cpu(j)
				seq_printf(p, "%10u ", tau_interrupts(j));
Stephen Rothwell's avatar
Stephen Rothwell committed
214
215
216
217
218
219
220
221
222
			seq_puts(p, "  PowerPC             Thermal Assist (cpu temp)\n");
		}
#endif
#if defined(CONFIG_SMP) && !defined(CONFIG_PPC_MERGE)
		/* should this be per processor send/receive? */
		seq_printf(p, "IPI (recv/sent): %10u/%u\n",
				atomic_read(&ipi_recv), atomic_read(&ipi_sent));
#endif
#endif /* CONFIG_PPC32 */
Linus Torvalds's avatar
Linus Torvalds committed
223
		seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts);
Stephen Rothwell's avatar
Stephen Rothwell committed
224
	}
Linus Torvalds's avatar
Linus Torvalds committed
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
	return 0;
}

#ifdef CONFIG_HOTPLUG_CPU
void fixup_irqs(cpumask_t map)
{
	unsigned int irq;
	static int warned;

	for_each_irq(irq) {
		cpumask_t mask;

		if (irq_desc[irq].status & IRQ_PER_CPU)
			continue;

240
		cpus_and(mask, irq_desc[irq].affinity, map);
Linus Torvalds's avatar
Linus Torvalds committed
241
242
243
244
		if (any_online_cpu(mask) == NR_CPUS) {
			printk("Breaking affinity for irq %i\n", irq);
			mask = map;
		}
245
246
		if (irq_desc[irq].chip->set_affinity)
			irq_desc[irq].chip->set_affinity(irq, mask);
Linus Torvalds's avatar
Linus Torvalds committed
247
248
249
250
251
252
253
254
255
256
257
258
		else if (irq_desc[irq].action && !(warned++))
			printk("Cannot set affinity for irq %i\n", irq);
	}

	local_irq_enable();
	mdelay(1);
	local_irq_disable();
}
#endif

void do_IRQ(struct pt_regs *regs)
{
259
	struct pt_regs *old_regs = set_irq_regs(regs);
260
	unsigned int irq;
261
262
263
#ifdef CONFIG_IRQSTACKS
	struct thread_info *curtp, *irqtp;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
264

Stephen Rothwell's avatar
Stephen Rothwell committed
265
        irq_enter();
Linus Torvalds's avatar
Linus Torvalds committed
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

#ifdef CONFIG_DEBUG_STACKOVERFLOW
	/* Debugging check for stack overflow: is there less than 2KB free? */
	{
		long sp;

		sp = __get_SP() & (THREAD_SIZE-1);

		if (unlikely(sp < (sizeof(struct thread_info) + 2048))) {
			printk("do_IRQ: stack overflow: %ld\n",
				sp - sizeof(struct thread_info));
			dump_stack();
		}
	}
#endif

Stephen Rothwell's avatar
Stephen Rothwell committed
282
283
	/*
	 * Every platform is required to implement ppc_md.get_irq.
284
	 * This function will either return an irq number or NO_IRQ to
Stephen Rothwell's avatar
Stephen Rothwell committed
285
	 * indicate there are no more pending.
286
287
	 * The value NO_IRQ_IGNORE is for buggy hardware and means that this
	 * IRQ has already been handled. -- Tom
Stephen Rothwell's avatar
Stephen Rothwell committed
288
	 */
289
	irq = ppc_md.get_irq();
Linus Torvalds's avatar
Linus Torvalds committed
290

291
	if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
292
293
294
295
296
#ifdef CONFIG_IRQSTACKS
		/* Switch to the irq stack to handle this */
		curtp = current_thread_info();
		irqtp = hardirq_ctx[smp_processor_id()];
		if (curtp != irqtp) {
297
298
299
300
			struct irq_desc *desc = irq_desc + irq;
			void *handler = desc->handle_irq;
			if (handler == NULL)
				handler = &__do_IRQ;
301
302
			irqtp->task = curtp->task;
			irqtp->flags = 0;
303
			call_handle_irq(irq, desc, irqtp, handler);
304
305
306
307
308
			irqtp->task = NULL;
			if (irqtp->flags)
				set_bits(irqtp->flags, &curtp->flags);
		} else
#endif
309
			generic_handle_irq(irq);
310
	} else if (irq != NO_IRQ_IGNORE)
311
312
313
		/* That's not SMP safe ... but who cares ? */
		ppc_spurious_interrupts++;

Stephen Rothwell's avatar
Stephen Rothwell committed
314
        irq_exit();
315
	set_irq_regs(old_regs);
Stephen Rothwell's avatar
Stephen Rothwell committed
316

317
#ifdef CONFIG_PPC_ISERIES
318
319
	if (firmware_has_feature(FW_FEATURE_ISERIES) &&
			get_lppaca()->int_dword.fields.decr_int) {
320
321
322
		get_lppaca()->int_dword.fields.decr_int = 0;
		/* Signal a fake decrementer interrupt */
		timer_interrupt(regs);
323
324
325
	}
#endif
}
Linus Torvalds's avatar
Linus Torvalds committed
326
327
328
329

void __init init_IRQ(void)
{
	ppc_md.init_IRQ();
Stephen Rothwell's avatar
Stephen Rothwell committed
330
#ifdef CONFIG_PPC64
Linus Torvalds's avatar
Linus Torvalds committed
331
	irq_ctx_init();
Stephen Rothwell's avatar
Stephen Rothwell committed
332
#endif
Linus Torvalds's avatar
Linus Torvalds committed
333
334
335
336
}


#ifdef CONFIG_IRQSTACKS
337
338
struct thread_info *softirq_ctx[NR_CPUS] __read_mostly;
struct thread_info *hardirq_ctx[NR_CPUS] __read_mostly;
Linus Torvalds's avatar
Linus Torvalds committed
339
340
341
342
343
344

void irq_ctx_init(void)
{
	struct thread_info *tp;
	int i;

345
	for_each_possible_cpu(i) {
Linus Torvalds's avatar
Linus Torvalds committed
346
347
348
349
350
351
352
353
354
355
356
357
		memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
		tp = softirq_ctx[i];
		tp->cpu = i;
		tp->preempt_count = SOFTIRQ_OFFSET;

		memset((void *)hardirq_ctx[i], 0, THREAD_SIZE);
		tp = hardirq_ctx[i];
		tp->cpu = i;
		tp->preempt_count = HARDIRQ_OFFSET;
	}
}

358
359
360
361
362
363
364
365
366
367
static inline void do_softirq_onstack(void)
{
	struct thread_info *curtp, *irqtp;

	curtp = current_thread_info();
	irqtp = softirq_ctx[smp_processor_id()];
	irqtp->task = curtp->task;
	call_do_softirq(irqtp);
	irqtp->task = NULL;
}
Linus Torvalds's avatar
Linus Torvalds committed
368

369
370
371
372
#else
#define do_softirq_onstack()	__do_softirq()
#endif /* CONFIG_IRQSTACKS */

Linus Torvalds's avatar
Linus Torvalds committed
373
374
375
376
377
378
379
380
381
void do_softirq(void)
{
	unsigned long flags;

	if (in_interrupt())
		return;

	local_irq_save(flags);

382
	if (local_softirq_pending())
383
		do_softirq_onstack();
Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
387
388
389
390

	local_irq_restore(flags);
}
EXPORT_SYMBOL(do_softirq);


/*
391
 * IRQ controller and virtual interrupts
Linus Torvalds's avatar
Linus Torvalds committed
392
393
 */

394
#ifdef CONFIG_PPC_MERGE
Linus Torvalds's avatar
Linus Torvalds committed
395

396
397
static LIST_HEAD(irq_hosts);
static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED;
398
399
static DEFINE_PER_CPU(unsigned int, irq_radix_reader);
static unsigned int irq_radix_writer;
400
401
402
struct irq_map_entry irq_map[NR_IRQS];
static unsigned int irq_virq_count = NR_IRQS;
static struct irq_host *irq_default_host;
Linus Torvalds's avatar
Linus Torvalds committed
403

404
405
406
407
struct irq_host *irq_alloc_host(unsigned int revmap_type,
				unsigned int revmap_arg,
				struct irq_host_ops *ops,
				irq_hw_number_t inval_irq)
Linus Torvalds's avatar
Linus Torvalds committed
408
{
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
	struct irq_host *host;
	unsigned int size = sizeof(struct irq_host);
	unsigned int i;
	unsigned int *rmap;
	unsigned long flags;

	/* Allocate structure and revmap table if using linear mapping */
	if (revmap_type == IRQ_HOST_MAP_LINEAR)
		size += revmap_arg * sizeof(unsigned int);
	if (mem_init_done)
		host = kzalloc(size, GFP_KERNEL);
	else {
		host = alloc_bootmem(size);
		if (host)
			memset(host, 0, size);
	}
	if (host == NULL)
		return NULL;
427

428
429
430
431
	/* Fill structure */
	host->revmap_type = revmap_type;
	host->inval_irq = inval_irq;
	host->ops = ops;
432

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
	spin_lock_irqsave(&irq_big_lock, flags);

	/* If it's a legacy controller, check for duplicates and
	 * mark it as allocated (we use irq 0 host pointer for that
	 */
	if (revmap_type == IRQ_HOST_MAP_LEGACY) {
		if (irq_map[0].host != NULL) {
			spin_unlock_irqrestore(&irq_big_lock, flags);
			/* If we are early boot, we can't free the structure,
			 * too bad...
			 * this will be fixed once slab is made available early
			 * instead of the current cruft
			 */
			if (mem_init_done)
				kfree(host);
			return NULL;
		}
		irq_map[0].host = host;
	}

	list_add(&host->link, &irq_hosts);
	spin_unlock_irqrestore(&irq_big_lock, flags);

	/* Additional setups per revmap type */
	switch(revmap_type) {
	case IRQ_HOST_MAP_LEGACY:
		/* 0 is always the invalid number for legacy */
		host->inval_irq = 0;
		/* setup us as the host for all legacy interrupts */
		for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
			irq_map[i].hwirq = 0;
			smp_wmb();
			irq_map[i].host = host;
			smp_wmb();

468
469
			/* Clear norequest flags */
			get_irq_desc(i)->status &= ~IRQ_NOREQUEST;
470
471
472
473
474

			/* Legacy flags are left to default at this point,
			 * one can then use irq_create_mapping() to
			 * explicitely change them
			 */
475
			ops->map(host, i, i);
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
		}
		break;
	case IRQ_HOST_MAP_LINEAR:
		rmap = (unsigned int *)(host + 1);
		for (i = 0; i < revmap_arg; i++)
			rmap[i] = IRQ_NONE;
		host->revmap_data.linear.size = revmap_arg;
		smp_wmb();
		host->revmap_data.linear.revmap = rmap;
		break;
	default:
		break;
	}

	pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type, host);

	return host;
Linus Torvalds's avatar
Linus Torvalds committed
493
494
}

495
struct irq_host *irq_find_host(struct device_node *node)
Linus Torvalds's avatar
Linus Torvalds committed
496
{
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
	struct irq_host *h, *found = NULL;
	unsigned long flags;

	/* We might want to match the legacy controller last since
	 * it might potentially be set to match all interrupts in
	 * the absence of a device node. This isn't a problem so far
	 * yet though...
	 */
	spin_lock_irqsave(&irq_big_lock, flags);
	list_for_each_entry(h, &irq_hosts, link)
		if (h->ops->match == NULL || h->ops->match(h, node)) {
			found = h;
			break;
		}
	spin_unlock_irqrestore(&irq_big_lock, flags);
	return found;
}
EXPORT_SYMBOL_GPL(irq_find_host);

void irq_set_default_host(struct irq_host *host)
{
	pr_debug("irq: Default host set to @0x%p\n", host);
Linus Torvalds's avatar
Linus Torvalds committed
519

520
521
	irq_default_host = host;
}
Linus Torvalds's avatar
Linus Torvalds committed
522

523
524
525
void irq_set_virq_count(unsigned int count)
{
	pr_debug("irq: Trying to set virq count to %d\n", count);
526

527
528
529
530
531
	BUG_ON(count < NUM_ISA_INTERRUPTS);
	if (count < NR_IRQS)
		irq_virq_count = count;
}

532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
/* radix tree not lockless safe ! we use a brlock-type mecanism
 * for now, until we can use a lockless radix tree
 */
static void irq_radix_wrlock(unsigned long *flags)
{
	unsigned int cpu, ok;

	spin_lock_irqsave(&irq_big_lock, *flags);
	irq_radix_writer = 1;
	smp_mb();
	do {
		barrier();
		ok = 1;
		for_each_possible_cpu(cpu) {
			if (per_cpu(irq_radix_reader, cpu)) {
				ok = 0;
				break;
			}
		}
		if (!ok)
			cpu_relax();
	} while(!ok);
}

static void irq_radix_wrunlock(unsigned long flags)
{
	smp_wmb();
	irq_radix_writer = 0;
	spin_unlock_irqrestore(&irq_big_lock, flags);
}

static void irq_radix_rdlock(unsigned long *flags)
{
	local_irq_save(*flags);
	__get_cpu_var(irq_radix_reader) = 1;
	smp_mb();
	if (likely(irq_radix_writer == 0))
		return;
	__get_cpu_var(irq_radix_reader) = 0;
	smp_wmb();
	spin_lock(&irq_big_lock);
	__get_cpu_var(irq_radix_reader) = 1;
	spin_unlock(&irq_big_lock);
}

static void irq_radix_rdunlock(unsigned long flags)
{
	__get_cpu_var(irq_radix_reader) = 0;
	local_irq_restore(flags);
}


584
unsigned int irq_create_mapping(struct irq_host *host,
585
				irq_hw_number_t hwirq)
586
587
588
{
	unsigned int virq, hint;

589
	pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
590
591
592
593
594
595
596
597
598

	/* Look for default host if nececssary */
	if (host == NULL)
		host = irq_default_host;
	if (host == NULL) {
		printk(KERN_WARNING "irq_create_mapping called for"
		       " NULL host, hwirq=%lx\n", hwirq);
		WARN_ON(1);
		return NO_IRQ;
Linus Torvalds's avatar
Linus Torvalds committed
599
	}
600
	pr_debug("irq: -> using host @%p\n", host);
Linus Torvalds's avatar
Linus Torvalds committed
601

602
603
604
605
606
	/* Check if mapping already exist, if it does, call
	 * host->ops->map() to update the flags
	 */
	virq = irq_find_mapping(host, hwirq);
	if (virq != IRQ_NONE) {
607
608
		if (host->ops->remap)
			host->ops->remap(host, virq, hwirq);
609
610
		pr_debug("irq: -> existing mapping on virq %d\n", virq);
		return virq;
Linus Torvalds's avatar
Linus Torvalds committed
611
612
	}

613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
	/* Get a virtual interrupt number */
	if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
		/* Handle legacy */
		virq = (unsigned int)hwirq;
		if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
			return NO_IRQ;
		return virq;
	} else {
		/* Allocate a virtual interrupt number */
		hint = hwirq % irq_virq_count;
		virq = irq_alloc_virt(host, 1, hint);
		if (virq == NO_IRQ) {
			pr_debug("irq: -> virq allocation failed\n");
			return NO_IRQ;
		}
	}
	pr_debug("irq: -> obtained virq %d\n", virq);

631
632
	/* Clear IRQ_NOREQUEST flag */
	get_irq_desc(virq)->status &= ~IRQ_NOREQUEST;
633
634

	/* map it */
635
636
637
638
	smp_wmb();
	irq_map[virq].hwirq = hwirq;
	smp_mb();
	if (host->ops->map(host, virq, hwirq)) {
639
640
641
642
		pr_debug("irq: -> mapping failed, freeing\n");
		irq_free_virt(virq, 1);
		return NO_IRQ;
	}
Linus Torvalds's avatar
Linus Torvalds committed
643
	return virq;
644
645
646
}
EXPORT_SYMBOL_GPL(irq_create_mapping);

647
648
unsigned int irq_create_of_mapping(struct device_node *controller,
				   u32 *intspec, unsigned int intsize)
649
650
651
{
	struct irq_host *host;
	irq_hw_number_t hwirq;
652
653
	unsigned int type = IRQ_TYPE_NONE;
	unsigned int virq;
Linus Torvalds's avatar
Linus Torvalds committed
654

655
656
657
658
	if (controller == NULL)
		host = irq_default_host;
	else
		host = irq_find_host(controller);
659
660
661
	if (host == NULL) {
		printk(KERN_WARNING "irq: no irq host found for %s !\n",
		       controller->full_name);
662
		return NO_IRQ;
663
	}
664
665
666
667
668
669

	/* If host has no translation, then we assume interrupt line */
	if (host->ops->xlate == NULL)
		hwirq = intspec[0];
	else {
		if (host->ops->xlate(host, controller, intspec, intsize,
670
				     &hwirq, &type))
671
			return NO_IRQ;
Linus Torvalds's avatar
Linus Torvalds committed
672
	}
673

674
675
676
677
678
679
680
681
682
683
	/* Create mapping */
	virq = irq_create_mapping(host, hwirq);
	if (virq == NO_IRQ)
		return virq;

	/* Set type if specified and different than the current one */
	if (type != IRQ_TYPE_NONE &&
	    type != (get_irq_desc(virq)->status & IRQF_TRIGGER_MASK))
		set_irq_type(virq, type);
	return virq;
Linus Torvalds's avatar
Linus Torvalds committed
684
}
685
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
Linus Torvalds's avatar
Linus Torvalds committed
686

687
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
Linus Torvalds's avatar
Linus Torvalds committed
688
{
689
	struct of_irq oirq;
Linus Torvalds's avatar
Linus Torvalds committed
690

691
692
	if (of_irq_map_one(dev, index, &oirq))
		return NO_IRQ;
Linus Torvalds's avatar
Linus Torvalds committed
693

694
695
696
697
	return irq_create_of_mapping(oirq.controller, oirq.specifier,
				     oirq.size);
}
EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
Linus Torvalds's avatar
Linus Torvalds committed
698

699
700
void irq_dispose_mapping(unsigned int virq)
{
701
	struct irq_host *host;
702
703
	irq_hw_number_t hwirq;
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
704

705
706
707
708
	if (virq == NO_IRQ)
		return;

	host = irq_map[virq].host;
709
710
711
	WARN_ON (host == NULL);
	if (host == NULL)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
712

713
714
715
	/* Never unmap legacy interrupts */
	if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
716

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
	/* remove chip and handler */
	set_irq_chip_and_handler(virq, NULL, NULL);

	/* Make sure it's completed */
	synchronize_irq(virq);

	/* Tell the PIC about it */
	if (host->ops->unmap)
		host->ops->unmap(host, virq);
	smp_mb();

	/* Clear reverse map */
	hwirq = irq_map[virq].hwirq;
	switch(host->revmap_type) {
	case IRQ_HOST_MAP_LINEAR:
		if (hwirq < host->revmap_data.linear.size)
			host->revmap_data.linear.revmap[hwirq] = IRQ_NONE;
		break;
	case IRQ_HOST_MAP_TREE:
		/* Check if radix tree allocated yet */
		if (host->revmap_data.tree.gfp_mask == 0)
			break;
739
		irq_radix_wrlock(&flags);
740
		radix_tree_delete(&host->revmap_data.tree, hwirq);
741
		irq_radix_wrunlock(flags);
742
743
		break;
	}
Linus Torvalds's avatar
Linus Torvalds committed
744

745
746
747
	/* Destroy map */
	smp_mb();
	irq_map[virq].hwirq = host->inval_irq;
Linus Torvalds's avatar
Linus Torvalds committed
748

749
750
	/* Set some flags */
	get_irq_desc(virq)->status |= IRQ_NOREQUEST;
Linus Torvalds's avatar
Linus Torvalds committed
751

752
753
	/* Free it */
	irq_free_virt(virq, 1);
Linus Torvalds's avatar
Linus Torvalds committed
754
}
755
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
Linus Torvalds's avatar
Linus Torvalds committed
756

757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
unsigned int irq_find_mapping(struct irq_host *host,
			      irq_hw_number_t hwirq)
{
	unsigned int i;
	unsigned int hint = hwirq % irq_virq_count;

	/* Look for default host if nececssary */
	if (host == NULL)
		host = irq_default_host;
	if (host == NULL)
		return NO_IRQ;

	/* legacy -> bail early */
	if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
		return hwirq;

	/* Slow path does a linear search of the map */
	if (hint < NUM_ISA_INTERRUPTS)
		hint = NUM_ISA_INTERRUPTS;
	i = hint;
	do  {
		if (irq_map[i].host == host &&
		    irq_map[i].hwirq == hwirq)
			return i;
		i++;
		if (i >= irq_virq_count)
			i = NUM_ISA_INTERRUPTS;
	} while(i != hint);
	return NO_IRQ;
}
EXPORT_SYMBOL_GPL(irq_find_mapping);
Linus Torvalds's avatar
Linus Torvalds committed
788

789
790
791

unsigned int irq_radix_revmap(struct irq_host *host,
			      irq_hw_number_t hwirq)
Linus Torvalds's avatar
Linus Torvalds committed
792
{
793
794
795
796
	struct radix_tree_root *tree;
	struct irq_map_entry *ptr;
	unsigned int virq;
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
797

798
	WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
Linus Torvalds's avatar
Linus Torvalds committed
799

800
801
802
803
804
805
806
807
808
	/* Check if the radix tree exist yet. We test the value of
	 * the gfp_mask for that. Sneaky but saves another int in the
	 * structure. If not, we fallback to slow mode
	 */
	tree = &host->revmap_data.tree;
	if (tree->gfp_mask == 0)
		return irq_find_mapping(host, hwirq);

	/* Now try to resolve */
809
	irq_radix_rdlock(&flags);
810
	ptr = radix_tree_lookup(tree, hwirq);
811
812
	irq_radix_rdunlock(flags);

813
814
815
	/* Found it, return */
	if (ptr) {
		virq = ptr - irq_map;
816
		return virq;
Linus Torvalds's avatar
Linus Torvalds committed
817
	}
818
819
820

	/* If not there, try to insert it */
	virq = irq_find_mapping(host, hwirq);
821
822
	if (virq != NO_IRQ) {
		irq_radix_wrlock(&flags);
823
		radix_tree_insert(tree, hwirq, &irq_map[virq]);
824
825
		irq_radix_wrunlock(flags);
	}
826
	return virq;
Linus Torvalds's avatar
Linus Torvalds committed
827
828
}

829
830
unsigned int irq_linear_revmap(struct irq_host *host,
			       irq_hw_number_t hwirq)
831
{
832
	unsigned int *revmap;
833

834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
	WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR);

	/* Check revmap bounds */
	if (unlikely(hwirq >= host->revmap_data.linear.size))
		return irq_find_mapping(host, hwirq);

	/* Check if revmap was allocated */
	revmap = host->revmap_data.linear.revmap;
	if (unlikely(revmap == NULL))
		return irq_find_mapping(host, hwirq);

	/* Fill up revmap with slow path if no mapping found */
	if (unlikely(revmap[hwirq] == NO_IRQ))
		revmap[hwirq] = irq_find_mapping(host, hwirq);

	return revmap[hwirq];
850
851
}

852
853
854
855
856
857
unsigned int irq_alloc_virt(struct irq_host *host,
			    unsigned int count,
			    unsigned int hint)
{
	unsigned long flags;
	unsigned int i, j, found = NO_IRQ;
858

859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
	if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
		return NO_IRQ;

	spin_lock_irqsave(&irq_big_lock, flags);

	/* Use hint for 1 interrupt if any */
	if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
	    hint < irq_virq_count && irq_map[hint].host == NULL) {
		found = hint;
		goto hint_found;
	}

	/* Look for count consecutive numbers in the allocatable
	 * (non-legacy) space
	 */
874
875
876
877
878
879
880
881
882
883
	for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) {
		if (irq_map[i].host != NULL)
			j = 0;
		else
			j++;

		if (j == count) {
			found = i - count + 1;
			break;
		}
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
	}
	if (found == NO_IRQ) {
		spin_unlock_irqrestore(&irq_big_lock, flags);
		return NO_IRQ;
	}
 hint_found:
	for (i = found; i < (found + count); i++) {
		irq_map[i].hwirq = host->inval_irq;
		smp_wmb();
		irq_map[i].host = host;
	}
	spin_unlock_irqrestore(&irq_big_lock, flags);
	return found;
}

void irq_free_virt(unsigned int virq, unsigned int count)
Linus Torvalds's avatar
Linus Torvalds committed
900
901
{
	unsigned long flags;
902
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
903

904
905
	WARN_ON (virq < NUM_ISA_INTERRUPTS);
	WARN_ON (count == 0 || (virq + count) > irq_virq_count);
Linus Torvalds's avatar
Linus Torvalds committed
906

907
908
909
	spin_lock_irqsave(&irq_big_lock, flags);
	for (i = virq; i < (virq + count); i++) {
		struct irq_host *host;
Linus Torvalds's avatar
Linus Torvalds committed
910

911
912
913
		if (i < NUM_ISA_INTERRUPTS ||
		    (virq + count) > irq_virq_count)
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
914

915
916
917
918
919
920
		host = irq_map[i].host;
		irq_map[i].hwirq = host->inval_irq;
		smp_wmb();
		irq_map[i].host = NULL;
	}
	spin_unlock_irqrestore(&irq_big_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
921
}
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936

void irq_early_init(void)
{
	unsigned int i;

	for (i = 0; i < NR_IRQS; i++)
		get_irq_desc(i)->status |= IRQ_NOREQUEST;
}

/* We need to create the radix trees late */
static int irq_late_init(void)
{
	struct irq_host *h;
	unsigned long flags;

937
	irq_radix_wrlock(&flags);
938
939
940
941
	list_for_each_entry(h, &irq_hosts, link) {
		if (h->revmap_type == IRQ_HOST_MAP_TREE)
			INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC);
	}
942
	irq_radix_wrunlock(flags);
943
944
945
946
947
948

	return 0;
}
arch_initcall(irq_late_init);

#endif /* CONFIG_PPC_MERGE */
Linus Torvalds's avatar
Linus Torvalds committed
949

Jake Moilanen's avatar
Jake Moilanen committed
950
951
952
953
954
955
956
957
#ifdef CONFIG_PCI_MSI
int pci_enable_msi(struct pci_dev * pdev)
{
	if (ppc_md.enable_msi)
		return ppc_md.enable_msi(pdev);
	else
		return -1;
}
Jake Moilanen's avatar
Jake Moilanen committed
958
EXPORT_SYMBOL(pci_enable_msi);
Jake Moilanen's avatar
Jake Moilanen committed
959
960
961
962
963
964

void pci_disable_msi(struct pci_dev * pdev)
{
	if (ppc_md.disable_msi)
		ppc_md.disable_msi(pdev);
}
Jake Moilanen's avatar
Jake Moilanen committed
965
EXPORT_SYMBOL(pci_disable_msi);
Jake Moilanen's avatar
Jake Moilanen committed
966
967
968
969
970
971
972

void pci_scan_msi_device(struct pci_dev *dev) {}
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) {return -1;}
void pci_disable_msix(struct pci_dev *dev) {}
void msi_remove_pci_irq_vectors(struct pci_dev *dev) {}
void disable_msi_mode(struct pci_dev *dev, int pos, int type) {}
void pci_no_msi(void) {}
Jake Moilanen's avatar
Jake Moilanen committed
973
974
EXPORT_SYMBOL(pci_enable_msix);
EXPORT_SYMBOL(pci_disable_msix);
Jake Moilanen's avatar
Jake Moilanen committed
975
976
977

#endif

978
#ifdef CONFIG_PPC64
Linus Torvalds's avatar
Linus Torvalds committed
979
980
981
982
983
984
985
static int __init setup_noirqdistrib(char *str)
{
	distribute_irqs = 0;
	return 1;
}

__setup("noirqdistrib", setup_noirqdistrib);
Stephen Rothwell's avatar
Stephen Rothwell committed
986
#endif /* CONFIG_PPC64 */