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