irq.c 15.1 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
 *  Adapted for Power Macintosh by Paul Mackerras
 *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
Stephen Rothwell's avatar
Stephen Rothwell committed
10
 *
Linus Torvalds's avatar
Linus Torvalds committed
11
12
13
14
15
16
17
18
19
20
 * 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
21
22
23
24
25
26
27
28
 *
 * 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
29
30
 */

31
32
#undef DEBUG

33
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
34
35
36
37
#include <linux/threads.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
Stephen Rothwell's avatar
Stephen Rothwell committed
38
#include <linux/ptrace.h>
Linus Torvalds's avatar
Linus Torvalds committed
39
40
41
42
43
44
45
#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
46
47
#include <linux/seq_file.h>
#include <linux/cpumask.h>
Linus Torvalds's avatar
Linus Torvalds committed
48
49
#include <linux/profile.h>
#include <linux/bitops.h>
50
51
52
53
#include <linux/list.h>
#include <linux/radix-tree.h>
#include <linux/mutex.h>
#include <linux/bootmem.h>
Jake Moilanen's avatar
Jake Moilanen committed
54
#include <linux/pci.h>
55
#include <linux/debugfs.h>
56
57
#include <linux/of.h>
#include <linux/of_irq.h>
Linus Torvalds's avatar
Linus Torvalds committed
58
59
60
61
62
63
64
65
66

#include <asm/uaccess.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>
67
#include <asm/udbg.h>
68
#include <asm/smp.h>
69
#include <asm/debug.h>
70

71
#ifdef CONFIG_PPC64
Linus Torvalds's avatar
Linus Torvalds committed
72
#include <asm/paca.h>
73
#include <asm/firmware.h>
74
#include <asm/lv1call.h>
Stephen Rothwell's avatar
Stephen Rothwell committed
75
#endif
76
77
#define CREATE_TRACE_POINTS
#include <asm/trace.h>
Linus Torvalds's avatar
Linus Torvalds committed
78

79
80
81
DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
EXPORT_PER_CPU_SYMBOL(irq_stat);

82
int __irq_offset_value;
Stephen Rothwell's avatar
Stephen Rothwell committed
83
84

#ifdef CONFIG_PPC32
85
86
EXPORT_SYMBOL(__irq_offset_value);
atomic_t ppc_n_lost_interrupts;
Stephen Rothwell's avatar
Stephen Rothwell committed
87
88
89
90
91

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

#ifdef CONFIG_PPC64
95

Linus Torvalds's avatar
Linus Torvalds committed
96
int distribute_irqs = 1;
97

98
static inline notrace unsigned long get_irq_happened(void)
99
{
100
	unsigned long happened;
101
102

	__asm__ __volatile__("lbz %0,%1(13)"
103
	: "=r" (happened) : "i" (offsetof(struct paca_struct, irq_happened)));
104

105
	return happened;
106
107
}

