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