0fb6b9a8f8
Rather than an "iointr" routine that decomposes a vector into an IRQ, we maintain a vector table directly, hooking up each "iointr" routine at the correct vector. This also allows us to hook device interrupts up to specific vectors (c.f. Jensen). We can shave even more cycles off, here, and I will, but it requires some changes to the alpha_shared_intr stuff.
294 lines
7.0 KiB
C
294 lines
7.0 KiB
C
/* $NetBSD: shared_intr.c,v 1.16 2001/07/27 00:25:19 thorpej Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1996 Carnegie-Mellon University.
|
|
* All rights reserved.
|
|
*
|
|
* Authors: Chris G. Demetriou
|
|
*
|
|
* Permission to use, copy, modify and distribute this software and
|
|
* its documentation is hereby granted, provided that both the copyright
|
|
* notice and this permission notice appear in all copies of the
|
|
* software, derivative works or modified versions, and any portions
|
|
* thereof, and that both notices appear in supporting documentation.
|
|
*
|
|
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
|
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
|
|
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
|
*
|
|
* Carnegie Mellon requests users of this software to return to
|
|
*
|
|
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
|
* School of Computer Science
|
|
* Carnegie Mellon University
|
|
* Pittsburgh PA 15213-3890
|
|
*
|
|
* any improvements or extensions that they make and grant Carnegie the
|
|
* rights to redistribute these changes.
|
|
*/
|
|
|
|
/*
|
|
* Common shared-interrupt-line functionality.
|
|
*/
|
|
|
|
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
|
|
|
|
__KERNEL_RCSID(0, "$NetBSD: shared_intr.c,v 1.16 2001/07/27 00:25:19 thorpej Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <machine/intr.h>
|
|
|
|
static const char *intr_typename __P((int));
|
|
|
|
static const char *
|
|
intr_typename(int type)
|
|
{
|
|
|
|
switch (type) {
|
|
case IST_UNUSABLE:
|
|
return ("disabled");
|
|
case IST_NONE:
|
|
return ("none");
|
|
case IST_PULSE:
|
|
return ("pulsed");
|
|
case IST_EDGE:
|
|
return ("edge-triggered");
|
|
case IST_LEVEL:
|
|
return ("level-triggered");
|
|
}
|
|
panic("intr_typename: unknown type %d", type);
|
|
}
|
|
|
|
struct alpha_shared_intr *
|
|
alpha_shared_intr_alloc(unsigned int n, unsigned int namesize)
|
|
{
|
|
struct alpha_shared_intr *intr;
|
|
unsigned int i;
|
|
|
|
intr = malloc(n * sizeof (struct alpha_shared_intr), M_DEVBUF,
|
|
cold ? M_NOWAIT : M_WAITOK);
|
|
if (intr == NULL)
|
|
panic("alpha_shared_intr_alloc: couldn't malloc intr");
|
|
|
|
for (i = 0; i < n; i++) {
|
|
TAILQ_INIT(&intr[i].intr_q);
|
|
intr[i].intr_sharetype = IST_NONE;
|
|
intr[i].intr_dfltsharetype = IST_NONE;
|
|
intr[i].intr_nstrays = 0;
|
|
intr[i].intr_maxstrays = 5;
|
|
intr[i].intr_private = NULL;
|
|
if (namesize != 0) {
|
|
intr[i].intr_string = malloc(namesize, M_DEVBUF,
|
|
cold ? M_NOWAIT : M_WAITOK);
|
|
if (intr[i].intr_string == NULL)
|
|
panic("alpha_shared_intr_alloc: couldn't "
|
|
"malloc intr string");
|
|
} else
|
|
intr[i].intr_string = NULL;
|
|
}
|
|
|
|
return (intr);
|
|
}
|
|
|
|
int
|
|
alpha_shared_intr_dispatch(struct alpha_shared_intr *intr, unsigned int num)
|
|
{
|
|
struct alpha_shared_intrhand *ih;
|
|
int rv, handled;
|
|
|
|
atomic_add_ulong(&intr[num].intr_evcnt.ev_count, 1);
|
|
|
|
ih = intr[num].intr_q.tqh_first;
|
|
handled = 0;
|
|
while (ih != NULL) {
|
|
|
|
/*
|
|
* The handler returns one of three values:
|
|
* 0: This interrupt wasn't for me.
|
|
* 1: This interrupt was for me.
|
|
* -1: This interrupt might have been for me, but I can't say
|
|
* for sure.
|
|
*/
|
|
rv = (*ih->ih_fn)(ih->ih_arg);
|
|
|
|
handled = handled || (rv != 0);
|
|
ih = ih->ih_q.tqe_next;
|
|
}
|
|
|
|
return (handled);
|
|
}
|
|
|
|
void *
|
|
alpha_shared_intr_establish(struct alpha_shared_intr *intr, unsigned int num,
|
|
int type, int level, int (*fn)(void *), void *arg, const char *basename)
|
|
{
|
|
struct alpha_shared_intrhand *ih;
|
|
|
|
if (intr[num].intr_sharetype == IST_UNUSABLE) {
|
|
printf("alpha_shared_intr_establish: %s %d: unusable\n",
|
|
basename, num);
|
|
return NULL;
|
|
}
|
|
|
|
/* no point in sleeping unless someone can free memory. */
|
|
ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
if (ih == NULL)
|
|
panic("alpha_shared_intr_establish: can't malloc intrhand");
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (type == IST_NONE)
|
|
panic("alpha_shared_intr_establish: bogus type");
|
|
#endif
|
|
|
|
switch (intr[num].intr_sharetype) {
|
|
case IST_EDGE:
|
|
case IST_LEVEL:
|
|
if (type == intr[num].intr_sharetype)
|
|
break;
|
|
case IST_PULSE:
|
|
if (type != IST_NONE) {
|
|
if (intr[num].intr_q.tqh_first == NULL) {
|
|
printf("alpha_shared_intr_establish: %s %d: warning: using %s on %s\n",
|
|
basename, num, intr_typename(type),
|
|
intr_typename(intr[num].intr_sharetype));
|
|
type = intr[num].intr_sharetype;
|
|
} else {
|
|
panic("alpha_shared_intr_establish: %s %d: can't share %s with %s",
|
|
basename, num, intr_typename(type),
|
|
intr_typename(intr[num].intr_sharetype));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IST_NONE:
|
|
/* not currently used; safe */
|
|
break;
|
|
}
|
|
|
|
ih->ih_intrhead = intr;
|
|
ih->ih_fn = fn;
|
|
ih->ih_arg = arg;
|
|
ih->ih_level = level;
|
|
ih->ih_num = num;
|
|
|
|
intr[num].intr_sharetype = type;
|
|
TAILQ_INSERT_TAIL(&intr[num].intr_q, ih, ih_q);
|
|
|
|
return (ih);
|
|
}
|
|
|
|
void
|
|
alpha_shared_intr_disestablish(struct alpha_shared_intr *intr, void *cookie,
|
|
const char *basename)
|
|
{
|
|
struct alpha_shared_intrhand *ih = cookie;
|
|
unsigned int num = ih->ih_num;
|
|
|
|
/*
|
|
* Just remove it from the list and free the entry. We let
|
|
* the caller deal with resetting the share type, if appropriate.
|
|
*/
|
|
TAILQ_REMOVE(&intr[num].intr_q, ih, ih_q);
|
|
}
|
|
|
|
int
|
|
alpha_shared_intr_get_sharetype(struct alpha_shared_intr *intr,
|
|
unsigned int num)
|
|
{
|
|
|
|
return (intr[num].intr_sharetype);
|
|
}
|
|
|
|
int
|
|
alpha_shared_intr_isactive(struct alpha_shared_intr *intr, unsigned int num)
|
|
{
|
|
|
|
return (intr[num].intr_q.tqh_first != NULL);
|
|
}
|
|
|
|
int
|
|
alpha_shared_intr_firstactive(struct alpha_shared_intr *intr, unsigned int num)
|
|
{
|
|
|
|
return (intr[num].intr_q.tqh_first != NULL &&
|
|
intr[num].intr_q.tqh_first->ih_q.tqe_next == NULL);
|
|
}
|
|
|
|
void
|
|
alpha_shared_intr_set_dfltsharetype(struct alpha_shared_intr *intr,
|
|
unsigned int num, int newdfltsharetype)
|
|
{
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (alpha_shared_intr_isactive(intr, num))
|
|
panic("alpha_shared_intr_set_dfltsharetype on active intr");
|
|
#endif
|
|
|
|
intr[num].intr_dfltsharetype = newdfltsharetype;
|
|
intr[num].intr_sharetype = intr[num].intr_dfltsharetype;
|
|
}
|
|
|
|
void
|
|
alpha_shared_intr_set_maxstrays(struct alpha_shared_intr *intr,
|
|
unsigned int num, int newmaxstrays)
|
|
{
|
|
int s = splhigh();
|
|
intr[num].intr_maxstrays = newmaxstrays;
|
|
intr[num].intr_nstrays = 0;
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
alpha_shared_intr_stray(struct alpha_shared_intr *intr, unsigned int num,
|
|
const char *basename)
|
|
{
|
|
|
|
intr[num].intr_nstrays++;
|
|
|
|
if (intr[num].intr_maxstrays == 0)
|
|
return;
|
|
|
|
if (intr[num].intr_nstrays <= intr[num].intr_maxstrays)
|
|
log(LOG_ERR, "stray %s %d%s\n", basename, num,
|
|
intr[num].intr_nstrays >= intr[num].intr_maxstrays ?
|
|
"; stopped logging" : "");
|
|
}
|
|
|
|
void
|
|
alpha_shared_intr_set_private(struct alpha_shared_intr *intr,
|
|
unsigned int num, void *v)
|
|
{
|
|
|
|
intr[num].intr_private = v;
|
|
}
|
|
|
|
void *
|
|
alpha_shared_intr_get_private(struct alpha_shared_intr *intr,
|
|
unsigned int num)
|
|
{
|
|
|
|
return (intr[num].intr_private);
|
|
}
|
|
|
|
struct evcnt *
|
|
alpha_shared_intr_evcnt(struct alpha_shared_intr *intr,
|
|
unsigned int num)
|
|
{
|
|
|
|
return (&intr[num].intr_evcnt);
|
|
}
|
|
|
|
char *
|
|
alpha_shared_intr_string(struct alpha_shared_intr *intr,
|
|
unsigned int num)
|
|
{
|
|
|
|
return (intr[num].intr_string);
|
|
}
|