Steven Rostedt's avatar
Steven Rostedt committed
108
static inline notrace void set_soft_enabled(unsigned long enable)
109
110
111
112
113
{
	__asm__ __volatile__("stb %0,%1(13)"
	: : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
}

114
static inline notrace int decrementer_check_overflow(void)
115
{
116
117
118
 	u64 now = get_tb_or_rtc();
 	u64 *next_tb = &__get_cpu_var(decrementers_next_tb);
 
119
120
	if (now >= *next_tb)
		set_dec(1);
121
	return now >= *next_tb;
122
123
}

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* This is called whenever we are re-enabling interrupts
 * and returns either 0 (nothing to do) or 500/900 if there's
 * either an EE or a DEC to generate.
 *
 * This is called in two contexts: From arch_local_irq_restore()
 * before soft-enabling interrupts, and from the exception exit
 * path when returning from an interrupt from a soft-disabled to
 * a soft enabled context. In both case we have interrupts hard
 * disabled.
 *
 * We take care of only clearing the bits we handled in the
 * PACA irq_happened field since we can only re-emit one at a
 * time and we don't want to "lose" one.
 */
notrace unsigned int __check_irq_replay(void)
139
{
140
	/*
141
142
143
	 * We use local_paca rather than get_paca() to avoid all
	 * the debug_smp_processor_id() business in this low level
	 * function
144
	 */
145
	unsigned char happened = local_paca->irq_happened;
146

147
148
149
150
151
152
153
154
155
156
	/* Clear bit 0 which we wouldn't clear otherwise */
	local_paca->irq_happened &= ~PACA_IRQ_HARD_DIS;

	/*
	 * Force the delivery of pending soft-disabled interrupts on PS3.
	 * Any HV call will have this side effect.
	 */
	if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
		u64 tmp, tmp2;
		lv1_get_version_info(&tmp, &tmp2);
157
158
	}

159
	/*
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
	 * We may have missed a decrementer interrupt. We check the
	 * decrementer itself rather than the paca irq_happened field
	 * in case we also had a rollover while hard disabled
	 */
	local_paca->irq_happened &= ~PACA_IRQ_DEC;
	if (decrementer_check_overflow())
		return 0x900;

	/* Finally check if an external interrupt happened */
	local_paca->irq_happened &= ~PACA_IRQ_EE;
	if (happened & PACA_IRQ_EE)
		return 0x500;

#ifdef CONFIG_PPC_BOOK3E
	/* Finally check if an EPR external interrupt happened
	 * this bit is typically set if we need to handle another
	 * "edge" interrupt from within the MPIC "EPR" handler
177
	 */
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
	local_paca->irq_happened &= ~PACA_IRQ_EE_EDGE;
	if (happened & PACA_IRQ_EE_EDGE)
		return 0x500;

	local_paca->irq_happened &= ~PACA_IRQ_DBELL;
	if (happened & PACA_IRQ_DBELL)
		return 0x280;
#endif /* CONFIG_PPC_BOOK3E */

	/* There should be nothing left ! */
	BUG_ON(local_paca->irq_happened != 0);

	return 0;
}

notrace void arch_local_irq_restore(unsigned long en)
{
	unsigned char irq_happened;
	unsigned int replay;

	/* Write the new soft-enabled value */
	set_soft_enabled(en);
	if (!en)
		return;
	/*
	 * From this point onward, we can take interrupts, preempt,
	 * etc... unless we got hard-disabled. We check if an event
	 * happened. If none happened, we know we can just return.
	 *
	 * We may have preempted before the check below, in which case
	 * we are checking the "new" CPU instead of the old one. This
	 * is only a problem if an event happened on the "old" CPU.
	 *
211
212
	 * External interrupt events will have caused interrupts to
	 * be hard-disabled, so there is no problem, we
213
	 * cannot have preempted.
214
	 */
215
216
	irq_happened = get_irq_happened();
	if (!irq_happened)
217
		return;
218
219

	/*
220
221
222
223
224
225
226
227
228
	 * We need to hard disable to get a trusted value from
	 * __check_irq_replay(). We also need to soft-disable
	 * again to avoid warnings in there due to the use of
	 * per-cpu variables.
	 *
	 * We know that if the value in irq_happened is exactly 0x01
	 * then we are already hard disabled (there are other less
	 * common cases that we'll ignore for now), so we skip the
	 * (expensive) mtmsrd.
229
	 */
230
231
232
	if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
		__hard_irq_disable();
	set_soft_enabled(0);
233

234
	/*
235
236
237
	 * Check if anything needs to be re-emitted. We haven't
	 * soft-enabled yet to avoid warnings in decrementer_check_overflow
	 * accessing per-cpu variables
238
	 */
239
240
241
242
	replay = __check_irq_replay();

	/* We can soft-enable now */
	set_soft_enabled(1);
243
244

	/*
245
246
	 * And replay if we have to. This will return with interrupts
	 * hard-enabled.
247
	 */
248
249
250
	if (replay) {
		__replay_interrupt(replay);
		return;
251
252
	}

253
	/* Finally, let's ensure we are hard enabled */
254
	__hard_irq_enable();
255
}
David Howells's avatar
David Howells committed
256
EXPORT_SYMBOL(arch_local_irq_restore);
257
258
259
260
261
262
263
264
265
266
267
268
269

/*
 * This is specifically called by assembly code to re-enable interrupts
 * if they are currently disabled. This is typically called before
 * schedule() or do_signal() when returning to userspace. We do it
 * in C to avoid the burden of dealing with lockdep etc...
 */
