1 /* $NetBSD: btvmeii.c,v 1.14 2008/04/10 19:13:36 cegger 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.14 2008/04/10 19:13:36 cegger 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 <sys/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(struct device *, struct cfdata *, void *); 58 static void b3_2706_attach(struct device *, struct device *, void *); 59 60 /* exported via tag structs */ 61 int b3_2706_map_vme(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(void *, vme_mapresc_t); 65 66 int b3_2706_vme_probe(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(void *, int, int, vme_intr_handle_t *); 72 void *b3_2706_establish_vmeint(void *, vme_intr_handle_t, int, 73 int (*)(void *), void *); 74 void b3_2706_disestablish_vmeint(void *, void *); 75 void b3_2706_vmeint(void *, int, int); 76 77 int b3_2706_dmamap_create(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(void *, bus_dmamap_t); 82 83 int b3_2706_dmamem_alloc(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(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)(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 CFATTACH_DECL(btvmeii, sizeof(struct b3_2706_softc), 125 b3_2706_match, b3_2706_attach, NULL, NULL); 126 127 /* 128 * The adapter consists of a DEC PCI-PCI-bridge with two 129 * PCI devices behind it: A Tundra Universe as device 4 and 130 * some FPGA with glue logics as device 8. 131 * As long as the autoconf code doesn't provide more support 132 * for dependant devices, we have to duplicate a part of the 133 * "ppb" functions here. 134 */ 135 136 static int 137 b3_2706_match(parent, match, aux) 138 struct device *parent; 139 struct cfdata *match; 140 void *aux; 141 { 142 struct pci_attach_args *pa = aux; 143 pci_chipset_tag_t pc = pa->pa_pc; 144 int secbus; 145 pcitag_t tag; 146 pcireg_t id; 147 148 if ((PCI_VENDOR(pa->pa_id) != PCI_VENDOR_DEC) 149 || (PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_DEC_21152)) 150 return (0); 151 152 secbus = PPB_BUSINFO_SECONDARY(pci_conf_read(pc, pa->pa_tag, 153 PPB_REG_BUSINFO)); 154 if (secbus == 0) { 155 printf("b3_2706_match: ppb not configured\n"); 156 return (0); 157 } 158 159 tag = pci_make_tag(pc, secbus, 4, 0); 160 id = pci_conf_read(pc, tag, PCI_ID_REG); 161 162 if ((PCI_VENDOR(id) != PCI_VENDOR_NEWBRIDGE) 163 || (PCI_PRODUCT(id) != PCI_PRODUCT_NEWBRIDGE_CA91CX42)) { 164 #ifdef DEBUG 165 printf("b3_2706_match: no tundra\n"); 166 #endif 167 return (0); 168 } 169 170 tag = pci_make_tag(pc, secbus, 8, 0); 171 id = pci_conf_read(pc, tag, PCI_ID_REG); 172 173 if ((PCI_VENDOR(id) != PCI_VENDOR_BIT3) 174 || (PCI_PRODUCT(id) != PCI_PRODUCT_BIT3_PCIVME2706)) { 175 #ifdef DEBUG 176 printf("b3_2706_match: no bit3 chip\n"); 177 #endif 178 return (0); 179 } 180 181 return (5); /* beat "ppb" */ 182 } 183 184 static void 185 b3_2706_attach(parent, self, aux) 186 struct device *parent, *self; 187 void *aux; 188 { 189 struct b3_2706_softc *sc = (struct b3_2706_softc *)self; 190 struct pci_attach_args *pa = aux; 191 pci_chipset_tag_t pc = pa->pa_pc; 192 struct pci_attach_args aa; 193 int secbus; 194 pcireg_t intr; 195 pcitag_t tag; 196 bus_addr_t swappbase; 197 int i; 198 199 struct vmebus_attach_args vaa; 200 201 aprint_naive(": VME bus adapter\n"); 202 aprint_normal("\n"); 203 204 secbus = PPB_BUSINFO_SECONDARY(pci_conf_read(pc, pa->pa_tag, 205 PPB_REG_BUSINFO)); 206 207 memcpy(&aa, pa, sizeof(struct pci_attach_args)); 208 aa.pa_device = 4; 209 aa.pa_function = 0; 210 aa.pa_tag = pci_make_tag(pc, secbus, 4, 0); 211 aa.pa_intrswiz += 4; 212 intr = pci_conf_read(pc, aa.pa_tag, PCI_INTERRUPT_REG); 213 /* 214 * swizzle it based on the number of 215 * busses we're behind and our device 216 * number. 217 */ 218 aa.pa_intrpin = ((1 + aa.pa_intrswiz - 1) % 4) + 1; 219 aa.pa_intrline = PCI_INTERRUPT_LINE(intr); 220 221 if (univ_pci_attach(&sc->univdata, &aa, device_xname(self), 222 b3_2706_vmeint, sc)) { 223 aprint_error_dev(self, "error initializing universe chip\n"); 224 return; 225 } 226 227 /* 228 * don't waste KVM - the byteswap register is aliased in 229 * a 512k window, we need it only once 230 */ 231 tag = pci_make_tag(pc, secbus, 8, 0); 232 sc->swapt = pa->pa_memt; 233 if (pci_mapreg_info(pc, tag, 0x10, 234 PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 235 &swappbase, 0, 0) || 236 bus_space_map(sc->swapt, swappbase, 4, 0, &sc->swaph)) { 237 aprint_error_dev(self, "can't map byteswap register\n"); 238 return; 239 } 240 /* 241 * Set up cycle specific byteswap mode. 242 * XXX Readback yields "all-ones" for me, and it doesn't seem 243 * to matter what I write into the register - the data don't 244 * get swapped. Adapter fault or documentation bug? 245 */ 246 bus_space_write_4(sc->swapt, sc->swaph, 0, 0x00000490); 247 248 /* VME space is mapped as needed */ 249 sc->vmet = pa->pa_memt; 250 if (pci_mapreg_info(pc, tag, 0x14, 251 PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 252 &sc->vmepbase, 0, 0)) { 253 aprint_error_dev(self, "VME range not assigned\n"); 254 return; 255 } 256 #ifdef BIT3DEBUG 257 aprint_debug_dev(self, "VME window @%lx\n", 258 (long)sc->vmepbase); 259 #endif 260 261 for (i = 0; i < 8; i++) { 262 sc->windowused[i] = 0; 263 } 264 sc->vmeext = extent_create("pcivme", sc->vmepbase, 265 sc->vmepbase + 32*1024*1024 - 1, M_DEVBUF, 266 sc->vmemap, sizeof(sc->vmemap), 267 EX_NOCOALESCE); 268 269 sc->sc_vct.cookie = self; 270 sc->sc_vct.vct_probe = b3_2706_vme_probe; 271 sc->sc_vct.vct_map = b3_2706_map_vme; 272 sc->sc_vct.vct_unmap = b3_2706_unmap_vme; 273 sc->sc_vct.vct_int_map = b3_2706_map_vmeint; 274 sc->sc_vct.vct_int_establish = b3_2706_establish_vmeint; 275 sc->sc_vct.vct_int_disestablish = b3_2706_disestablish_vmeint; 276 sc->sc_vct.vct_dmamap_create = b3_2706_dmamap_create; 277 sc->sc_vct.vct_dmamap_destroy = b3_2706_dmamap_destroy; 278 sc->sc_vct.vct_dmamem_alloc = b3_2706_dmamem_alloc; 279 sc->sc_vct.vct_dmamem_free = b3_2706_dmamem_free; 280 281 vaa.va_vct = &(sc->sc_vct); 282 vaa.va_bdt = pa->pa_dmat; /* XXX */ 283 vaa.va_slaveconfig = 0; /* XXX CSR window? */ 284 285 config_found(self, &vaa, 0); 286 } 287 288 #define sc ((struct b3_2706_softc*)vsc) 289 290 int 291 b3_2706_map_vme(vsc, vmeaddr, len, am, datasizes, swap, tag, handle, resc) 292 void *vsc; 293 vme_addr_t vmeaddr; 294 vme_size_t len; 295 vme_am_t am; 296 vme_datasize_t datasizes; 297 vme_swap_t swap; 298 bus_space_tag_t *tag; 299 bus_space_handle_t *handle; 300 vme_mapresc_t *resc; 301 { 302 int idx, i, wnd, res; 303 unsigned long boundary, maplen, pcibase; 304 vme_addr_t vmebase, vmeend; 305 static int windoworder[8] = {1, 2, 3, 5, 6, 7, 0, 4}; 306 307 /* prefer windows with fine granularity for small mappings */ 308 wnd = -1; 309 if (len <= 32*1024) 310 idx = 6; 311 else 312 idx = 0; 313 for (i = 0; i < 8; i++) { 314 if (!sc->windowused[windoworder[idx]]) { 315 wnd = windoworder[idx]; 316 sc->windowused[wnd] = 1; 317 break; 318 } 319 idx = (idx + 1) % 8; 320 } 321 if (wnd == -1) 322 return (ENOSPC); 323 324 boundary = (wnd & 3) ? 64*1024 : 4*1024; 325 326 /* first mapped address */ 327 vmebase = vmeaddr & ~(boundary - 1); 328 /* base of last mapped page */ 329 vmeend = (vmeaddr + len - 1) & ~(boundary - 1); 330 /* bytes in outgoing window required */ 331 maplen = vmeend - vmebase + boundary; 332 333 if (extent_alloc(sc->vmeext, maplen, boundary, 0, EX_FAST, &pcibase)) { 334 sc->windowused[wnd] = 0; 335 return (ENOMEM); 336 } 337 338 res = univ_pci_mapvme(&sc->univdata, wnd, vmebase, maplen, 339 am, datasizes, pcibase); 340 if (res) { 341 extent_free(sc->vmeext, pcibase, maplen, 0); 342 sc->windowused[wnd] = 0; 343 return (res); 344 } 345 346 res = bus_space_map(sc->vmet, pcibase + (vmeaddr - vmebase), len, 347 0, handle); 348 if (res) { 349 univ_pci_unmapvme(&sc->univdata, wnd); 350 extent_free(sc->vmeext, pcibase, maplen, 0); 351 sc->windowused[wnd] = 0; 352 return (res); 353 } 354 355 *tag = sc->vmet; 356 357 /* 358 * save all data needed for later unmapping 359 */ 360 sc->vmemaprescs[wnd].wnd = wnd; 361 sc->vmemaprescs[wnd].pcibase = pcibase; 362 sc->vmemaprescs[wnd].maplen = maplen; 363 sc->vmemaprescs[wnd].handle = *handle; 364 sc->vmemaprescs[wnd].len = len; 365 *resc = &sc->vmemaprescs[wnd]; 366 return (0); 367 } 368 369 void 370 b3_2706_unmap_vme(vsc, resc) 371 void *vsc; 372 vme_mapresc_t resc; 373 { 374 struct b3_2706_vmemaprescs *r = resc; 375 376 bus_space_unmap(sc->vmet, r->handle, r->len); 377 extent_free(sc->vmeext, r->pcibase, r->maplen, 0); 378 379 if (!sc->windowused[r->wnd]) 380 panic("b3_2706_unmap_vme: bad window"); 381 univ_pci_unmapvme(&sc->univdata, r->wnd); 382 sc->windowused[r->wnd] = 0; 383 } 384 385 int 386 b3_2706_vme_probe(vsc, addr, len, am, datasize, callback, cbarg) 387 void *vsc; 388 vme_addr_t addr; 389 vme_size_t len; 390 vme_am_t am; 391 vme_datasize_t datasize; 392 int (*callback)(void *, bus_space_tag_t, bus_space_handle_t); 393 void *cbarg; 394 { 395 bus_space_tag_t tag; 396 bus_space_handle_t handle; 397 vme_mapresc_t resc; 398 int res, i; 399 volatile u_int32_t dummy; 400 401 res = b3_2706_map_vme(vsc, addr, len, am, datasize, 0, 402 &tag, &handle, &resc); 403 if (res) 404 return (res); 405 406 if (univ_pci_vmebuserr(&sc->univdata, 1)) 407 printf("b3_2706_vme_badaddr: TA bit not clean - reset\n"); 408 409 if (callback) 410 res = (*callback)(cbarg, tag, handle); 411 else { 412 for (i = 0; i < len;) { 413 switch (datasize) { 414 case VME_D8: 415 dummy = bus_space_read_1(tag, handle, i); 416 i++; 417 break; 418 case VME_D16: 419 dummy = bus_space_read_2(tag, handle, i); 420 i += 2; 421 break; 422 case VME_D32: 423 dummy = bus_space_read_4(tag, handle, i); 424 i += 4; 425 break; 426 default: 427 panic("b3_2706_vme_probe: invalid datasize %x", 428 datasize); 429 } 430 } 431 } 432 433 if (univ_pci_vmebuserr(&sc->univdata, 0)) { 434 #ifdef BIT3DEBUG 435 printf("b3_2706_vme_badaddr: caught TA\n"); 436 #endif 437 univ_pci_vmebuserr(&sc->univdata, 1); 438 res = EIO; 439 } 440 441 b3_2706_unmap_vme(vsc, resc); 442 return (res); 443 } 444 445 int 446 b3_2706_map_vmeint(vsc, level, vector, handlep) 447 void *vsc; 448 int level, vector; 449 vme_intr_handle_t *handlep; 450 { 451 452 *handlep = (void *)(long)((level << 8) | vector); /* XXX */ 453 return (0); 454 } 455 456 void * 457 b3_2706_establish_vmeint(vsc, handle, prior, func, arg) 458 void *vsc; 459 vme_intr_handle_t handle; 460 int prior; 461 int (*func)(void *); 462 void *arg; 463 { 464 struct b3_2706_vmeintrhand *ih; 465 long lv; 466 int s; 467 468 /* no point in sleeping unless someone can free memory. */ 469 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 470 if (ih == NULL) 471 panic("b3_2706_map_vmeint: can't malloc handler info"); 472 473 lv = (long)handle; /* XXX */ 474 475 ih->ih_fun = func; 476 ih->ih_arg = arg; 477 ih->ih_level = lv >> 8; 478 ih->ih_vector = lv & 0xff; 479 ih->ih_prior = prior; 480 ih->ih_count = 0; 481 482 s = splhigh(); 483 TAILQ_INSERT_TAIL(&(sc->intrhdls), ih, ih_next); 484 splx(s); 485 486 return (ih); 487 } 488 489 void 490 b3_2706_disestablish_vmeint(vsc, cookie) 491 void *vsc; 492 void *cookie; 493 { 494 struct b3_2706_vmeintrhand *ih = cookie; 495 int s; 496 497 if (!ih) { 498 printf("b3_2706_unmap_vmeint: NULL arg\n"); 499 return; 500 } 501 502 s = splhigh(); 503 TAILQ_REMOVE(&(sc->intrhdls), ih, ih_next); 504 splx(s); 505 506 free(ih, M_DEVBUF); 507 } 508 509 void 510 b3_2706_vmeint(vsc, level, vector) 511 void *vsc; 512 int level, vector; 513 { 514 struct b3_2706_vmeintrhand *ih; 515 int found; 516 517 #ifdef BIT3DEBUG 518 printf("b3_2706_vmeint: VME IRQ %d, vec %x\n", level, vector); 519 #endif 520 found = 0; 521 522 for (ih = sc->intrhdls.tqh_first; ih; 523 ih = ih->ih_next.tqe_next) { 524 if ((ih->ih_level == level) && 525 ((ih->ih_vector == -1) || 526 (ih->ih_vector == vector))) { 527 int s, res; 528 /* 529 * We should raise the interrupt level 530 * to ih->ih_prior here. How to do this 531 * machine-independently? 532 * To be safe, raise to the maximum. 533 */ 534 s = splhigh(); 535 found |= (res = (*(ih->ih_fun))(ih->ih_arg)); 536 splx(s); 537 if (res) 538 ih->ih_count++; 539 if (res == 1) 540 break; 541 } 542 } 543 if (!found) 544 sc->strayintrs++; 545 } 546 547 int 548 b3_2706_dmamap_create(vsc, len, am, datasize, swap, 549 nsegs, segsz, bound, 550 flags, mapp) 551 void *vsc; 552 vme_size_t len; 553 vme_am_t am; 554 vme_datasize_t datasize; 555 vme_swap_t swap; 556 int nsegs; 557 vme_size_t segsz; 558 vme_addr_t bound; 559 int flags; 560 bus_dmamap_t *mapp; 561 { 562 return (EINVAL); 563 } 564 565 void 566 b3_2706_dmamap_destroy(vsc, map) 567 void *vsc; 568 bus_dmamap_t map; 569 { 570 } 571 572 int 573 b3_2706_dmamem_alloc(vsc, len, am, datasizes, swap, segs, nsegs, rsegs, flags) 574 void *vsc; 575 vme_size_t len; 576 vme_am_t am; 577 vme_datasize_t datasizes; 578 vme_swap_t swap; 579 bus_dma_segment_t *segs; 580 int nsegs; 581 int *rsegs; 582 int flags; 583 { 584 return (EINVAL); 585 } 586 587 void 588 b3_2706_dmamem_free(vsc, segs, nsegs) 589 void *vsc; 590 bus_dma_segment_t *segs; 591 int nsegs; 592 { 593 } 594 595 #undef sc 596