obio.c revision 1.33
1/*	$NetBSD: obio.c,v 1.33 1997/05/18 21:26:22 pk Exp $	*/
2
3/*
4 * Copyright (c) 1993, 1994 Theo de Raadt
5 * Copyright (c) 1995, 1997 Paul Kranenburg
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by Theo de Raadt.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/device.h>
37#include <sys/malloc.h>
38
39#ifdef DEBUG
40#include <sys/proc.h>
41#include <sys/syslog.h>
42#endif
43
44#include <vm/vm.h>
45
46#include <machine/autoconf.h>
47#include <machine/pmap.h>
48#include <machine/oldmon.h>
49#include <machine/cpu.h>
50#include <machine/ctlreg.h>
51#include <sparc/sparc/asm.h>
52#include <sparc/sparc/vaddrs.h>
53#include <sparc/sparc/cpuvar.h>
54#include <sparc/dev/sbusvar.h>
55#include <sparc/dev/vmereg.h>
56
57struct vmebus_softc {
58	struct device	 sc_dev;	/* base device */
59	struct vmebusreg *sc_reg; 	/* VME control registers */
60	struct vmebusvec *sc_vec;	/* VME interrupt vector */
61	struct rom_range *sc_range;	/* ROM range property */
62	int		 sc_nrange;
63};
64struct  vmebus_softc *vmebus_sc;/*XXX*/
65
66struct bus_softc {
67	union {
68		struct	device scu_dev;		/* base device */
69		struct	sbus_softc scu_sbus;	/* obio is another sbus slot */
70		struct	vmebus_softc scu_vme;
71	} bu;
72};
73
74
75/* autoconfiguration driver */
76static int	busmatch __P((struct device *, struct cfdata *, void *));
77static void	obioattach __P((struct device *, struct device *, void *));
78static void	vmesattach __P((struct device *, struct device *, void *));
79static void	vmelattach __P((struct device *, struct device *, void *));
80static void	vmeattach __P((struct device *, struct device *, void *));
81
82int		busprint __P((void *, const char *));
83int		vmeprint __P((void *, const char *));
84static int	busattach __P((struct device *, struct cfdata *, void *, int));
85void *		bus_map __P((struct rom_reg *, int, int));
86int		obio_scan __P((struct device *, struct cfdata *, void *));
87int 		vmes_scan __P((struct device *, struct cfdata *, void *));
88int 		vmel_scan __P((struct device *, struct cfdata *, void *));
89int 		vmeintr __P((void *));
90
91struct cfattach obio_ca = {
92	sizeof(struct bus_softc), busmatch, obioattach
93};
94
95struct cfdriver obio_cd = {
96	NULL, "obio", DV_DULL
97};
98
99struct cfattach vmel_ca = {
100	sizeof(struct bus_softc), busmatch, vmelattach
101};
102
103struct cfdriver vmel_cd = {
104	NULL, "vmel", DV_DULL
105};
106
107struct cfattach vmes_ca = {
108	sizeof(struct bus_softc), busmatch, vmesattach
109};
110
111struct cfdriver vmes_cd = {
112	NULL, "vmes", DV_DULL
113};
114
115struct cfattach vme_ca = {
116	sizeof(struct bus_softc), busmatch, vmeattach
117};
118
119struct cfdriver vme_cd = {
120	NULL, "vme", DV_DULL
121};
122
123struct intrhand **vmeints;
124
125
126int
127busmatch(parent, cf, aux)
128	struct device *parent;
129	struct cfdata *cf;
130	void *aux;
131{
132	register struct confargs *ca = aux;
133	register struct romaux *ra = &ca->ca_ra;
134
135	if (CPU_ISSUN4M)
136		return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
137
138	if (!CPU_ISSUN4)
139		return (0);
140
141	return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
142}
143
144int
145busprint(args, obio)
146	void *args;
147	const char *obio;
148{
149	register struct confargs *ca = args;
150
151	if (ca->ca_ra.ra_name == NULL)
152		ca->ca_ra.ra_name = "<unknown>";
153
154	if (obio)
155		printf("[%s at %s]", ca->ca_ra.ra_name, obio);
156
157	printf(" addr %p", ca->ca_ra.ra_paddr);
158
159	if (CPU_ISSUN4 && ca->ca_ra.ra_intr[0].int_vec != -1)
160		printf(" vec 0x%x", ca->ca_ra.ra_intr[0].int_vec);
161
162	return (UNCONF);
163}
164
165int
166vmeprint(args, name)
167	void *args;
168	const char *name;
169{
170	register struct confargs *ca = args;
171
172	if (name)
173		printf("%s at %s", ca->ca_ra.ra_name, name);
174	return (UNCONF);
175}
176
177void
178obioattach(parent, self, args)
179	struct device *parent, *self;
180	void *args;
181{
182#if defined(SUN4M)
183	register struct bus_softc *sc = (struct bus_softc *)self;
184	struct confargs oca, *ca = args;
185	register struct romaux *ra = &ca->ca_ra;
186	register int node0, node;
187	register char *name;
188	register const char *sp;
189	const char *const *ssp;
190	int rlen;
191	extern int autoconf_nzs;
192
193	static const char *const special4m[] = {
194		/* find these first */
195		"eeprom",
196		"counter",
197#if 0 /* Not all sun4m's have an `auxio' */
198		"auxio",
199#endif
200		"",
201		/* place device to ignore here */
202		"interrupt",
203		NULL
204	};
205#endif
206
207	if (CPU_ISSUN4) {
208		if (self->dv_unit > 0) {
209			printf(" unsupported\n");
210			return;
211		}
212		printf("\n");
213
214		(void)config_search(obio_scan, self, args);
215		bus_untmp();
216	}
217
218#if defined(SUN4M)
219	if (!CPU_ISSUN4M)
220		return;
221
222	/*
223	 * There is only one obio bus (it is in fact one of the Sbus slots)
224	 * How about VME?
225	 */
226	if (self->dv_unit > 0) {
227		printf(" unsupported\n");
228		return;
229	}
230
231	printf("\n");
232
233	if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "obio") == 0)
234		oca.ca_ra.ra_bp = ra->ra_bp + 1;
235	else
236		oca.ca_ra.ra_bp = NULL;
237
238	node = ra->ra_node;
239	rlen = getproplen(node, "ranges");
240	if (rlen > 0) {
241		sc->bu.scu_sbus.sc_nrange = rlen / sizeof(struct rom_range);
242		sc->bu.scu_sbus.sc_range =
243			(struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
244		if (sc->bu.scu_sbus.sc_range == 0)
245			panic("obio: PROM ranges too large: %d", rlen);
246		(void)getprop(node, "ranges", sc->bu.scu_sbus.sc_range, rlen);
247	}
248
249	/*
250	 * Loop through ROM children, fixing any relative addresses
251	 * and then configuring each device.
252	 * We first do the crucial ones, such as eeprom, etc.
253	 */
254	node0 = firstchild(ra->ra_node);
255	for (ssp = special4m ; *(sp = *ssp) != 0; ssp++) {
256		if ((node = findnode(node0, sp)) == 0) {
257			printf("could not find %s amongst obio devices\n", sp);
258			panic(sp);
259		}
260		if (!romprop(&oca.ca_ra, sp, node))
261			continue;
262
263		sbus_translate(self, &oca);
264		oca.ca_bustype = BUS_OBIO;
265		(void) config_found(self, (void *)&oca, busprint);
266	}
267
268	for (node = node0; node; node = nextsibling(node)) {
269		name = getpropstring(node, "name");
270		for (ssp = special4m ; (sp = *ssp) != NULL; ssp++)
271			if (strcmp(name, sp) == 0)
272				break;
273
274		if (sp != NULL || !romprop(&oca.ca_ra, name, node))
275			continue;
276
277		if (strcmp(name, "zs") == 0)
278			/* XXX - see autoconf.c for this hack */
279			autoconf_nzs++;
280
281		/* Translate into parent address spaces */
282		sbus_translate(self, &oca);
283		oca.ca_bustype = BUS_OBIO;
284		(void) config_found(self, (void *)&oca, busprint);
285	}
286#endif
287}
288
289void
290vmesattach(parent, self, args)
291	struct device *parent, *self;
292	void *args;
293{
294	if (self->dv_unit > 0 ||
295	    (CPU_ISSUN4M && strncmp(parent->dv_xname, "vme", 3) != 0)) {
296		printf(" unsupported\n");
297		return;
298	}
299	printf("\n");
300
301	if (vmeints == NULL) {
302		vmeints = (struct intrhand **)malloc(256 *
303		    sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
304		bzero(vmeints, 256 * sizeof(struct intrhand *));
305	}
306	(void)config_search(vmes_scan, self, args);
307	bus_untmp();
308}
309
310void
311vmelattach(parent, self, args)
312	struct device *parent, *self;
313	void *args;
314{
315	if (self->dv_unit > 0 ||
316	    (CPU_ISSUN4M && strncmp(parent->dv_xname, "vme", 3) != 0)) {
317		printf(" unsupported\n");
318		return;
319	}
320	printf("\n");
321
322	if (vmeints == NULL) {
323		vmeints = (struct intrhand **)malloc(256 *
324		    sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
325		bzero(vmeints, 256 * sizeof(struct intrhand *));
326	}
327	(void)config_search(vmel_scan, self, args);
328	bus_untmp();
329}
330
331void
332vmeattach(parent, self, aux)
333	struct device *parent, *self;
334	void *aux;
335{
336	struct vmebus_softc *sc = (struct vmebus_softc *)self;
337	struct confargs *ca = aux;
338	register struct romaux *ra = &ca->ca_ra;
339	int node, rlen;
340	struct confargs oca;
341
342	if (!CPU_ISSUN4M || self->dv_unit > 0) {
343		printf(" unsupported\n");
344		return;
345	}
346
347	node = ra->ra_node;
348
349	sc->sc_reg = (struct vmebusreg *)
350		mapdev(&ra->ra_reg[0], 0, 0, ra->ra_reg[0].rr_len,
351			ra->ra_reg[0].rr_iospace);
352	sc->sc_vec = (struct vmebusvec *)
353		mapdev(&ra->ra_reg[1], 0, 0, ra->ra_reg[1].rr_len,
354			ra->ra_reg[1].rr_iospace);
355
356	/*
357	 * Get "range" property, though we don't do anything with it yet.
358	 */
359	rlen = getproplen(node, "ranges");
360	if (rlen > 0) {
361		sc->sc_nrange = rlen / sizeof(struct rom_range);
362		sc->sc_range =
363			(struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
364		if (sc->sc_range == 0)
365			panic("vme: PROM ranges too large: %d", rlen);
366		(void)getprop(node, "ranges", sc->sc_range, rlen);
367	}
368
369	vmebus_sc = sc;
370	printf(": version %x\n",
371	       sc->sc_reg->vmebus_cr & VMEBUS_CR_IMPL);
372
373	if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "vme") == 0)
374		oca.ca_ra.ra_bp = ra->ra_bp + 1;
375	else
376		oca.ca_ra.ra_bp = NULL;
377
378	oca.ca_ra.ra_name = "vmes";
379	oca.ca_bustype = BUS_MAIN;
380	(void)config_found(self, (void *)&oca, vmeprint);
381
382	oca.ca_ra.ra_name = "vmel";
383	oca.ca_bustype = BUS_MAIN;
384	(void)config_found(self, (void *)&oca, vmeprint);
385}
386
387int
388busattach(parent, cf, args, bustype)
389	struct device *parent;
390	struct cfdata *cf;
391	void *args;
392	int bustype;
393{
394#if defined(SUN4) || defined(SUN4M)
395	register struct confargs *ca = args;
396	struct confargs oca;
397	caddr_t tmp;
398
399	if (bustype == BUS_OBIO && CPU_ISSUN4) {
400
401		/*
402		 * avoid sun4m entries which don't have valid PA's.
403		 * no point in even probing them.
404		 */
405		if (cf->cf_loc[0] == -1) return 0;
406
407		/*
408		 * On the 4/100 obio addresses must be mapped at
409		 * 0x0YYYYYYY, but alias higher up (we avoid the
410		 * alias condition because it causes pmap difficulties)
411		 * XXX: We also assume that 4/[23]00 obio addresses
412		 * must be 0xZYYYYYYY, where (Z != 0)
413		 */
414		if (cpuinfo.cpu_type == CPUTYP_4_100 &&
415		    (cf->cf_loc[0] & 0xf0000000))
416			return 0;
417		if (cpuinfo.cpu_type != CPUTYP_4_100 &&
418		    !(cf->cf_loc[0] & 0xf0000000))
419			return 0;
420	}
421
422	/* XXX - streamline bustype stuff */
423	oca.ca_ra.ra_iospace = CPU_ISSUN4
424		? -1
425		: ((bustype == BUS_VME32) ? VME_SUN4M_32 : VME_SUN4M_16);
426	oca.ca_ra.ra_paddr = (void *)cf->cf_loc[0];
427	oca.ca_ra.ra_len = 0;
428	oca.ca_ra.ra_nreg = 1;
429	if (oca.ca_ra.ra_paddr)
430		tmp = (caddr_t)mapdev(oca.ca_ra.ra_reg,
431				      TMPMAP_VA, 0, NBPG, bustype);
432	else
433		tmp = NULL;
434	oca.ca_ra.ra_vaddr = tmp;
435	oca.ca_ra.ra_intr[0].int_pri = cf->cf_loc[1];
436	if (bustype == BUS_VME16 || bustype == BUS_VME32)
437		oca.ca_ra.ra_intr[0].int_vec = cf->cf_loc[2];
438	else
439		oca.ca_ra.ra_intr[0].int_vec = -1;
440	oca.ca_ra.ra_nintr = 1;
441	oca.ca_ra.ra_name = cf->cf_driver->cd_name;
442	if (ca->ca_ra.ra_bp != NULL &&
443	  ((bustype == BUS_VME16 && strcmp(ca->ca_ra.ra_bp->name,"vmes") ==0) ||
444	   (bustype == BUS_VME32 && strcmp(ca->ca_ra.ra_bp->name,"vmel") ==0) ||
445	   (bustype == BUS_OBIO && strcmp(ca->ca_ra.ra_bp->name,"obio") == 0)))
446		oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1;
447	else
448		oca.ca_ra.ra_bp = NULL;
449	oca.ca_bustype = bustype;
450
451	if ((*cf->cf_attach->ca_match)(parent, cf, &oca) == 0)
452		return 0;
453
454	/*
455	 * check if XXmatch routine replaced the temporary mapping with
456	 * a real mapping.   If not, then make sure we don't pass the
457	 * tmp mapping to the attach routine.
458	 */
459	if (oca.ca_ra.ra_vaddr == tmp)
460		oca.ca_ra.ra_vaddr = NULL; /* wipe out tmp address */
461	/*
462	 * the match routine will set "ra_len" if it wants us to
463	 * establish a mapping for it.
464	 * (which won't be seen on future XXmatch calls,
465	 * so not as useful as it seems.)
466	 */
467	if (oca.ca_ra.ra_len)
468		oca.ca_ra.ra_vaddr =
469		    bus_map(oca.ca_ra.ra_reg, oca.ca_ra.ra_len, oca.ca_bustype);
470
471	config_attach(parent, cf, &oca, busprint);
472	return 1;
473#else
474	return 0;
475#endif
476}
477
478int
479obio_scan(parent, child, args)
480	struct device *parent;
481	struct cfdata *child;
482	void *args;
483{
484	return busattach(parent, child, args, BUS_OBIO);
485}
486
487int
488vmes_scan(parent, child, args)
489	struct device *parent;
490	struct cfdata *child;
491	void *args;
492{
493	return busattach(parent, child, args, BUS_VME16);
494}
495
496int
497vmel_scan(parent, child, args)
498	struct device *parent;
499	struct cfdata *child;
500	void *args;
501{
502	return busattach(parent, child, args, BUS_VME32);
503}
504
505int pil_to_vme[] = {
506	-1,	/* pil 0 */
507	-1,	/* pil 1 */
508	1,	/* pil 2 */
509	2,	/* pil 3 */
510	-1,	/* pil 4 */
511	3,	/* pil 5 */
512	-1,	/* pil 6 */
513	4,	/* pil 7 */
514	-1,	/* pil 8 */
515	5,	/* pil 9 */
516	-1,	/* pil 10 */
517	6,	/* pil 11 */
518	-1,	/* pil 12 */
519	7,	/* pil 13 */
520	-1,	/* pil 14 */
521	-1,	/* pil 15 */
522};
523
524int
525vmeintr(arg)
526	void *arg;
527{
528	int pil = (int)arg, level, vec;
529	struct intrhand *ih;
530	int i = 0;
531
532	level = (pil_to_vme[pil] << 1) | 1;
533
534	if (CPU_ISSUN4) {
535		vec = ldcontrolb((caddr_t)(AC_VMEINTVEC | level));
536	} else if (CPU_ISSUN4M) {
537		vec = vmebus_sc->sc_vec->vmebusvec[level];
538	} else
539		panic("vme: spurious interrupt");
540
541	if (vec == -1) {
542		printf("vme: spurious interrupt\n");
543		return 0;
544	}
545
546	for (ih = vmeints[vec]; ih; ih = ih->ih_next)
547		if (ih->ih_fun)
548			i += (ih->ih_fun)(ih->ih_arg);
549	return (i);
550}
551
552void
553vmeintr_establish(vec, level, ih)
554	int vec, level;
555	struct intrhand *ih;
556{
557	struct intrhand *ihs;
558
559	if (!CPU_ISSUN4) {
560		panic("vmeintr_establish: not supported on cpu-type %d",
561		      cputyp);
562	}
563
564	if (vec == -1)
565		panic("vmeintr_establish: uninitialized vec\n");
566
567	if (vmeints[vec] == NULL)
568		vmeints[vec] = ih;
569	else {
570		for (ihs = vmeints[vec]; ihs->ih_next; ihs = ihs->ih_next)
571			;
572		ihs->ih_next = ih;
573	}
574
575	/* ensure the interrupt subsystem will call us at this level */
576	for (ihs = intrhand[level]; ihs; ihs = ihs->ih_next)
577		if (ihs->ih_fun == vmeintr)
578			return;
579
580	ihs = (struct intrhand *)malloc(sizeof(struct intrhand),
581	    M_TEMP, M_NOWAIT);
582	if (ihs == NULL)
583		panic("vme_addirq");
584	bzero(ihs, sizeof *ihs);
585	ihs->ih_fun = vmeintr;
586	ihs->ih_arg = (void *)level;
587	intr_establish(level, ihs);
588}
589
590#define	getpte(va)		lda(va, ASI_PTE)
591
592/*
593 * If we can find a mapping that was established by the rom, use it.
594 * Else, create a new mapping.
595 */
596void *
597bus_map(pa, len, bustype)
598	struct rom_reg *pa;
599	int len;
600	int bustype;
601{
602
603	if (CPU_ISSUN4 && len <= NBPG) {
604		u_long	pf = (u_long)(pa->rr_paddr) >> PGSHIFT;
605		u_long	va, pte;
606		int pgtype = -1;
607
608		switch (bt2pmt[bustype]) {
609		case PMAP_OBIO:
610			pgtype = PG_OBIO;
611			break;
612		case PMAP_VME32:
613			pgtype = PG_VME32;
614			break;
615		case PMAP_VME16:
616			pgtype = PG_VME16;
617			break;
618		}
619
620		for (va = OLDMON_STARTVADDR; va < OLDMON_ENDVADDR; va += NBPG) {
621			pte = getpte(va);
622			if ((pte & PG_V) != 0 && (pte & PG_TYPE) == pgtype &&
623			    (pte & PG_PFNUM) == pf)
624				return ((void *)
625				    (va | ((u_long)pa->rr_paddr & PGOFSET)) );
626					/* note: preserve page offset */
627		}
628	}
629
630	if (CPU_ISSUN4M)
631		pa->rr_iospace =
632		     (bustype == BUS_VME32) ? VME_SUN4M_32 : VME_SUN4M_16;
633	return mapiodev(pa, 0, len, bustype);
634}
635
636void
637bus_untmp()
638{
639	pmap_remove(pmap_kernel(), TMPMAP_VA, TMPMAP_VA+NBPG);
640}
641