void restore_interrupts(void)
{
	if (irqs_disabled())
		local_irq_enable();
}

Stephen Rothwell's avatar
Stephen Rothwell committed
270
#endif /* CONFIG_PPC64 */
Linus Torvalds's avatar
Linus Torvalds committed
271

272
int arch_show_interrupts(struct seq_file *p, int prec)
273
274
275
276
277
278
279
280
281
282
283
284
{
	int j;

#if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT)
	if (tau_initialized) {
		seq_printf(p, "%*s: ", prec, "TAU");
		for_each_online_cpu(j)
			seq_printf(p, "%10u ", tau_interrupts(j));
		seq_puts(p, "  PowerPC             Thermal Assist (cpu temp)\n");
	}
#endif /* CONFIG_PPC32 && CONFIG_TAU_INT */

285
286
287
288
289
	seq_printf(p, "%*s: ", prec, "LOC");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", per_cpu(irq_stat, j).timer_irqs);
        seq_printf(p, "  Local timer interrupts\n");

290
291
292
293
294
	seq_printf(p, "%*s: ", prec, "SPU");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", per_cpu(irq_stat, j).spurious_irqs);
	seq_printf(p, "  Spurious interrupts\n");

295
296
297
298
299
300
301
302
303
304
	seq_printf(p, "%*s: ", prec, "CNT");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", per_cpu(irq_stat, j).pmu_irqs);
	seq_printf(p, "  Performance monitoring interrupts\n");

	seq_printf(p, "%*s: ", prec, "MCE");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", per_cpu(irq_stat, j).mce_exceptions);
	seq_printf(p, "  Machine check exceptions\n");

305
306
307
	return 0;
}

308
309
310
311
312
313
314
315
316
/*
 * /proc/stat helpers
 */
u64 arch_irq_stat_cpu(unsigned int cpu)
{
	u64 sum = per_cpu(irq_stat, cpu).timer_irqs;

	sum += per_cpu(irq_stat, cpu).pmu_irqs;
	sum += per_cpu(irq_stat, cpu).mce_exceptions;
317
	sum += per_cpu(irq_stat, cpu).spurious_irqs;
318
319
320
321

	return sum;
}

