autoprobe.c 4.5 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);
T
Thomas Gleixner 已提交
43 44 45 46 47 48 49
		if (!desc->action && !(desc->status & IRQ_NOPROBE)) {
			/*
			 * Some chips need to know about probing in
			 * progress:
			 */
			if (desc->chip->set_type)
				desc->chip->set_type(i, IRQ_TYPE_PROBE);
50
			desc->chip->startup(i);
T
Thomas Gleixner 已提交
51
		}
L
Linus Torvalds 已提交
52 53 54 55
		spin_unlock_irq(&desc->lock);
	}

	/* Wait for longstanding interrupts to trigger. */
56
	msleep(20);
L
Linus Torvalds 已提交
57 58 59 60 61 62 63 64 65 66

	/*
	 * 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);
67
		if (!desc->action && !(desc->status & IRQ_NOPROBE)) {
L
Linus Torvalds 已提交
68
			desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
69
			if (desc->chip->startup(i))
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77
				desc->status |= IRQ_PENDING;
		}
		spin_unlock_irq(&desc->lock);
	}

	/*
	 * Wait for spurious interrupts to trigger
	 */
78
	msleep(100);
L
Linus Torvalds 已提交
79 80 81 82

	/*
	 * Now filter out any obviously spurious interrupts
	 */
83
	mask = 0;
L
Linus Torvalds 已提交
84 85 86
	for (i = 0; i < NR_IRQS; i++) {
		unsigned int status;

87
		desc = irq_desc + i;
L
Linus Torvalds 已提交
88 89 90 91 92 93 94
		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;
95
				desc->chip->shutdown(i);
L
Linus Torvalds 已提交
96 97
			} else
				if (i < 32)
98
					mask |= 1 << i;
L
Linus Torvalds 已提交
99 100 101 102
		}
		spin_unlock_irq(&desc->lock);
	}

103
	return mask;
L
Linus Torvalds 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
}
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++) {
126
		struct irq_desc *desc = irq_desc + i;
L
Linus Torvalds 已提交
127 128 129 130 131 132 133 134 135 136
		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;
137
			desc->chip->shutdown(i);
L
Linus Torvalds 已提交
138 139 140
		}
		spin_unlock_irq(&desc->lock);
	}
141
	mutex_unlock(&probing_active);
L
Linus Torvalds 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

	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++) {
169
		struct irq_desc *desc = irq_desc + i;
L
Linus Torvalds 已提交
170 171 172 173 174 175 176 177 178 179 180 181
		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;
182
			desc->chip->shutdown(i);
L
Linus Torvalds 已提交
183 184 185
		}
		spin_unlock_irq(&desc->lock);
	}
186
	mutex_unlock(&probing_active);
L
Linus Torvalds 已提交
187 188 189

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

L
Linus Torvalds 已提交
191 192 193 194
	return irq_found;
}
EXPORT_SYMBOL(probe_irq_off);