sbus.c revision 1.19 1 /* $NetBSD: sbus.c,v 1.19 1999/06/20 00:51:30 eeh Exp $ */
2
3 /*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
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) 1992, 1993
41 * The Regents of the University of California. All rights reserved.
42 *
43 * This software was developed by the Computer Systems Engineering group
44 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
45 * contributed to Berkeley.
46 *
47 * All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the University of
50 * California, Lawrence Berkeley Laboratory.
51 *
52 * Redistribution and use in source and binary forms, with or without
53 * modification, are permitted provided that the following conditions
54 * are met:
55 * 1. Redistributions of source code must retain the above copyright
56 * notice, this list of conditions and the following disclaimer.
57 * 2. Redistributions in binary form must reproduce the above copyright
58 * notice, this list of conditions and the following disclaimer in the
59 * documentation and/or other materials provided with the distribution.
60 * 3. All advertising materials mentioning features or use of this software
61 * must display the following acknowledgement:
62 * This product includes software developed by the University of
63 * California, Berkeley and its contributors.
64 * 4. Neither the name of the University nor the names of its contributors
65 * may be used to endorse or promote products derived from this software
66 * without specific prior written permission.
67 *
68 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
69 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
70 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
71 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
72 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
73 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
74 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
75 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
76 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
77 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
78 * SUCH DAMAGE.
79 *
80 * @(#)sbus.c 8.1 (Berkeley) 6/11/93
81 */
82
83 /*
84 * Copyright (c) 1999 Eduardo Horvath
85 *
86 * Redistribution and use in source and binary forms, with or without
87 * modification, are permitted provided that the following conditions
88 * are met:
89 * 1. Redistributions of source code must retain the above copyright
90 * notice, this list of conditions and the following disclaimer.
91 *
92 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
93 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
95 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
96 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
97 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
98 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
99 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
100 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
101 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
102 * SUCH DAMAGE.
103 *
104 */
105
106
107 /*
108 * Sbus stuff.
109 */
110 #include "opt_ddb.h"
111
112 #include <sys/param.h>
113 #include <sys/extent.h>
114 #include <sys/malloc.h>
115 #include <sys/systm.h>
116 #include <sys/device.h>
117 #include <vm/vm.h>
118
119 #include <machine/bus.h>
120 #include <sparc64/sparc64/vaddrs.h>
121 #include <sparc64/dev/iommureg.h>
122 #include <sparc64/dev/iommuvar.h>
123 #include <sparc64/dev/sbusreg.h>
124 #include <dev/sbus/sbusvar.h>
125
126 #include <machine/autoconf.h>
127 #include <machine/ctlreg.h>
128 #include <machine/cpu.h>
129 #include <machine/sparc64.h>
130
131 #ifdef DEBUG
132 #define SDB_DVMA 0x1
133 #define SDB_INTR 0x2
134 int sbusdebug = 0;
135 #endif
136
137 void sbusreset __P((int));
138
139 static bus_space_tag_t sbus_alloc_bustag __P((struct sbus_softc *));
140 static bus_dma_tag_t sbus_alloc_dmatag __P((struct sbus_softc *));
141 static int sbus_get_intr __P((struct sbus_softc *, int,
142 struct sbus_intr **, int *));
143 static int sbus_bus_mmap __P((bus_space_tag_t, bus_type_t, bus_addr_t,
144 int, bus_space_handle_t *));
145 static int _sbus_bus_map __P((
146 bus_space_tag_t,
147 bus_type_t,
148 bus_addr_t, /*offset*/
149 bus_size_t, /*size*/
150 int, /*flags*/
151 vaddr_t, /*preferred virtual address */
152 bus_space_handle_t *));
153 static void *sbus_intr_establish __P((
154 bus_space_tag_t,
155 int, /*level*/
156 int, /*flags*/
157 int (*) __P((void *)), /*handler*/
158 void *)); /*handler arg*/
159
160
161 /* autoconfiguration driver */
162 int sbus_match __P((struct device *, struct cfdata *, void *));
163 void sbus_attach __P((struct device *, struct device *, void *));
164
165
166 struct cfattach sbus_ca = {
167 sizeof(struct sbus_softc), sbus_match, sbus_attach
168 };
169
170 extern struct cfdriver sbus_cd;
171
172 /*
173 * DVMA routines
174 */
175 int sbus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *,
176 bus_size_t, struct proc *, int));
177 void sbus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t));
178 void sbus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t, bus_addr_t,
179 bus_size_t, int));
180 int sbus_dmamem_alloc __P((bus_dma_tag_t tag, bus_size_t size,
181 bus_size_t alignment, bus_size_t boundary,
182 bus_dma_segment_t *segs, int nsegs, int *rsegs, int flags));
183 void sbus_dmamem_free __P((bus_dma_tag_t tag, bus_dma_segment_t *segs,
184 int nsegs));
185 int sbus_dmamem_map __P((bus_dma_tag_t tag, bus_dma_segment_t *segs,
186 int nsegs, size_t size, caddr_t *kvap, int flags));
187 void sbus_dmamem_unmap __P((bus_dma_tag_t tag, caddr_t kva,
188 size_t size));
189
190
191 /*
192 * Child devices receive the Sbus interrupt level in their attach
193 * arguments. We translate these to CPU IPLs using the following
194 * tables. Note: obio bus interrupt levels are identical to the
195 * processor IPL.
196 *
197 * The second set of tables is used when the Sbus interrupt level
198 * cannot be had from the PROM as an `interrupt' property. We then
199 * fall back on the `intr' property which contains the CPU IPL.
200 */
201
202 /* Translate Sbus interrupt level to processor IPL */
203 static int intr_sbus2ipl_4c[] = {
204 0, 1, 2, 3, 5, 7, 8, 9
205 };
206 static int intr_sbus2ipl_4m[] = {
207 0, 2, 3, 5, 7, 9, 11, 13
208 };
209
210 /*
211 * This value is or'ed into the attach args' interrupt level cookie
212 * if the interrupt level comes from an `intr' property, i.e. it is
213 * not an Sbus interrupt level.
214 */
215 #define SBUS_INTR_COMPAT 0x80000000
216
217
218 /*
219 * Print the location of some sbus-attached device (called just
220 * before attaching that device). If `sbus' is not NULL, the
221 * device was found but not configured; print the sbus as well.
222 * Return UNCONF (config_find ignores this if the device was configured).
223 */
224 int
225 sbus_print(args, busname)
226 void *args;
227 const char *busname;
228 {
229 struct sbus_attach_args *sa = args;
230 int i;
231
232 if (busname)
233 printf("%s at %s", sa->sa_name, busname);
234 printf(" slot %ld offset 0x%lx", (long)sa->sa_slot,
235 (u_long)sa->sa_offset);
236 for (i=0; i<sa->sa_nintr; i++) {
237 struct sbus_intr *sbi = &sa->sa_intr[i];
238
239 printf(" vector %lx ipl %ld",
240 (u_long)sbi->sbi_vec,
241 (long)INTLEV(sbi->sbi_pri));
242 }
243 return (UNCONF);
244 }
245
246 int
247 sbus_match(parent, cf, aux)
248 struct device *parent;
249 struct cfdata *cf;
250 void *aux;
251 {
252 struct mainbus_attach_args *ma = aux;
253
254 return (strcmp(cf->cf_driver->cd_name, ma->ma_name) == 0);
255 }
256
257 /*
258 * Attach an Sbus.
259 */
260 void
261 sbus_attach(parent, self, aux)
262 struct device *parent;
263 struct device *self;
264 void *aux;
265 {
266 struct sbus_softc *sc = (struct sbus_softc *)self;
267 struct mainbus_attach_args *ma = aux;
268 int node = ma->ma_node;
269
270 int node0, error;
271 bus_space_tag_t sbt;
272 struct sbus_attach_args sa;
273 char *busname = "sbus";
274 struct bootpath *bp = ma->ma_bp;
275
276
277 sc->sc_bustag = ma->ma_bustag;
278 sc->sc_dmatag = ma->ma_dmatag;
279 sc->sc_sysio = (struct sysioreg*)(u_long)ma->ma_address[0]; /* Use prom mapping for sysio. */
280 sc->sc_ign = ma->ma_interrupts[0] & INTMAP_IGN; /* Find interrupt group no */
281
282 /* Setup interrupt translation tables */
283 sc->sc_intr2ipl = CPU_ISSUN4C
284 ? intr_sbus2ipl_4c
285 : intr_sbus2ipl_4m;
286
287 /*
288 * Record clock frequency for synchronous SCSI.
289 * IS THIS THE CORRECT DEFAULT??
290 */
291 sc->sc_clockfreq = getpropint(node, "clock-frequency", 25*1000*1000);
292 printf(": clock = %s MHz\n", clockfreq(sc->sc_clockfreq));
293
294 sbt = sbus_alloc_bustag(sc);
295 sc->sc_dmatag = sbus_alloc_dmatag(sc);
296
297 /*
298 * Get the SBus burst transfer size if burst transfers are supported
299 */
300 sc->sc_burst = getpropint(node, "burst-sizes", 0);
301
302 /* Propagate bootpath */
303 if (bp != NULL && strcmp(bp->name, busname) == 0)
304 bp++;
305 else
306 bp = NULL;
307
308 /*
309 * Collect address translations from the OBP.
310 */
311 error = getprop(node, "ranges", sizeof(struct sbus_range),
312 &sc->sc_nrange, (void **)&sc->sc_range);
313 if (error)
314 panic("%s: error getting ranges property", sc->sc_dev.dv_xname);
315
316 /* initailise the IOMMU */
317
318 /* punch in our copies */
319 sc->sc_is.is_bustag = sc->sc_bustag;
320 sc->sc_is.is_iommu = &sc->sc_sysio->sys_iommu;
321 sc->sc_is.is_sb = &sc->sc_sysio->sys_strbuf;
322
323 #ifdef DEBUG
324 if (sbusdebug & SDB_DVMA)
325 printf("sysio base %p phys %p\n",
326 (long)sc->sc_sysio, (long)pmap_extract(pmap_kernel(), (vaddr_t)sc->sc_sysio));
327 #endif
328
329 /* XXX should have instance number */
330 iommu_init("SBus dvma", &sc->sc_is, 0);
331
332 /*
333 * Loop through ROM children, fixing any relative addresses
334 * and then configuring each device.
335 * `specials' is an array of device names that are treated
336 * specially:
337 */
338 node0 = firstchild(node);
339 for (node = node0; node; node = nextsibling(node)) {
340 char *name = getpropstring(node, "name");
341
342 if (sbus_setup_attach_args(sc, sbt, sc->sc_dmatag,
343 node, bp, &sa) != 0) {
344 printf("sbus_attach: %s: incomplete\n", name);
345 continue;
346 }
347 (void) config_found(&sc->sc_dev, (void *)&sa, sbus_print);
348 sbus_destroy_attach_args(&sa);
349 }
350 }
351
352 int
353 sbus_setup_attach_args(sc, bustag, dmatag, node, bp, sa)
354 struct sbus_softc *sc;
355 bus_space_tag_t bustag;
356 bus_dma_tag_t dmatag;
357 int node;
358 struct bootpath *bp;
359 struct sbus_attach_args *sa;
360 {
361 /*struct sbus_reg sbusreg;*/
362 /*int base;*/
363 int error;
364 int n;
365
366 bzero(sa, sizeof(struct sbus_attach_args));
367 error = getprop(node, "name", 1, &n, (void **)&sa->sa_name);
368 if (error != 0)
369 return (error);
370 sa->sa_name[n] = '\0';
371
372 sa->sa_bustag = bustag;
373 sa->sa_dmatag = dmatag;
374 sa->sa_node = node;
375 sa->sa_bp = bp;
376
377 error = getprop(node, "reg", sizeof(struct sbus_reg),
378 &sa->sa_nreg, (void **)&sa->sa_reg);
379 if (error != 0) {
380 char buf[32];
381 if (error != ENOENT ||
382 !node_has_property(node, "device_type") ||
383 strcmp(getpropstringA(node, "device_type", buf),
384 "hierarchical") != 0)
385 return (error);
386 }
387 for (n = 0; n < sa->sa_nreg; n++) {
388 /* Convert to relative addressing, if necessary */
389 u_int32_t base = sa->sa_reg[n].sbr_offset;
390 if (SBUS_ABS(base)) {
391 sa->sa_reg[n].sbr_slot = SBUS_ABS_TO_SLOT(base);
392 sa->sa_reg[n].sbr_offset = SBUS_ABS_TO_OFFSET(base);
393 }
394 }
395
396 if ((error = sbus_get_intr(sc, node, &sa->sa_intr, &sa->sa_nintr)) != 0)
397 return (error);
398
399 error = getprop(node, "address", sizeof(u_int32_t),
400 &sa->sa_npromvaddrs, (void **)&sa->sa_promvaddrs);
401 if (error != 0 && error != ENOENT)
402 return (error);
403
404 return (0);
405 }
406
407 void
408 sbus_destroy_attach_args(sa)
409 struct sbus_attach_args *sa;
410 {
411 if (sa->sa_name != NULL)
412 free(sa->sa_name, M_DEVBUF);
413
414 if (sa->sa_nreg != 0)
415 free(sa->sa_reg, M_DEVBUF);
416
417 if (sa->sa_intr)
418 free(sa->sa_intr, M_DEVBUF);
419
420 if (sa->sa_promvaddrs)
421 free((void *)sa->sa_promvaddrs, M_DEVBUF);
422
423 bzero(sa, sizeof(struct sbus_attach_args));/*DEBUG*/
424 }
425
426
427 int
428 _sbus_bus_map(t, btype, offset, size, flags, vaddr, hp)
429 bus_space_tag_t t;
430 bus_type_t btype;
431 bus_addr_t offset;
432 bus_size_t size;
433 int flags;
434 vaddr_t vaddr;
435 bus_space_handle_t *hp;
436 {
437 struct sbus_softc *sc = t->cookie;
438 int64_t slot = btype;
439 int i;
440
441 for (i = 0; i < sc->sc_nrange; i++) {
442 bus_addr_t paddr;
443
444 if (sc->sc_range[i].cspace != slot)
445 continue;
446
447 /* We've found the connection to the parent bus */
448 paddr = sc->sc_range[i].poffset + offset;
449 paddr |= ((bus_addr_t)sc->sc_range[i].pspace<<32);
450 #ifdef DEBUG
451 if (sbusdebug & SDB_DVMA)
452 printf("\n_sbus_bus_map: mapping paddr slot %lx offset %lx poffset %lx paddr %lx\n",
453 (long)slot, (long)offset, (long)sc->sc_range[i].poffset, (long)paddr);
454 #endif
455 return (bus_space_map2(sc->sc_bustag, 0, paddr,
456 size, flags, vaddr, hp));
457 }
458
459 return (EINVAL);
460 }
461
462 int
463 sbus_bus_mmap(t, btype, paddr, flags, hp)
464 bus_space_tag_t t;
465 bus_type_t btype;
466 bus_addr_t paddr;
467 int flags;
468 bus_space_handle_t *hp;
469 {
470 bus_addr_t offset = paddr;
471 int slot = (paddr>>32);
472 struct sbus_softc *sc = t->cookie;
473 int i;
474
475 for (i = 0; i < sc->sc_nrange; i++) {
476 bus_addr_t paddr;
477
478 if (sc->sc_range[i].cspace != slot)
479 continue;
480
481 paddr = sc->sc_range[i].poffset + offset;
482 paddr |= ((bus_addr_t)sc->sc_range[i].pspace<<32);
483 return (bus_space_mmap(sc->sc_bustag, 0, paddr,
484 flags, hp));
485 }
486
487 return (-1);
488 }
489
490
491 /*
492 * Each attached device calls sbus_establish after it initializes
493 * its sbusdev portion.
494 */
495 void
496 sbus_establish(sd, dev)
497 register struct sbusdev *sd;
498 register struct device *dev;
499 {
500 register struct sbus_softc *sc;
501 register struct device *curdev;
502
503 /*
504 * We have to look for the sbus by name, since it is not necessarily
505 * our immediate parent (i.e. sun4m /iommu/sbus/espdma/esp)
506 * We don't just use the device structure of the above-attached
507 * sbus, since we might (in the future) support multiple sbus's.
508 */
509 for (curdev = dev->dv_parent; ; curdev = curdev->dv_parent) {
510 if (!curdev || !curdev->dv_xname)
511 panic("sbus_establish: can't find sbus parent for %s",
512 sd->sd_dev->dv_xname
513 ? sd->sd_dev->dv_xname
514 : "<unknown>" );
515
516 if (strncmp(curdev->dv_xname, "sbus", 4) == 0)
517 break;
518 }
519 sc = (struct sbus_softc *) curdev;
520
521 sd->sd_dev = dev;
522 sd->sd_bchain = sc->sc_sbdev;
523 sc->sc_sbdev = sd;
524 }
525
526 /*
527 * Reset the given sbus. (???)
528 */
529 void
530 sbusreset(sbus)
531 int sbus;
532 {
533 register struct sbusdev *sd;
534 struct sbus_softc *sc = sbus_cd.cd_devs[sbus];
535 struct device *dev;
536
537 printf("reset %s:", sc->sc_dev.dv_xname);
538 for (sd = sc->sc_sbdev; sd != NULL; sd = sd->sd_bchain) {
539 if (sd->sd_reset) {
540 dev = sd->sd_dev;
541 (*sd->sd_reset)(dev);
542 printf(" %s", dev->dv_xname);
543 }
544 }
545 /* Reload iommu regs */
546 iommu_reset(&sc->sc_is);
547 }
548
549 /*
550 * Get interrupt attributes for an Sbus device.
551 */
552 int
553 sbus_get_intr(sc, node, ipp, np)
554 struct sbus_softc *sc;
555 int node;
556 struct sbus_intr **ipp;
557 int *np;
558 {
559 int *ipl;
560 int i, n, error;
561 char buf[32];
562
563 /*
564 * The `interrupts' property contains the Sbus interrupt level.
565 */
566 ipl = NULL;
567 if (getprop(node, "interrupts", sizeof(int), np, (void **)&ipl) == 0) {
568 /* Change format to an `struct sbus_intr' array */
569 struct sbus_intr *ip;
570 /* Default to interrupt level 2 -- otherwise unused */
571 int pri = INTLEVENCODE(2);
572 ip = malloc(*np * sizeof(struct sbus_intr), M_DEVBUF, M_NOWAIT);
573 if (ip == NULL)
574 return (ENOMEM);
575 /* Now things get ugly. We need to take this value which is
576 * the interrupt vector number and encode the IPL into it
577 * somehow. Luckily, the interrupt vector has lots of free
578 * space and we can easily stuff the IPL in there for a while.
579 */
580 getpropstringA(node, "device_type", buf);
581 if (!buf[0]) {
582 getpropstringA(node, "name", buf);
583 }
584 for (i=0; intrmap[i].in_class; i++) {
585 if (strcmp(intrmap[i].in_class, buf) == 0) {
586 pri = INTLEVENCODE(intrmap[i].in_lev);
587 break;
588 }
589 }
590 for (n = 0; n < *np; n++) {
591 /*
592 * We encode vector and priority into sbi_pri so we
593 * can pass them as a unit. This will go away if
594 * sbus_establish ever takes an sbus_intr instead
595 * of an integer level.
596 * Stuff the real vector in sbi_vec.
597 */
598 ip[n].sbi_pri = pri|ipl[n];
599 ip[n].sbi_vec = ipl[n];
600 }
601 free(ipl, M_DEVBUF);
602 *ipp = ip;
603 return (0);
604 }
605
606 /* We really don't support the following */
607 /* printf("\nWARNING: sbus_get_intr() \"interrupts\" not found -- using \"intr\"\n"); */
608 /* And some devices don't even have interrupts */
609 /*
610 * Fall back on `intr' property.
611 */
612 *ipp = NULL;
613 error = getprop(node, "intr", sizeof(struct sbus_intr),
614 np, (void **)ipp);
615 switch (error) {
616 case 0:
617 for (n = *np; n-- > 0;) {
618 /*
619 * Move the interrupt vector into place.
620 * We could remap the level, but the SBUS priorities
621 * are probably good enough.
622 */
623 (*ipp)[n].sbi_vec = (*ipp)[n].sbi_pri;
624 (*ipp)[n].sbi_pri |= INTLEVENCODE((*ipp)[n].sbi_pri);
625 }
626 break;
627 case ENOENT:
628 error = 0;
629 break;
630 }
631
632 return (error);
633 }
634
635
636 /*
637 * Install an interrupt handler for an Sbus device.
638 */
639 void *
640 sbus_intr_establish(t, level, flags, handler, arg)
641 bus_space_tag_t t;
642 int level;
643 int flags;
644 int (*handler) __P((void *));
645 void *arg;
646 {
647 struct sbus_softc *sc = t->cookie;
648 struct intrhand *ih;
649 int ipl;
650 long vec = level;
651
652 ih = (struct intrhand *)
653 malloc(sizeof(struct intrhand), M_DEVBUF, M_NOWAIT);
654 if (ih == NULL)
655 return (NULL);
656
657 if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) != 0)
658 ipl = vec;
659 else if ((vec & SBUS_INTR_COMPAT) != 0)
660 ipl = vec & ~SBUS_INTR_COMPAT;
661 else {
662 /* Decode and remove IPL */
663 ipl = INTLEV(vec);
664 vec = INTVEC(vec);
665 #ifdef DEBUG
666 if (sbusdebug & SDB_INTR) {
667 printf("\nsbus: intr[%ld]%lx: %lx\n", (long)ipl, (long)vec,
668 intrlev[vec]);
669 printf("Hunting for IRQ...\n");
670 }
671 #endif
672 if ((vec & INTMAP_OBIO) == 0) {
673 /* We're in an SBUS slot */
674 /* Register the map and clear intr registers */
675 #ifdef DEBUG
676 if (sbusdebug & SDB_INTR) {
677 int64_t *intrptr = &(&sc->sc_sysio->sbus_slot0_int)[INTSLOT(vec)];
678 int64_t intrmap = *intrptr;
679
680 printf("Found SBUS %lx IRQ as %llx in slot %ld\n",
681 (long)vec, (long)intrmap,
682 (long)INTSLOT(vec));
683 }
684 #endif
685 ih->ih_map = &(&sc->sc_sysio->sbus_slot0_int)[INTSLOT(vec)];
686 ih->ih_clr = &sc->sc_sysio->sbus0_clr_int[INTVEC(vec)];
687 /* Enable the interrupt */
688 vec |= INTMAP_V;
689 /* Insert IGN */
690 vec |= sc->sc_ign;
691 bus_space_write_8(sc->sc_bustag, ih->ih_map, 0, vec);
692 } else {
693 int64_t *intrptr = &sc->sc_sysio->scsi_int_map;
694 int64_t intrmap = 0;
695 int i;
696
697 /* Insert IGN */
698 vec |= sc->sc_ign;
699 for (i=0;
700 &intrptr[i] <= (int64_t *)&sc->sc_sysio->reserved_int_map &&
701 INTVEC(intrmap=intrptr[i]) != INTVEC(vec);
702 i++);
703 if (INTVEC(intrmap) == INTVEC(vec)) {
704 #ifdef DEBUG
705 if (sbusdebug & SDB_INTR)
706 printf("Found OBIO %lx IRQ as %lx in slot %d\n",
707 vec, (long)intrmap, i);
708 #endif
709 /* Register the map and clear intr registers */
710 ih->ih_map = &intrptr[i];
711 intrptr = (int64_t *)&sc->sc_sysio->scsi_clr_int;
712 ih->ih_clr = &intrptr[i];
713 /* Enable the interrupt */
714 intrmap |= INTMAP_V;
715 bus_space_write_8(sc->sc_bustag, ih->ih_map, 0, (u_long)intrmap);
716 } else panic("IRQ not found!");
717 }
718 }
719 #ifdef DEBUG
720 if (sbusdebug & SDB_INTR) { long i; for (i=0; i<1400000000; i++); }
721 #endif
722
723 ih->ih_fun = handler;
724 ih->ih_arg = arg;
725 ih->ih_number = vec;
726 ih->ih_pil = (1<<ipl);
727 intr_establish(ipl, ih);
728 return (ih);
729 }
730
731 static bus_space_tag_t
732 sbus_alloc_bustag(sc)
733 struct sbus_softc *sc;
734 {
735 bus_space_tag_t sbt;
736
737 sbt = (bus_space_tag_t)
738 malloc(sizeof(struct sparc_bus_space_tag), M_DEVBUF, M_NOWAIT);
739 if (sbt == NULL)
740 return (NULL);
741
742 bzero(sbt, sizeof *sbt);
743 sbt->cookie = sc;
744 sbt->parent = sc->sc_bustag;
745 sbt->type = SBUS_BUS_SPACE;
746 sbt->sparc_bus_map = _sbus_bus_map;
747 sbt->sparc_bus_mmap = sbus_bus_mmap;
748 sbt->sparc_intr_establish = sbus_intr_establish;
749 return (sbt);
750 }
751
752
753 static bus_dma_tag_t
754 sbus_alloc_dmatag(sc)
755 struct sbus_softc *sc;
756 {
757 bus_dma_tag_t sdt, psdt = sc->sc_dmatag;
758
759 sdt = (bus_dma_tag_t)
760 malloc(sizeof(struct sparc_bus_dma_tag), M_DEVBUF, M_NOWAIT);
761 if (sdt == NULL)
762 /* Panic? */
763 return (psdt);
764
765 sdt->_cookie = sc;
766 sdt->_parent = psdt;
767 #define PCOPY(x) sdt->x = psdt->x
768 PCOPY(_dmamap_create);
769 PCOPY(_dmamap_destroy);
770 sdt->_dmamap_load = sbus_dmamap_load;
771 PCOPY(_dmamap_load_mbuf);
772 PCOPY(_dmamap_load_uio);
773 PCOPY(_dmamap_load_raw);
774 sdt->_dmamap_unload = sbus_dmamap_unload;
775 sdt->_dmamap_sync = sbus_dmamap_sync;
776 sdt->_dmamem_alloc = sbus_dmamem_alloc;
777 sdt->_dmamem_free = sbus_dmamem_free;
778 sdt->_dmamem_map = sbus_dmamem_map;
779 sdt->_dmamem_unmap = sbus_dmamem_unmap;
780 PCOPY(_dmamem_mmap);
781 #undef PCOPY
782 sc->sc_dmatag = sdt;
783 return (sdt);
784 }
785
786 int
787 sbus_dmamap_load(t, map, buf, buflen, p, flags)
788 bus_dma_tag_t t;
789 bus_dmamap_t map;
790 void *buf;
791 bus_size_t buflen;
792 struct proc *p;
793 int flags;
794 {
795 int err, s;
796 bus_size_t sgsize;
797 paddr_t curaddr;
798 u_long dvmaddr;
799 vaddr_t vaddr = (vaddr_t)buf;
800 pmap_t pmap;
801 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
802
803 if (map->dm_nsegs) {
804 /* Already in use?? */
805 #ifdef DIAGNOSTIC
806 printf("sbus_dmamap_load: map still in use\n");
807 #endif
808 bus_dmamap_unload(t, map);
809 }
810
811 /*
812 * Make sure that on error condition we return "no valid mappings".
813 */
814 map->dm_nsegs = 0;
815
816 if (buflen > map->_dm_size)
817 #ifdef DEBUG
818 {
819 printf("sbus_dmamap_load(): error %d > %d -- map size exceeded!\n", buflen, map->_dm_size);
820 Debugger();
821 return (EINVAL);
822 }
823 #else
824 return (EINVAL);
825 #endif
826 sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
827
828 /*
829 * XXX Need to implement "don't dma across this boundry".
830 */
831
832 s = splhigh();
833 err = extent_alloc(sc->sc_is.is_dvmamap, sgsize, NBPG,
834 map->_dm_boundary, EX_NOWAIT, (u_long *)&dvmaddr);
835 splx(s);
836
837 if (err != 0)
838 return (err);
839
840 #ifdef DEBUG
841 if (dvmaddr == (bus_addr_t)-1)
842 {
843 printf("sbus_dmamap_load(): dvmamap_alloc(%d, %x) failed!\n", sgsize, flags);
844 Debugger();
845 }
846 #endif
847 if (dvmaddr == (bus_addr_t)-1)
848 return (ENOMEM);
849
850 /*
851 * We always use just one segment.
852 */
853 map->dm_mapsize = buflen;
854 map->dm_nsegs = 1;
855 map->dm_segs[0].ds_addr = dvmaddr + (vaddr & PGOFSET);
856 map->dm_segs[0].ds_len = sgsize;
857
858 if (p != NULL)
859 pmap = p->p_vmspace->vm_map.pmap;
860 else
861 pmap = pmap_kernel();
862
863 dvmaddr = trunc_page(map->dm_segs[0].ds_addr);
864 sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
865 for (; buflen > 0; ) {
866 /*
867 * Get the physical address for this page.
868 */
869 if ((curaddr = (bus_addr_t)pmap_extract(pmap, (vaddr_t)vaddr)) == NULL) {
870 bus_dmamap_unload(t, map);
871 return (-1);
872 }
873
874 /*
875 * Compute the segment size, and adjust counts.
876 */
877 sgsize = NBPG - ((u_long)vaddr & PGOFSET);
878 if (buflen < sgsize)
879 sgsize = buflen;
880
881 #ifdef DEBUG
882 if (sbusdebug & SDB_DVMA)
883 printf("sbus_dmamap_load: map %p loading va %lx at pa %lx\n",
884 map, (long)dvmaddr, (long)(curaddr & ~(NBPG-1)));
885 #endif
886 iommu_enter(&sc->sc_is, trunc_page(dvmaddr), trunc_page(curaddr), flags);
887
888 dvmaddr += PAGE_SIZE;
889 vaddr += sgsize;
890 buflen -= sgsize;
891 }
892 return (0);
893 }
894
895 void
896 sbus_dmamap_unload(t, map)
897 bus_dma_tag_t t;
898 bus_dmamap_t map;
899 {
900 vaddr_t addr;
901 int len, error, s;
902 bus_addr_t dvmaddr;
903 bus_size_t sgsize;
904 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
905
906 if (map->dm_nsegs != 1)
907 panic("sbus_dmamap_unload: nsegs = %d", map->dm_nsegs);
908
909 addr = trunc_page(map->dm_segs[0].ds_addr);
910 len = map->dm_segs[0].ds_len;
911
912 #ifdef DEBUG
913 if (sbusdebug & SDB_DVMA)
914 printf("sbus_dmamap_unload: map %p removing va %lx size %lx\n",
915 map, (long)addr, (long)len);
916 #endif
917 iommu_remove(&sc->sc_is, addr, len);
918 dvmaddr = (map->dm_segs[0].ds_addr & ~PGOFSET);
919 sgsize = map->dm_segs[0].ds_len;
920
921 /* Mark the mappings as invalid. */
922 map->dm_mapsize = 0;
923 map->dm_nsegs = 0;
924
925 /* Unmapping is bus dependent */
926 s = splhigh();
927 error = extent_free(sc->sc_is.is_dvmamap, dvmaddr, sgsize, EX_NOWAIT);
928 splx(s);
929 if (error != 0)
930 printf("warning: %ld of DVMA space lost\n", (long)sgsize);
931
932 cache_flush((caddr_t)dvmaddr, (u_int) sgsize);
933 }
934
935
936 void
937 sbus_dmamap_sync(t, map, offset, len, ops)
938 bus_dma_tag_t t;
939 bus_dmamap_t map;
940 bus_addr_t offset;
941 bus_size_t len;
942 int ops;
943 {
944 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
945 vaddr_t va = map->dm_segs[0].ds_addr + offset;
946
947 /*
948 * We only support one DMA segment; supporting more makes this code
949 * too unweildy.
950 */
951
952 if (ops&BUS_DMASYNC_PREREAD) {
953 #ifdef DEBUG
954 if (sbusdebug & SDB_DVMA)
955 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_PREREAD\n",
956 (long)va, (u_long)len);
957 #endif
958
959 /* Nothing to do */;
960 }
961 if (ops&BUS_DMASYNC_POSTREAD) {
962 /*
963 * We should sync the IOMMU streaming caches here first.
964 */
965 #ifdef DEBUG
966 if (sbusdebug & SDB_DVMA)
967 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_POSTREAD\n",
968 (long)va, (u_long)len);
969 #endif
970 while (len > 0) {
971
972 /*
973 * Streaming buffer flushes:
974 *
975 * 1 Tell strbuf to flush by storing va to strbuf_pgflush
976 * If we're not on a cache line boundary (64-bits):
977 * 2 Store 0 in flag
978 * 3 Store pointer to flag in flushsync
979 * 4 wait till flushsync becomes 0x1
980 *
981 * If it takes more than .5 sec, something went wrong.
982 */
983 #ifdef DEBUG
984 if (sbusdebug & SDB_DVMA)
985 printf("sbus_dmamap_sync: flushing va %p, %lu bytes left\n",
986 (long)va, (u_long)len);
987 #endif
988 bus_space_write_8(sc->sc_bustag, &sc->sc_is.is_sb->strbuf_pgflush, 0, va);
989 if (len <= NBPG) {
990 iommu_flush(&sc->sc_is);
991 len = 0;
992 } else
993 len -= NBPG;
994 va += NBPG;
995 }
996 }
997 if (ops&BUS_DMASYNC_PREWRITE) {
998 #ifdef DEBUG
999 if (sbusdebug & SDB_DVMA)
1000 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_PREWRITE\n",
1001 (long)va, (u_long)len);
1002 #endif
1003 /* Nothing to do */;
1004 }
1005 if (ops&BUS_DMASYNC_POSTWRITE) {
1006 #ifdef DEBUG
1007 if (sbusdebug & SDB_DVMA)
1008 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_POSTWRITE\n",
1009 (long)va, (u_long)len);
1010 #endif
1011 /* Nothing to do */;
1012 }
1013 bus_dmamap_sync(t->_parent, map, offset, len, ops);
1014 }
1015
1016
1017 /*
1018 * Take memory allocated by our parent bus and generate DVMA mappings for it.
1019 */
1020 int
1021 sbus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags)
1022 bus_dma_tag_t t;
1023 bus_size_t size, alignment, boundary;
1024 bus_dma_segment_t *segs;
1025 int nsegs;
1026 int *rsegs;
1027 int flags;
1028 {
1029 paddr_t curaddr;
1030 u_long dvmaddr;
1031 vm_page_t m;
1032 struct pglist *mlist;
1033 int error;
1034 int n, s;
1035 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
1036
1037 if ((error = bus_dmamem_alloc(t->_parent, size, alignment,
1038 boundary, segs, nsegs, rsegs, flags)))
1039 return (error);
1040
1041 /*
1042 * Allocate a DVMA mapping for our new memory.
1043 */
1044 for (n = 0; n < *rsegs; n++) {
1045 #if 1
1046 s = splhigh();
1047 if (extent_alloc(sc->sc_is.is_dvmamap, segs[0].ds_len, alignment,
1048 boundary, EX_NOWAIT, (u_long *)&dvmaddr)) {
1049 splx(s);
1050 /* Free what we got and exit */
1051 bus_dmamem_free(t->_parent, segs, nsegs);
1052 return (ENOMEM);
1053 }
1054 splx(s);
1055 #else
1056 dvmaddr = dvmamap_alloc(segs[0].ds_len, flags);
1057 if (dvmaddr == (bus_addr_t)-1) {
1058 /* Free what we got and exit */
1059 bus_dmamem_free(t->_parent, segs, nsegs);
1060 return (ENOMEM);
1061 }
1062 #endif
1063 segs[n].ds_addr = dvmaddr;
1064 size = segs[n].ds_len;
1065 mlist = segs[n]._ds_mlist;
1066
1067 /* Map memory into DVMA space */
1068 for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) {
1069 curaddr = VM_PAGE_TO_PHYS(m);
1070 #ifdef DEBUG
1071 if (sbusdebug & SDB_DVMA)
1072 printf("sbus_dmamem_alloc: map %p loading va %lx at pa %lx\n",
1073 (long)m, (long)dvmaddr, (long)(curaddr & ~(NBPG-1)));
1074 #endif
1075 iommu_enter(&sc->sc_is, dvmaddr, curaddr, flags);
1076 dvmaddr += PAGE_SIZE;
1077 }
1078 }
1079 return (0);
1080 }
1081
1082 void
1083 sbus_dmamem_free(t, segs, nsegs)
1084 bus_dma_tag_t t;
1085 bus_dma_segment_t *segs;
1086 int nsegs;
1087 {
1088 vaddr_t addr;
1089 int len;
1090 int n, s, error;
1091 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
1092
1093
1094 for (n=0; n<nsegs; n++) {
1095 addr = segs[n].ds_addr;
1096 len = segs[n].ds_len;
1097 iommu_remove(&sc->sc_is, addr, len);
1098 #if 1
1099 s = splhigh();
1100 error = extent_free(sc->sc_is.is_dvmamap, addr, len, EX_NOWAIT);
1101 splx(s);
1102 if (error != 0)
1103 printf("warning: %ld of DVMA space lost\n", (long)len);
1104 #else
1105 dvmamap_free(addr, len);
1106 #endif
1107 }
1108 bus_dmamem_free(t->_parent, segs, nsegs);
1109 }
1110
1111 /*
1112 * Map the DVMA mappings into the kernel pmap.
1113 * Check the flags to see whether we're streaming or coherent.
1114 */
1115 int
1116 sbus_dmamem_map(t, segs, nsegs, size, kvap, flags)
1117 bus_dma_tag_t t;
1118 bus_dma_segment_t *segs;
1119 int nsegs;
1120 size_t size;
1121 caddr_t *kvap;
1122 int flags;
1123 {
1124 vm_page_t m;
1125 vaddr_t va;
1126 bus_addr_t addr;
1127 struct pglist *mlist;
1128 int cbit;
1129
1130 /*
1131 * digest flags:
1132 */
1133 cbit = 0;
1134 if (flags & BUS_DMA_COHERENT) /* Disable vcache */
1135 cbit |= PMAP_NVC;
1136 if (flags & BUS_DMA_NOCACHE) /* sideffects */
1137 cbit |= PMAP_NC;
1138 /*
1139 * Now take this and map it into the CPU since it should already
1140 * be in the the IOMMU.
1141 */
1142 *kvap = (caddr_t)va = segs[0].ds_addr;
1143 mlist = segs[0]._ds_mlist;
1144 for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) {
1145
1146 if (size == 0)
1147 panic("_bus_dmamem_map: size botch");
1148
1149 addr = VM_PAGE_TO_PHYS(m);
1150 pmap_enter(pmap_kernel(), va, addr | cbit,
1151 VM_PROT_READ | VM_PROT_WRITE, TRUE,
1152 VM_PROT_READ | VM_PROT_WRITE);
1153 va += PAGE_SIZE;
1154 size -= PAGE_SIZE;
1155 }
1156
1157 return (0);
1158 }
1159
1160 /*
1161 * Unmap DVMA mappings from kernel
1162 */
1163 void
1164 sbus_dmamem_unmap(t, kva, size)
1165 bus_dma_tag_t t;
1166 caddr_t kva;
1167 size_t size;
1168 {
1169
1170 #ifdef DIAGNOSTIC
1171 if ((u_long)kva & PGOFSET)
1172 panic("_bus_dmamem_unmap");
1173 #endif
1174
1175 size = round_page(size);
1176 pmap_remove(pmap_kernel(), (vaddr_t)kva, size);
1177 }
1178