Linus Torvalds's avatar
Linus Torvalds committed
322
#ifdef CONFIG_HOTPLUG_CPU
323
void migrate_irqs(void)
Linus Torvalds's avatar
Linus Torvalds committed
324
{
325
	struct irq_desc *desc;
Linus Torvalds's avatar
Linus Torvalds committed
326
327
	unsigned int irq;
	static int warned;
328
	cpumask_var_t mask;
329
	const struct cpumask *map = cpu_online_mask;
Linus Torvalds's avatar
Linus Torvalds committed
330

331
	alloc_cpumask_var(&mask, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
332

333
	for_each_irq(irq) {
334
		struct irq_data *data;
335
336
		struct irq_chip *chip;

337
		desc = irq_to_desc(irq);
338
339
340
		if (!desc)
			continue;

341
342
		data = irq_desc_get_irq_data(desc);
		if (irqd_is_per_cpu(data))
Linus Torvalds's avatar
Linus Torvalds committed
343
344
			continue;

345
		chip = irq_data_get_irq_chip(data);
346

347
		cpumask_and(mask, data->affinity, map);
348
		if (cpumask_any(mask) >= nr_cpu_ids) {
Linus Torvalds's avatar
Linus Torvalds committed
349
			printk("Breaking affinity for irq %i\n", irq);
350
			cpumask_copy(mask, map);
Linus Torvalds's avatar
Linus Torvalds committed
351
		}
352
		if (chip->irq_set_affinity)
353
			chip->irq_set_affinity(data, mask, true);
354
		else if (desc->action && !(warned++))
Linus Torvalds's avatar
Linus Torvalds committed
355
356
357
			printk("Cannot set affinity for irq %i\n", irq);
	}

358
359
	free_cpumask_var(mask);

Linus Torvalds's avatar
Linus Torvalds committed
360
361
362
363
364
365
	local_irq_enable();
	mdelay(1);
	local_irq_disable();
}
#endif

366
367
368
369
370
371
static inline void handle_one_irq(unsigned int irq)
{
	struct thread_info *curtp, *irqtp;
	unsigned long saved_sp_limit;
	struct irq_desc *desc;

372
373
374
375
	desc = irq_to_desc(irq);
	if (!desc)
		return;

376
377
378
379
380
381
	/* Switch to the irq stack to handle this */
	curtp = current_thread_info();
	irqtp = hardirq_ctx[smp_processor_id()];

	if (curtp == irqtp) {
		/* We're already on the irq stack, just handle it */
382
		desc->handle_irq(irq, desc);
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
		return;
	}

	saved_sp_limit = current->thread.ksp_limit;

	irqtp->task = curtp->task;
	irqtp->flags = 0;

	/* Copy the softirq bits in preempt_count so that the
	 * softirq checks work in the hardirq context. */
	irqtp->preempt_count = (irqtp->preempt_count & ~SOFTIRQ_MASK) |
			       (curtp->preempt_count & SOFTIRQ_MASK);

	current->thread.ksp_limit = (unsigned long)irqtp +
		_ALIGN_UP(sizeof(struct thread_info), 16);

399
	call_handle_irq(irq, desc, irqtp, desc->handle_irq);
400
401
402
403
404
405
406
407
408
409
	current->thread.ksp_limit = saved_sp_limit;
	irqtp->task = NULL;

	/* Set any flag that may have been set on the
	 * alternate stack
	 */
	if (irqtp->flags)
		set_bits(irqtp->flags, &curtp->flags);
}

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
static inline void check_stack_overflow(void)
{
#ifdef CONFIG_DEBUG_STACKOVERFLOW
	long sp;

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

	/* check for stack overflow: is there less than 2KB free? */
	if (unlikely(sp < (sizeof(struct thread_info) + 2048))) {
		printk("do_IRQ: stack overflow: %ld\n",
			sp - sizeof(struct thread_info));
		dump_stack();
	}
#endif
}

Linus Torvalds's avatar
Linus Torvalds committed
426
427
void do_IRQ(struct pt_regs *regs)
{
428
	struct pt_regs *old_regs = set_irq_regs(regs);
429
	unsigned int irq;
Linus Torvalds's avatar
Linus Torvalds committed
430

431
432
	trace_irq_entry(regs);

433
	irq_enter();
Linus Torvalds's avatar
Linus Torvalds committed
434

435
	check_stack_overflow();
Linus Torvalds's avatar
Linus Torvalds committed
436

437
438
439
440
441
	/*
	 * Query the platform PIC for the interrupt & ack it.
	 *
	 * This will typically lower the interrupt line to the CPU
	 */
442
	irq = ppc_md.get_irq();
Linus Torvalds's avatar
Linus Torvalds committed
443

444
445
446
447
	/* We can hard enable interrupts now */
	may_hard_irq_enable();

	/* And finally process it */
448
	if (irq != NO_IRQ)
449
		handle_one_irq(irq);
450
	else
451
		__get_cpu_var(irq_stat).spurious_irqs++;
452

453
	irq_exit();
454
	set_irq_regs(old_regs);
Stephen Rothwell's avatar
Stephen Rothwell committed
455

456
	trace_irq_exit(regs);
457
}
Linus Torvalds's avatar
Linus Torvalds committed
458
459
460

void __init init_IRQ(void)
{
461
462
	if (ppc_md.init_IRQ)
		ppc_md.init_IRQ();
463
464
465

	exc_lvl_ctx_init();

Linus Torvalds's avatar
Linus Torvalds committed
466
467
468
	irq_ctx_init();
}

469
470
471
472
473
474
475
476
#if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
struct thread_info   *critirq_ctx[NR_CPUS] __read_mostly;
struct thread_info    *dbgirq_ctx[NR_CPUS] __read_mostly;
struct thread_info *mcheckirq_ctx[NR_CPUS] __read_mostly;

void exc_lvl_ctx_init(void)
{
	struct thread_info *tp;
477
	int i, cpu_nr;
478
479

	for_each_possible_cpu(i) {
480
481
482
483
484
485
486
487
#ifdef CONFIG_PPC64
		cpu_nr = i;
#else
		cpu_nr = get_hard_smp_processor_id(i);
#endif
		memset((void *)critirq_ctx[cpu_nr], 0, THREAD_SIZE);
		tp = critirq_ctx[cpu_nr];
		tp->cpu = cpu_nr;
488
489
490
		tp->preempt_count = 0;

#ifdef CONFIG_BOOKE
491
492
493
		memset((void *)dbgirq_ctx[cpu_nr], 0, THREAD_SIZE);
		tp = dbgirq_ctx[cpu_nr];
		tp->cpu = cpu_nr;
494
495
		tp->preempt_count = 0;

496
497
498
		memset((void *)mcheckirq_ctx[cpu_nr], 0, THREAD_SIZE);
		tp = mcheckirq_ctx[cpu_nr];
		tp->cpu = cpu_nr;
499
500
501
502
503
		tp->preempt_count = HARDIRQ_OFFSET;
#endif
	}
}
#endif
Linus Torvalds's avatar
Linus Torvalds committed
504

505
506
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
507
508
509
510
511
512

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

513
	for_each_possible_cpu(i) {
Linus Torvalds's avatar
Linus Torvalds committed
514
515
516
		memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
		tp = softirq_ctx[i];
		tp->cpu = i;
517
		tp->preempt_count = 0;
Linus Torvalds's avatar
Linus Torvalds committed
518
519
520
521
522
523
524
525

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

526
527
528
static inline void do_softirq_onstack(void)
{
	struct thread_info *curtp, *irqtp;
529
	unsigned long saved_sp_limit = current->thread.ksp_limit;
530
531
532
533

	curtp = current_thread_info();
	irqtp = softirq_ctx[smp_processor_id()];
	irqtp->task = curtp->task;
534
	irqtp->flags = 0;
535
536
	current->thread.ksp_limit = (unsigned long)irqtp +
				    _ALIGN_UP(sizeof(struct thread_info), 16);
537
	call_do_softirq(irqtp);
538
	current->thread.ksp_limit = saved_sp_limit;
539
	irqtp->task = NULL;
540
541
542
543
544
545

	/* Set any flag that may have been set on the
	 * alternate stack
	 */
	if (irqtp->flags)
		set_bits(irqtp->flags, &curtp->flags);
546
}
Linus Torvalds's avatar
Linus Torvalds committed
547
548
549
550
551
552
553
554
555
556

void do_softirq(void)
{
	unsigned long flags;

	if (in_interrupt())
		return;

	local_irq_save(flags);

557
	if (local_softirq_pending())
558
		do_softirq_onstack();
Linus Torvalds's avatar
Linus Torvalds committed
559
560
561
562

	local_irq_restore(flags);
}

563
564
irq_hw_number_t virq_to_hw(unsigned int virq)
{
565
566
	struct irq_data *irq_data = irq_get_irq_data(virq);
	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
567
568
569
}
EXPORT_SYMBOL_GPL(virq_to_hw);

570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
#ifdef CONFIG_SMP
int irq_choose_cpu(const struct cpumask *mask)
{
	int cpuid;

	if (cpumask_equal(mask, cpu_all_mask)) {
		static int irq_rover;
		static DEFINE_RAW_SPINLOCK(irq_rover_lock);
		unsigned long flags;

		/* Round-robin distribution... */
do_round_robin:
		raw_spin_lock_irqsave(&irq_rover_lock, flags);

		irq_rover = cpumask_next(irq_rover, cpu_online_mask);
		if (irq_rover >= nr_cpu_ids)
			irq_rover = cpumask_first(cpu_online_mask);

		cpuid = irq_rover;

		raw_spin_unlock_irqrestore(&irq_rover_lock, flags);
	} else {
		cpuid = cpumask_first_and(mask, cpu_online_mask);
		if (cpuid >= nr_cpu_ids)
			goto do_round_robin;
	}

	return get_hard_smp_processor_id(cpuid);
}
#else
int irq_choose_cpu(const struct cpumask *mask)
{
	return hard_smp_processor_id();
}
#endif
605

606
int arch_early_irq_init(void)
607
{
608
	return 0;
609
610
}

611
#ifdef CONFIG_PPC64
Linus Torvalds's avatar
Linus Torvalds committed
612
613
614
615
616
617
618
static int __init setup_noirqdistrib(char *str)
{
	distribute_irqs = 0;
	return 1;
}

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