autoprobe.c 4.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11
/*
 * linux/kernel/irq/autoprobe.c
 *
 * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
 *
 * This file contains the interrupt probing code and driver APIs.
 */

#include <linux/irq.h>
#include <linux/module.h>
#include <linux/interrupt.h>
12
#include <linux/delay.h>
L
Linus Torvalds 已提交
13 14 15 16 17 18

/*
 * Autodetection 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.
 */
19
static DEFINE_MUTEX(probing_active);
L
Linus Torvalds 已提交
20 21 22 23 24 25 26 27 28 29

/**
 *	probe_irq_on	- begin an interrupt autodetect
 *
 *	Commence probing for an interrupt. The interrupts are scanned
 *	and a mask of potential interrupt lines is returned.
 *
 */
unsigned long probe_irq_on(void)
{
30
	struct irq_desc *desc;
31
	unsigned long mask;
L
Linus Torvalds 已提交
32 33
	unsigned int i;

34
	mutex_lock(&probing_active);
L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42
	/*
	 * 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);
43 44
		if (!desc->action)
			desc->chip->startup(i);
L
Linus Torvalds 已提交
45 46 47 48
		spin_unlock_irq(&desc->lock);
	}

	/* Wait for longstanding interrupts to trigger. */
49
	msleep(20);
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61

	/*
	 * enable any unassigned irqs
	 * (we must startup again here because if a longstanding irq
	 * happened in the previous stage, it may have masked itself)
	 */
	for (i = NR_IRQS-1; i > 0; i--) {
		desc = irq_desc + i;

		spin_lock_irq(&desc->lock);
		if (!desc->action) {
			desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
62
			if (desc->chip->startup(i))
L
Linus Torvalds 已提交
63 64 65 66 67 68 69 70
				desc->status |= IRQ_PENDING;
		}
		spin_unlock_irq(&desc->lock);
	}

	/*
	 * Wait for spurious interrupts to trigger
	 */
71
	msleep(100);
L
Linus Torvalds 已提交
72 73 74 75

	/*
	 * Now filter out any obviously spurious interrupts
	 */
76
	mask = 0;
L
Linus Torvalds 已提交
77 78 79
	for (i = 0; i < NR_IRQS; i++) {
		unsigned int status;

80
		desc = irq_desc + i;
L
Linus Torvalds 已提交
81 82 83 84 85 86 87
		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;
88
				desc->chip->shutdown(i);
L
Linus Torvalds 已提交
89 90
			} else
				if (i < 32)
91
					mask |= 1 << i;
L
Linus Torvalds 已提交
92 93 94 95
		}
		spin_unlock_irq(&desc->lock);
	}

96
	return mask;
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
}
EXPORT_SYMBOL(probe_irq_on);

/**
 *	probe_irq_mask - scan a bitmap of interrupt lines
 *	@val:	mask of interrupts to consider
 *
 *	Scan the interrupt lines and return a bitmap of active
 *	autodetect interrupts. The interrupt probe logic state
 *	is then returned to its previous value.
 *
 *	Note: we need to scan all the irq's even though we will
 *	only return autodetect irq numbers - just so that we reset
 *	them all to a known state.
 */
unsigned int probe_irq_mask(unsigned long val)
{
	unsigned int mask;
	int i;

	mask = 0;
	for (i = 0; i < NR_IRQS; i++) {
119
		struct irq_desc *desc = irq_desc + i;
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127 128 129
		unsigned int status;

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

		if (status & IRQ_AUTODETECT) {
			if (i < 16 && !(status & IRQ_WAITING))
				mask |= 1 << i;

			desc->status = status & ~IRQ_AUTODETECT;
130
			desc->chip->shutdown(i);
L
Linus Torvalds 已提交
131 132 133
		}
		spin_unlock_irq(&desc->lock);
	}
134
	mutex_unlock(&probing_active);
L
Linus Torvalds 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

	return mask & val;
}
EXPORT_SYMBOL(probe_irq_mask);

/**
 *	probe_irq_off	- end an interrupt autodetect
 *	@val: mask of potential interrupts (unused)
 *
 *	Scans the unused interrupt lines and returns the line which
 *	appears to have triggered the interrupt. If no interrupt was
 *	found then zero is returned. If more than one interrupt is
 *	found then minus the first candidate is returned to indicate
 *	their is doubt.
 *
 *	The interrupt probe logic state is returned to its previous
 *	value.
 *
 *	BUGS: When used in a module (which arguably shouldn't happen)
 *	nothing prevents two IRQ probe callers from overlapping. The
 *	results of this are non-optimal.
 */
int probe_irq_off(unsigned long val)
{
	int i, irq_found = 0, nr_irqs = 0;

	for (i = 0; i < NR_IRQS; i++) {
162
		struct irq_desc *desc = irq_desc + i;
L
Linus Torvalds 已提交
163 164 165 166 167 168 169 170 171 172 173 174
		unsigned int status;

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

		if (status & IRQ_AUTODETECT) {
			if (!(status & IRQ_WAITING)) {
				if (!nr_irqs)
					irq_found = i;
				nr_irqs++;
			}
			desc->status = status & ~IRQ_AUTODETECT;
175
			desc->chip->shutdown(i);
L
Linus Torvalds 已提交
176 177 178
		}
		spin_unlock_irq(&desc->lock);
	}
179
	mutex_unlock(&probing_active);
L
Linus Torvalds 已提交
180 181 182

	if (nr_irqs > 1)
		irq_found = -irq_found;
183

L
Linus Torvalds 已提交
184 185 186 187
	return irq_found;
}
EXPORT_SYMBOL(probe_irq_off);