1 /* $NetBSD: btvmeii.c,v 1.5 2002/03/04 02:19:10 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 1999 5 * Matthias Drochner. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions, and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * Driver for the Bit3/SBS PCI-VME adapter Model 2706. 31 * Uses the common Tundra Universe code. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: btvmeii.c,v 1.5 2002/03/04 02:19:10 simonb Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/device.h> 41 42 #include <dev/pci/pcireg.h> 43 #include <dev/pci/pcivar.h> 44 #include <dev/pci/pcidevs.h> 45 46 #include <machine/bus.h> 47 #include <sys/malloc.h> 48 #include <sys/extent.h> 49 50 #include <dev/pci/ppbreg.h> 51 52 #include <dev/vme/vmereg.h> 53 #include <dev/vme/vmevar.h> 54 55 #include <dev/pci/universe_pci_var.h> 56 57 static int b3_2706_match __P((struct device *, struct cfdata *, void *)); 58 static void b3_2706_attach __P((struct device *, struct device *, void *)); 59 60 /* exported via tag structs */ 61 int b3_2706_map_vme __P((void *, vme_addr_t, vme_size_t, 62 vme_am_t, vme_datasize_t, vme_swap_t, 63 bus_space_tag_t *, bus_space_handle_t *, vme_mapresc_t*)); 64 void b3_2706_unmap_vme __P((void *, vme_mapresc_t)); 65 66 int b3_2706_vme_probe __P((void *, vme_addr_t, vme_size_t, vme_am_t, 67 vme_datasize_t, 68 int (*)(void *, bus_space_tag_t, bus_space_handle_t), 69 void *)); 70 71 int b3_2706_map_vmeint __P((void *, int, int, vme_intr_handle_t *)); 72 void *b3_2706_establish_vmeint __P((void *, vme_intr_handle_t, int, 73 int (*)(void *), void *)); 74 void b3_2706_disestablish_vmeint __P((void *, void *)); 75 void b3_2706_vmeint __P((void *, int, int)); 76 77 int b3_2706_dmamap_create __P((void *, vme_size_t, 78 vme_am_t, vme_datasize_t, vme_swap_t, 79 int, vme_size_t, vme_addr_t, 80 int, bus_dmamap_t *)); 81 void b3_2706_dmamap_destroy __P((void *, bus_dmamap_t)); 82 83 int b3_2706_dmamem_alloc __P((void *, vme_size_t, 84 vme_am_t, vme_datasize_t, vme_swap_t, 85 bus_dma_segment_t *, int, int *, int)); 86 void b3_2706_dmamem_free __P((void *, bus_dma_segment_t *, int)); 87 88 struct b3_2706_vmemaprescs { 89 int wnd; 90 unsigned long pcibase, maplen; 91 bus_space_handle_t handle; 92 u_int32_t len; 93 }; 94 95 struct b3_2706_vmeintrhand { 96 TAILQ_ENTRY(b3_2706_vmeintrhand) ih_next; 97 int (*ih_fun) __P((void*)); 98 void *ih_arg; 99 int ih_level; 100 int ih_vector; 101 int ih_prior; 102 u_long ih_count; 103 }; 104 105 struct b3_2706_softc { 106 struct device sc_dev; 107 struct univ_pci_data univdata; 108 bus_space_tag_t swapt, vmet; 109 bus_space_handle_t swaph; 110 bus_addr_t vmepbase; 111 112 int windowused[8]; 113 struct b3_2706_vmemaprescs vmemaprescs[8]; 114 struct extent *vmeext; 115 char vmemap[EXTENT_FIXED_STORAGE_SIZE(8)]; 116 117 struct vme_chipset_tag sc_vct; 118 119 /* list of VME interrupt handlers */ 120 TAILQ_HEAD(, b3_2706_vmeintrhand) intrhdls; 121 int strayintrs; 122 }; 123 124 struct cfattach btvmeii_ca = { 125 sizeof(struct b3_2706_softc), b3_2706_match, b3_2706_attach, 126 #if 0 127 b3_2706_detach 128 #endif 129 }; 130 131 /* 132 * The adapter consists of a DEC PCI-PCI-bridge with two 133 * PCI devices behind it: A Tundra Universe as device 4 and 134 * some FPGA with glue logics as device 8. 135 * As long as the autoconf code doesn't provide more support 136 * for dependant devices, we have to duplicate a part of the 137 * "ppb" functions here. 138 */ 139 140 static int 141 b3_2706_match(parent, match, aux) 142 struct device *parent; 143 struct cfdata *match; 144 void *aux; 145 { 146 struct pci_attach_args *pa = aux; 147 pci_chipset_tag_t pc = pa->pa_pc; 148 int secbus; 149 pcitag_t tag; 150 pcireg_t id; 151 152 if ((PCI_VENDOR(pa->pa_id) != PCI_VENDOR_DEC) 153 || (PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_DEC_21152)) 154 return (0); 155 156 secbus = PPB_BUSINFO_SECONDARY(pci_conf_read(pc, pa->pa_tag, 157 PPB_REG_BUSINFO)); 158 if (secbus == 0) { 159 printf("b3_2706_match: ppb not configured\n"); 160 return (0); 161 } 162 163 tag = pci_make_tag(pc, secbus, 4, 0); 164 id = pci_conf_read(pc, tag, PCI_ID_REG); 165 166 if ((PCI_VENDOR(id) != PCI_VENDOR_NEWBRIDGE) 167 || (PCI_PRODUCT(id) != PCI_PRODUCT_NEWBRIDGE_CA91CX42)) { 168 #ifdef DEBUG 169 printf("b3_2706_match: no tundra\n"); 170 #endif 171 return (0); 172 } 173 174 tag = pci_make_tag(pc, secbus, 8, 0); 175 id = pci_conf_read(pc, tag, PCI_ID_REG); 176 177 if ((PCI_VENDOR(id) != PCI_VENDOR_BIT3) 178 || (PCI_PRODUCT(id) != PCI_PRODUCT_BIT3_PCIVME2706)) { 179 #ifdef DEBUG 180 printf("b3_2706_match: no bit3 chip\n"); 181 #endif 182 return (0); 183 } 184 185 return (5); /* beat "ppb" */ 186 } 187 188 static void 189 b3_2706_attach(parent, self, aux) 190 struct device *parent, *self; 191 void *aux; 192 { 193 struct b3_2706_softc *sc = (struct b3_2706_softc *)self; 194 struct pci_attach_args *pa = aux; 195 pci_chipset_tag_t pc = pa->pa_pc; 196 struct pci_attach_args aa; 197 int secbus; 198 pcireg_t intr; 199 pcitag_t tag; 200 bus_addr_t swappbase; 201 int i; 202 203 struct vmebus_attach_args vaa; 204 205 printf("\n"); 206 207 secbus = PPB_BUSINFO_SECONDARY(pci_conf_read(pc, pa->pa_tag, 208 PPB_REG_BUSINFO)); 209 210 memcpy(&aa, pa, sizeof(struct pci_attach_args)); 211 aa.pa_device = 4; 212 aa.pa_function = 0; 213 aa.pa_tag = pci_make_tag(pc, secbus, 4, 0); 214 aa.pa_intrswiz += 4; 215 intr = pci_conf_read(pc, aa.pa_tag, PCI_INTERRUPT_REG); 216 /* 217 * swizzle it based on the number of 218 * busses we're behind and our device 219 * number. 220 */ 221 aa.pa_intrpin = ((1 + aa.pa_intrswiz - 1) % 4) + 1; 222 aa.pa_intrline = PCI_INTERRUPT_LINE(intr); 223 224 if (univ_pci_attach(&sc->univdata, &aa, self->dv_xname, 225 b3_2706_vmeint, sc)) { 226 printf("%s: error initializing universe chip\n", 227 self->dv_xname); 228 return; 229 } 230 231 /* 232 * don't waste KVM - the byteswap register is aliased in 233 * a 512k window, we need it only once 234 */ 235 tag = pci_make_tag(pc, secbus, 8, 0); 236 sc->swapt = pa->pa_memt; 237 if (pci_mapreg_info(pc, tag, 0x10, 238 PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 239 &swappbase, 0, 0) || 240 bus_space_map(sc->swapt, swappbase, 4, 0, &sc->swaph)) { 241 printf("%s: can't map byteswap register\n", self->dv_xname); 242 return; 243 } 244 /* 245 * Set up cycle specific byteswap mode. 246 * XXX Readback yields "all-ones" for me, and it doesn't seem 247 * to matter what I write into the register - the data don't 248 * get swapped. Adapter fault or documentation bug? 249 */ 250 bus_space_write_4(sc->swapt, sc->swaph, 0, 0x00000490); 251 252 /* VME space is mapped as needed */ 253 sc->vmet = pa->pa_memt; 254 if (pci_mapreg_info(pc, tag, 0x14, 255 PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 256 &sc->vmepbase, 0, 0)) { 257 printf("%s: VME range not assigned\n", self->dv_xname); 258 return; 259 } 260 #ifdef BIT3DEBUG 261 printf("%s: VME window @%lx\n", self->dv_xname, (long)sc->vmepbase); 262 #endif 263 264 for (i = 0; i < 8; i++) { 265 sc->windowused[i] = 0; 266 } 267 sc->vmeext = extent_create("pcivme", sc->vmepbase, 268 sc->vmepbase + 32*1024*1024 - 1, M_DEVBUF, 269 sc->vmemap, sizeof(sc->vmemap), 270 EX_NOCOALESCE); 271 272 sc->sc_vct.cookie = self; 273 sc->sc_vct.vct_probe = b3_2706_vme_probe; 274 sc->sc_vct.vct_map = b3_2706_map_vme; 275 sc->sc_vct.vct_unmap = b3_2706_unmap_vme; 276 sc->sc_vct.vct_int_map = b3_2706_map_vmeint; 277 sc->sc_vct.vct_int_establish = b3_2706_establish_vmeint; 278 sc->sc_vct.vct_int_disestablish = b3_2706_disestablish_vmeint; 279 sc->sc_vct.vct_dmamap_create = b3_2706_dmamap_create; 280 sc->sc_vct.vct_dmamap_destroy = b3_2706_dmamap_destroy; 281 sc->sc_vct.vct_dmamem_alloc = b3_2706_dmamem_alloc; 282 sc->sc_vct.vct_dmamem_free = b3_2706_dmamem_free; 283 284 vaa.va_vct = &(sc->sc_vct); 285 vaa.va_bdt = pa->pa_dmat; /* XXX */ 286 vaa.va_slaveconfig = 0; /* XXX CSR window? */ 287 288 config_found(self, &vaa, 0); 289 } 290 291 #define sc ((struct b3_2706_softc*)vsc) 292 293 int 294 b3_2706_map_vme(vsc, vmeaddr, len, am, datasizes, swap, tag, handle, resc) 295 void *vsc; 296 vme_addr_t vmeaddr; 297 vme_size_t len; 298 vme_am_t am; 299 vme_datasize_t datasizes; 300 vme_swap_t swap; 301 bus_space_tag_t *tag; 302 bus_space_handle_t *handle; 303 vme_mapresc_t *resc; 304 { 305 int idx, i, wnd, res; 306 unsigned long boundary, maplen, pcibase; 307 vme_addr_t vmebase, vmeend; 308 static int windoworder[8] = {1, 2, 3, 5, 6, 7, 0, 4}; 309 310 /* prefer windows with fine granularity for small mappings */ 311 wnd = -1; 312 if (len <= 32*1024) 313 idx = 6; 314 else 315 idx = 0; 316 for (i = 0; i < 8; i++) { 317 if (!sc->windowused[windoworder[idx]]) { 318 wnd = windoworder[idx]; 319 sc->windowused[wnd] = 1; 320 break; 321 } 322 idx = (idx + 1) % 8; 323 } 324 if (wnd == -1) 325 return (ENOSPC); 326 327 boundary = (wnd & 3) ? 64*1024 : 4*1024; 328 329 /* first mapped address */ 330 vmebase = vmeaddr & ~(boundary - 1); 331 /* base of last mapped page */ 332 vmeend = (vmeaddr + len - 1) & ~(boundary - 1); 333 /* bytes in outgoing window required */ 334 maplen = vmeend - vmebase + boundary; 335 336 if (extent_alloc(sc->vmeext, maplen, boundary, 0, EX_FAST, &pcibase)) { 337 sc->windowused[wnd] = 0; 338 return (ENOMEM); 339 } 340 341 res = univ_pci_mapvme(&sc->univdata, wnd, vmebase, maplen, 342 am, datasizes, pcibase); 343 if (res) { 344 extent_free(sc->vmeext, pcibase, maplen, 0); 345 sc->windowused[wnd] = 0; 346 return (res); 347 } 348 349 res = bus_space_map(sc->vmet, pcibase + (vmeaddr - vmebase), len, 350 0, handle); 351 if (res) { 352 univ_pci_unmapvme(&sc->univdata, wnd); 353 extent_free(sc->vmeext, pcibase, maplen, 0); 354 sc->windowused[wnd] = 0; 355 return (res); 356 } 357 358 *tag = sc->vmet; 359 360 /* 361 * save all data needed for later unmapping 362 */ 363 sc->vmemaprescs[wnd].wnd = wnd; 364 sc->vmemaprescs[wnd].pcibase = pcibase; 365 sc->vmemaprescs[wnd].maplen = maplen; 366 sc->vmemaprescs[wnd].handle = *handle; 367 sc->vmemaprescs[wnd].len = len; 368 *resc = &sc->vmemaprescs[wnd]; 369 return (0); 370 } 371 372 void 373 b3_2706_unmap_vme(vsc, resc) 374 void *vsc; 375 vme_mapresc_t resc; 376 { 377 struct b3_2706_vmemaprescs *r = resc; 378 379 bus_space_unmap(sc->vmet, r->handle, r->len); 380 extent_free(sc->vmeext, r->pcibase, r->maplen, 0); 381 382 if (!sc->windowused[r->wnd]) 383 panic("b3_2706_unmap_vme: bad window"); 384 univ_pci_unmapvme(&sc->univdata, r->wnd); 385 sc->windowused[r->wnd] = 0; 386 } 387 388 int 389 b3_2706_vme_probe(vsc, addr, len, am, datasize, callback, cbarg) 390 void *vsc; 391 vme_addr_t addr; 392 vme_size_t len; 393 vme_am_t am; 394 vme_datasize_t datasize; 395 int (*callback) __P((void *, bus_space_tag_t, bus_space_handle_t)); 396 void *cbarg; 397 { 398 bus_space_tag_t tag; 399 bus_space_handle_t handle; 400 vme_mapresc_t resc; 401 int res, i; 402 volatile u_int32_t dummy; 403 404 res = b3_2706_map_vme(vsc, addr, len, am, datasize, 0, 405 &tag, &handle, &resc); 406 if (res) 407 return (res); 408 409 if (univ_pci_vmebuserr(&sc->univdata, 1)) 410 printf("b3_2706_vme_badaddr: TA bit not clean - reset\n"); 411 412 if (callback) 413 res = (*callback)(cbarg, tag, handle); 414 else { 415 for (i = 0; i < len;) { 416 switch (datasize) { 417 case VME_D8: 418 dummy = bus_space_read_1(tag, handle, i); 419 i++; 420 break; 421 case VME_D16: 422 dummy = bus_space_read_2(tag, handle, i); 423 i += 2; 424 break; 425 case VME_D32: 426 dummy = bus_space_read_4(tag, handle, i); 427 i += 4; 428 break; 429 default: 430 panic("b3_2706_vme_probe: invalid datasize %x", 431 datasize); 432 } 433 } 434 } 435 436 if (univ_pci_vmebuserr(&sc->univdata, 0)) { 437 #ifdef BIT3DEBUG 438 printf("b3_2706_vme_badaddr: caught TA\n"); 439 #endif 440 univ_pci_vmebuserr(&sc->univdata, 1); 441 res = EIO; 442 } 443 444 b3_2706_unmap_vme(vsc, resc); 445 return (res); 446 } 447 448 int 449 b3_2706_map_vmeint(vsc, level, vector, handlep) 450 void *vsc; 451 int level, vector; 452 vme_intr_handle_t *handlep; 453 { 454 455 *handlep = (void *)(long)((level << 8) | vector); /* XXX */ 456 return (0); 457 } 458 459 void * 460 b3_2706_establish_vmeint(vsc, handle, prior, func, arg) 461 void *vsc; 462 vme_intr_handle_t handle; 463 int prior; 464 int (*func) __P((void *)); 465 void *arg; 466 { 467 struct b3_2706_vmeintrhand *ih; 468 long lv; 469 int s; 470 471 /* no point in sleeping unless someone can free memory. */ 472 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 473 if (ih == NULL) 474 panic("b3_2706_map_vmeint: can't malloc handler info"); 475 476 lv = (long)handle; /* XXX */ 477 478 ih->ih_fun = func; 479 ih->ih_arg = arg; 480 ih->ih_level = lv >> 8; 481 ih->ih_vector = lv & 0xff; 482 ih->ih_prior = prior; 483 ih->ih_count = 0; 484 485 s = splhigh(); 486 TAILQ_INSERT_TAIL(&(sc->intrhdls), ih, ih_next); 487 splx(s); 488 489 return (ih); 490 } 491 492 void 493 b3_2706_disestablish_vmeint(vsc, cookie) 494 void *vsc; 495 void *cookie; 496 { 497 struct b3_2706_vmeintrhand *ih = cookie; 498 int s; 499 500 if (!ih) { 501 printf("b3_2706_unmap_vmeint: NULL arg\n"); 502 return; 503 } 504 505 s = splhigh(); 506 TAILQ_REMOVE(&(sc->intrhdls), ih, ih_next); 507 splx(s); 508 509 free(ih, M_DEVBUF); 510 } 511 512 void 513 b3_2706_vmeint(vsc, level, vector) 514 void *vsc; 515 int level, vector; 516 { 517 struct b3_2706_vmeintrhand *ih; 518 int found; 519 520 #ifdef BIT3DEBUG 521 printf("b3_2706_vmeint: VME IRQ %d, vec %x\n", level, vector); 522 #endif 523 found = 0; 524 525 for (ih = sc->intrhdls.tqh_first; ih; 526 ih = ih->ih_next.tqe_next) { 527 if ((ih->ih_level == level) && 528 ((ih->ih_vector == -1) || 529 (ih->ih_vector == vector))) { 530 int s, res; 531 /* 532 * We should raise the interrupt level 533 * to ih->ih_prior here. How to do this 534 * machine-independantly? 535 * To be safe, raise to the maximum. 536 */ 537 s = splhigh(); 538 found |= (res = (*(ih->ih_fun))(ih->ih_arg)); 539 splx(s); 540 if (res) 541 ih->ih_count++; 542 if (res == 1) 543 break; 544 } 545 } 546 if (!found) 547 sc->strayintrs++; 548 } 549 550 int 551 b3_2706_dmamap_create(vsc, len, am, datasize, swap, 552 nsegs, segsz, bound, 553 flags, mapp) 554 void *vsc; 555 vme_size_t len; 556 vme_am_t am; 557 vme_datasize_t datasize; 558 vme_swap_t swap; 559 int nsegs; 560 vme_size_t segsz; 561 vme_addr_t bound; 562 int flags; 563 bus_dmamap_t *mapp; 564 { 565 return (EINVAL); 566 } 567 568 void 569 b3_2706_dmamap_destroy(vsc, map) 570 void *vsc; 571 bus_dmamap_t map; 572 { 573 } 574 575 int 576 b3_2706_dmamem_alloc(vsc, len, am, datasizes, swap, segs, nsegs, rsegs, flags) 577 void *vsc; 578 vme_size_t len; 579 vme_am_t am; 580 vme_datasize_t datasizes; 581 vme_swap_t swap; 582 bus_dma_segment_t *segs; 583 int nsegs; 584 int *rsegs; 585 int flags; 586 { 587 return (EINVAL); 588 } 589 590 void 591 b3_2706_dmamem_free(vsc, segs, nsegs) 592 void *vsc; 593 bus_dma_segment_t *segs; 594 int nsegs; 595 { 596 } 597 598 #undef sc 599