Annotation of sys/arch/sparc64/dev/ebus.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ebus.c,v 1.17 2007/04/10 17:47:55 miod Exp $ */
! 2: /* $NetBSD: ebus.c,v 1.24 2001/07/25 03:49:54 eeh Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1999, 2000 Matthew R. Green
! 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. The name of the author may not be used to endorse or promote products
! 17: * derived from this software without specific prior written permission.
! 18: *
! 19: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 20: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 21: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 22: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 23: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
! 24: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! 25: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
! 26: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
! 27: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 29: * SUCH DAMAGE.
! 30: */
! 31:
! 32: /*
! 33: * UltraSPARC 5 and beyond ebus support.
! 34: *
! 35: * note that this driver is not complete:
! 36: * - ebus2 dma code is completely unwritten
! 37: * - interrupt establish is written and appears to work
! 38: * - bus map code is written and appears to work
! 39: */
! 40:
! 41: #ifdef DEBUG
! 42: #define EDB_PROM 0x01
! 43: #define EDB_CHILD 0x02
! 44: #define EDB_INTRMAP 0x04
! 45: #define EDB_BUSMAP 0x08
! 46: #define EDB_BUSDMA 0x10
! 47: #define EDB_INTR 0x20
! 48: int ebus_debug = 0x0;
! 49: #define DPRINTF(l, s) do { if (ebus_debug & l) printf s; } while (0)
! 50: #else
! 51: #define DPRINTF(l, s)
! 52: #endif
! 53:
! 54: #include <sys/param.h>
! 55: #include <sys/conf.h>
! 56: #include <sys/device.h>
! 57: #include <sys/errno.h>
! 58: #include <sys/extent.h>
! 59: #include <sys/malloc.h>
! 60: #include <sys/systm.h>
! 61: #include <sys/time.h>
! 62:
! 63: #define _SPARC_BUS_DMA_PRIVATE
! 64: #include <machine/bus.h>
! 65: #include <machine/autoconf.h>
! 66: #include <machine/openfirm.h>
! 67:
! 68: #include <dev/pci/pcivar.h>
! 69: #include <dev/pci/pcireg.h>
! 70: #include <dev/pci/pcidevs.h>
! 71:
! 72: #include <sparc64/dev/iommureg.h>
! 73: #include <sparc64/dev/iommuvar.h>
! 74: #include <sparc64/dev/psychoreg.h>
! 75: #include <sparc64/dev/psychovar.h>
! 76: #include <sparc64/dev/ebusreg.h>
! 77: #include <sparc64/dev/ebusvar.h>
! 78: #include <sparc64/sparc64/cache.h>
! 79:
! 80: int ebus_match(struct device *, void *, void *);
! 81: void ebus_attach(struct device *, struct device *, void *);
! 82:
! 83: struct cfattach ebus_ca = {
! 84: sizeof(struct ebus_softc), ebus_match, ebus_attach
! 85: };
! 86:
! 87: struct cfdriver ebus_cd = {
! 88: NULL, "ebus", DV_DULL
! 89: };
! 90:
! 91:
! 92: void ebus_find_ino(struct ebus_softc *, struct ebus_attach_args *);
! 93: int ebus_find_node(struct pci_attach_args *);
! 94:
! 95: /*
! 96: * here are our bus space and bus dma routines.
! 97: */
! 98: static paddr_t ebus_bus_mmap(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
! 99: off_t, int, int);
! 100: static int _ebus_bus_map(bus_space_tag_t, bus_space_tag_t, bus_addr_t,
! 101: bus_size_t, int, bus_space_handle_t *);
! 102: bus_space_tag_t ebus_alloc_mem_tag(struct ebus_softc *, bus_space_tag_t);
! 103: bus_space_tag_t ebus_alloc_io_tag(struct ebus_softc *, bus_space_tag_t);
! 104: bus_space_tag_t _ebus_alloc_bus_tag(struct ebus_softc *sc, const char *,
! 105: bus_space_tag_t, int);
! 106:
! 107:
! 108: int
! 109: ebus_match(struct device *parent, void *match, void *aux)
! 110: {
! 111: struct pci_attach_args *pa = aux;
! 112: char name[10];
! 113: int node;
! 114:
! 115: /* Only attach if there's a PROM node. */
! 116: node = PCITAG_NODE(pa->pa_tag);
! 117: if (node == -1) return (0);
! 118:
! 119: /* Match a real ebus */
! 120: OF_getprop(node, "name", &name, sizeof(name));
! 121: if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
! 122: PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN &&
! 123: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_EBUS &&
! 124: strcmp(name, "ebus") == 0)
! 125: return (1);
! 126:
! 127: /* Or a real RIO ebus */
! 128: if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
! 129: PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN &&
! 130: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_RIO_EBUS &&
! 131: strcmp(name, "ebus") == 0)
! 132: return (1);
! 133:
! 134: /* Or a PCI-ISA bridge XXX I hope this is on-board. */
! 135: if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
! 136: PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_ISA) {
! 137: return (1);
! 138: }
! 139:
! 140: return (0);
! 141: }
! 142:
! 143: /*
! 144: * attach an ebus and all its children. this code is modeled
! 145: * after the sbus code which does similar things.
! 146: */
! 147: void
! 148: ebus_attach(struct device *parent, struct device *self, void *aux)
! 149: {
! 150: struct ebus_softc *sc = (struct ebus_softc *)self;
! 151: struct pci_attach_args *pa = aux;
! 152: struct ebus_attach_args eba;
! 153: struct ebus_interrupt_map_mask *immp;
! 154: int node, nmapmask, error;
! 155:
! 156: printf("\n");
! 157:
! 158: sc->sc_memtag = ebus_alloc_mem_tag(sc, pa->pa_memt);
! 159: sc->sc_iotag = ebus_alloc_io_tag(sc, pa->pa_iot);
! 160: sc->sc_dmatag = ebus_alloc_dma_tag(sc, pa->pa_dmat);
! 161:
! 162: node = PCITAG_NODE(pa->pa_tag);
! 163: if (node == -1)
! 164: panic("could not find ebus node");
! 165:
! 166: sc->sc_node = node;
! 167:
! 168: /*
! 169: * fill in our softc with information from the prom
! 170: */
! 171: sc->sc_intmap = NULL;
! 172: sc->sc_range = NULL;
! 173: error = getprop(node, "interrupt-map",
! 174: sizeof(struct ebus_interrupt_map),
! 175: &sc->sc_nintmap, (void **)&sc->sc_intmap);
! 176: switch (error) {
! 177: case 0:
! 178: immp = &sc->sc_intmapmask;
! 179: error = getprop(node, "interrupt-map-mask",
! 180: sizeof(struct ebus_interrupt_map_mask), &nmapmask,
! 181: (void **)&immp);
! 182: if (error)
! 183: panic("could not get ebus interrupt-map-mask");
! 184: if (nmapmask != 1)
! 185: panic("ebus interrupt-map-mask is broken");
! 186: break;
! 187: case ENOENT:
! 188: break;
! 189: default:
! 190: panic("ebus interrupt-map: error %d", error);
! 191: break;
! 192: }
! 193:
! 194: error = getprop(node, "ranges", sizeof(struct ebus_ranges),
! 195: &sc->sc_nrange, &sc->sc_range);
! 196: if (error)
! 197: panic("ebus ranges: error %d", error);
! 198:
! 199: /*
! 200: * now attach all our children
! 201: */
! 202: DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
! 203: for (node = firstchild(node); node; node = nextsibling(node)) {
! 204: if (ebus_setup_attach_args(sc, node, &eba) != 0) {
! 205: DPRINTF(EDB_CHILD,
! 206: ("ebus_attach: %s: incomplete\n",
! 207: getpropstring(node, "name")));
! 208: continue;
! 209: } else {
! 210: DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
! 211: eba.ea_name));
! 212: (void)config_found(self, &eba, ebus_print);
! 213: }
! 214: ebus_destroy_attach_args(&eba);
! 215: }
! 216: }
! 217:
! 218: int
! 219: ebus_setup_attach_args(struct ebus_softc *sc, int node,
! 220: struct ebus_attach_args *ea)
! 221: {
! 222: int n, rv;
! 223:
! 224: bzero(ea, sizeof(struct ebus_attach_args));
! 225: rv = getprop(node, "name", 1, &n, (void **)&ea->ea_name);
! 226: if (rv != 0)
! 227: return (rv);
! 228: ea->ea_name[n] = '\0';
! 229:
! 230: ea->ea_node = node;
! 231: ea->ea_memtag = sc->sc_memtag;
! 232: ea->ea_iotag = sc->sc_iotag;
! 233: ea->ea_dmatag = sc->sc_dmatag;
! 234:
! 235: rv = getprop(node, "reg", sizeof(struct ebus_regs), &ea->ea_nregs,
! 236: (void **)&ea->ea_regs);
! 237: if (rv)
! 238: return (rv);
! 239:
! 240: rv = getprop(node, "address", sizeof(u_int32_t), &ea->ea_nvaddrs,
! 241: (void **)&ea->ea_vaddrs);
! 242: if (rv != ENOENT) {
! 243: if (rv)
! 244: return (rv);
! 245:
! 246: if (ea->ea_nregs != ea->ea_nvaddrs)
! 247: printf("ebus loses: device %s: %d regs and %d addrs\n",
! 248: ea->ea_name, ea->ea_nregs, ea->ea_nvaddrs);
! 249: } else
! 250: ea->ea_nvaddrs = 0;
! 251:
! 252: if (getprop(node, "interrupts", sizeof(u_int32_t), &ea->ea_nintrs,
! 253: (void **)&ea->ea_intrs))
! 254: ea->ea_nintrs = 0;
! 255: else
! 256: ebus_find_ino(sc, ea);
! 257:
! 258: return (0);
! 259: }
! 260:
! 261: void
! 262: ebus_destroy_attach_args(struct ebus_attach_args *ea)
! 263: {
! 264: if (ea->ea_name)
! 265: free((void *)ea->ea_name, M_DEVBUF);
! 266: if (ea->ea_regs)
! 267: free((void *)ea->ea_regs, M_DEVBUF);
! 268: if (ea->ea_intrs)
! 269: free((void *)ea->ea_intrs, M_DEVBUF);
! 270: if (ea->ea_vaddrs)
! 271: free((void *)ea->ea_vaddrs, M_DEVBUF);
! 272: }
! 273:
! 274: int
! 275: ebus_print(void *aux, const char *p)
! 276: {
! 277: struct ebus_attach_args *ea = aux;
! 278: int i;
! 279:
! 280: if (p)
! 281: printf("\"%s\" at %s", ea->ea_name, p);
! 282: for (i = 0; i < ea->ea_nregs; i++)
! 283: printf("%s %x-%x", i == 0 ? " addr" : ",",
! 284: ea->ea_regs[i].lo,
! 285: ea->ea_regs[i].lo + ea->ea_regs[i].size - 1);
! 286: for (i = 0; i < ea->ea_nintrs; i++)
! 287: printf(" ipl %d", ea->ea_intrs[i]);
! 288: return (UNCONF);
! 289: }
! 290:
! 291:
! 292: /*
! 293: * find the INO values for each interrupt and fill them in.
! 294: *
! 295: * for each "reg" property of this device, mask its hi and lo
! 296: * values with the "interrupt-map-mask"'s hi/lo values, and also
! 297: * mask the interrupt number with the interrupt mask. search the
! 298: * "interrupt-map" list for matching values of hi, lo and interrupt
! 299: * to give the INO for this interrupt.
! 300: */
! 301: void
! 302: ebus_find_ino(struct ebus_softc *sc, struct ebus_attach_args *ea)
! 303: {
! 304: u_int32_t hi, lo, intr;
! 305: int i, j, k;
! 306:
! 307: if (sc->sc_nintmap == 0) {
! 308: for (i = 0; i < ea->ea_nintrs; i++) {
! 309: OF_mapintr(ea->ea_node, &ea->ea_intrs[i],
! 310: sizeof(ea->ea_intrs[0]),
! 311: sizeof(ea->ea_intrs[0]));
! 312: }
! 313: return;
! 314: }
! 315:
! 316: DPRINTF(EDB_INTRMAP,
! 317: ("ebus_find_ino: searching %d interrupts", ea->ea_nintrs));
! 318:
! 319: for (j = 0; j < ea->ea_nintrs; j++) {
! 320:
! 321: intr = ea->ea_intrs[j] & sc->sc_intmapmask.intr;
! 322:
! 323: DPRINTF(EDB_INTRMAP,
! 324: ("; intr %x masked to %x", ea->ea_intrs[j], intr));
! 325: for (i = 0; i < ea->ea_nregs; i++) {
! 326: hi = ea->ea_regs[i].hi & sc->sc_intmapmask.hi;
! 327: lo = ea->ea_regs[i].lo & sc->sc_intmapmask.lo;
! 328:
! 329: DPRINTF(EDB_INTRMAP,
! 330: ("; reg hi.lo %08x.%08x masked to %08x.%08x",
! 331: ea->ea_regs[i].hi, ea->ea_regs[i].lo, hi, lo));
! 332: for (k = 0; k < sc->sc_nintmap; k++) {
! 333: DPRINTF(EDB_INTRMAP,
! 334: ("; checking hi.lo %08x.%08x intr %x",
! 335: sc->sc_intmap[k].hi, sc->sc_intmap[k].lo,
! 336: sc->sc_intmap[k].intr));
! 337: if (hi == sc->sc_intmap[k].hi &&
! 338: lo == sc->sc_intmap[k].lo &&
! 339: intr == sc->sc_intmap[k].intr) {
! 340: ea->ea_intrs[j] =
! 341: sc->sc_intmap[k].cintr;
! 342: DPRINTF(EDB_INTRMAP,
! 343: ("; FOUND IT! changing to %d\n",
! 344: sc->sc_intmap[k].cintr));
! 345: goto next_intr;
! 346: }
! 347: }
! 348: }
! 349: next_intr:;
! 350: }
! 351: }
! 352:
! 353: bus_space_tag_t
! 354: ebus_alloc_mem_tag(struct ebus_softc *sc, bus_space_tag_t parent)
! 355: {
! 356: return (_ebus_alloc_bus_tag(sc, "mem", parent,
! 357: 0x02)); /* 32-bit mem space (where's the #define???) */
! 358: }
! 359:
! 360: bus_space_tag_t
! 361: ebus_alloc_io_tag(struct ebus_softc *sc, bus_space_tag_t parent)
! 362: {
! 363: return (_ebus_alloc_bus_tag(sc, "io", parent,
! 364: 0x01)); /* IO space (where's the #define???) */
! 365: }
! 366:
! 367: /*
! 368: * bus space and bus dma below here
! 369: */
! 370: bus_space_tag_t
! 371: _ebus_alloc_bus_tag(struct ebus_softc *sc, const char *name,
! 372: bus_space_tag_t parent, int ss)
! 373: {
! 374: struct sparc_bus_space_tag *bt;
! 375:
! 376: bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT);
! 377: if (bt == NULL)
! 378: panic("could not allocate ebus bus tag");
! 379:
! 380: bzero(bt, sizeof *bt);
! 381: snprintf(bt->name, sizeof(bt->name), "%s_%s",
! 382: sc->sc_dev.dv_xname, name);
! 383: bt->cookie = sc;
! 384: bt->parent = parent;
! 385: bt->default_type = ss;
! 386: bt->asi = parent->asi;
! 387: bt->sasi = parent->sasi;
! 388: bt->sparc_bus_map = _ebus_bus_map;
! 389: bt->sparc_bus_mmap = ebus_bus_mmap;
! 390:
! 391: return (bt);
! 392: }
! 393:
! 394: bus_dma_tag_t
! 395: ebus_alloc_dma_tag(struct ebus_softc *sc, bus_dma_tag_t pdt)
! 396: {
! 397: bus_dma_tag_t dt;
! 398:
! 399: dt = (bus_dma_tag_t)
! 400: malloc(sizeof(struct sparc_bus_dma_tag), M_DEVBUF, M_NOWAIT);
! 401: if (dt == NULL)
! 402: panic("could not allocate ebus dma tag");
! 403:
! 404: bzero(dt, sizeof *dt);
! 405: dt->_cookie = sc;
! 406: dt->_parent = pdt;
! 407: sc->sc_dmatag = dt;
! 408: return (dt);
! 409: }
! 410:
! 411: /*
! 412: * bus space support. <sparc64/dev/psychoreg.h> has a discussion
! 413: * about PCI physical addresses, which also applies to ebus.
! 414: */
! 415: static int
! 416: _ebus_bus_map(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t offset,
! 417: bus_size_t size, int flags, bus_space_handle_t *hp)
! 418: {
! 419: struct ebus_softc *sc = t->cookie;
! 420: struct ebus_ranges *range = sc->sc_range;
! 421: bus_addr_t hi, lo;
! 422: int i;
! 423:
! 424: DPRINTF(EDB_BUSMAP,
! 425: ("\n_ebus_bus_map: type %d off %016llx sz %x flags %d",
! 426: (int)t->default_type, (unsigned long long)offset, (int)size,
! 427: (int)flags));
! 428:
! 429: if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
! 430: printf("\n_ebus_bus_map: invalid parent");
! 431: return (EINVAL);
! 432: }
! 433:
! 434: t = t->parent;
! 435:
! 436: if (flags & BUS_SPACE_MAP_PROMADDRESS) {
! 437: return ((*t->sparc_bus_map)
! 438: (t, t0, offset, size, flags, hp));
! 439: }
! 440:
! 441: hi = offset >> 32UL;
! 442: lo = offset & 0xffffffff;
! 443:
! 444: DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
! 445: for (i = 0; i < sc->sc_nrange; i++) {
! 446: bus_addr_t pciaddr;
! 447:
! 448: if (hi != range[i].child_hi)
! 449: continue;
! 450: if (lo < range[i].child_lo ||
! 451: (lo + size) > (range[i].child_lo + range[i].size))
! 452: continue;
! 453:
! 454: if(((range[i].phys_hi >> 24) & 3) != t->default_type)
! 455: continue;
! 456:
! 457: pciaddr = ((bus_addr_t)range[i].phys_mid << 32UL) |
! 458: range[i].phys_lo;
! 459: pciaddr += lo;
! 460: DPRINTF(EDB_BUSMAP,
! 461: ("\n_ebus_bus_map: mapping space %x paddr offset %qx "
! 462: "pciaddr %qx\n", (int)t->default_type,
! 463: (unsigned long long)offset, (unsigned long long)pciaddr));
! 464: return ((*t->sparc_bus_map)(t, t0, pciaddr, size, flags, hp));
! 465: }
! 466: DPRINTF(EDB_BUSMAP, (": FAILED\n"));
! 467: return (EINVAL);
! 468: }
! 469:
! 470: static paddr_t
! 471: ebus_bus_mmap(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t paddr,
! 472: off_t off, int prot, int flags)
! 473: {
! 474: bus_addr_t offset = paddr;
! 475: struct ebus_softc *sc = t->cookie;
! 476: struct ebus_ranges *range = sc->sc_range;
! 477: int i;
! 478:
! 479: if (t->parent == 0 || t->parent->sparc_bus_mmap == 0) {
! 480: printf("\nebus_bus_mmap: invalid parent");
! 481: return (-1);
! 482: }
! 483:
! 484: t = t->parent;
! 485:
! 486: for (i = 0; i < sc->sc_nrange; i++) {
! 487: bus_addr_t paddr = ((bus_addr_t)range[i].child_hi << 32) |
! 488: range[i].child_lo;
! 489:
! 490: if (offset != paddr)
! 491: continue;
! 492:
! 493: DPRINTF(EDB_BUSMAP, ("\n_ebus_bus_mmap: mapping paddr %qx\n",
! 494: (unsigned long long)paddr));
! 495: return ((*t->sparc_bus_mmap)(t, t0, paddr, off, prot, flags));
! 496: }
! 497:
! 498: return (-1);
! 499: }
CVSweb