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