329 lines
8.8 KiB
C
329 lines
8.8 KiB
C
/* $NetBSD: intr.c,v 1.19 2009/07/08 12:23:09 tsutsui Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1996 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Adam Glass, Gordon W. Ross, Jason R. Thorpe, and Leo Weppelman.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.19 2009/07/08 12:23:09 tsutsui Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/vmmeter.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/device.h>
|
|
#include <sys/cpu.h>
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#include <machine/intr.h>
|
|
|
|
#define AVEC_MIN 1
|
|
#define AVEC_MAX 7
|
|
#define AVEC_LOC 25
|
|
#define UVEC_MIN 0
|
|
#define UVEC_MAX 191
|
|
#define UVEC_LOC 64
|
|
|
|
typedef LIST_HEAD(, intrhand) ih_list_t;
|
|
ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1];
|
|
ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1];
|
|
int idepth;
|
|
volatile int ssir;
|
|
|
|
void
|
|
intr_init(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) {
|
|
LIST_INIT(&autovec_list[i]);
|
|
}
|
|
for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) {
|
|
LIST_INIT(&uservec_list[i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Establish an interrupt vector.
|
|
* - vector
|
|
* The vector numer the interrupt should be hooked on. It can either
|
|
* be an auto-vector or a user-vector.
|
|
* - type
|
|
* A bit-wise of:
|
|
* - AUTO_VEC (mutually exclusive with USER_VEC)
|
|
* Attach to one of the 7 auto vectors
|
|
* - USER_VEC (mutually exclusive with AUTO_VEC)
|
|
* Attach to one of the 192 user vectors
|
|
* - FAST_VEC
|
|
* The interrupt function 'ih_fun' will be
|
|
* put into the 'real' interrupt table. This
|
|
* means:
|
|
* - This vector can't be shared
|
|
* - 'ih_fun' must save registers
|
|
* - 'ih_fun' must do it's own interrupt accounting
|
|
* - The argument to 'ih_fun' is a standard
|
|
* interrupt frame.
|
|
* - ARG_CLOCKRAME
|
|
* The 'ih_fun' function will be called with
|
|
* a standard clock-frame instead of 'ih_arg'.
|
|
*
|
|
* - pri
|
|
* When multiple interrupts are established on the same vector,
|
|
* interrupts with the highest priority will be called first. The
|
|
* basic ordering is the order of establishment.
|
|
* - ih_fun
|
|
* The interrupt function to be called
|
|
* - ih_arg
|
|
* The argument given to 'ih_fun' when ARG_CLOCKFRAME is not
|
|
* specified.
|
|
*/
|
|
|
|
struct intrhand *
|
|
intr_establish(int vector, int type, int pri, hw_ifun_t ih_fun, void *ih_arg)
|
|
{
|
|
struct intrhand *ih, *cur_vec;
|
|
ih_list_t *vec_list;
|
|
u_long *hard_vec;
|
|
int s;
|
|
|
|
/* no point in sleeping unless someone can free memory. */
|
|
ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
if (ih == NULL)
|
|
panic("intr_establish: can't malloc handler info");
|
|
|
|
/*
|
|
* Initialize vector info
|
|
*/
|
|
ih->ih_fun = ih_fun;
|
|
ih->ih_arg = ih_arg;
|
|
ih->ih_type = type;
|
|
ih->ih_pri = pri;
|
|
ih->ih_vector = vector;
|
|
|
|
/*
|
|
* Do some validity checking on the 'vector' argument and determine
|
|
* vector list this interrupt should be on.
|
|
*/
|
|
switch(type & (AUTO_VEC|USER_VEC)) {
|
|
case AUTO_VEC:
|
|
if (vector < AVEC_MIN || vector > AVEC_MAX)
|
|
return (NULL);
|
|
vec_list = &autovec_list[vector-1];
|
|
hard_vec = &autovects[vector-1];
|
|
ih->ih_intrcnt = &intrcnt_auto[vector-1];
|
|
break;
|
|
case USER_VEC:
|
|
if (vector < UVEC_MIN || vector > UVEC_MAX)
|
|
return (NULL);
|
|
vec_list = &uservec_list[vector];
|
|
hard_vec = &uservects[vector];
|
|
ih->ih_intrcnt = &intrcnt_user[vector];
|
|
break;
|
|
default:
|
|
printf("intr_establish: bogus vector type\n");
|
|
free(ih, M_DEVBUF);
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* If the vec_list is empty, we insert ourselves at the head of the
|
|
* list and we re-route the 'hard-vector' to the appropriate handler.
|
|
*/
|
|
if (vec_list->lh_first == NULL) {
|
|
|
|
s = splhigh();
|
|
LIST_INSERT_HEAD(vec_list, ih, ih_link);
|
|
if (type & FAST_VEC)
|
|
*hard_vec = (u_long)ih->ih_fun;
|
|
else if(*hard_vec != (u_long)intr_glue) {
|
|
/*
|
|
* Normally, all settable vectors are already
|
|
* re-routed to the intr_glue() function. The
|
|
* marvelous exeption to these are the HBL/VBL
|
|
* interrupts. They happen *very* often and
|
|
* can't be turned off on the Falcon. So they
|
|
* are normally vectored to an 'rte' instruction.
|
|
*/
|
|
*hard_vec = (u_long)intr_glue;
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return ih;
|
|
}
|
|
|
|
/*
|
|
* Check for FAST_VEC botches
|
|
*/
|
|
cur_vec = vec_list->lh_first;
|
|
if (cur_vec->ih_type & FAST_VEC) {
|
|
free(ih, M_DEVBUF);
|
|
printf("intr_establish: vector cannot be shared\n");
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* We traverse the list and place ourselves after any handlers with
|
|
* our current (or higher) priority level.
|
|
*/
|
|
for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
|
|
cur_vec = cur_vec->ih_link.le_next) {
|
|
if (ih->ih_pri > cur_vec->ih_pri) {
|
|
|
|
s = splhigh();
|
|
LIST_INSERT_BEFORE(cur_vec, ih, ih_link);
|
|
splx(s);
|
|
|
|
return (ih);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We're the least important entry, it seems. We just go
|
|
* on the end.
|
|
*/
|
|
s = splhigh();
|
|
LIST_INSERT_AFTER(cur_vec, ih, ih_link);
|
|
splx(s);
|
|
|
|
return ih;
|
|
}
|
|
|
|
int
|
|
intr_disestablish(struct intrhand *ih)
|
|
{
|
|
ih_list_t *vec_list;
|
|
u_long *hard_vec;
|
|
int vector, s;
|
|
struct intrhand *cur_vec;
|
|
|
|
vector = ih->ih_vector;
|
|
switch(ih->ih_type & (AUTO_VEC|USER_VEC)) {
|
|
case AUTO_VEC:
|
|
if (vector < AVEC_MIN || vector > AVEC_MAX)
|
|
return 0;
|
|
vec_list = &autovec_list[vector-1];
|
|
hard_vec = &autovects[vector-1];
|
|
break;
|
|
case USER_VEC:
|
|
if (vector < UVEC_MIN || vector > UVEC_MAX)
|
|
return 0;
|
|
vec_list = &uservec_list[vector];
|
|
hard_vec = &uservects[vector];
|
|
break;
|
|
default:
|
|
printf("intr_disestablish: bogus vector type\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if the vector is really in the list we think it's in....
|
|
*/
|
|
for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
|
|
cur_vec = cur_vec->ih_link.le_next) {
|
|
if (ih == cur_vec)
|
|
break;
|
|
}
|
|
if (ih != cur_vec) {
|
|
printf("intr_disestablish: 'ih' has inconsistent data\n");
|
|
return 0;
|
|
}
|
|
|
|
s = splhigh();
|
|
LIST_REMOVE(ih, ih_link);
|
|
if ((vec_list->lh_first == NULL) && (ih->ih_type & FAST_VEC))
|
|
*hard_vec = (u_long)intr_glue;
|
|
splx(s);
|
|
|
|
free(ih, M_DEVBUF);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* This is the dispatcher called by the low-level
|
|
* assembly language interrupt-glue routine.
|
|
*/
|
|
void
|
|
intr_dispatch(struct clockframe frame)
|
|
{
|
|
static int unexpected, straycount;
|
|
int vector;
|
|
int handled = 0;
|
|
ih_list_t *vec_list;
|
|
struct intrhand *ih;
|
|
|
|
uvmexp.intrs++;
|
|
vector = (frame.cf_vo & 0xfff) >> 2;
|
|
if (vector < (AVEC_LOC+AVEC_MAX) && vector >= AVEC_LOC)
|
|
vec_list = &autovec_list[vector - AVEC_LOC];
|
|
else if (vector <= (UVEC_LOC+UVEC_MAX) && vector >= UVEC_LOC)
|
|
vec_list = &uservec_list[vector - UVEC_LOC];
|
|
else panic("intr_dispatch: Bogus vector %d", vector);
|
|
|
|
if ((ih = vec_list->lh_first) == NULL) {
|
|
printf("intr_dispatch: vector %d unexpected\n", vector);
|
|
if (++unexpected > 10)
|
|
panic("intr_dispatch: too many unexpected interrupts");
|
|
return;
|
|
}
|
|
ih->ih_intrcnt[0]++;
|
|
|
|
/* Give all the handlers a chance. */
|
|
for ( ; ih != NULL; ih = ih->ih_link.le_next)
|
|
handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME)
|
|
? &frame : ih->ih_arg, frame.cf_sr);
|
|
|
|
if (handled)
|
|
straycount = 0;
|
|
else if (++straycount > 50)
|
|
panic("intr_dispatch: too many stray interrupts");
|
|
else
|
|
printf("intr_dispatch: stray level %d interrupt\n", vector);
|
|
}
|
|
|
|
bool
|
|
cpu_intr_p(void)
|
|
{
|
|
|
|
return idepth != 0;
|
|
}
|
|
|
|
const uint16_t ipl2psl_table[NIPL] = {
|
|
[IPL_NONE] = PSL_S | PSL_IPL0,
|
|
[IPL_SOFTCLOCK] = PSL_S | PSL_IPL1,
|
|
[IPL_SOFTBIO] = PSL_S | PSL_IPL1,
|
|
[IPL_SOFTNET] = PSL_S | PSL_IPL1,
|
|
[IPL_SOFTSERIAL] = PSL_S | PSL_IPL1,
|
|
[IPL_VM] = PSL_S | PSL_IPL4,
|
|
[IPL_SCHED] = PSL_S | PSL_IPL6,
|
|
[IPL_HIGH] = PSL_S | PSL_IPL7,
|
|
};
|