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