irq.c 13.2 KB
Newer Older
Paul Mundt's avatar
Paul Mundt committed
1
/* $Id: irq.c,v 1.12 2003/06/28 15:34:55 lethal Exp $
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * linux/arch/sh/kernel/irq.c
 *
 *	Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
 *
 *
 * SuperH version:  Copyright (C) 1999  Niibe Yutaka
 */

/*
 * IRQs are in fact implemented a bit like signal handlers for the kernel.
 * Naturally it's not a 1:1 relation, but there are similarities.
 */

#include <linux/config.h>
17
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
18 19 20 21 22 23 24 25
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/timex.h>
Linus Torvalds's avatar
Linus Torvalds committed
26
#include <linux/mm.h>
Linus Torvalds's avatar
Linus Torvalds committed
27
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31
#include <linux/random.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
32
#include <linux/seq_file.h>
Linus Torvalds's avatar
Linus Torvalds committed
33 34 35 36 37 38 39 40 41 42 43 44

#include <asm/system.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/pgalloc.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <linux/irq.h>

/*
 * Controller mappings for all interrupt sources:
 */
45 46 47 48 49
irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
	[0 ... NR_IRQS-1] = {
		.handler = &no_irq_type,
	}
};
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54

/*
 * Special irq handlers.
 */

Paul Mundt's avatar
Paul Mundt committed
55 56
irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs)
{ return IRQ_NONE; }
Linus Torvalds's avatar
Linus Torvalds committed
57 58 59 60 61 62 63 64 65 66 67 68

/*
 * Generic no controller code
 */

static void enable_none(unsigned int irq) { }
static unsigned int startup_none(unsigned int irq) { return 0; }
static void disable_none(unsigned int irq) { }
static void ack_none(unsigned int irq)
{
/*
 * 'what should we do if we get a hw irq event on an illegal vector'.
69
 * each architecture has to answer this themselves, it doesn't deserve
Linus Torvalds's avatar
Linus Torvalds committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
 * a generic callback i think.
 */
	printk("unexpected IRQ trap at vector %02x\n", irq);
}

/* startup is the same as "enable", shutdown is same as "disable" */
#define shutdown_none	disable_none
#define end_none	enable_none

struct hw_interrupt_type no_irq_type = {
	"none",
	startup_none,
	shutdown_none,
	enable_none,
	disable_none,
	ack_none,
	end_none
};

/*
 * Generic, controller-independent functions:
 */

