xref: /netbsd-src/sys/arch/hp300/dev/frodo.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: frodo.c,v 1.29 2008/04/28 20:23:19 martin 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  *
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 /*
33  * Copyright (c) 1997 Michael Smith.  All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54  * SUCH DAMAGE.
55  */
56 
57 /*
58  * Support for the "Frodo" (a.k.a. "Apollo Utility") chip found
59  * in HP Apollo 9000/4xx workstations, as well as
60  * HP 9000/362 and 9000/382 controllers.
61  */
62 
63 #include <sys/cdefs.h>
64 __KERNEL_RCSID(0, "$NetBSD: frodo.c,v 1.29 2008/04/28 20:23:19 martin Exp $");
65 
66 #define	_HP300_INTR_H_PRIVATE
67 
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/kernel.h>
71 #include <sys/syslog.h>
72 #include <sys/device.h>
73 
74 #include <machine/bus.h>
75 #include <machine/cpu.h>
76 #include <machine/intr.h>
77 #include <machine/hp300spu.h>
78 
79 #include <hp300/dev/intiovar.h>
80 
81 #include <hp300/dev/frodoreg.h>
82 #include <hp300/dev/frodovar.h>
83 
84 /*
85  * Description of a Frodo interrupt handler.
86  */
87 struct frodo_interhand {
88 	int	(*ih_fn)(void *);
89 	void	*ih_arg;
90 	int	ih_priority;
91 };
92 
93 struct frodo_softc {
94 	device_t	sc_dev;		/* generic device glue */
95 	volatile uint8_t *sc_regs;	/* register base */
96 	struct frodo_interhand sc_intr[FRODO_NINTR]; /* interrupt handlers */
97 	int		sc_ipl;
98 	void		*sc_ih;		/* out interrupt cookie */
99 	int		sc_refcnt;	/* number of interrupt refs */
100 	struct bus_space_tag sc_tag;	/* bus space tag */
101 };
102 
103 static int	frodomatch(device_t, cfdata_t, void *);
104 static void	frodoattach(device_t, device_t, void *);
105 
106 static int	frodoprint(void *, const char *);
107 static int	frodosubmatch(device_t, cfdata_t, const int *, void *);
108 
109 static int	frodointr(void *);
110 
111 static void	frodo_imask(struct frodo_softc *, uint16_t, uint16_t);
112 
113 CFATTACH_DECL_NEW(frodo, sizeof(struct frodo_softc),
114     frodomatch, frodoattach, NULL, NULL);
115 
116 static const struct frodo_device frodo_subdevs[] = {
117 	{ "dnkbd",	FRODO_APCI_OFFSET(0),	FRODO_INTR_APCI0 },
118 	{ "com",	FRODO_APCI_OFFSET(1),	FRODO_INTR_APCI1 },
119 	{ "com",	FRODO_APCI_OFFSET(2),	FRODO_INTR_APCI2 },
120 	{ "com",	FRODO_APCI_OFFSET(3),	FRODO_INTR_APCI3 },
121 	{ NULL,		0,			0 }
122 };
123 
124 static int
125 frodomatch(device_t parent, cfdata_t cf, void *aux)
126 {
127 	struct intio_attach_args *ia = aux;
128 	static int frodo_matched = 0;
129 
130 	/* only allow one instance */
131 	if (frodo_matched)
132 		return 0;
133 
134 	if (strcmp(ia->ia_modname, "frodo") != 0)
135 		return 0;
136 
137 	if (badaddr((void *)ia->ia_addr))
138 		return 0;
139 
140 	frodo_matched = 1;
141 	return 1;
142 }
143 
144 static void
145 frodoattach(device_t parent, device_t self, void *aux)
146 {
147 	struct frodo_softc *sc = device_private(self);
148 	struct intio_attach_args *ia = aux;
149 	bus_space_tag_t bst = &sc->sc_tag;
150 	const struct frodo_device *fd;
151 	struct frodo_attach_args fa;
152 
153 	sc->sc_dev = self;
154 	sc->sc_regs = (volatile uint8_t *)ia->ia_addr;
155 	sc->sc_ipl = ia->ia_ipl;
156 
157 	if ((FRODO_READ(sc, FRODO_IISR) & FRODO_IISR_SERVICE) == 0)
158 		aprint_error(": service mode enabled");
159 	aprint_normal("\n");
160 
161 	/* Initialize bus_space_tag_t for frodo */
162 	frodo_init_bus_space(bst);
163 
164 	/* Clear all of the interrupt handlers. */
165 	memset(sc->sc_intr, 0, sizeof(sc->sc_intr));
166 	sc->sc_refcnt = 0;
167 
168 	/*
169 	 * Disable all of the interrupt lines; we reenable them
170 	 * as subdevices attach.
171 	 */
172 	frodo_imask(sc, 0, 0xffff);
173 
174 	/* Clear any pending interrupts. */
175 	FRODO_WRITE(sc, FRODO_PIC_PU, 0xff);
176 	FRODO_WRITE(sc, FRODO_PIC_PL, 0xff);
177 
178 	/* Set interrupt polarities. */
179 	FRODO_WRITE(sc, FRODO_PIO_IPR, 0x10);
180 
181 	/* ...and configure for edge triggering. */
182 	FRODO_WRITE(sc, FRODO_PIO_IELR, 0xcf);
183 
184 	/*
185 	 * We defer hooking up our interrupt handler until
186 	 * a subdevice hooks up theirs.
187 	 */
188 	sc->sc_ih = NULL;
189 
190 	/* ... and attach subdevices. */
191 	for (fd = frodo_subdevs; fd->fd_name != NULL; fd++) {
192 		/*
193 		 * Skip the first serial port if we're not a 425e;
194 		 * it's mapped to the DCA at select code 9 on all
195 		 * other models.
196 		 */
197 		if (fd->fd_offset == FRODO_APCI_OFFSET(1) &&
198 		    mmuid != MMUID_425_E)
199 			continue;
200 		fa.fa_name = fd->fd_name;
201 		fa.fa_bst = bst;
202 		fa.fa_base = ia->ia_iobase;
203 		fa.fa_offset = fd->fd_offset;
204 		fa.fa_line = fd->fd_line;
205 		config_found_sm_loc(self, "frodo", NULL, &fa, frodoprint,
206 		    frodosubmatch);
207 	}
208 }
209 
210 static int
211 frodosubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
212 {
213 	struct frodo_attach_args *fa = aux;
214 
215 	if (cf->frodocf_offset != FRODO_UNKNOWN_OFFSET &&
216 	    cf->frodocf_offset != fa->fa_offset)
217 		return 0;
218 
219 	return config_match(parent, cf, aux);
220 }
221 
222 static int
223 frodoprint(void *aux, const char *pnp)
224 {
225 	struct frodo_attach_args *fa = aux;
226 
227 	if (pnp)
228 		aprint_normal("%s at %s", fa->fa_name, pnp);
229 	aprint_normal(" offset 0x%x", fa->fa_offset);
230 	return UNCONF;
231 }
232 
233 void
234 frodo_intr_establish(device_t frdev, int (*func)(void *), void *arg,
235     int line, int priority)
236 {
237 	struct frodo_softc *sc = device_private(frdev);
238 	struct hp300_intrhand *ih = sc->sc_ih;
239 
240 	if (line < 0 || line >= FRODO_NINTR) {
241 		aprint_error_dev(frdev, "bad interrupt line %d\n", line);
242 		goto lose;
243 	}
244 	if (sc->sc_intr[line].ih_fn != NULL) {
245 		aprint_error_dev(frdev, "interrupt line %d already used\n",
246 		    line);
247 		goto lose;
248 	}
249 
250 	/* Install the handler. */
251 	sc->sc_intr[line].ih_fn = func;
252 	sc->sc_intr[line].ih_arg = arg;
253 	sc->sc_intr[line].ih_priority = priority;
254 
255 	/*
256 	 * If this is the first one, establish the frodo
257 	 * interrupt handler.  If not, reestablish at a
258 	 * higher priority if necessary.
259 	 */
260 	if (ih == NULL || ih->ih_priority < priority) {
261 		if (ih != NULL)
262 			intr_disestablish(ih);
263 		sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, priority);
264 	}
265 
266 	sc->sc_refcnt++;
267 
268 	/* Enable the interrupt line. */
269 	frodo_imask(sc, (1 << line), 0);
270 	return;
271  lose:
272 	panic("frodo_intr_establish");
273 }
274 
275 void
276 frodo_intr_disestablish(device_t frdev, int line)
277 {
278 	struct frodo_softc *sc = device_private(frdev);
279 	struct hp300_intrhand *ih = sc->sc_ih;
280 	int newpri;
281 
282 	if (sc->sc_intr[line].ih_fn == NULL) {
283 		printf("%s: no handler for line %d\n",
284 		    device_xname(sc->sc_dev), line);
285 		panic("frodo_intr_disestablish");
286 	}
287 
288 	sc->sc_intr[line].ih_fn = NULL;
289 	frodo_imask(sc, 0, (1 << line));
290 
291 	/* If this was the last, unhook ourselves. */
292 	if (sc->sc_refcnt-- == 1) {
293 		intr_disestablish(ih);
294 		return;
295 	}
296 
297 	/* Lower our priority, if appropriate. */
298 	for (newpri = 0, line = 0; line < FRODO_NINTR; line++)
299 		if (sc->sc_intr[line].ih_fn != NULL &&
300 		    sc->sc_intr[line].ih_priority > newpri)
301 			newpri = sc->sc_intr[line].ih_priority;
302 
303 	if (newpri != ih->ih_priority) {
304 		intr_disestablish(ih);
305 		sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, newpri);
306 	}
307 }
308 
309 static int
310 frodointr(void *arg)
311 {
312 	struct frodo_softc *sc = arg;
313 	struct frodo_interhand *fih;
314 	int line, taken = 0;
315 
316 	/* Any interrupts pending? */
317 	if (FRODO_GETPEND(sc) == 0)
318 		return 0;
319 
320 	do {
321 		/*
322 		 * Get pending interrupt; this also clears it for us.
323 		 */
324 		line = FRODO_IPEND(sc);
325 		fih = &sc->sc_intr[line];
326 		if (fih->ih_fn == NULL ||
327 		    (*fih->ih_fn)(fih->ih_arg) == 0)
328 			printf("%s: spurious interrupt on line %d\n",
329 			    device_xname(sc->sc_dev), line);
330 		if (taken++ > 100)
331 			panic("frodointr: looping!");
332 	} while (FRODO_GETPEND(sc) != 0);
333 
334 	return 1;
335 }
336 
337 static void
338 frodo_imask(struct frodo_softc *sc, uint16_t set, uint16_t clear)
339 {
340 	uint16_t imask;
341 
342 	imask = FRODO_GETMASK(sc);
343 
344 	imask |= set;
345 	imask &= ~clear;
346 
347 	FRODO_SETMASK(sc, imask);
348 }
349 
350 /*
351  * frodo chip specific bus_space(9) support functions.
352  */
353 static uint8_t frodo_bus_space_read_sparse_1(bus_space_tag_t,
354     bus_space_handle_t, bus_size_t);
355 static void frodo_bus_space_write_sparse_1(bus_space_tag_t,
356     bus_space_handle_t, bus_size_t, uint8_t);
357 
358 static void frodo_bus_space_read_multi_sparse_1(bus_space_tag_t,
359     bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t);
360 static void frodo_bus_space_write_multi_sparse_1(bus_space_tag_t,
361     bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t);
362 
363 static void frodo_bus_space_read_region_sparse_1(bus_space_tag_t,
364     bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t);
365 static void frodo_bus_space_write_region_sparse_1(bus_space_tag_t,
366     bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t);
367 
368 static void frodo_bus_space_set_multi_sparse_1(bus_space_tag_t,
369     bus_space_handle_t, bus_size_t, uint8_t, bus_size_t);
370 
371 static void frodo_bus_space_set_region_sparse_1(bus_space_tag_t,
372     bus_space_handle_t, bus_size_t, uint8_t, bus_size_t);
373 
374 /*
375  * frodo_init_bus_space():
376  *	Initialize bus_space functions in bus_space_tag_t
377  *	for frodo devices which have sparse address space.
378  */
379 void
380 frodo_init_bus_space(bus_space_tag_t bst)
381 {
382 
383 	memset(bst, 0, sizeof(struct bus_space_tag));
384 	bst->bustype = HP300_BUS_SPACE_INTIO;
385 
386 	bst->bsr1 = frodo_bus_space_read_sparse_1;
387 	bst->bsw1 = frodo_bus_space_write_sparse_1;
388 
389 	bst->bsrm1 = frodo_bus_space_read_multi_sparse_1;
390 	bst->bswm1 = frodo_bus_space_write_multi_sparse_1;
391 
392 	bst->bsrr1 = frodo_bus_space_read_region_sparse_1;
393 	bst->bswr1 = frodo_bus_space_write_region_sparse_1;
394 
395 	bst->bssm1 = frodo_bus_space_set_multi_sparse_1;
396 
397 	bst->bssr1 = frodo_bus_space_set_region_sparse_1;
398 }
399 
400 static uint8_t
401 frodo_bus_space_read_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
402     bus_size_t offset)
403 {
404 
405 	return *(volatile uint8_t *)(bsh + (offset << 2));
406 }
407 
408 static void
409 frodo_bus_space_write_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
410     bus_size_t offset, uint8_t val)
411 {
412 
413 	*(volatile uint8_t *)(bsh + (offset << 2)) = val;
414 }
415 
416 static void
417 frodo_bus_space_read_multi_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
418     bus_size_t offset, uint8_t *addr, bus_size_t len)
419 {
420 
421 	__asm volatile (
422 	"	movl	%0,%%a0		;\n"
423 	"	movl	%1,%%a1		;\n"
424 	"	movl	%2,%%d0		;\n"
425 	"1:	movb	%%a0@,%%a1@+	;\n"
426 	"	subql	#1,%%d0		;\n"
427 	"	jne	1b"
428 	    :
429 	    : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
430 	    : "%a0","%a1","%d0");
431 }
432 
433 static void
434 frodo_bus_space_write_multi_sparse_1(bus_space_tag_t bst,
435     bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr,
436     bus_size_t len)
437 {
438 
439 	__asm volatile (
440 	"	movl	%0,%%a0		;\n"
441 	"	movl	%1,%%a1		;\n"
442 	"	movl	%2,%%d0		;\n"
443 	"1:	movb	%%a1@+,%%a0@	;\n"
444 	"	subql	#1,%%d0		;\n"
445 	"	jne	1b"
446 	    :
447 	    : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
448 	    : "%a0","%a1","%d0");
449 }
450 
451 static void
452 frodo_bus_space_read_region_sparse_1(bus_space_tag_t bst,
453     bus_space_handle_t bsh, bus_size_t offset, uint8_t *addr, bus_size_t len)
454 {
455 	__asm volatile (
456 	"	movl	%0,%%a0		;\n"
457 	"	movl	%1,%%a1		;\n"
458 	"	movl	%2,%%d0		;\n"
459 	"1:	movb	%%a0@,%%a1@+	;\n"
460 	"	addql	#4,%%a0		;\n"
461 	"	subql	#1,%%d0		;\n"
462 	"	jne	1b"
463 	    :
464 	    : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
465 	    : "%a0","%a1","%d0");
466 }
467 
468 static void
469 frodo_bus_space_write_region_sparse_1(bus_space_tag_t bst,
470     bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr,
471     bus_size_t len)
472 {
473 
474 	__asm volatile (
475 	"	movl	%0,%%a0		;\n"
476 	"	movl	%1,%%a1		;\n"
477 	"	movl	%2,%%d0		;\n"
478 	"1:	movb	%%a1@+,%%a0@	;\n"
479 	"	addql	#4,%%a0		;\n"
480 	"	subql	#1,%%d0		;\n"
481 	"	jne	1b"
482 	    :
483 	    : "r" (bsh + (offset << 2)), "g" (addr), "g" (len)
484 	    : "%a0","%a1","%d0");
485 }
486 
487 static void
488 frodo_bus_space_set_multi_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
489     bus_size_t offset, uint8_t val, bus_size_t count)
490 {
491 	__asm volatile (
492 	"	movl	%0,%%a0		;\n"
493 	"	movl	%1,%%d1		;\n"
494 	"	movl	%2,%%d0		;\n"
495 	"1:	movb	%%d1,%%a0@	;\n"
496 	"	subql	#1,%%d0		;\n"
497 	"	jne	1b"
498 	    :
499 	    : "r" (bsh + (offset << 2)), "g" (val), "g" (count)
500 	    : "%a0","%d0","%d1");
501 }
502 
503 static void
504 frodo_bus_space_set_region_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh,
505     bus_size_t offset, uint8_t val, bus_size_t count)
506 {
507 
508 	__asm volatile (
509 	"	movl	%0,%%a0		;\n"
510 	"	movl	%1,%%d1		;\n"
511 	"	movl	%2,%%d0		;\n"
512 	"1:	movb	%%d1,%%a0@	;\n"
513 	"	addql	#4,%%a0		;\n"
514 	"	subql	#1,%%d0		;\n"
515 	"	jne	1b"
516 	    :
517 	    : "r" (bsh + (offset << 2)), "g" (val), "g" (count)
518 	    : "%a0","%d0","%d1");
519 }
520