intr_machdep.c revision 122572
1121982Sjhb/*-
2121982Sjhb * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3121982Sjhb * All rights reserved.
4121982Sjhb *
5121982Sjhb * Redistribution and use in source and binary forms, with or without
6121982Sjhb * modification, are permitted provided that the following conditions
7121982Sjhb * are met:
8121982Sjhb * 1. Redistributions of source code must retain the above copyright
9121982Sjhb *    notice, this list of conditions and the following disclaimer.
10121982Sjhb * 2. Redistributions in binary form must reproduce the above copyright
11121982Sjhb *    notice, this list of conditions and the following disclaimer in the
12121982Sjhb *    documentation and/or other materials provided with the distribution.
13121982Sjhb * 3. Neither the name of the author nor the names of any co-contributors
14121982Sjhb *    may be used to endorse or promote products derived from this software
15121982Sjhb *    without specific prior written permission.
16121982Sjhb *
17121982Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18121982Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19121982Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20121982Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21121982Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22121982Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23121982Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24121982Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25121982Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26121982Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27121982Sjhb * SUCH DAMAGE.
28121982Sjhb *
29121982Sjhb * $FreeBSD: head/sys/i386/i386/intr_machdep.c 122572 2003-11-12 18:13:57Z jhb $
30121982Sjhb */
31121982Sjhb
32121982Sjhb/*
33121982Sjhb * Machine dependent interrupt code for i386.  For the i386, we have to
34121982Sjhb * deal with different PICs.  Thus, we use the passed in vector to lookup
35121982Sjhb * an interrupt source associated with that vector.  The interrupt source
36121982Sjhb * describes which PIC the source belongs to and includes methods to handle
37121982Sjhb * that source.
38121982Sjhb */
39121982Sjhb
40121982Sjhb#include "opt_ddb.h"
41121982Sjhb
42121982Sjhb#include <sys/param.h>
43121982Sjhb#include <sys/bus.h>
44121982Sjhb#include <sys/interrupt.h>
45121982Sjhb#include <sys/lock.h>
46121982Sjhb#include <sys/ktr.h>
47121982Sjhb#include <sys/kernel.h>
48121982Sjhb#include <sys/mutex.h>
49121982Sjhb#include <sys/proc.h>
50121982Sjhb#include <sys/syslog.h>
51121982Sjhb#include <sys/systm.h>
52122572Sjhb#include <machine/clock.h>
53121982Sjhb#include <machine/intr_machdep.h>
54121982Sjhb#ifdef DDB
55121982Sjhb#include <ddb/ddb.h>
56121982Sjhb#endif
57121982Sjhb
58121982Sjhb#define	MAX_STRAY_LOG	5
59121982Sjhb
60121982Sjhbtypedef void (*mask_fn)(int vector);
61121982Sjhb
62121982Sjhbstatic int intrcnt_index;
63121982Sjhbstatic struct intsrc *interrupt_sources[NUM_IO_INTS];
64121982Sjhbstatic struct mtx intr_table_lock;
65121982Sjhb
66121982Sjhbstatic void	intr_init(void *__dummy);
67121982Sjhbstatic void	intrcnt_setname(const char *name, int index);
68121982Sjhbstatic void	intrcnt_updatename(struct intsrc *is);
69121982Sjhbstatic void	intrcnt_register(struct intsrc *is);
70121982Sjhb
71121982Sjhb/*
72121982Sjhb * Register a new interrupt source with the global interrupt system.
73121982Sjhb * The global interrupts need to be disabled when this function is
74121982Sjhb * called.
75121982Sjhb */
76121982Sjhbint
77121982Sjhbintr_register_source(struct intsrc *isrc)
78121982Sjhb{
79121982Sjhb	int error, vector;
80121982Sjhb
81121982Sjhb	vector = isrc->is_pic->pic_vector(isrc);
82121982Sjhb	if (interrupt_sources[vector] != NULL)
83121982Sjhb		return (EEXIST);
84121982Sjhb	/*
85121982Sjhb	 * Ok, so this is kind of a nasty optimization that only works
86121982Sjhb	 * because sizeof(int) == sizeof(void *) on i386.  If we passed
87121982Sjhb	 * in the actual vector to ithread_create and then used wrapper
88121982Sjhb	 * functions for disable_intsrc and enable_intsrc, then we'd
89121982Sjhb	 * have to go lookup in the table everytime we enabled/disabled
90121982Sjhb	 * the interrupt source.  That involves looking at a lock, etc.
91121982Sjhb	 * and is just ugly.  Instead, we cast the pointer to the intsrc
92121982Sjhb	 * to an int (yuck) and pass in the actual PIC methods meaning
93121982Sjhb	 * that when we enable/disable an interrupt we call the PIC
94121982Sjhb	 * methods directly.
95121982Sjhb	 */
96121982Sjhb	error = ithread_create(&isrc->is_ithread, (intptr_t)isrc, 0,
97121982Sjhb	    (mask_fn)isrc->is_pic->pic_disable_source,
98121982Sjhb	    (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector);
99121982Sjhb	if (error)
100121982Sjhb		return (error);
101121982Sjhb	mtx_lock_spin(&intr_table_lock);
102121982Sjhb	if (interrupt_sources[vector] != NULL) {
103121982Sjhb		mtx_unlock_spin(&intr_table_lock);
104121982Sjhb		ithread_destroy(isrc->is_ithread);
105121982Sjhb		return (EEXIST);
106121982Sjhb	}
107121982Sjhb	intrcnt_register(isrc);
108121982Sjhb	interrupt_sources[vector] = isrc;
109121982Sjhb	mtx_unlock_spin(&intr_table_lock);
110121982Sjhb	return (0);
111121982Sjhb}
112121982Sjhb
113121982Sjhbstruct intsrc *
114121982Sjhbintr_lookup_source(int vector)
115121982Sjhb{
116121982Sjhb
117121982Sjhb	return (interrupt_sources[vector]);
118121982Sjhb}
119121982Sjhb
120121982Sjhbint
121121982Sjhbintr_add_handler(const char *name, int vector, driver_intr_t handler,
122121982Sjhb    void *arg, enum intr_type flags, void **cookiep)
123121982Sjhb{
124121982Sjhb	struct intsrc *isrc;
125121982Sjhb	int error;
126121982Sjhb
127121982Sjhb	isrc = intr_lookup_source(vector);
128121982Sjhb	if (isrc == NULL)
129121982Sjhb		return (EINVAL);
130121982Sjhb	error = ithread_add_handler(isrc->is_ithread, name, handler, arg,
131121982Sjhb	    ithread_priority(flags), flags, cookiep);
132121982Sjhb	if (error == 0) {
133121982Sjhb		intrcnt_updatename(isrc);
134121982Sjhb		isrc->is_pic->pic_enable_intr(isrc);
135121982Sjhb		isrc->is_pic->pic_enable_source(isrc);
136121982Sjhb	}
137121982Sjhb	return (error);
138121982Sjhb}
139121982Sjhb
140121982Sjhbint
141121982Sjhbintr_remove_handler(void *cookie)
142121982Sjhb{
143121982Sjhb	int error;
144121982Sjhb
145121982Sjhb	error = ithread_remove_handler(cookie);
146121982Sjhb#ifdef XXX
147121982Sjhb	if (error == 0)
148121982Sjhb		intrcnt_updatename(/* XXX */);
149121982Sjhb#endif
150121982Sjhb	return (error);
151121982Sjhb}
152121982Sjhb
153121982Sjhbvoid
154121982Sjhbintr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
155121982Sjhb{
156122572Sjhb	struct thread *td;
157121982Sjhb	struct ithd *it;
158121982Sjhb	struct intrhand *ih;
159121982Sjhb	int error, vector;
160121982Sjhb
161122572Sjhb	td = curthread;
162122572Sjhb	td->td_intr_nesting_level++;
163122572Sjhb
164121982Sjhb	/*
165121982Sjhb	 * We count software interrupts when we process them.  The
166121982Sjhb	 * code here follows previous practice, but there's an
167121982Sjhb	 * argument for counting hardware interrupts when they're
168121982Sjhb	 * processed too.
169121982Sjhb	 */
170121982Sjhb	atomic_add_long(isrc->is_count, 1);
171121982Sjhb	atomic_add_int(&cnt.v_intr, 1);
172121982Sjhb
173122572Sjhb	it = isrc->is_ithread;
174122572Sjhb	ih = TAILQ_FIRST(&it->it_handlers);
175122572Sjhb
176121982Sjhb	/*
177122572Sjhb	 * XXX: We assume that IRQ 0 is only used for the ISA timer
178122572Sjhb	 * device (clk).
179121982Sjhb	 */
180122572Sjhb	vector = isrc->is_pic->pic_vector(isrc);
181122572Sjhb	if (vector == 0)
182122572Sjhb		clkintr_pending = 1;
183122572Sjhb
184121982Sjhb	critical_enter();
185122572Sjhb	if (ih != NULL && ih->ih_flags & IH_FAST) {
186122572Sjhb		/*
187122572Sjhb		 * Execute fast interrupt handlers directly.
188122572Sjhb		 * To support clock handlers, if a handler registers
189122572Sjhb		 * with a NULL argument, then we pass it a pointer to
190122572Sjhb		 * a trapframe as its argument.
191122572Sjhb		 */
192121982Sjhb		TAILQ_FOREACH(ih, &it->it_handlers, ih_next) {
193121982Sjhb			MPASS(ih->ih_flags & IH_FAST);
194121982Sjhb			CTR3(KTR_INTR, "%s: executing handler %p(%p)",
195121982Sjhb			    __func__, ih->ih_handler,
196121982Sjhb			    ih->ih_argument == NULL ? iframe :
197121982Sjhb			    ih->ih_argument);
198121982Sjhb			if (ih->ih_argument == NULL)
199121982Sjhb				ih->ih_handler(iframe);
200121982Sjhb			else
201121982Sjhb				ih->ih_handler(ih->ih_argument);
202121982Sjhb		}
203122572Sjhb		isrc->is_pic->pic_eoi_source(isrc);
204121982Sjhb		error = 0;
205122572Sjhb	} else {
206122572Sjhb		/*
207122572Sjhb		 * For stray and threaded interrupts, we mask and EOI the
208122572Sjhb		 * source.
209122572Sjhb		 */
210122572Sjhb		isrc->is_pic->pic_disable_source(isrc);
211122572Sjhb		isrc->is_pic->pic_eoi_source(isrc);
212122572Sjhb		if (ih == NULL)
213122572Sjhb			error = EINVAL;
214122572Sjhb		else
215122572Sjhb			error = ithread_schedule(it, !cold);
216122572Sjhb	}
217121982Sjhb	critical_exit();
218121982Sjhb	if (error == EINVAL) {
219121982Sjhb		atomic_add_long(isrc->is_straycount, 1);
220121982Sjhb		if (*isrc->is_straycount < MAX_STRAY_LOG)
221121982Sjhb			log(LOG_ERR, "stray irq%d\n", vector);
222121982Sjhb		else if (*isrc->is_straycount == MAX_STRAY_LOG)
223121982Sjhb			log(LOG_CRIT,
224121982Sjhb			    "too many stray irq %d's: not logging anymore\n",
225121982Sjhb			    vector);
226121982Sjhb	}
227122572Sjhb	td->td_intr_nesting_level--;
228121982Sjhb}
229121982Sjhb
230121982Sjhbvoid
231121982Sjhbintr_resume(void)
232121982Sjhb{
233121982Sjhb	struct intsrc **isrc;
234121982Sjhb	int i;
235121982Sjhb
236121982Sjhb	mtx_lock_spin(&intr_table_lock);
237121982Sjhb	for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++)
238121982Sjhb		if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL)
239121982Sjhb			(*isrc)->is_pic->pic_resume(*isrc);
240121982Sjhb	mtx_unlock_spin(&intr_table_lock);
241121982Sjhb}
242121982Sjhb
243121982Sjhbvoid
244121982Sjhbintr_suspend(void)
245121982Sjhb{
246121982Sjhb	struct intsrc **isrc;
247121982Sjhb	int i;
248121982Sjhb
249121982Sjhb	mtx_lock_spin(&intr_table_lock);
250121982Sjhb	for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++)
251121982Sjhb		if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL)
252121982Sjhb			(*isrc)->is_pic->pic_suspend(*isrc);
253121982Sjhb	mtx_unlock_spin(&intr_table_lock);
254121982Sjhb}
255121982Sjhb
256121982Sjhbstatic void
257121982Sjhbintrcnt_setname(const char *name, int index)
258121982Sjhb{
259121982Sjhb
260121982Sjhb	snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
261121982Sjhb	    MAXCOMLEN, name);
262121982Sjhb}
263121982Sjhb
264121982Sjhbstatic void
265121982Sjhbintrcnt_updatename(struct intsrc *is)
266121982Sjhb{
267121982Sjhb
268121982Sjhb	intrcnt_setname(is->is_ithread->it_td->td_proc->p_comm, is->is_index);
269121982Sjhb}
270121982Sjhb
271121982Sjhbstatic void
272121982Sjhbintrcnt_register(struct intsrc *is)
273121982Sjhb{
274121982Sjhb	char straystr[MAXCOMLEN + 1];
275121982Sjhb
276121982Sjhb	/* mtx_assert(&intr_table_lock, MA_OWNED); */
277121982Sjhb	KASSERT(is->is_ithread != NULL, ("%s: isrc with no ithread", __func__));
278121982Sjhb	is->is_index = intrcnt_index;
279121982Sjhb	intrcnt_index += 2;
280121982Sjhb	snprintf(straystr, MAXCOMLEN + 1, "stray irq%d",
281121982Sjhb	    is->is_pic->pic_vector(is));
282121982Sjhb	intrcnt_updatename(is);
283121982Sjhb	is->is_count = &intrcnt[is->is_index];
284121982Sjhb	intrcnt_setname(straystr, is->is_index + 1);
285121982Sjhb	is->is_straycount = &intrcnt[is->is_index + 1];
286121982Sjhb}
287121982Sjhb
288121982Sjhbstatic void
289121982Sjhbintr_init(void *dummy __unused)
290121982Sjhb{
291121982Sjhb
292121982Sjhb	intrcnt_setname("???", 0);
293121982Sjhb	intrcnt_index = 1;
294121982Sjhb	mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN);
295121982Sjhb}
296121982SjhbSYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL)
297121982Sjhb
298121982Sjhb#ifdef DDB
299121982Sjhb/*
300121982Sjhb * Dump data about interrupt handlers
301121982Sjhb */
302121982SjhbDB_SHOW_COMMAND(irqs, db_show_irqs)
303121982Sjhb{
304121982Sjhb	struct intsrc **isrc;
305121982Sjhb	int i, quit, verbose;
306121982Sjhb
307121982Sjhb	quit = 0;
308121982Sjhb	if (strcmp(modif, "v") == 0)
309121982Sjhb		verbose = 1;
310121982Sjhb	else
311121982Sjhb		verbose = 0;
312121982Sjhb	isrc = interrupt_sources;
313121982Sjhb	db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE);
314121982Sjhb	for (i = 0; i < NUM_IO_INTS && !quit; i++, isrc++)
315121982Sjhb		if (*isrc != NULL)
316121982Sjhb			db_dump_ithread((*isrc)->is_ithread, verbose);
317121982Sjhb}
318121982Sjhb#endif
319