#if defined(CONFIG_PROC_FS)
Linus Torvalds's avatar
Linus Torvalds committed
94
int show_interrupts(struct seq_file *p, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
95
{
96
	int i = *(loff_t *) v, j;
Linus Torvalds's avatar
Linus Torvalds committed
97
	struct irqaction * action;
98
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
99

100 101 102 103 104 105 106
	if (i == 0) {
		seq_puts(p, "           ");
		for (j=0; j<NR_CPUS; j++)
			if (cpu_online(j))
				seq_printf(p, "CPU%d       ",j);
		seq_putc(p, '\n');
	}
Linus Torvalds's avatar
Linus Torvalds committed
107

108
	if (i < ACTUAL_NR_IRQS) {
109
		spin_lock_irqsave(&irq_desc[i].lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
110
		action = irq_desc[i].action;
111 112
		if (!action)
			goto unlock;
Linus Torvalds's avatar
Linus Torvalds committed
113 114 115 116
		seq_printf(p, "%3d: ",i);
		seq_printf(p, "%10u ", kstat_irqs(i));
		seq_printf(p, " %14s", irq_desc[i].handler->typename);
		seq_printf(p, "  %s", action->name);
Linus Torvalds's avatar
Linus Torvalds committed
117 118

		for (action=action->next; action; action = action->next)
Linus Torvalds's avatar
Linus Torvalds committed
119 120
			seq_printf(p, ", %s", action->name);
		seq_putc(p, '\n');
121 122
unlock:
		spin_unlock_irqrestore(&irq_desc[i].lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
123
	}
Linus Torvalds's avatar
Linus Torvalds committed
124
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127 128 129 130 131 132 133 134 135 136
}
#endif

/*
 * This should really return information about whether
 * we should do bottom half handling etc. Right now we
 * end up _always_ checking the bottom half, which is a
 * waste of time and is not what some drivers would
 * prefer.
 */
int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
{
Paul Mundt's avatar
Paul Mundt committed
137 138
	int status = 1;	/* Force the "do bottom halves" bit */
	int retval = 0;
Linus Torvalds's avatar
Linus Torvalds committed
139 140

	if (!(action->flags & SA_INTERRUPT))
141
		local_irq_enable();
Linus Torvalds's avatar
Linus Torvalds committed
142 143 144

	do {
		status |= action->flags;
Paul Mundt's avatar
Paul Mundt committed
145
		retval |= action->handler(irq, action->dev_id, regs);
Linus Torvalds's avatar
Linus Torvalds committed
146 147
		action = action->next;
	} while (action);
Paul Mundt's avatar
Paul Mundt committed
148

Linus Torvalds's avatar
Linus Torvalds committed
149 150
	if (status & SA_SAMPLE_RANDOM)
		add_interrupt_randomness(irq);
Paul Mundt's avatar
Paul Mundt committed
151

152
	local_irq_disable();
Linus Torvalds's avatar
Linus Torvalds committed
153

Paul Mundt's avatar
Paul Mundt committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167
	if (retval != 1) {
		static int count = 100;

		if (count) {
			count--;

			if (retval) {
				printk("irq event %d: bogus retval mask %x\n",
					irq, retval);
			} else {
				printk("irq %d: nobody cared\n", irq);
			}
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172 173 174 175 176 177

	return status;
}

/*
 * Generic enable/disable code: this just calls
 * down into the PIC-specific version for the actual
 * hardware disable after having gotten the irq
 * controller lock. 
 */
Paul Mundt's avatar
Paul Mundt committed
178
inline void disable_irq_nosync(unsigned int irq)
Linus Torvalds's avatar
Linus Torvalds committed
179
{
Linus Torvalds's avatar
Linus Torvalds committed
180
	irq_desc_t *desc = irq_desc + irq;
Linus Torvalds's avatar
Linus Torvalds committed
181 182
	unsigned long flags;

Linus Torvalds's avatar
Linus Torvalds committed
183 184 185 186
	spin_lock_irqsave(&desc->lock, flags);
	if (!desc->depth++) {
		desc->status |= IRQ_DISABLED;
		desc->handler->disable(irq);
Linus Torvalds's avatar
Linus Torvalds committed
187
	}
Linus Torvalds's avatar
Linus Torvalds committed
188
	spin_unlock_irqrestore(&desc->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
189 190 191 192 193 194 195 196 197
}

/*
 * Synchronous version of the above, making sure the IRQ is
 * no longer running on any other IRQ..
 */
void disable_irq(unsigned int irq)
{
	disable_irq_nosync(irq);
Paul Mundt's avatar
Paul Mundt committed
198
	synchronize_irq(irq);
Linus Torvalds's avatar
Linus Torvalds committed
199 200 201 202
}

void enable_irq(unsigned int irq)
{
Linus Torvalds's avatar
Linus Torvalds committed
203
	irq_desc_t *desc = irq_desc + irq;
Linus Torvalds's avatar
Linus Torvalds committed
204 205
	unsigned long flags;

Linus Torvalds's avatar
Linus Torvalds committed
206 207
	spin_lock_irqsave(&desc->lock, flags);
	switch (desc->depth) {
Linus Torvalds's avatar
Linus Torvalds committed
208
	case 1: {
Linus Torvalds's avatar
Linus Torvalds committed
209 210
		unsigned int status = desc->status & ~IRQ_DISABLED;
		desc->status = status;
Linus Torvalds's avatar
Linus Torvalds committed
211
		if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
Linus Torvalds's avatar
Linus Torvalds committed
212 213
			desc->status = status | IRQ_REPLAY;
			hw_resend_irq(desc->handler,irq);
Linus Torvalds's avatar
Linus Torvalds committed
214
		}
Linus Torvalds's avatar
Linus Torvalds committed
215
		desc->handler->enable(irq);
Linus Torvalds's avatar
Linus Torvalds committed
216 217 218
		/* fall-through */
	}
	default:
Linus Torvalds's avatar
Linus Torvalds committed
219
		desc->depth--;
Linus Torvalds's avatar
Linus Torvalds committed
220 221 222 223 224
		break;
	case 0:
		printk("enable_irq() unbalanced from %p\n",
		       __builtin_return_address(0));
	}
Linus Torvalds's avatar
Linus Torvalds committed
225
	spin_unlock_irqrestore(&desc->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
}

/*
 * do_IRQ handles all normal device IRQ's.
 */
asmlinkage int do_IRQ(unsigned long r4, unsigned long r5,
		      unsigned long r6, unsigned long r7,
		      struct pt_regs regs)
{	
	/* 
	 * We ack quickly, we don't want the irq controller
	 * thinking we're snobs just because some other CPU has
	 * disabled global interrupts (we have already done the
	 * INT_ACK cycles, it's too late to try to pretend to the
	 * controller that we aren't taking the interrupt).
	 *
	 * 0 return value means that this irq is already being
	 * handled by some other CPU. (or is disabled)
	 */
	int irq;
	int cpu = smp_processor_id();
	irq_desc_t *desc;
	struct irqaction * action;
	unsigned int status;

Paul Mundt's avatar
Paul Mundt committed
251 252
	irq_enter();

Linus Torvalds's avatar
Linus Torvalds committed
253
	/* Get IRQ number */
Linus Torvalds's avatar
Linus Torvalds committed
254
	asm volatile("stc	r2_bank, %0\n\t"
Linus Torvalds's avatar
Linus Torvalds committed
255 256 257 258 259 260 261
		     "shlr2	%0\n\t"
		     "shlr2	%0\n\t"
		     "shlr	%0\n\t"
		     "add	#-16, %0\n\t"
		     :"=z" (irq));
	irq = irq_demux(irq);

262
	kstat_cpu(cpu).irqs[irq]++;
Linus Torvalds's avatar
Linus Torvalds committed
263
	desc = irq_desc + irq;
Linus Torvalds's avatar
Linus Torvalds committed
264
	spin_lock(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
265 266 267 268 269 270 271 272 273 274 275 276 277
	desc->handler->ack(irq);
	/*
	   REPLAY is when Linux resends an IRQ that was dropped earlier
	   WAITING is used by probe to mark irqs that are being tested
	   */
	status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
	status |= IRQ_PENDING; /* we _want_ to handle it */

	/*
	 * If the IRQ is disabled for whatever reason, we cannot
	 * use the action we have.
	 */
	action = NULL;
Paul Mundt's avatar
Paul Mundt committed
278
	if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
Linus Torvalds's avatar
Linus Torvalds committed
279 280 281 282 283 284 285 286 287 288 289 290
		action = desc->action;
		status &= ~IRQ_PENDING; /* we commit to handling */
		status |= IRQ_INPROGRESS; /* we are handling it */
	}
	desc->status = status;

	/*
	 * If there is no IRQ handler or it was disabled, exit early.
	   Since we set PENDING, if another processor is handling
	   a different instance of this same irq, the other processor
	   will take care of it.
	 */
Paul Mundt's avatar
Paul Mundt committed
291
	if (unlikely(!action))
Linus Torvalds's avatar
Linus Torvalds committed
292
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
293 294 295 296 297 298 299 300 301 302 303 304

	/*
	 * Edge triggered interrupts need to remember
	 * pending events.
	 * This applies to any hw interrupts that allow a second
	 * instance of the same irq to arrive while we are in do_IRQ
	 * or in the handler. But the code here only handles the _second_
	 * instance of the irq, not the third or fourth. So it is mostly
	 * useful for irq hardware that does not mask cleanly in an
	 * SMP environment.
	 */
	for (;;) {
Linus Torvalds's avatar
Linus Torvalds committed
305
		spin_unlock(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
306
		handle_IRQ_event(irq, &regs, action);
Linus Torvalds's avatar
Linus Torvalds committed
307
		spin_lock(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
308

Paul Mundt's avatar
Paul Mundt committed
309
		if (likely(!(desc->status & IRQ_PENDING)))
Linus Torvalds's avatar
Linus Torvalds committed
310 311 312 313
			break;
		desc->status &= ~IRQ_PENDING;
	}
	desc->status &= ~IRQ_INPROGRESS;
Paul Mundt's avatar
Paul Mundt committed
314

Linus Torvalds's avatar
Linus Torvalds committed
315 316 317 318 319 320 321
out:
	/*
	 * The ->end() handler has to deal with interrupts which got
	 * disabled while the handler was running.
	 */
	desc->handler->end(irq);
	spin_unlock(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
322

Paul Mundt's avatar
Paul Mundt committed
323 324
	irq_exit();

Linus Torvalds's avatar
Linus Torvalds committed
325 326 327 328
	return 1;
}

int request_irq(unsigned int irq, 
Paul Mundt's avatar
Paul Mundt committed
329
		irqreturn_t (*handler)(int, void *, struct pt_regs *),
Linus Torvalds's avatar
Linus Torvalds committed
330 331 332 333 334 335 336 337 338 339 340 341 342
		unsigned long irqflags, 
		const char * devname,
		void *dev_id)
{
	int retval;
	struct irqaction * action;

	if (irq >= ACTUAL_NR_IRQS)
		return -EINVAL;
	if (!handler)
		return -EINVAL;

	action = (struct irqaction *)
Paul Mundt's avatar
Paul Mundt committed
343
			kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	if (!action)
		return -ENOMEM;

	action->handler = handler;
	action->flags = irqflags;
	action->mask = 0;
	action->name = devname;
	action->next = NULL;
	action->dev_id = dev_id;

	retval = setup_irq(irq, action);
	if (retval)
		kfree(action);
	return retval;
}

360 361
EXPORT_SYMBOL(request_irq);

Linus Torvalds's avatar
Linus Torvalds committed
362 363
void free_irq(unsigned int irq, void *dev_id)
{
Linus Torvalds's avatar
Linus Torvalds committed
364
	irq_desc_t *desc;
Linus Torvalds's avatar
Linus Torvalds committed
365 366 367 368 369 370
	struct irqaction **p;
	unsigned long flags;

	if (irq >= ACTUAL_NR_IRQS)
		return;

Linus Torvalds's avatar
Linus Torvalds committed
371 372 373
	desc = irq_desc + irq;
	spin_lock_irqsave(&desc->lock,flags);
	p = &desc->action;
Linus Torvalds's avatar
Linus Torvalds committed
374 375 376 377 378 379 380 381 382 383
	for (;;) {
		struct irqaction * action = *p;
		if (action) {
			struct irqaction **pp = p;
			p = &action->next;
			if (action->dev_id != dev_id)
				continue;

			/* Found it - now remove it from the list of entries */
			*pp = action->next;
Linus Torvalds's avatar
Linus Torvalds committed
384 385 386
			if (!desc->action) {
				desc->status |= IRQ_DISABLED;
				desc->handler->shutdown(irq);
Linus Torvalds's avatar
Linus Torvalds committed
387
			}
Linus Torvalds's avatar
Linus Torvalds committed
388
			spin_unlock_irqrestore(&desc->lock,flags);
Paul Mundt's avatar
Paul Mundt committed
389
			synchronize_irq(irq);
Linus Torvalds's avatar
Linus Torvalds committed
390 391 392 393
			kfree(action);
			return;
		}
		printk("Trying to free free IRQ%d\n",irq);
Linus Torvalds's avatar
Linus Torvalds committed
394
		spin_unlock_irqrestore(&desc->lock,flags);
Linus Torvalds's avatar
Linus Torvalds committed
395 396 397 398
		return;
	}
}

399 400
EXPORT_SYMBOL(free_irq);

Linus Torvalds's avatar
Linus Torvalds committed
401 402
static DECLARE_MUTEX(probe_sem);

Linus Torvalds's avatar
Linus Torvalds committed
403 404 405 406 407 408 409 410 411 412 413
/*
 * IRQ autodetection code..
 *
 * This depends on the fact that any interrupt that
 * comes in on to an unassigned handler will get stuck
 * with "IRQ_WAITING" cleared and the interrupt
 * disabled.
 */
unsigned long probe_irq_on(void)
{
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
414
	irq_desc_t *desc;
Linus Torvalds's avatar
Linus Torvalds committed
415
	unsigned long val;
Linus Torvalds's avatar
Linus Torvalds committed
416 417 418 419 420 421 422 423 424 425 426 427
	unsigned long delay;

	down(&probe_sem);
	/* 
	 * something may have generated an irq long ago and we want to
	 * flush such a longstanding irq before considering it as spurious. 
	 */
	for (i = NR_IRQS-1; i > 0; i--) {
		desc = irq_desc + i;

		spin_lock_irq(&desc->lock);
		if (!desc->action)
Linus Torvalds's avatar
Linus Torvalds committed
428
			desc->handler->startup(i);
Linus Torvalds's avatar
Linus Torvalds committed
429 430 431 432 433
		spin_unlock_irq(&desc->lock);
	}

	/* Wait for longstanding interrupts to trigger. */
	for (delay = jiffies + HZ/50; time_after(delay, jiffies); )
Paul Mundt's avatar
Paul Mundt committed
434
		/* about 20ms delay */ barrier();
Linus Torvalds's avatar
Linus Torvalds committed
435 436

	/*
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439
	 * enable any unassigned irqs
	 * (we must startup again here because if a longstanding irq
	 * happened in the previous stage, it may have masked itself)
Linus Torvalds's avatar
Linus Torvalds committed
440 441
	 */
	for (i = NR_IRQS-1; i > 0; i--) {
Linus Torvalds's avatar
Linus Torvalds committed
442 443 444 445 446 447 448
		desc = irq_desc + i;

		spin_lock_irq(&desc->lock);
		if (!desc->action) {
			desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
			if (desc->handler->startup(i))
				desc->status |= IRQ_PENDING;
Linus Torvalds's avatar
Linus Torvalds committed
449
		}
Linus Torvalds's avatar
Linus Torvalds committed
450
		spin_unlock_irq(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
451 452 453 454 455 456 457 458 459 460 461 462 463
	}

	/*
	 * Wait for spurious interrupts to trigger
	 */
	for (delay = jiffies + HZ/10; time_after(delay, jiffies); )
		/* about 100ms delay */ synchronize_irq();

	/*
	 * Now filter out any obviously spurious interrupts
	 */
	val = 0;
	for (i=0; i<NR_IRQS; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477
		desc = irq_desc + i;
		unsigned int status;

		spin_lock_irq(&desc->lock);
		status = desc->status;

		if (status & IRQ_AUTODETECT) {
			/* It triggered already - consider it spurious. */
			if (!(status & IRQ_WAITING)) {
				desc->status = status & ~IRQ_AUTODETECT;
				desc->handler->shutdown(i);
			} else
				if (i < 32)
					val |= 1 << i;
Linus Torvalds's avatar
Linus Torvalds committed
478
		}
Linus Torvalds's avatar
Linus Torvalds committed
479
		spin_unlock_irq(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
480 481 482 483 484
	}

	return val;
}

485 486
EXPORT_SYMBOL(probe_irq_on);

Linus Torvalds's avatar
Linus Torvalds committed
487 488 489 490 491 492 493
int probe_irq_off(unsigned long val)
{
	int i, irq_found, nr_irqs;

	nr_irqs = 0;
	irq_found = 0;
	for (i=0; i<NR_IRQS; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
494 495
		irq_desc_t *desc = irq_desc + i;
		unsigned int status;
Linus Torvalds's avatar
Linus Torvalds committed
496

Linus Torvalds's avatar
Linus Torvalds committed
497 498
		spin_lock_irq(&desc->lock);
		status = desc->status;
Linus Torvalds's avatar
Linus Torvalds committed
499

Linus Torvalds's avatar
Linus Torvalds committed
500 501 502 503 504 505 506 507
		if (status & IRQ_AUTODETECT) {
			if (!(status & IRQ_WAITING)) {
				if (!nr_irqs)
					irq_found = i;
				nr_irqs++;
			}
			desc->status = status & ~IRQ_AUTODETECT;
			desc->handler->shutdown(i);
Linus Torvalds's avatar
Linus Torvalds committed
508
		}
Linus Torvalds's avatar
Linus Torvalds committed
509
		spin_unlock_irq(&desc->lock);
Linus Torvalds's avatar
Linus Torvalds committed
510
	}
Linus Torvalds's avatar
Linus Torvalds committed
511
	up(&probe_sem);
Linus Torvalds's avatar
Linus Torvalds committed
512 513 514 515 516 517

	if (nr_irqs > 1)
		irq_found = -irq_found;
	return irq_found;
}

518 519
EXPORT_SYMBOL(probe_irq_off);

Linus Torvalds's avatar
Linus Torvalds committed
520 521 522 523 524
int setup_irq(unsigned int irq, struct irqaction * new)
{
	int shared = 0;
	struct irqaction *old, **p;
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
525
	irq_desc_t *desc = irq_desc + irq;
Linus Torvalds's avatar
Linus Torvalds committed
526

Paul Mundt's avatar
Paul Mundt committed
527 528 529
	if (desc->handler == &no_irq_type)
		return -ENOSYS;
 	/*
Linus Torvalds's avatar
Linus Torvalds committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
	 * Some drivers like serial.c use request_irq() heavily,
	 * so we have to be careful not to interfere with a
	 * running system.
	 */
	if (new->flags & SA_SAMPLE_RANDOM) {
		/*
		 * This function might sleep, we want to call it first,
		 * outside of the atomic block.
		 * Yes, this might clear the entropy pool if the wrong
		 * driver is attempted to be loaded, without actually
		 * installing a new handler, but is this really a problem,
		 * only the sysadmin is able to do this.
		 */
		rand_initialize_irq(irq);
	}

	/*
	 * The following block of code has to be executed atomically
	 */
Linus Torvalds's avatar
Linus Torvalds committed
549 550
	spin_lock_irqsave(&desc->lock,flags);
	p = &desc->action;
Linus Torvalds's avatar
Linus Torvalds committed
551 552 553
	if ((old = *p) != NULL) {
		/* Can't share interrupts unless both agree to */
		if (!(old->flags & new->flags & SA_SHIRQ)) {
Linus Torvalds's avatar
Linus Torvalds committed
554
			spin_unlock_irqrestore(&desc->lock,flags);
Linus Torvalds's avatar
Linus Torvalds committed
555 556 557 558 559 560 561 562 563 564 565 566 567 568
			return -EBUSY;
		}

		/* add new interrupt at end of irq queue */
		do {
			p = &old->next;
			old = *p;
		} while (old);
		shared = 1;
	}

	*p = new;

	if (!shared) {
Linus Torvalds's avatar
Linus Torvalds committed
569
		desc->depth = 0;
Paul Mundt's avatar
Paul Mundt committed
570
		desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
Linus Torvalds's avatar
Linus Torvalds committed
571
		desc->handler->startup(irq);
Linus Torvalds's avatar
Linus Torvalds committed
572
	}
Linus Torvalds's avatar
Linus Torvalds committed
573
	spin_unlock_irqrestore(&desc->lock,flags);
Linus Torvalds's avatar
Linus Torvalds committed
574 575 576 577 578 579 580 581 582
	return 0;
}

#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)

void init_irq_proc(void)
{
}
#endif
Paul Mundt's avatar
Paul Mundt committed
583 584 585 586 587 588 589 590 591 592 593 594 595

/* Taken from the 2.5 alpha port */
#ifdef CONFIG_SMP
void synchronize_irq(unsigned int irq)
{
	/* is there anything to synchronize with? */
	if (!irq_desc[irq].action)
		return;

	while (irq_desc[irq].status & IRQ_INPROGRESS)
		barrier();
}
#endif