sbus.c revision 1.22 1 /* $NetBSD: sbus.c,v 1.22 1999/11/25 05:03:53 mrg 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 *, 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 /* XXX should have instance number */
324 iommu_init("SBus dvma", &sc->sc_is, 0);
325
326 /*
327 * Loop through ROM children, fixing any relative addresses
328 * and then configuring each device.
329 * `specials' is an array of device names that are treated
330 * specially:
331 */
332 node0 = firstchild(node);
333 for (node = node0; node; node = nextsibling(node)) {
334 char *name = getpropstring(node, "name");
335
336 if (sbus_setup_attach_args(sc, sbt, sc->sc_dmatag,
337 node, bp, &sa) != 0) {
338 printf("sbus_attach: %s: incomplete\n", name);
339 continue;
340 }
341 (void) config_found(&sc->sc_dev, (void *)&sa, sbus_print);
342 sbus_destroy_attach_args(&sa);
343 }
344 }
345
346 int
347 sbus_setup_attach_args(sc, bustag, dmatag, node, bp, sa)
348 struct sbus_softc *sc;
349 bus_space_tag_t bustag;
350 bus_dma_tag_t dmatag;
351 int node;
352 struct bootpath *bp;
353 struct sbus_attach_args *sa;
354 {
355 /*struct sbus_reg sbusreg;*/
356 /*int base;*/
357 int error;
358 int n;
359
360 bzero(sa, sizeof(struct sbus_attach_args));
361 error = getprop(node, "name", 1, &n, (void **)&sa->sa_name);
362 if (error != 0)
363 return (error);
364 sa->sa_name[n] = '\0';
365
366 sa->sa_bustag = bustag;
367 sa->sa_dmatag = dmatag;
368 sa->sa_node = node;
369 sa->sa_bp = bp;
370
371 error = getprop(node, "reg", sizeof(struct sbus_reg),
372 &sa->sa_nreg, (void **)&sa->sa_reg);
373 if (error != 0) {
374 char buf[32];
375 if (error != ENOENT ||
376 !node_has_property(node, "device_type") ||
377 strcmp(getpropstringA(node, "device_type", buf),
378 "hierarchical") != 0)
379 return (error);
380 }
381 for (n = 0; n < sa->sa_nreg; n++) {
382 /* Convert to relative addressing, if necessary */
383 u_int32_t base = sa->sa_reg[n].sbr_offset;
384 if (SBUS_ABS(base)) {
385 sa->sa_reg[n].sbr_slot = SBUS_ABS_TO_SLOT(base);
386 sa->sa_reg[n].sbr_offset = SBUS_ABS_TO_OFFSET(base);
387 }
388 }
389
390 if ((error = sbus_get_intr(sc, node, &sa->sa_intr, &sa->sa_nintr,
391 sa->sa_slot)) != 0)
392 return (error);
393
394 error = getprop(node, "address", sizeof(u_int32_t),
395 &sa->sa_npromvaddrs, (void **)&sa->sa_promvaddrs);
396 if (error != 0 && error != ENOENT)
397 return (error);
398
399 return (0);
400 }
401
402 void
403 sbus_destroy_attach_args(sa)
404 struct sbus_attach_args *sa;
405 {
406 if (sa->sa_name != NULL)
407 free(sa->sa_name, M_DEVBUF);
408
409 if (sa->sa_nreg != 0)
410 free(sa->sa_reg, M_DEVBUF);
411
412 if (sa->sa_intr)
413 free(sa->sa_intr, M_DEVBUF);
414
415 if (sa->sa_promvaddrs)
416 free((void *)sa->sa_promvaddrs, M_DEVBUF);
417
418 bzero(sa, sizeof(struct sbus_attach_args));/*DEBUG*/
419 }
420
421
422 int
423 _sbus_bus_map(t, btype, offset, size, flags, vaddr, hp)
424 bus_space_tag_t t;
425 bus_type_t btype;
426 bus_addr_t offset;
427 bus_size_t size;
428 int flags;
429 vaddr_t vaddr;
430 bus_space_handle_t *hp;
431 {
432 struct sbus_softc *sc = t->cookie;
433 int64_t slot = btype;
434 int i;
435
436 for (i = 0; i < sc->sc_nrange; i++) {
437 bus_addr_t paddr;
438
439 if (sc->sc_range[i].cspace != slot)
440 continue;
441
442 /* We've found the connection to the parent bus */
443 paddr = sc->sc_range[i].poffset + offset;
444 paddr |= ((bus_addr_t)sc->sc_range[i].pspace<<32);
445 #ifdef DEBUG
446 if (sbusdebug & SDB_DVMA)
447 printf("\n_sbus_bus_map: mapping paddr slot %lx offset %lx poffset %lx paddr %lx\n",
448 (long)slot, (long)offset, (long)sc->sc_range[i].poffset, (long)paddr);
449 #endif
450 return (bus_space_map2(sc->sc_bustag, 0, paddr,
451 size, flags, vaddr, hp));
452 }
453
454 return (EINVAL);
455 }
456
457 int
458 sbus_bus_mmap(t, btype, paddr, flags, hp)
459 bus_space_tag_t t;
460 bus_type_t btype;
461 bus_addr_t paddr;
462 int flags;
463 bus_space_handle_t *hp;
464 {
465 bus_addr_t offset = paddr;
466 int slot = (paddr>>32);
467 struct sbus_softc *sc = t->cookie;
468 int i;
469
470 for (i = 0; i < sc->sc_nrange; i++) {
471 bus_addr_t paddr;
472
473 if (sc->sc_range[i].cspace != slot)
474 continue;
475
476 paddr = sc->sc_range[i].poffset + offset;
477 paddr |= ((bus_addr_t)sc->sc_range[i].pspace<<32);
478 return (bus_space_mmap(sc->sc_bustag, 0, paddr,
479 flags, hp));
480 }
481
482 return (-1);
483 }
484
485
486 /*
487 * Each attached device calls sbus_establish after it initializes
488 * its sbusdev portion.
489 */
490 void
491 sbus_establish(sd, dev)
492 register struct sbusdev *sd;
493 register struct device *dev;
494 {
495 register struct sbus_softc *sc;
496 register struct device *curdev;
497
498 /*
499 * We have to look for the sbus by name, since it is not necessarily
500 * our immediate parent (i.e. sun4m /iommu/sbus/espdma/esp)
501 * We don't just use the device structure of the above-attached
502 * sbus, since we might (in the future) support multiple sbus's.
503 */
504 for (curdev = dev->dv_parent; ; curdev = curdev->dv_parent) {
505 if (!curdev || !curdev->dv_xname)
506 panic("sbus_establish: can't find sbus parent for %s",
507 sd->sd_dev->dv_xname
508 ? sd->sd_dev->dv_xname
509 : "<unknown>" );
510
511 if (strncmp(curdev->dv_xname, "sbus", 4) == 0)
512 break;
513 }
514 sc = (struct sbus_softc *) curdev;
515
516 sd->sd_dev = dev;
517 sd->sd_bchain = sc->sc_sbdev;
518 sc->sc_sbdev = sd;
519 }
520
521 /*
522 * Reset the given sbus. (???)
523 */
524 void
525 sbusreset(sbus)
526 int sbus;
527 {
528 register struct sbusdev *sd;
529 struct sbus_softc *sc = sbus_cd.cd_devs[sbus];
530 struct device *dev;
531
532 printf("reset %s:", sc->sc_dev.dv_xname);
533 for (sd = sc->sc_sbdev; sd != NULL; sd = sd->sd_bchain) {
534 if (sd->sd_reset) {
535 dev = sd->sd_dev;
536 (*sd->sd_reset)(dev);
537 printf(" %s", dev->dv_xname);
538 }
539 }
540 /* Reload iommu regs */
541 iommu_reset(&sc->sc_is);
542 }
543
544 /*
545 * Get interrupt attributes for an Sbus device.
546 */
547 int
548 sbus_get_intr(sc, node, ipp, np, slot)
549 struct sbus_softc *sc;
550 int node;
551 struct sbus_intr **ipp;
552 int *np;
553 int slot;
554 {
555 int *ipl;
556 int n, i;
557 char buf[32];
558
559 /*
560 * The `interrupts' property contains the Sbus interrupt level.
561 */
562 ipl = NULL;
563 if (getprop(node, "interrupts", sizeof(int), np, (void **)&ipl) == 0) {
564 struct sbus_intr *ip;
565 int pri;
566
567 /* Default to interrupt level 2 -- otherwise unused */
568 pri = INTLEVENCODE(2);
569
570 /* Change format to an `struct sbus_intr' array */
571 ip = malloc(*np * sizeof(struct sbus_intr), M_DEVBUF, M_NOWAIT);
572 if (ip == NULL)
573 return (ENOMEM);
574
575 /*
576 * Now things get ugly. We need to take this value which is
577 * the interrupt vector number and encode the IPL into it
578 * somehow. Luckily, the interrupt vector has lots of free
579 * space and we can easily stuff the IPL in there for a while.
580 */
581 getpropstringA(node, "device_type", buf);
582 if (!buf[0])
583 getpropstringA(node, "name", buf);
584
585 for (i = 0; intrmap[i].in_class; i++)
586 if (strcmp(intrmap[i].in_class, buf) == 0) {
587 pri = INTLEVENCODE(intrmap[i].in_lev);
588 break;
589 }
590
591 /*
592 * Sbus card devices need the slot number encoded into
593 * the vector as this is generally not done.
594 */
595 if ((ipl[0] & INTMAP_OBIO) == 0)
596 pri |= slot << 3;
597
598 for (n = 0; n < *np; n++) {
599 /*
600 * We encode vector and priority into sbi_pri so we
601 * can pass them as a unit. This will go away if
602 * sbus_establish ever takes an sbus_intr instead
603 * of an integer level.
604 * Stuff the real vector in sbi_vec.
605 */
606
607 ip[n].sbi_pri = pri|ipl[n];
608 ip[n].sbi_vec = ipl[n];
609 }
610 free(ipl, M_DEVBUF);
611 *ipp = ip;
612 }
613
614 return (0);
615 }
616
617
618 /*
619 * Install an interrupt handler for an Sbus device.
620 */
621 void *
622 sbus_intr_establish(t, level, flags, handler, arg)
623 bus_space_tag_t t;
624 int level;
625 int flags;
626 int (*handler) __P((void *));
627 void *arg;
628 {
629 struct sbus_softc *sc = t->cookie;
630 struct intrhand *ih;
631 int ipl;
632 long vec = level;
633
634 ih = (struct intrhand *)
635 malloc(sizeof(struct intrhand), M_DEVBUF, M_NOWAIT);
636 if (ih == NULL)
637 return (NULL);
638
639 if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) != 0)
640 ipl = vec;
641 else if ((vec & SBUS_INTR_COMPAT) != 0)
642 ipl = vec & ~SBUS_INTR_COMPAT;
643 else {
644 /* Decode and remove IPL */
645 ipl = INTLEV(vec);
646 vec = INTVEC(vec);
647 #ifdef DEBUG
648 if (sbusdebug & SDB_INTR) {
649 printf("\nsbus: intr[%ld]%lx: %lx\n", (long)ipl, (long)vec,
650 intrlev[vec]);
651 printf("Hunting for IRQ...\n");
652 }
653 #endif
654 if ((vec & INTMAP_OBIO) == 0) {
655 /* We're in an SBUS slot */
656 /* Register the map and clear intr registers */
657
658 int slot = INTSLOT(level);
659
660 ih->ih_map = &(&sc->sc_sysio->sbus_slot0_int)[slot];
661 ih->ih_clr = &sc->sc_sysio->sbus0_clr_int[vec];
662 #ifdef DEBUG
663 if (sbusdebug & SDB_INTR) {
664 int64_t intrmap = *ih->ih_map;
665
666 printf("Found SBUS %lx IRQ as %llx in slot %d\n",
667 (long)vec, (long long)intrmap, slot);
668 printf("\tmap addr %p clr addr %p\n", ih->ih_map, ih->ih_clr);
669 }
670 #endif
671 /* Enable the interrupt */
672 vec |= INTMAP_V;
673 /* Insert IGN */
674 vec |= sc->sc_ign;
675 bus_space_write_8(sc->sc_bustag, ih->ih_map, 0, vec);
676 } else {
677 int64_t *intrptr = &sc->sc_sysio->scsi_int_map;
678 int64_t intrmap = 0;
679 int i;
680
681 /* Insert IGN */
682 vec |= sc->sc_ign;
683 for (i = 0; &intrptr[i] <=
684 (int64_t *)&sc->sc_sysio->reserved_int_map &&
685 INTVEC(intrmap = intrptr[i]) != INTVEC(vec); i++)
686 ;
687 if (INTVEC(intrmap) == INTVEC(vec)) {
688 #ifdef DEBUG
689 if (sbusdebug & SDB_INTR)
690 printf("Found OBIO %lx IRQ as %lx in slot %d\n",
691 vec, (long)intrmap, i);
692 #endif
693 /* Register the map and clear intr registers */
694 ih->ih_map = &intrptr[i];
695 intrptr = (int64_t *)&sc->sc_sysio->scsi_clr_int;
696 ih->ih_clr = &intrptr[i];
697 /* Enable the interrupt */
698 intrmap |= INTMAP_V;
699 bus_space_write_8(sc->sc_bustag, ih->ih_map, 0, (u_long)intrmap);
700 } else panic("IRQ not found!");
701 }
702 }
703 #ifdef DEBUG
704 if (sbusdebug & SDB_INTR) { long i; for (i=0; i<400000000; i++); }
705 #endif
706
707 ih->ih_fun = handler;
708 ih->ih_arg = arg;
709 ih->ih_number = vec;
710 ih->ih_pil = (1<<ipl);
711 intr_establish(ipl, ih);
712 return (ih);
713 }
714
715 static bus_space_tag_t
716 sbus_alloc_bustag(sc)
717 struct sbus_softc *sc;
718 {
719 bus_space_tag_t sbt;
720
721 sbt = (bus_space_tag_t)
722 malloc(sizeof(struct sparc_bus_space_tag), M_DEVBUF, M_NOWAIT);
723 if (sbt == NULL)
724 return (NULL);
725
726 bzero(sbt, sizeof *sbt);
727 sbt->cookie = sc;
728 sbt->parent = sc->sc_bustag;
729 sbt->type = SBUS_BUS_SPACE;
730 sbt->sparc_bus_map = _sbus_bus_map;
731 sbt->sparc_bus_mmap = sbus_bus_mmap;
732 sbt->sparc_intr_establish = sbus_intr_establish;
733 return (sbt);
734 }
735
736
737 static bus_dma_tag_t
738 sbus_alloc_dmatag(sc)
739 struct sbus_softc *sc;
740 {
741 bus_dma_tag_t sdt, psdt = sc->sc_dmatag;
742
743 sdt = (bus_dma_tag_t)
744 malloc(sizeof(struct sparc_bus_dma_tag), M_DEVBUF, M_NOWAIT);
745 if (sdt == NULL)
746 /* Panic? */
747 return (psdt);
748
749 sdt->_cookie = sc;
750 sdt->_parent = psdt;
751 #define PCOPY(x) sdt->x = psdt->x
752 PCOPY(_dmamap_create);
753 PCOPY(_dmamap_destroy);
754 sdt->_dmamap_load = sbus_dmamap_load;
755 PCOPY(_dmamap_load_mbuf);
756 PCOPY(_dmamap_load_uio);
757 PCOPY(_dmamap_load_raw);
758 sdt->_dmamap_unload = sbus_dmamap_unload;
759 sdt->_dmamap_sync = sbus_dmamap_sync;
760 sdt->_dmamem_alloc = sbus_dmamem_alloc;
761 sdt->_dmamem_free = sbus_dmamem_free;
762 sdt->_dmamem_map = sbus_dmamem_map;
763 sdt->_dmamem_unmap = sbus_dmamem_unmap;
764 PCOPY(_dmamem_mmap);
765 #undef PCOPY
766 sc->sc_dmatag = sdt;
767 return (sdt);
768 }
769
770 int
771 sbus_dmamap_load(t, map, buf, buflen, p, flags)
772 bus_dma_tag_t t;
773 bus_dmamap_t map;
774 void *buf;
775 bus_size_t buflen;
776 struct proc *p;
777 int flags;
778 {
779 int err, s;
780 bus_size_t sgsize;
781 paddr_t curaddr;
782 u_long dvmaddr;
783 vaddr_t vaddr = (vaddr_t)buf;
784 pmap_t pmap;
785 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
786
787 if (map->dm_nsegs) {
788 /* Already in use?? */
789 #ifdef DIAGNOSTIC
790 printf("sbus_dmamap_load: map still in use\n");
791 #endif
792 bus_dmamap_unload(t, map);
793 }
794
795 /*
796 * Make sure that on error condition we return "no valid mappings".
797 */
798 map->dm_nsegs = 0;
799
800 if (buflen > map->_dm_size)
801 #ifdef DEBUG
802 {
803 printf("sbus_dmamap_load(): error %d > %d -- map size exceeded!\n", buflen, map->_dm_size);
804 Debugger();
805 return (EINVAL);
806 }
807 #else
808 return (EINVAL);
809 #endif
810 sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
811
812 /*
813 * XXX Need to implement "don't dma across this boundry".
814 */
815
816 s = splhigh();
817 err = extent_alloc(sc->sc_is.is_dvmamap, sgsize, NBPG,
818 map->_dm_boundary, EX_NOWAIT, (u_long *)&dvmaddr);
819 splx(s);
820
821 if (err != 0)
822 return (err);
823
824 #ifdef DEBUG
825 if (dvmaddr == (bus_addr_t)-1)
826 {
827 printf("sbus_dmamap_load(): dvmamap_alloc(%d, %x) failed!\n", sgsize, flags);
828 Debugger();
829 }
830 #endif
831 if (dvmaddr == (bus_addr_t)-1)
832 return (ENOMEM);
833
834 /*
835 * We always use just one segment.
836 */
837 map->dm_mapsize = buflen;
838 map->dm_nsegs = 1;
839 map->dm_segs[0].ds_addr = dvmaddr + (vaddr & PGOFSET);
840 map->dm_segs[0].ds_len = sgsize;
841
842 if (p != NULL)
843 pmap = p->p_vmspace->vm_map.pmap;
844 else
845 pmap = pmap_kernel();
846
847 dvmaddr = trunc_page(map->dm_segs[0].ds_addr);
848 sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
849 for (; buflen > 0; ) {
850 /*
851 * Get the physical address for this page.
852 */
853 if (pmap_extract(pmap, (vaddr_t)vaddr, &curaddr) == FALSE) {
854 bus_dmamap_unload(t, map);
855 return (-1);
856 }
857
858 /*
859 * Compute the segment size, and adjust counts.
860 */
861 sgsize = NBPG - ((u_long)vaddr & PGOFSET);
862 if (buflen < sgsize)
863 sgsize = buflen;
864
865 #ifdef DEBUG
866 if (sbusdebug & SDB_DVMA)
867 printf("sbus_dmamap_load: map %p loading va %lx at pa %lx\n",
868 map, (long)dvmaddr, (long)(curaddr & ~(NBPG-1)));
869 #endif
870 iommu_enter(&sc->sc_is, trunc_page(dvmaddr), trunc_page(curaddr), flags);
871
872 dvmaddr += PAGE_SIZE;
873 vaddr += sgsize;
874 buflen -= sgsize;
875 }
876 return (0);
877 }
878
879 void
880 sbus_dmamap_unload(t, map)
881 bus_dma_tag_t t;
882 bus_dmamap_t map;
883 {
884 vaddr_t addr;
885 int len, error, s;
886 bus_addr_t dvmaddr;
887 bus_size_t sgsize;
888 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
889
890 if (map->dm_nsegs != 1)
891 panic("sbus_dmamap_unload: nsegs = %d", map->dm_nsegs);
892
893 addr = trunc_page(map->dm_segs[0].ds_addr);
894 len = map->dm_segs[0].ds_len;
895
896 #ifdef DEBUG
897 if (sbusdebug & SDB_DVMA)
898 printf("sbus_dmamap_unload: map %p removing va %lx size %lx\n",
899 map, (long)addr, (long)len);
900 #endif
901 iommu_remove(&sc->sc_is, addr, len);
902 dvmaddr = (map->dm_segs[0].ds_addr & ~PGOFSET);
903 sgsize = map->dm_segs[0].ds_len;
904
905 /* Mark the mappings as invalid. */
906 map->dm_mapsize = 0;
907 map->dm_nsegs = 0;
908
909 /* Unmapping is bus dependent */
910 s = splhigh();
911 error = extent_free(sc->sc_is.is_dvmamap, dvmaddr, sgsize, EX_NOWAIT);
912 splx(s);
913 if (error != 0)
914 printf("warning: %ld of DVMA space lost\n", (long)sgsize);
915
916 cache_flush((caddr_t)dvmaddr, (u_int) sgsize);
917 }
918
919
920 void
921 sbus_dmamap_sync(t, map, offset, len, ops)
922 bus_dma_tag_t t;
923 bus_dmamap_t map;
924 bus_addr_t offset;
925 bus_size_t len;
926 int ops;
927 {
928 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
929 vaddr_t va = map->dm_segs[0].ds_addr + offset;
930
931 /*
932 * We only support one DMA segment; supporting more makes this code
933 * too unweildy.
934 */
935
936 if (ops&BUS_DMASYNC_PREREAD) {
937 #ifdef DEBUG
938 if (sbusdebug & SDB_DVMA)
939 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_PREREAD\n",
940 (long)va, (u_long)len);
941 #endif
942
943 /* Nothing to do */;
944 }
945 if (ops&BUS_DMASYNC_POSTREAD) {
946 /*
947 * We should sync the IOMMU streaming caches here first.
948 */
949 #ifdef DEBUG
950 if (sbusdebug & SDB_DVMA)
951 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_POSTREAD\n",
952 (long)va, (u_long)len);
953 #endif
954 while (len > 0) {
955
956 /*
957 * Streaming buffer flushes:
958 *
959 * 1 Tell strbuf to flush by storing va to strbuf_pgflush
960 * If we're not on a cache line boundary (64-bits):
961 * 2 Store 0 in flag
962 * 3 Store pointer to flag in flushsync
963 * 4 wait till flushsync becomes 0x1
964 *
965 * If it takes more than .5 sec, something went wrong.
966 */
967 #ifdef DEBUG
968 if (sbusdebug & SDB_DVMA)
969 printf("sbus_dmamap_sync: flushing va %p, %lu bytes left\n",
970 (long)va, (u_long)len);
971 #endif
972 bus_space_write_8(sc->sc_bustag, &sc->sc_is.is_sb->strbuf_pgflush, 0, va);
973 if (len <= NBPG) {
974 iommu_flush(&sc->sc_is);
975 len = 0;
976 } else
977 len -= NBPG;
978 va += NBPG;
979 }
980 }
981 if (ops&BUS_DMASYNC_PREWRITE) {
982 #ifdef DEBUG
983 if (sbusdebug & SDB_DVMA)
984 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_PREWRITE\n",
985 (long)va, (u_long)len);
986 #endif
987 /* Nothing to do */;
988 }
989 if (ops&BUS_DMASYNC_POSTWRITE) {
990 #ifdef DEBUG
991 if (sbusdebug & SDB_DVMA)
992 printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_POSTWRITE\n",
993 (long)va, (u_long)len);
994 #endif
995 /* Nothing to do */;
996 }
997 bus_dmamap_sync(t->_parent, map, offset, len, ops);
998 }
999
1000
1001 /*
1002 * Take memory allocated by our parent bus and generate DVMA mappings for it.
1003 */
1004 int
1005 sbus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags)
1006 bus_dma_tag_t t;
1007 bus_size_t size, alignment, boundary;
1008 bus_dma_segment_t *segs;
1009 int nsegs;
1010 int *rsegs;
1011 int flags;
1012 {
1013 paddr_t curaddr;
1014 u_long dvmaddr;
1015 vm_page_t m;
1016 struct pglist *mlist;
1017 int error;
1018 int n, s;
1019 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
1020
1021 if ((error = bus_dmamem_alloc(t->_parent, size, alignment,
1022 boundary, segs, nsegs, rsegs, flags)))
1023 return (error);
1024
1025 /*
1026 * Allocate a DVMA mapping for our new memory.
1027 */
1028 for (n = 0; n < *rsegs; n++) {
1029 #if 1
1030 s = splhigh();
1031 if (extent_alloc(sc->sc_is.is_dvmamap, segs[0].ds_len, alignment,
1032 boundary, EX_NOWAIT, (u_long *)&dvmaddr)) {
1033 splx(s);
1034 /* Free what we got and exit */
1035 bus_dmamem_free(t->_parent, segs, nsegs);
1036 return (ENOMEM);
1037 }
1038 splx(s);
1039 #else
1040 dvmaddr = dvmamap_alloc(segs[0].ds_len, flags);
1041 if (dvmaddr == (bus_addr_t)-1) {
1042 /* Free what we got and exit */
1043 bus_dmamem_free(t->_parent, segs, nsegs);
1044 return (ENOMEM);
1045 }
1046 #endif
1047 segs[n].ds_addr = dvmaddr;
1048 size = segs[n].ds_len;
1049 mlist = segs[n]._ds_mlist;
1050
1051 /* Map memory into DVMA space */
1052 for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) {
1053 curaddr = VM_PAGE_TO_PHYS(m);
1054 #ifdef DEBUG
1055 if (sbusdebug & SDB_DVMA)
1056 printf("sbus_dmamem_alloc: map %p loading va %lx at pa %lx\n",
1057 (long)m, (long)dvmaddr, (long)(curaddr & ~(NBPG-1)));
1058 #endif
1059 iommu_enter(&sc->sc_is, dvmaddr, curaddr, flags);
1060 dvmaddr += PAGE_SIZE;
1061 }
1062 }
1063 return (0);
1064 }
1065
1066 void
1067 sbus_dmamem_free(t, segs, nsegs)
1068 bus_dma_tag_t t;
1069 bus_dma_segment_t *segs;
1070 int nsegs;
1071 {
1072 vaddr_t addr;
1073 int len;
1074 int n, s, error;
1075 struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
1076
1077
1078 for (n=0; n<nsegs; n++) {
1079 addr = segs[n].ds_addr;
1080 len = segs[n].ds_len;
1081 iommu_remove(&sc->sc_is, addr, len);
1082 #if 1
1083 s = splhigh();
1084 error = extent_free(sc->sc_is.is_dvmamap, addr, len, EX_NOWAIT);
1085 splx(s);
1086 if (error != 0)
1087 printf("warning: %ld of DVMA space lost\n", (long)len);
1088 #else
1089 dvmamap_free(addr, len);
1090 #endif
1091 }
1092 bus_dmamem_free(t->_parent, segs, nsegs);
1093 }
1094
1095 /*
1096 * Map the DVMA mappings into the kernel pmap.
1097 * Check the flags to see whether we're streaming or coherent.
1098 */
1099 int
1100 sbus_dmamem_map(t, segs, nsegs, size, kvap, flags)
1101 bus_dma_tag_t t;
1102 bus_dma_segment_t *segs;
1103 int nsegs;
1104 size_t size;
1105 caddr_t *kvap;
1106 int flags;
1107 {
1108 vm_page_t m;
1109 vaddr_t va;
1110 bus_addr_t addr;
1111 struct pglist *mlist;
1112 int cbit;
1113
1114 /*
1115 * digest flags:
1116 */
1117 cbit = 0;
1118 if (flags & BUS_DMA_COHERENT) /* Disable vcache */
1119 cbit |= PMAP_NVC;
1120 if (flags & BUS_DMA_NOCACHE) /* sideffects */
1121 cbit |= PMAP_NC;
1122 /*
1123 * Now take this and map it into the CPU since it should already
1124 * be in the the IOMMU.
1125 */
1126 *kvap = (caddr_t)va = segs[0].ds_addr;
1127 mlist = segs[0]._ds_mlist;
1128 for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) {
1129
1130 if (size == 0)
1131 panic("_bus_dmamem_map: size botch");
1132
1133 addr = VM_PAGE_TO_PHYS(m);
1134 pmap_enter(pmap_kernel(), va, addr | cbit,
1135 VM_PROT_READ | VM_PROT_WRITE,
1136 VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED);
1137 va += PAGE_SIZE;
1138 size -= PAGE_SIZE;
1139 }
1140
1141 return (0);
1142 }
1143
1144 /*
1145 * Unmap DVMA mappings from kernel
1146 */
1147 void
1148 sbus_dmamem_unmap(t, kva, size)
1149 bus_dma_tag_t t;
1150 caddr_t kva;
1151 size_t size;
1152 {
1153
1154 #ifdef DIAGNOSTIC
1155 if ((u_long)kva & PGOFSET)
1156 panic("_bus_dmamem_unmap");
1157 #endif
1158
1159 size = round_page(size);
1160 pmap_remove(pmap_kernel(), (vaddr_t)kva, size);
1161 }
1162