1 /* $OpenBSD: sti_pci.c,v 1.14 2024/08/17 08:45:22 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2007, 2023 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice, this permission notice, and the disclaimer below 9 * appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 24 #include <dev/pci/pcireg.h> 25 #include <dev/pci/pcivar.h> 26 #include <dev/pci/pcidevs.h> 27 28 #include <dev/wscons/wsdisplayvar.h> 29 30 #include <dev/ic/stireg.h> 31 #include <dev/ic/stivar.h> 32 33 int sti_pci_match(struct device *, void *, void *); 34 void sti_pci_attach(struct device *, struct device *, void *); 35 36 struct sti_pci_softc { 37 struct sti_softc sc_base; 38 39 pci_chipset_tag_t sc_pc; 40 pcitag_t sc_tag; 41 42 bus_space_handle_t sc_romh; 43 }; 44 45 const struct cfattach sti_pci_ca = { 46 sizeof(struct sti_pci_softc), sti_pci_match, sti_pci_attach 47 }; 48 49 const struct pci_matchid sti_pci_devices[] = { 50 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_EG }, 51 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX2 }, 52 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX4 }, 53 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX6 }, 54 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FXE }, 55 #ifdef notyet 56 { PCI_VENDOR_IBM, PCI_PRODUCT_IBM_FIREGL2 } 57 #endif 58 }; 59 60 int sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int); 61 int sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *); 62 void sti_pci_enable_rom(struct sti_softc *); 63 void sti_pci_disable_rom(struct sti_softc *); 64 65 int sti_pci_is_console(struct pci_attach_args *, bus_addr_t *); 66 67 int 68 sti_pci_match(struct device *parent, void *cf, void *aux) 69 { 70 struct pci_attach_args *paa = aux; 71 72 return pci_matchbyid(paa, sti_pci_devices, 73 sizeof(sti_pci_devices) / sizeof(sti_pci_devices[0])); 74 } 75 76 void 77 sti_pci_attach(struct device *parent, struct device *self, void *aux) 78 { 79 struct sti_pci_softc *spc = (void *)self; 80 struct pci_attach_args *paa = aux; 81 82 spc->sc_pc = paa->pa_pc; 83 spc->sc_tag = paa->pa_tag; 84 spc->sc_base.sc_enable_rom = sti_pci_enable_rom; 85 spc->sc_base.sc_disable_rom = sti_pci_disable_rom; 86 87 printf("\n"); 88 89 if (sti_check_rom(spc, paa) != 0) 90 return; 91 92 printf("%s", self->dv_xname); 93 if (sti_pci_is_console(paa, spc->sc_base.bases) != 0) 94 spc->sc_base.sc_flags |= STI_CONSOLE; 95 if (sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt, 96 spc->sc_romh, STI_CODEBASE_MAIN) == 0) 97 startuphook_establish(sti_end_attach, spc); 98 } 99 100 /* 101 * Enable PCI ROM. 102 */ 103 void 104 sti_pci_enable_rom(struct sti_softc *sc) 105 { 106 struct sti_pci_softc *spc = (struct sti_pci_softc *)sc; 107 pcireg_t address; 108 109 if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) { 110 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG); 111 address |= PCI_ROM_ENABLE; 112 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address); 113 SET(sc->sc_flags, STI_ROM_ENABLED); 114 } 115 } 116 117 /* 118 * Disable PCI ROM. 119 */ 120 void 121 sti_pci_disable_rom(struct sti_softc *sc) 122 { 123 struct sti_pci_softc *spc = (struct sti_pci_softc *)sc; 124 pcireg_t address; 125 126 if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) { 127 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG); 128 address &= ~PCI_ROM_ENABLE; 129 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address); 130 131 CLR(sc->sc_flags, STI_ROM_ENABLED); 132 } 133 } 134 135 /* 136 * We have to be extremely careful with output in this file, as the 137 * device we are trying to attach might be the console, and we are 138 * still using the PDC routines for output at this point. 139 * 140 * On some devices, if not all, PDC routines assume the STI ROM is *NOT* 141 * mapped when they are invoked, and they will cause the system to freeze 142 * if it is mapped. 143 * 144 * As a result, we need to make sure the ROM is not mapped when invoking 145 * printf(). The following wrapper takes care of this to reduce the risk 146 * of making a mistake. 147 */ 148 149 static int 150 sti_local_printf(struct sti_softc *sc, const char *fmt, ...) 151 { 152 va_list ap; 153 int rc; 154 int enabled = sc->sc_flags & STI_ROM_ENABLED; 155 156 if (enabled) 157 sti_pci_disable_rom(sc); 158 va_start(ap, fmt); 159 rc = vprintf(fmt, ap); 160 if (enabled) 161 sti_pci_enable_rom(sc); 162 163 return rc; 164 } 165 166 #define printf(fmt, ...) sti_local_printf(sc, fmt, ## __VA_ARGS__) 167 168 /* 169 * PCI ROM Data Structure (6.3.1.2) 170 */ 171 172 struct pcirom_ds { 173 uint32_t signature; 174 uint16_t vid; 175 uint16_t pid; 176 uint16_t reserved_8; 177 uint16_t dslen; 178 uint8_t rev; 179 uint8_t class[3]; 180 uint16_t romlen; 181 uint16_t level; 182 uint8_t arch; 183 uint8_t indicator; 184 uint16_t reserved_16; 185 }; 186 187 /* 188 * Callback data used while walking PCI ROM images to pick the most 189 * appropriate image. 190 */ 191 192 struct sti_pcirom_walk_ctx { 193 #ifdef STIDEBUG 194 struct sti_softc *sc; 195 #endif 196 bus_addr_t romoffs; 197 }; 198 199 int sti_pcirom_check(bus_space_tag_t, bus_space_handle_t, bus_addr_t, 200 const struct pcirom_ds *, void *); 201 int sti_pcirom_walk(struct sti_softc *, bus_space_tag_t, bus_space_handle_t, 202 bus_size_t, int (*)(bus_space_tag_t, bus_space_handle_t, bus_addr_t, 203 const struct pcirom_ds *, void *), void *); 204 205 /* 206 * Grovel the STI ROM image. 207 */ 208 209 int 210 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa) 211 { 212 struct sti_softc *sc = &spc->sc_base; 213 pcireg_t address, mask; 214 bus_space_handle_t romh; 215 bus_size_t romsize, stiromsize; 216 bus_addr_t offs; 217 uint8_t region_bars[STI_REGION_MAX]; 218 int i; 219 int rc; 220 221 struct sti_pcirom_walk_ctx ctx; 222 223 /* sort of inline sti_pci_enable_rom(sc) */ 224 address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG); 225 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, ~PCI_ROM_ENABLE); 226 mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG); 227 address |= PCI_ROM_ENABLE; 228 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, address); 229 sc->sc_flags |= STI_ROM_ENABLED; 230 231 /* 232 * Map the complete ROM for now. 233 */ 234 235 romsize = PCI_ROM_SIZE(mask); 236 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize, 237 0, &romh); 238 if (rc != 0) { 239 printf("%s: can't map PCI ROM (%d)\n", 240 sc->sc_dev.dv_xname, rc); 241 goto disable_return; 242 } 243 244 /* 245 * Iterate over the ROM images, pick the best candidate. 246 */ 247 248 #ifdef STIDEBUG 249 ctx.sc = sc; 250 #endif 251 ctx.romoffs = (bus_addr_t)-1; 252 rc = sti_pcirom_walk(sc, pa->pa_memt, romh, romsize, sti_pcirom_check, 253 &ctx); 254 255 if (ctx.romoffs == (bus_addr_t)-1) { 256 if (rc == 0) { 257 printf("%s: found no ROM with correct microcode" 258 " architecture\n", sc->sc_dev.dv_xname); 259 rc = ENOEXEC; 260 } 261 goto unmap_disable_return; 262 } 263 264 /* 265 * Read the STI region BAR assignments. 266 */ 267 268 offs = ctx.romoffs + 269 (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, ctx.romoffs + 0x0e); 270 bus_space_read_region_1(pa->pa_memt, romh, offs, region_bars, 271 STI_REGION_MAX); 272 for (i = 0; i < STI_REGION_MAX; i++) { 273 /* 274 * Region 0 is supposed to always be the ROM. FireGL-UX 275 * ROM agrees so well that it will report the expansion 276 * ROM BAR rather than any regular BAR. 277 * We'll address this later after remapping the ROM. 278 */ 279 if (i == 0 && region_bars[i] == PCI_ROM_REG) 280 continue; 281 282 rc = sti_readbar(sc, pa, i, region_bars[i]); 283 if (rc != 0) 284 goto unmap_disable_return; 285 } 286 287 /* 288 * Find out where the STI ROM itself lies within the PCI ROM, 289 * and its size. 290 */ 291 292 offs = ctx.romoffs + 293 (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, ctx.romoffs + 0x08); 294 stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, 295 offs + 0x18); 296 stiromsize = letoh32(stiromsize); 297 298 /* 299 * Replace our mapping with a smaller mapping of only the area 300 * we are interested in. 301 */ 302 303 bus_space_unmap(pa->pa_memt, romh, romsize); 304 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs, 305 stiromsize, 0, &spc->sc_romh); 306 if (rc != 0) { 307 printf("%s: can't map STI ROM (%d)\n", 308 sc->sc_dev.dv_xname, rc); 309 goto disable_return; 310 } 311 312 /* 313 * Now set up region 0 if we had skipped it earlier. 314 */ 315 316 if (region_bars[0] == PCI_ROM_REG) { 317 sc->bases[0] = 318 (bus_addr_t)bus_space_vaddr(pa->pa_memt, spc->sc_romh) - 319 (offs - ctx.romoffs); 320 } 321 322 sti_pci_disable_rom(sc); 323 return 0; 324 325 unmap_disable_return: 326 bus_space_unmap(pa->pa_memt, romh, romsize); 327 disable_return: 328 sti_pci_disable_rom(sc); 329 return rc; 330 } 331 332 /* 333 * Decode a BAR register. 334 */ 335 int 336 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region, 337 int bar) 338 { 339 bus_addr_t addr; 340 bus_size_t size; 341 pcireg_t type; 342 int rc; 343 344 if (bar == 0) { 345 sc->bases[region] = 0; 346 return 0; 347 } 348 349 if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) { 350 #ifdef DIAGNOSTIC 351 printf("%s: unexpected bar %02x for region %d\n", 352 sc->sc_dev.dv_xname, bar, region); 353 #endif 354 return EINVAL; 355 } 356 357 type = pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar); 358 rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, type, &addr, &size, 359 NULL); 360 if (rc != 0) { 361 printf("%s: invalid bar %02x for region %d\n", 362 sc->sc_dev.dv_xname, bar, region); 363 return rc; 364 } 365 366 sc->bases[region] = addr; 367 return 0; 368 } 369 370 /* 371 * Check a PCI ROM image for an STI ROM. 372 */ 373 374 int 375 sti_pcirom_check(bus_space_tag_t romt, bus_space_handle_t romh, bus_addr_t offs, 376 const struct pcirom_ds *ds, void *v) 377 { 378 struct sti_pcirom_walk_ctx *ctx = v; 379 #ifdef STIDEBUG 380 struct sti_softc *sc = ctx->sc; 381 #endif 382 uint32_t tmp32; 383 384 /* 385 * Check for a valid STI ROM header. 386 */ 387 388 tmp32 = bus_space_read_4(romt, romh, offs + 0); 389 tmp32 = letoh32(tmp32); 390 if (tmp32 != 0x55aa0000) { 391 /* Not an STI ROM image, onto the next */ 392 #ifdef STIDEBUG 393 printf("Invalid HP ROM signature (%08x)\n", tmp32); 394 #endif 395 return 0; 396 } 397 398 /* 399 * Check ROM type. 400 */ 401 402 tmp32 = bus_space_read_4(romt, romh, offs + 4); 403 tmp32 = letoh32(tmp32); 404 if (tmp32 != 0x00000001) { /* 1 == STI ROM */ 405 #ifdef STIDEBUG 406 printf("Unknown HP ROM type (%08x)\n", tmp32); 407 #endif 408 return 0; 409 } 410 411 #ifdef STIDEBUG 412 printf("ROM architecture code %02x", ds->arch); 413 #endif 414 switch (ds->arch) { 415 #ifdef __hppa__ 416 /* 417 * The PCI specification assigns value 0x02 to PA-RISC, but 418 * according to the STI specification (and to hardware), 419 * the correct value to check for is 0x10. 420 */ 421 case 0x10: 422 if (ctx->romoffs == (bus_addr_t)-1) 423 ctx->romoffs = offs; 424 break; 425 #endif 426 #ifdef __i386__ 427 case 0x00: 428 if (ctx->romoffs == (bus_addr_t)-1) 429 ctx->romoffs = offs; 430 break; 431 #endif 432 default: 433 #ifdef STIDEBUG 434 printf(" (wrong architecture)"); 435 #endif 436 break; 437 } 438 439 #ifdef STIDEBUG 440 if (ctx->romoffs == offs) 441 printf(" -> SELECTED"); 442 printf("\n"); 443 #endif 444 445 return 0; 446 } 447 448 /* 449 * Iterate over all PCI ROM images. 450 * This code is absolutely not related to sti(4) and could be moved 451 * elsewhere in case other drivers have a need for it, but is kept 452 * here for now due to the printf() wrapper requirement (which is why 453 * there is an otherwise unnecessary softc argument). 454 */ 455 456 int 457 sti_pcirom_walk(struct sti_softc *sc, bus_space_tag_t romt, 458 bus_space_handle_t romh, bus_size_t romsize, int (*cb)(bus_space_tag_t, 459 bus_space_handle_t, bus_addr_t, const struct pcirom_ds *, void *), 460 void *cbarg) 461 { 462 bus_addr_t offs; 463 bus_size_t subsize; 464 int rc = 0; 465 466 for (offs = 0; offs < romsize; offs += subsize) { 467 struct pcirom_ds ds; 468 bus_addr_t dsoffs; 469 uint16_t tmp16; 470 471 #ifdef STIDEBUG 472 printf("Checking for ROM image at offset %08lx/%08lx\n", 473 offs, romsize); 474 #endif 475 476 /* 477 * Check for a valid ROM header (6.3.1.1). 478 */ 479 480 tmp16 = bus_space_read_2(romt, romh, offs + 0); 481 tmp16 = letoh16(tmp16); 482 if (tmp16 != 0x55aa) { 483 if (offs == 0) { 484 printf("%s: invalid PCI ROM header signature" 485 " (%04x)\n", 486 sc->sc_dev.dv_xname, tmp16); 487 rc = EINVAL; 488 } 489 break; 490 } 491 492 /* 493 * Check for a valid ROM data structure (6.3.1.2). 494 */ 495 496 dsoffs = (bus_addr_t)bus_space_read_2(romt, romh, offs + 0x18); 497 #ifdef STIDEBUG 498 printf("PCI DS offset %04lx\n", dsoffs); 499 #endif 500 if ((dsoffs & 0x03) != 0 || dsoffs < 0x1a || 501 offs + dsoffs + sizeof(struct pcirom_ds) > romsize) { 502 if (offs == 0) { 503 printf("%s: ill-formed PCI Data Structure" 504 " (offset %04lx)\n", 505 sc->sc_dev.dv_xname, dsoffs); 506 rc = EINVAL; 507 } else { 508 #ifdef STIDEBUG 509 printf("Ill-formed PCI Data Structure" 510 " (offset %04lx)\n", dsoffs); 511 #endif 512 } 513 break; 514 } 515 516 bus_space_read_region_1(romt, romh, offs + dsoffs, 517 (uint8_t *)&ds, sizeof ds); 518 /* convert sizes to host endianness */ 519 ds.dslen = letoh16(ds.dslen); 520 ds.romlen = letoh16(ds.romlen); 521 #if 0 /* not used in this code */ 522 ds.vid = letoh16(ds.vid); 523 ds.pid = letoh16(ds.pid); 524 ds.level = letoh16(ds.level); 525 #endif 526 if (ds.signature != 0x50434952) { /* PCIR */ 527 if (offs == 0) { 528 printf("%s: invalid PCI data signature" 529 " (%08x)\n", 530 sc->sc_dev.dv_xname, ds.signature); 531 rc = EINVAL; 532 } else { 533 #ifdef STIDEBUG 534 printf(" invalid PCI data signature %08x\n", 535 ds.signature); 536 #endif 537 } 538 break; 539 } 540 541 #ifdef STIDEBUG 542 printf("PCI DS length %04lx\n", ds.dslen); 543 #endif 544 if (ds.dslen < sizeof ds || dsoffs + ds.dslen > romsize) { 545 if (offs == 0) { 546 printf("%s: ill-formed PCI Data Structure" 547 " (size %04lx)\n", 548 sc->sc_dev.dv_xname, ds.dslen); 549 rc = EINVAL; 550 } else { 551 #ifdef STIDEBUG 552 printf("Ill-formed PCI Data Structure" 553 " (size %04lx)\n", ds.dslen); 554 #endif 555 } 556 break; 557 } 558 559 subsize = ((bus_size_t)ds.romlen) << 9; 560 #ifdef STIDEBUG 561 printf("ROM image size %08lx\n", subsize); 562 #endif 563 if (subsize == 0 || offs + subsize > romsize) { 564 if (offs == 0) { 565 printf("%s: invalid ROM image size" 566 " (%04lx)\n", 567 sc->sc_dev.dv_xname, subsize); 568 rc = EINVAL; 569 } else { 570 #ifdef STIDEBUG 571 printf("Invalid ROM image size" 572 " (%04lx)\n", subsize); 573 #endif 574 } 575 break; 576 } 577 578 rc = (*cb)(romt, romh, offs, &ds, cbarg); 579 if (rc != 0) 580 break; 581 582 if ((ds.indicator & 0x80) != 0) { 583 /* no more ROM images */ 584 #ifdef STIDEBUG 585 printf("no more ROM images\n"); 586 #endif 587 break; 588 } 589 } 590 591 return rc; 592 } 593