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