1/* $NetBSD: shared_intr.c,v 1.20 2009/03/14 14:45:52 dsl Exp $ */
2
3/*
4 * Copyright (c) 1996 Carnegie-Mellon University.
5 * All rights reserved.
6 *
7 * Authors: Chris G. Demetriou
8 *
9 * Permission to use, copy, modify and distribute this software and
10 * its documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation.
14 *
15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18 *
19 * Carnegie Mellon requests users of this software to return to
20 *
21 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22 *  School of Computer Science
23 *  Carnegie Mellon University
24 *  Pittsburgh PA 15213-3890
25 *
26 * any improvements or extensions that they make and grant Carnegie the
27 * rights to redistribute these changes.
28 */
29
30/*
31 * Common shared-interrupt-line functionality.
32 */
33
34#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
35
36__KERNEL_RCSID(0, "$NetBSD: shared_intr.c,v 1.20 2009/03/14 14:45:52 dsl Exp $");
37
38#include <sys/param.h>
39#include <sys/kernel.h>
40#include <sys/systm.h>
41#include <sys/malloc.h>
42#include <sys/syslog.h>
43#include <sys/queue.h>
44#include <sys/atomic.h>
45#include <sys/intr.h>
46
47static const char *intr_typename(int);
48
49static const char *
50intr_typename(int type)
51{
52
53	switch (type) {
54	case IST_UNUSABLE:
55		return ("disabled");
56	case IST_NONE:
57		return ("none");
58	case IST_PULSE:
59		return ("pulsed");
60	case IST_EDGE:
61		return ("edge-triggered");
62	case IST_LEVEL:
63		return ("level-triggered");
64	}
65	panic("intr_typename: unknown type %d", type);
66}
67
68struct alpha_shared_intr *
69alpha_shared_intr_alloc(unsigned int n, unsigned int namesize)
70{
71	struct alpha_shared_intr *intr;
72	unsigned int i;
73
74	intr = malloc(n * sizeof (struct alpha_shared_intr), M_DEVBUF,
75	    cold ? M_NOWAIT : M_WAITOK);
76	if (intr == NULL)
77		panic("alpha_shared_intr_alloc: couldn't malloc intr");
78
79	for (i = 0; i < n; i++) {
80		TAILQ_INIT(&intr[i].intr_q);
81		intr[i].intr_sharetype = IST_NONE;
82		intr[i].intr_dfltsharetype = IST_NONE;
83		intr[i].intr_nstrays = 0;
84		intr[i].intr_maxstrays = 5;
85		intr[i].intr_private = NULL;
86		if (namesize != 0) {
87			intr[i].intr_string = malloc(namesize, M_DEVBUF,
88			    cold ? M_NOWAIT : M_WAITOK);
89			if (intr[i].intr_string == NULL)
90				panic("alpha_shared_intr_alloc: couldn't "
91				    "malloc intr string");
92		} else
93			intr[i].intr_string = NULL;
94	}
95
96	return (intr);
97}
98
99int
100alpha_shared_intr_dispatch(struct alpha_shared_intr *intr, unsigned int num)
101{
102	struct alpha_shared_intrhand *ih;
103	int rv, handled;
104
105	atomic_add_long(&intr[num].intr_evcnt.ev_count, 1);
106
107	ih = intr[num].intr_q.tqh_first;
108	handled = 0;
109	while (ih != NULL) {
110
111		/*
112		 * The handler returns one of three values:
113		 *   0:	This interrupt wasn't for me.
114		 *   1: This interrupt was for me.
115		 *  -1: This interrupt might have been for me, but I can't say
116		 *      for sure.
117		 */
118		rv = (*ih->ih_fn)(ih->ih_arg);
119
120		handled = handled || (rv != 0);
121		ih = ih->ih_q.tqe_next;
122	}
123
124	return (handled);
125}
126
127void *
128alpha_shared_intr_establish(struct alpha_shared_intr *intr, unsigned int num,
129    int type, int level, int (*fn)(void *), void *arg, const char *basename)
130{
131	struct alpha_shared_intrhand *ih;
132
133	if (intr[num].intr_sharetype == IST_UNUSABLE) {
134		printf("alpha_shared_intr_establish: %s %d: unusable\n",
135		    basename, num);
136		return NULL;
137	}
138
139	/* no point in sleeping unless someone can free memory. */
140	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
141	if (ih == NULL)
142		panic("alpha_shared_intr_establish: can't malloc intrhand");
143
144#ifdef DIAGNOSTIC
145	if (type == IST_NONE)
146		panic("alpha_shared_intr_establish: bogus type");
147#endif
148
149	switch (intr[num].intr_sharetype) {
150	case IST_EDGE:
151	case IST_LEVEL:
152		if (type == intr[num].intr_sharetype)
153			break;
154	case IST_PULSE:
155		if (type != IST_NONE) {
156			if (intr[num].intr_q.tqh_first == NULL) {
157				printf("alpha_shared_intr_establish: %s %d: warning: using %s on %s\n",
158				    basename, num, intr_typename(type),
159				    intr_typename(intr[num].intr_sharetype));
160				type = intr[num].intr_sharetype;
161			} else {
162				panic("alpha_shared_intr_establish: %s %d: can't share %s with %s",
163				    basename, num, intr_typename(type),
164				    intr_typename(intr[num].intr_sharetype));
165			}
166		}
167		break;
168
169	case IST_NONE:
170		/* not currently used; safe */
171		break;
172	}
173
174	ih->ih_intrhead = intr;
175	ih->ih_fn = fn;
176	ih->ih_arg = arg;
177	ih->ih_level = level;
178	ih->ih_num = num;
179
180	intr[num].intr_sharetype = type;
181	TAILQ_INSERT_TAIL(&intr[num].intr_q, ih, ih_q);
182
183	return (ih);
184}
185
186void
187alpha_shared_intr_disestablish(struct alpha_shared_intr *intr, void *cookie,
188    const char *basename)
189{
190	struct alpha_shared_intrhand *ih = cookie;
191	unsigned int num = ih->ih_num;
192
193	/*
194	 * Just remove it from the list and free the entry.  We let
195	 * the caller deal with resetting the share type, if appropriate.
196	 */
197	TAILQ_REMOVE(&intr[num].intr_q, ih, ih_q);
198}
199
200int
201alpha_shared_intr_get_sharetype(struct alpha_shared_intr *intr,
202    unsigned int num)
203{
204
205	return (intr[num].intr_sharetype);
206}
207
208int
209alpha_shared_intr_isactive(struct alpha_shared_intr *intr, unsigned int num)
210{
211
212	return (intr[num].intr_q.tqh_first != NULL);
213}
214
215int
216alpha_shared_intr_firstactive(struct alpha_shared_intr *intr, unsigned int num)
217{
218
219	return (intr[num].intr_q.tqh_first != NULL &&
220		intr[num].intr_q.tqh_first->ih_q.tqe_next == NULL);
221}
222
223void
224alpha_shared_intr_set_dfltsharetype(struct alpha_shared_intr *intr,
225    unsigned int num, int newdfltsharetype)
226{
227
228#ifdef DIAGNOSTIC
229	if (alpha_shared_intr_isactive(intr, num))
230		panic("alpha_shared_intr_set_dfltsharetype on active intr");
231#endif
232
233	intr[num].intr_dfltsharetype = newdfltsharetype;
234	intr[num].intr_sharetype = intr[num].intr_dfltsharetype;
235}
236
237void
238alpha_shared_intr_set_maxstrays(struct alpha_shared_intr *intr,
239    unsigned int num, int newmaxstrays)
240{
241	int s = splhigh();
242	intr[num].intr_maxstrays = newmaxstrays;
243	intr[num].intr_nstrays = 0;
244	splx(s);
245}
246
247void
248alpha_shared_intr_reset_strays(struct alpha_shared_intr *intr,
249    unsigned int num)
250{
251
252	/*
253	 * Don't bother blocking interrupts; this doesn't have to be
254	 * precise, but it does need to be fast.
255	 */
256	intr[num].intr_nstrays = 0;
257}
258
259void
260alpha_shared_intr_stray(struct alpha_shared_intr *intr, unsigned int num,
261    const char *basename)
262{
263
264	intr[num].intr_nstrays++;
265
266	if (intr[num].intr_maxstrays == 0)
267		return;
268
269	if (intr[num].intr_nstrays <= intr[num].intr_maxstrays)
270		log(LOG_ERR, "stray %s %d%s\n", basename, num,
271		    intr[num].intr_nstrays >= intr[num].intr_maxstrays ?
272		      "; stopped logging" : "");
273}
274
275void
276alpha_shared_intr_set_private(struct alpha_shared_intr *intr,
277    unsigned int num, void *v)
278{
279
280	intr[num].intr_private = v;
281}
282
283void *
284alpha_shared_intr_get_private(struct alpha_shared_intr *intr,
285    unsigned int num)
286{
287
288	return (intr[num].intr_private);
289}
290
291struct evcnt *
292alpha_shared_intr_evcnt(struct alpha_shared_intr *intr,
293    unsigned int num)
294{
295
296	return (&intr[num].intr_evcnt);
297}
298
299char *
300alpha_shared_intr_string(struct alpha_shared_intr *intr,
301    unsigned int num)
302{
303
304	return (intr[num].intr_string);
305}
306