xref: /netbsd-src/sys/arch/hp300/dev/frodo.c (revision 220b5c059a84c51ea44107ea8951a57ffaecdc8c)
1 /*	$NetBSD: frodo.c,v 1.8 2001/12/08 04:22:46 gmcgarry Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1997 Michael Smith.  All rights reserved.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63 
64 /*
65  * Support for the "Frodo" (a.k.a. "Apollo Utility") chip found
66  * in HP Apollo 9000/4xx workstations.
67  */
68 
69 #define	_HP300_INTR_H_PRIVATE
70 
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <sys/kernel.h>
74 #include <sys/syslog.h>
75 #include <sys/device.h>
76 
77 #include <machine/cpu.h>
78 #include <machine/intr.h>
79 #include <machine/hp300spu.h>
80 
81 #include <hp300/dev/intiovar.h>
82 
83 #include <hp300/dev/frodoreg.h>
84 #include <hp300/dev/frodovar.h>
85 
86 /*
87  * Description of a Frodo interrupt handler.
88  */
89 struct frodo_interhand {
90 	int	(*ih_fn) __P((void *));
91 	void	*ih_arg;
92 	int	ih_priority;
93 };
94 
95 struct frodo_softc {
96 	struct device	sc_dev;		/* generic device glue */
97 	volatile u_int8_t *sc_regs;	/* register base */
98 	struct frodo_interhand sc_intr[FRODO_NINTR]; /* interrupt handlers */
99 	int		sc_ipl;
100 	void		*sc_ih;		/* out interrupt cookie */
101 	int		sc_refcnt;	/* number of interrupt refs */
102 };
103 
104 int	frodomatch __P((struct device *, struct cfdata *, void *));
105 void	frodoattach __P((struct device *, struct device *, void *));
106 
107 int	frodoprint __P((void *, const char *));
108 int	frodosubmatch __P((struct device *, struct cfdata *, void *));
109 
110 int	frodointr __P((void *));
111 
112 void	frodo_imask __P((struct frodo_softc *, u_int16_t, u_int16_t));
113 
114 struct cfattach frodo_ca = {
115 	sizeof(struct frodo_softc), frodomatch, frodoattach
116 };
117 
118 struct frodo_attach_args frodo_subdevs[] = {
119 	{ "dnkbd",	FRODO_APCI_OFFSET(0),	FRODO_INTR_APCI0 },
120 	{ "apci",	FRODO_APCI_OFFSET(1),	FRODO_INTR_APCI1 },
121 	{ "apci",	FRODO_APCI_OFFSET(2),	FRODO_INTR_APCI2 },
122 	{ "apci",	FRODO_APCI_OFFSET(3),	FRODO_INTR_APCI3 },
123 	{ NULL,		0,			0 },
124 };
125 
126 int
127 frodomatch(parent, match, aux)
128 	struct device *parent;
129 	struct cfdata *match;
130 	void *aux;
131 {
132 	struct intio_attach_args *ia = aux;
133 	static int frodo_matched = 0;
134 
135 	/* only allow one instance */
136 	if (frodo_matched)
137 		return (0);
138 
139 	if (strcmp(ia->ia_modname, "frodo   ") != 0)
140 		return (0);
141 
142 	if (badaddr((caddr_t)ia->ia_addr))
143 		return (0);
144 
145 	frodo_matched = 1;
146 	return (1);
147 }
148 
149 void
150 frodoattach(parent, self, aux)
151 	struct device *parent, *self;
152 	void *aux;
153 {
154 	struct frodo_softc *sc = (struct frodo_softc *)self;
155 	struct intio_attach_args *ia = aux;
156 	int i;
157 
158 	sc->sc_regs = (volatile u_int8_t *)ia->ia_addr;
159 	sc->sc_ipl = ia->ia_ipl;
160 
161 	if ((FRODO_READ(sc, FRODO_IISR) & FRODO_IISR_SERVICE) == 0)
162 		printf(": service mode enabled");
163 	printf("\n");
164 
165 	/* Clear all of the interrupt handlers. */
166 	memset(sc->sc_intr, 0, sizeof(sc->sc_intr));
167 	sc->sc_refcnt = 0;
168 
169 	/*
170 	 * Disable all of the interrupt lines; we reenable them
171 	 * as subdevices attach.
172 	 */
173 	frodo_imask(sc, 0, 0xffff);
174 
175 	/* Clear any pending interrupts. */
176 	FRODO_WRITE(sc, FRODO_PIC_PU, 0xff);
177 	FRODO_WRITE(sc, FRODO_PIC_PL, 0xff);
178 
179 	/* Set interrupt polarities. */
180 	FRODO_WRITE(sc, FRODO_PIO_IPR, 0x10);
181 
182 	/* ...and configure for edge triggering. */
183 	FRODO_WRITE(sc, FRODO_PIO_IELR, 0xcf);
184 
185 	/*
186 	 * We defer hooking up our interrupt handler until
187 	 * a subdevice hooks up theirs.
188 	 */
189 	sc->sc_ih = NULL;
190 
191 	/* ... and attach subdevices. */
192 	for (i = 0; frodo_subdevs[i].fa_name != NULL; i++) {
193 		/*
194 		 * Skip the first serial port if we're not a 425e;
195 		 * it's mapped to the DCA at select code 9 on all
196 		 * other models.
197 		 */
198 		if (frodo_subdevs[i].fa_offset == FRODO_APCI_OFFSET(1) &&
199 		    mmuid != MMUID_425_E)
200 			continue;
201 		config_found_sm(self, &frodo_subdevs[i],
202 		    frodoprint, frodosubmatch);
203 	}
204 }
205 
206 int
207 frodosubmatch(parent, cf, aux)
208 	struct device *parent;
209 	struct cfdata *cf;
210 	void *aux;
211 {
212 	struct frodo_attach_args *fa = aux;
213 
214 	if (cf->frodocf_offset != FRODO_UNKNOWN_OFFSET &&
215 	    cf->frodocf_offset != fa->fa_offset)
216 		return (0);
217 
218 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
219 }
220 
221 int
222 frodoprint(aux, pnp)
223 	void *aux;
224 	const char *pnp;
225 {
226 	struct frodo_attach_args *fa = aux;
227 
228 	if (pnp)
229 		printf("%s at %s", fa->fa_name, pnp);
230 	printf(" offset 0x%x", fa->fa_offset);
231 	return (UNCONF);
232 }
233 
234 void
235 frodo_intr_establish(frdev, func, arg, line, priority)
236 	struct device *frdev;
237 	int (*func) __P((void *));
238 	void *arg;
239 	int line;
240 	int priority;
241 {
242 	struct frodo_softc *sc = (struct frodo_softc *)frdev;
243 	struct hp300_intrhand *ih = sc->sc_ih;
244 
245 	if (line < 0 || line >= FRODO_NINTR) {
246 		printf("%s: bad interrupt line %d\n",
247 		    sc->sc_dev.dv_xname, line);
248 		goto lose;
249 	}
250 	if (sc->sc_intr[line].ih_fn != NULL) {
251 		printf("%s: interrupt line %d already used\n",
252 		    sc->sc_dev.dv_xname, line);
253 		goto lose;
254 	}
255 
256 	/* Install the handler. */
257 	sc->sc_intr[line].ih_fn = func;
258 	sc->sc_intr[line].ih_arg = arg;
259 	sc->sc_intr[line].ih_priority = priority;
260 
261 	/*
262 	 * If this is the first one, establish the frodo
263 	 * interrupt handler.  If not, reestablish at a
264 	 * higher priority if necessary.
265 	 */
266 	if (ih == NULL || ih->ih_priority < priority) {
267 		if (ih != NULL)
268 			intr_disestablish(ih);
269 		sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, priority);
270 	}
271 
272 	sc->sc_refcnt++;
273 
274 	/* Enable the interrupt line. */
275 	frodo_imask(sc, (1 << line), 0);
276 	return;
277  lose:
278 	panic("frodo_intr_establish");
279 }
280 
281 void
282 frodo_intr_disestablish(frdev, line)
283 	struct device *frdev;
284 	int line;
285 {
286 	struct frodo_softc *sc = (struct frodo_softc *)frdev;
287 	struct hp300_intrhand *ih = sc->sc_ih;
288 	int newpri;
289 
290 	if (sc->sc_intr[line].ih_fn == NULL) {
291 		printf("%s: no handler for line %d\n",
292 		    sc->sc_dev.dv_xname, line);
293 		panic("frodo_intr_disestablish");
294 	}
295 
296 	sc->sc_intr[line].ih_fn = NULL;
297 	frodo_imask(sc, 0, (1 << line));
298 
299 	/* If this was the last, unhook ourselves. */
300 	if (sc->sc_refcnt-- == 1) {
301 		intr_disestablish(ih);
302 		return;
303 	}
304 
305 	/* Lower our priority, if appropriate. */
306 	for (newpri = 0, line = 0; line < FRODO_NINTR; line++)
307 		if (sc->sc_intr[line].ih_fn != NULL &&
308 		    sc->sc_intr[line].ih_priority > newpri)
309 			newpri = sc->sc_intr[line].ih_priority;
310 
311 	if (newpri != ih->ih_priority) {
312 		intr_disestablish(ih);
313 		sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, newpri);
314 	}
315 }
316 
317 int
318 frodointr(arg)
319 	void *arg;
320 {
321 	struct frodo_softc *sc = arg;
322 	struct frodo_interhand *fih;
323 	int line, taken = 0;
324 
325 	/* Any interrupts pending? */
326 	if (FRODO_GETPEND(sc) == 0)
327 		return (0);
328 
329 	do {
330 		/*
331 		 * Get pending interrupt; this also clears it for us.
332 		 */
333 		line = FRODO_IPEND(sc);
334 		fih = &sc->sc_intr[line];
335 		if (fih->ih_fn == NULL ||
336 		    (*fih->ih_fn)(fih->ih_arg) == 0)
337 			printf("%s: spurious interrupt on line %d\n",
338 			    sc->sc_dev.dv_xname, line);
339 		if (taken++ > 100)
340 			panic("frodointr: looping!");
341 	} while (FRODO_GETPEND(sc) != 0);
342 
343 	return (1);
344 }
345 
346 void
347 frodo_imask(sc, set, clear)
348 	struct frodo_softc *sc;
349 	u_int16_t set, clear;
350 {
351 	u_int16_t imask;
352 
353 	imask = FRODO_GETMASK(sc);
354 
355 	imask |= set;
356 	imask &= ~clear;
357 
358 	FRODO_SETMASK(sc, imask);
359 }
360