Home | History | Annotate | Line # | Download | only in atari
intr.c revision 1.23
      1 /*	$NetBSD: intr.c,v 1.22 2010/04/13 11:22:22 tsutsui Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1996 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Adam Glass, Gordon W. Ross, Jason R. Thorpe, and Leo Weppelman.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.22 2010/04/13 11:22:22 tsutsui Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 #include <sys/malloc.h>
     39 #include <sys/vmmeter.h>
     40 #include <sys/queue.h>
     41 #include <sys/device.h>
     42 #include <sys/cpu.h>
     43 
     44 #include <machine/intr.h>
     45 
     46 #define	AVEC_MIN	1
     47 #define	AVEC_MAX	7
     48 #define	AVEC_LOC	25
     49 #define	UVEC_MIN	0
     50 #define	UVEC_MAX	191
     51 #define	UVEC_LOC	64
     52 
     53 typedef LIST_HEAD(, intrhand) ih_list_t;
     54 ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1];
     55 ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1];
     56 int idepth;
     57 volatile int ssir;
     58 
     59 void
     60 intr_init(void)
     61 {
     62 	int i;
     63 
     64 	for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) {
     65 		LIST_INIT(&autovec_list[i]);
     66 	}
     67 	for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) {
     68 		LIST_INIT(&uservec_list[i]);
     69 	}
     70 }
     71 
     72 /*
     73  * Establish an interrupt vector.
     74  *   - vector
     75  *	The vector numer the interrupt should be hooked on. It can either
     76  *	be an auto-vector or a user-vector.
     77  *   - type
     78  *	A bit-wise of:
     79  *		- AUTO_VEC (mutually exclusive with USER_VEC)
     80  *			Attach to one of the 7 auto vectors
     81  *		- USER_VEC (mutually exclusive with AUTO_VEC)
     82  *			Attach to one of the 192 user vectors
     83  *		- FAST_VEC
     84  *			The interrupt function 'ih_fun' will be
     85  *			put into the 'real' interrupt table. This
     86  *			means:
     87  *				- This vector can't be shared
     88  *				- 'ih_fun' must save registers
     89  *				- 'ih_fun' must do it's own interrupt accounting
     90  *				- The argument to 'ih_fun' is a standard
     91  *				  interrupt frame.
     92  *		- ARG_CLOCKRAME
     93  *			The 'ih_fun' function will be called with
     94  *			a standard clock-frame instead of 'ih_arg'.
     95  *
     96  *   - pri
     97  *	When multiple interrupts are established on the same vector,
     98  *	interrupts with the highest priority will be called first. The
     99  *	basic ordering is the order of establishment.
    100  *   - ih_fun
    101  *	The interrupt function to be called
    102  *   - ih_arg
    103  *	The argument given to 'ih_fun' when ARG_CLOCKFRAME is not
    104  *	specified.
    105  */
    106 
    107 struct intrhand *
    108 intr_establish(int vector, int type, int pri, hw_ifun_t ih_fun, void *ih_arg)
    109 {
    110 	struct intrhand	*ih, *cur_vec;
    111 	ih_list_t	*vec_list;
    112 	u_long		*hard_vec;
    113 	int		s;
    114 
    115 	/* no point in sleeping unless someone can free memory. */
    116 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
    117 	if (ih == NULL)
    118 		panic("intr_establish: can't malloc handler info");
    119 
    120 	/*
    121 	 * Initialize vector info
    122 	 */
    123 	ih->ih_fun    = ih_fun;
    124 	ih->ih_arg    = ih_arg;
    125 	ih->ih_type   = type;
    126 	ih->ih_pri    = pri;
    127 	ih->ih_vector = vector;
    128 
    129 	/*
    130 	 * Do some validity checking on the 'vector' argument and determine
    131 	 * vector list this interrupt should be on.
    132 	 */
    133 	switch (type & (AUTO_VEC|USER_VEC)) {
    134 	case AUTO_VEC:
    135 		if (vector < AVEC_MIN || vector > AVEC_MAX)
    136 			return NULL;
    137 		vec_list = &autovec_list[vector-1];
    138 		hard_vec = &autovects[vector-1];
    139 		ih->ih_intrcnt = &intrcnt_auto[vector-1];
    140 		break;
    141 	case USER_VEC:
    142 		if (vector < UVEC_MIN || vector > UVEC_MAX)
    143 			return NULL;
    144 		vec_list = &uservec_list[vector];
    145 		hard_vec = &uservects[vector];
    146 		ih->ih_intrcnt = &intrcnt_user[vector];
    147 		break;
    148 	default:
    149 		printf("intr_establish: bogus vector type\n");
    150 		free(ih, M_DEVBUF);
    151 		return NULL;
    152 	}
    153 
    154 	/*
    155 	 * If the vec_list is empty, we insert ourselves at the head of the
    156 	 * list and we re-route the 'hard-vector' to the appropriate handler.
    157 	 */
    158 	if (vec_list->lh_first == NULL) {
    159 
    160 		s = splhigh();
    161 		LIST_INSERT_HEAD(vec_list, ih, ih_link);
    162 		if (type & FAST_VEC)
    163 			*hard_vec = (u_long)ih->ih_fun;
    164 		else if (*hard_vec != (u_long)intr_glue) {
    165 			/*
    166 			 * Normally, all settable vectors are already
    167 			 * re-routed to the intr_glue() function. The
    168 			 * marvelous exception to these are the HBL/VBL
    169 			 * interrupts. They happen *very* often and
    170 			 * can't be turned off on the Falcon. So they
    171 			 * are normally vectored to an 'rte' instruction.
    172 			 */
    173 			*hard_vec = (u_long)intr_glue;
    174 		}
    175 
    176 		splx(s);
    177 
    178 		return ih;
    179 	}
    180 
    181 	/*
    182 	 * Check for FAST_VEC botches
    183 	 */
    184 	cur_vec = vec_list->lh_first;
    185 	if (cur_vec->ih_type & FAST_VEC) {
    186 		free(ih, M_DEVBUF);
    187 		printf("intr_establish: vector cannot be shared\n");
    188 		return NULL;
    189 	}
    190 
    191 	/*
    192 	 * We traverse the list and place ourselves after any handlers with
    193 	 * our current (or higher) priority level.
    194 	 */
    195 	for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
    196 	    cur_vec = cur_vec->ih_link.le_next) {
    197 		if (ih->ih_pri > cur_vec->ih_pri) {
    198 
    199 			s = splhigh();
    200 			LIST_INSERT_BEFORE(cur_vec, ih, ih_link);
    201 			splx(s);
    202 
    203 			return ih;
    204 		}
    205 	}
    206 
    207 	/*
    208 	 * We're the least important entry, it seems.  We just go
    209 	 * on the end.
    210 	 */
    211 	s = splhigh();
    212 	LIST_INSERT_AFTER(cur_vec, ih, ih_link);
    213 	splx(s);
    214 
    215 	return ih;
    216 }
    217 
    218 int
    219 intr_disestablish(struct intrhand *ih)
    220 {
    221 	ih_list_t	*vec_list;
    222 	u_long		*hard_vec;
    223 	int		vector, s;
    224 	struct intrhand	*cur_vec;
    225 
    226 	vector = ih->ih_vector;
    227 	switch (ih->ih_type & (AUTO_VEC|USER_VEC)) {
    228 	case AUTO_VEC:
    229 		if (vector < AVEC_MIN || vector > AVEC_MAX)
    230 			return 0;
    231 		vec_list = &autovec_list[vector-1];
    232 		hard_vec = &autovects[vector-1];
    233 		break;
    234 	case USER_VEC:
    235 		if (vector < UVEC_MIN || vector > UVEC_MAX)
    236 			return 0;
    237 		vec_list = &uservec_list[vector];
    238 		hard_vec = &uservects[vector];
    239 		break;
    240 	default:
    241 		printf("intr_disestablish: bogus vector type\n");
    242 		return 0;
    243 	}
    244 
    245 	/*
    246 	 * Check if the vector is really in the list we think it's in....
    247 	 */
    248 	for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
    249 	    cur_vec = cur_vec->ih_link.le_next) {
    250 		if (ih == cur_vec)
    251 			break;
    252 	}
    253 	if (ih != cur_vec) {
    254 		printf("intr_disestablish: 'ih' has inconsistent data\n");
    255 		return 0;
    256 	}
    257 
    258 	s = splhigh();
    259 	LIST_REMOVE(ih, ih_link);
    260 	if ((vec_list->lh_first == NULL) && (ih->ih_type & FAST_VEC))
    261 		*hard_vec = (u_long)intr_glue;
    262 	splx(s);
    263 
    264 	free(ih, M_DEVBUF);
    265 	return 1;
    266 }
    267 
    268 /*
    269  * This is the dispatcher called by the low-level
    270  * assembly language interrupt-glue routine.
    271  */
    272 void
    273 intr_dispatch(struct clockframe frame)
    274 {
    275 	static int	unexpected, straycount;
    276 	int		vector;
    277 	int		handled = 0;
    278 	ih_list_t	*vec_list;
    279 	struct intrhand	*ih;
    280 
    281 	curcpu()->ci_data.cpu_nintr++;
    282 	vector = (frame.cf_vo & 0xfff) >> 2;
    283 	if (vector < (AVEC_LOC+AVEC_MAX) && vector >= AVEC_LOC)
    284 		vec_list = &autovec_list[vector - AVEC_LOC];
    285 	else if (vector <= (UVEC_LOC+UVEC_MAX) && vector >= UVEC_LOC)
    286 		vec_list = &uservec_list[vector - UVEC_LOC];
    287 	else
    288 		panic("intr_dispatch: Bogus vector %d", vector);
    289 
    290 	if ((ih = vec_list->lh_first) == NULL) {
    291 		printf("intr_dispatch: vector %d unexpected\n", vector);
    292 		if (++unexpected > 10)
    293 			panic("intr_dispatch: too many unexpected interrupts");
    294 		return;
    295 	}
    296 	ih->ih_intrcnt[0]++;
    297 
    298 	/* Give all the handlers a chance. */
    299 	for (; ih != NULL; ih = ih->ih_link.le_next)
    300 		handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME) ?
    301 		    &frame : ih->ih_arg, frame.cf_sr);
    302 
    303 	if (handled)
    304 		straycount = 0;
    305 	else if (++straycount > 50)
    306 		panic("intr_dispatch: too many stray interrupts");
    307 	else
    308 		printf("intr_dispatch: stray level %d interrupt\n", vector);
    309 }
    310 
    311 bool
    312 cpu_intr_p(void)
    313 {
    314 
    315 	return idepth != 0;
    316 }
    317 
    318 const uint16_t ipl2psl_table[NIPL] = {
    319 	[IPL_NONE]       = PSL_S | PSL_IPL0,
    320 	[IPL_SOFTCLOCK]  = PSL_S | PSL_IPL1,
    321 	[IPL_SOFTBIO]    = PSL_S | PSL_IPL1,
    322 	[IPL_SOFTNET]    = PSL_S | PSL_IPL1,
    323 	[IPL_SOFTSERIAL] = PSL_S | PSL_IPL1,
    324 	[IPL_VM]         = PSL_S | PSL_IPL4,
    325 	[IPL_SCHED]      = PSL_S | PSL_IPL6,
    326 	[IPL_HIGH]       = PSL_S | PSL_IPL7,
    327 };
    328