• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/kernel/irq/
1/* Modified by Broadcom Corp. Portions Copyright (c) Broadcom Corp, 2012. */
2/*
3 * linux/kernel/irq/spurious.c
4 *
5 * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
6 *
7 * This file contains spurious interrupt handling.
8 */
9
10#include <linux/jiffies.h>
11#include <linux/irq.h>
12#include <linux/module.h>
13#include <linux/kallsyms.h>
14#include <linux/interrupt.h>
15#include <linux/moduleparam.h>
16#include <linux/timer.h>
17
18#if defined(CONFIG_BUZZZ)
19#include <asm/buzzz.h>
20#endif	/*  CONFIG_BUZZZ */
21
22static int irqfixup __read_mostly;
23
24#define POLL_SPURIOUS_IRQ_INTERVAL (HZ/10)
25static void poll_spurious_irqs(unsigned long dummy);
26static DEFINE_TIMER(poll_spurious_irq_timer, poll_spurious_irqs, 0, 0);
27
28/*
29 * Recovery handler for misrouted interrupts.
30 */
31static int try_one_irq(int irq, struct irq_desc *desc)
32{
33	struct irqaction *action;
34	int ok = 0, work = 0;
35
36#if defined(BUZZZ_KEVT_LVL) && (BUZZZ_KEVT_LVL >= 1)
37	buzzz_kevt_log1(BUZZZ_KEVT_ID_IRQ_MISROUTED, irq);
38#endif	/* BUZZZ_KEVT_LVL */
39
40	raw_spin_lock(&desc->lock);
41	/* Already running on another processor */
42	if (desc->status & IRQ_INPROGRESS) {
43		/*
44		 * Already running: If it is shared get the other
45		 * CPU to go looking for our mystery interrupt too
46		 */
47		if (desc->action && (desc->action->flags & IRQF_SHARED))
48			desc->status |= IRQ_PENDING;
49		raw_spin_unlock(&desc->lock);
50		return ok;
51	}
52	/* Honour the normal IRQ locking */
53	desc->status |= IRQ_INPROGRESS;
54	action = desc->action;
55	raw_spin_unlock(&desc->lock);
56
57	while (action) {
58		/* Only shared IRQ handlers are safe to call */
59		if (action->flags & IRQF_SHARED) {
60			if (action->handler(irq, action->dev_id) ==
61				IRQ_HANDLED)
62				ok = 1;
63		}
64		action = action->next;
65	}
66	local_irq_disable();
67	/* Now clean up the flags */
68	raw_spin_lock(&desc->lock);
69	action = desc->action;
70
71	/*
72	 * While we were looking for a fixup someone queued a real
73	 * IRQ clashing with our walk:
74	 */
75	while ((desc->status & IRQ_PENDING) && action) {
76		/*
77		 * Perform real IRQ processing for the IRQ we deferred
78		 */
79		work = 1;
80		raw_spin_unlock(&desc->lock);
81		handle_IRQ_event(irq, action);
82		raw_spin_lock(&desc->lock);
83		desc->status &= ~IRQ_PENDING;
84	}
85	desc->status &= ~IRQ_INPROGRESS;
86	/*
87	 * If we did actual work for the real IRQ line we must let the
88	 * IRQ controller clean up too
89	 */
90	if (work && desc->chip && desc->chip->end)
91		desc->chip->end(irq);
92	raw_spin_unlock(&desc->lock);
93
94	return ok;
95}
96
97static int misrouted_irq(int irq)
98{
99	struct irq_desc *desc;
100	int i, ok = 0;
101
102	for_each_irq_desc(i, desc) {
103		if (!i)
104			 continue;
105
106		if (i == irq)	/* Already tried */
107			continue;
108
109		if (try_one_irq(i, desc))
110			ok = 1;
111	}
112	/* So the caller can adjust the irq error counts */
113	return ok;
114}
115
116static void poll_spurious_irqs(unsigned long dummy)
117{
118	struct irq_desc *desc;
119	int i;
120
121	for_each_irq_desc(i, desc) {
122		unsigned int status;
123
124		if (!i)
125			 continue;
126
127		/* Racy but it doesn't matter */
128		status = desc->status;
129		barrier();
130		if (!(status & IRQ_SPURIOUS_DISABLED))
131			continue;
132
133		local_irq_disable();
134		try_one_irq(i, desc);
135		local_irq_enable();
136	}
137
138	mod_timer(&poll_spurious_irq_timer,
139		  jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
140}
141
142/*
143 * If 99,900 of the previous 100,000 interrupts have not been handled
144 * then assume that the IRQ is stuck in some manner. Drop a diagnostic
145 * and try to turn the IRQ off.
146 *
147 * (The other 100-of-100,000 interrupts may have been a correctly
148 *  functioning device sharing an IRQ with the failing one)
149 *
150 * Called under desc->lock
151 */
152
153static void
154__report_bad_irq(unsigned int irq, struct irq_desc *desc,
155		 irqreturn_t action_ret)
156{
157	struct irqaction *action;
158
159	if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) {
160		printk(KERN_ERR "irq event %d: bogus return value %x\n",
161				irq, action_ret);
162	} else {
163		printk(KERN_ERR "irq %d: nobody cared (try booting with "
164				"the \"irqpoll\" option)\n", irq);
165	}
166	dump_stack();
167	printk(KERN_ERR "handlers:\n");
168
169	action = desc->action;
170	while (action) {
171		printk(KERN_ERR "[<%p>]", action->handler);
172		print_symbol(" (%s)",
173			(unsigned long)action->handler);
174		printk("\n");
175		action = action->next;
176	}
177}
178
179static void
180report_bad_irq(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret)
181{
182	static int count = 100;
183
184	if (count > 0) {
185		count--;
186		__report_bad_irq(irq, desc, action_ret);
187	}
188}
189
190static inline int
191try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
192		  irqreturn_t action_ret)
193{
194	struct irqaction *action;
195
196	if (!irqfixup)
197		return 0;
198
199	/* We didn't actually handle the IRQ - see if it was misrouted? */
200	if (action_ret == IRQ_NONE)
201		return 1;
202
203	/*
204	 * But for 'irqfixup == 2' we also do it for handled interrupts if
205	 * they are marked as IRQF_IRQPOLL (or for irq zero, which is the
206	 * traditional PC timer interrupt.. Legacy)
207	 */
208	if (irqfixup < 2)
209		return 0;
210
211	if (!irq)
212		return 1;
213
214	/*
215	 * Since we don't get the descriptor lock, "action" can
216	 * change under us.  We don't really care, but we don't
217	 * want to follow a NULL pointer. So tell the compiler to
218	 * just load it once by using a barrier.
219	 */
220	action = desc->action;
221	barrier();
222	return action && (action->flags & IRQF_IRQPOLL);
223}
224
225void note_interrupt(unsigned int irq, struct irq_desc *desc,
226		    irqreturn_t action_ret)
227{
228	if (unlikely(action_ret != IRQ_HANDLED)) {
229		/*
230		 * If we are seeing only the odd spurious IRQ caused by
231		 * bus asynchronicity then don't eventually trigger an error,
232		 * otherwise the counter becomes a doomsday timer for otherwise
233		 * working systems
234		 */
235		if (time_after(jiffies, desc->last_unhandled + HZ/10))
236			desc->irqs_unhandled = 1;
237		else
238			desc->irqs_unhandled++;
239		desc->last_unhandled = jiffies;
240		if (unlikely(action_ret != IRQ_NONE))
241			report_bad_irq(irq, desc, action_ret);
242	}
243
244	if (unlikely(try_misrouted_irq(irq, desc, action_ret))) {
245		int ok = misrouted_irq(irq);
246		if (action_ret == IRQ_NONE)
247			desc->irqs_unhandled -= ok;
248	}
249
250	desc->irq_count++;
251	if (likely(desc->irq_count < 100000))
252		return;
253
254	desc->irq_count = 0;
255	if (unlikely(desc->irqs_unhandled > 99900)) {
256		/*
257		 * The interrupt is stuck
258		 */
259		__report_bad_irq(irq, desc, action_ret);
260		/*
261		 * Now kill the IRQ
262		 */
263		printk(KERN_EMERG "Disabling IRQ #%d\n", irq);
264		desc->status |= IRQ_DISABLED | IRQ_SPURIOUS_DISABLED;
265		desc->depth++;
266		desc->chip->disable(irq);
267
268		mod_timer(&poll_spurious_irq_timer,
269			  jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
270	}
271	desc->irqs_unhandled = 0;
272}
273
274int noirqdebug __read_mostly;
275
276int noirqdebug_setup(char *str)
277{
278	noirqdebug = 1;
279	printk(KERN_INFO "IRQ lockup detection disabled\n");
280
281	return 1;
282}
283
284__setup("noirqdebug", noirqdebug_setup);
285module_param(noirqdebug, bool, 0644);
286MODULE_PARM_DESC(noirqdebug, "Disable irq lockup detection when true");
287
288static int __init irqfixup_setup(char *str)
289{
290	irqfixup = 1;
291	printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n");
292	printk(KERN_WARNING "This may impact system performance.\n");
293
294	return 1;
295}
296
297__setup("irqfixup", irqfixup_setup);
298module_param(irqfixup, int, 0644);
299
300static int __init irqpoll_setup(char *str)
301{
302	irqfixup = 2;
303	printk(KERN_WARNING "Misrouted IRQ fixup and polling support "
304				"enabled\n");
305	printk(KERN_WARNING "This may significantly impact system "
306				"performance\n");
307	return 1;
308}
309
310__setup("irqpoll", irqpoll_setup);
311