1 /* $NetBSD: sti_pci.c,v 1.1 2010/11/09 12:24:48 skrll Exp $ */ 2 3 /* $OpenBSD: sti_pci.c,v 1.7 2009/02/06 22:51:04 miod Exp $ */ 4 5 /* 6 * Copyright (c) 2006, 2007 Miodrag Vallat. 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice, this permission notice, and the disclaimer below 11 * appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/device.h> 25 26 #include <dev/pci/pcireg.h> 27 #include <dev/pci/pcivar.h> 28 #include <dev/pci/pcidevs.h> 29 30 #include <dev/wscons/wsdisplayvar.h> 31 32 #include <dev/ic/stireg.h> 33 #include <dev/ic/stivar.h> 34 35 #ifdef STIDEBUG 36 #define DPRINTF(s) do { \ 37 if (stidebug) \ 38 printf s; \ 39 } while(0) 40 41 extern int stidebug; 42 #else 43 #define DPRINTF(s) /* */ 44 #endif 45 46 int sti_pci_match(device_t, cfdata_t, void *); 47 void sti_pci_attach(device_t, device_t, void *); 48 49 void sti_pci_end_attach(device_t dev); 50 51 struct sti_pci_softc { 52 device_t sc_dev; 53 54 struct sti_softc sc_base; 55 56 pci_chipset_tag_t sc_pc; 57 pcitag_t sc_tag; 58 59 bus_space_handle_t sc_romh; 60 }; 61 62 CFATTACH_DECL_NEW(sti_pci, sizeof(struct sti_pci_softc), 63 sti_pci_match, sti_pci_attach, NULL, NULL); 64 65 int sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int); 66 int sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *); 67 void sti_pci_enable_rom(struct sti_softc *); 68 void sti_pci_disable_rom(struct sti_softc *); 69 void sti_pci_enable_rom_internal(struct sti_pci_softc *); 70 void sti_pci_disable_rom_internal(struct sti_pci_softc *); 71 72 int sti_pci_is_console(struct pci_attach_args *, bus_addr_t *); 73 74 #define PCI_ROM_ENABLE 0x00000001 75 #define PCI_ROM_ADDR_MASK 0xfffff800 76 #define PCI_ROM_ADDR(mr) \ 77 ((mr) & PCI_ROM_ADDR_MASK) 78 #define PCI_ROM_SIZE(mr) \ 79 (PCI_ROM_ADDR(mr) & -PCI_ROM_ADDR(mr)) 80 81 int 82 sti_pci_match(device_t parent, cfdata_t cf, void *aux) 83 { 84 struct pci_attach_args *paa = aux; 85 86 if (PCI_VENDOR(paa->pa_id) != PCI_VENDOR_HP) 87 return 0; 88 89 if (PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_EG || 90 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX2 || 91 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX4 || 92 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX6 || 93 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FXE) 94 return 1; 95 96 return 0; 97 } 98 99 void 100 sti_pci_attach(device_t parent, device_t self, void *aux) 101 { 102 struct sti_pci_softc *spc = device_private(self); 103 struct pci_attach_args *paa = aux; 104 int ret; 105 106 spc->sc_dev = self; 107 108 spc->sc_pc = paa->pa_pc; 109 spc->sc_tag = paa->pa_tag; 110 spc->sc_base.sc_dev = self; 111 spc->sc_base.sc_enable_rom = sti_pci_enable_rom; 112 spc->sc_base.sc_disable_rom = sti_pci_disable_rom; 113 114 aprint_normal("\n"); 115 116 if (sti_check_rom(spc, paa) != 0) 117 return; 118 119 aprint_normal("%s", device_xname(self)); 120 ret = sti_pci_is_console(paa, spc->sc_base. bases); 121 if (ret != 0) 122 spc->sc_base.sc_flags |= STI_CONSOLE; 123 124 ret = sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt, 125 spc->sc_romh, STI_CODEBASE_MAIN); 126 if (ret == 0) 127 config_interrupts(self, sti_pci_end_attach); 128 129 } 130 131 void sti_pci_end_attach(device_t dev) 132 { 133 struct sti_pci_softc *spc = device_private(dev); 134 struct sti_softc *sc = &spc->sc_base; 135 136 sti_end_attach(sc); 137 } 138 139 140 /* 141 * Grovel the STI ROM image. 142 */ 143 int 144 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa) 145 { 146 struct sti_softc *sc = &spc->sc_base; 147 pcireg_t address, mask; 148 bus_space_handle_t romh; 149 bus_size_t romsize, subsize, stiromsize; 150 bus_addr_t selected, offs, suboffs; 151 uint32_t tmp; 152 int i; 153 int rc; 154 155 /* sort of inline sti_pci_enable_rom(sc) */ 156 address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM); 157 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, ~PCI_ROM_ENABLE); 158 mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM); 159 address |= PCI_ROM_ENABLE; 160 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, address); 161 sc->sc_flags |= STI_ROM_ENABLED; 162 /* 163 * Map the complete ROM for now. 164 */ 165 166 romsize = PCI_ROM_SIZE(mask); 167 DPRINTF(("%s: mapping rom @ %lx for %lx\n", __func__, 168 (long)PCI_ROM_ADDR(address), (long)romsize)); 169 170 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize, 171 0, &romh); 172 if (rc != 0) { 173 aprint_error_dev(sc->sc_dev, "can't map PCI ROM (%d)\n", rc); 174 goto fail2; 175 } 176 177 sti_pci_disable_rom_internal(spc); 178 /* 179 * Iterate over the ROM images, pick the best candidate. 180 */ 181 182 selected = (bus_addr_t)-1; 183 for (offs = 0; offs < romsize; offs += subsize) { 184 sti_pci_enable_rom_internal(spc); 185 /* 186 * Check for a valid ROM header. 187 */ 188 tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0); 189 tmp = le32toh(tmp); 190 if (tmp != 0x55aa0000) { 191 sti_pci_disable_rom_internal(spc); 192 if (offs == 0) { 193 aprint_error_dev(sc->sc_dev, 194 "invalid PCI ROM header signature (%08x)\n", 195 tmp); 196 rc = EINVAL; 197 } 198 break; 199 } 200 201 /* 202 * Check ROM type. 203 */ 204 tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4); 205 tmp = le32toh(tmp); 206 if (tmp != 0x00000001) { /* 1 == STI ROM */ 207 sti_pci_disable_rom_internal(spc); 208 if (offs == 0) { 209 aprint_error_dev(sc->sc_dev, 210 "invalid PCI ROM type (%08x)\n", tmp); 211 rc = EINVAL; 212 } 213 break; 214 } 215 216 subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, 217 offs + 0x0c); 218 subsize <<= 9; 219 220 #ifdef STIDEBUG 221 sti_pci_disable_rom_internal(spc); 222 DPRINTF(("ROM offset %08x size %08x type %08x", 223 (u_int)offs, (u_int)subsize, tmp)); 224 sti_pci_enable_rom_internal(spc); 225 #endif 226 227 /* 228 * Check for a valid ROM data structure. 229 * We do not need it except to know what architecture the ROM 230 * code is for. 231 */ 232 233 suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh, 234 offs + 0x18); 235 tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0); 236 tmp = le32toh(tmp); 237 if (tmp != 0x50434952) { /* PCIR */ 238 sti_pci_disable_rom_internal(spc); 239 if (offs == 0) { 240 aprint_error_dev(sc->sc_dev, "invalid PCI data" 241 " signature (%08x)\n", tmp); 242 rc = EINVAL; 243 } else { 244 DPRINTF((" invalid PCI data signature %08x\n", 245 tmp)); 246 continue; 247 } 248 } 249 250 tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14); 251 sti_pci_disable_rom_internal(spc); 252 DPRINTF((" code %02x", tmp)); 253 254 switch (tmp) { 255 #ifdef __hppa__ 256 case 0x10: 257 if (selected == (bus_addr_t)-1) 258 selected = offs; 259 break; 260 #endif 261 #ifdef __i386__ 262 case 0x00: 263 if (selected == (bus_addr_t)-1) 264 selected = offs; 265 break; 266 #endif 267 default: 268 #ifdef STIDEBUG 269 DPRINTF((" (wrong architecture)")); 270 #endif 271 break; 272 } 273 DPRINTF(("%s\n", selected == offs ? " -> SELECTED" : "")); 274 } 275 276 if (selected == (bus_addr_t)-1) { 277 if (rc == 0) { 278 aprint_error_dev(sc->sc_dev, "found no ROM with " 279 "correct microcode architecture\n"); 280 rc = ENOEXEC; 281 } 282 goto fail; 283 } 284 285 /* 286 * Read the STI region BAR assignments. 287 */ 288 289 sti_pci_enable_rom_internal(spc); 290 offs = selected + 291 (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e); 292 for (i = 0; i < STI_REGION_MAX; i++) { 293 rc = sti_readbar(sc, pa, i, 294 bus_space_read_1(pa->pa_memt, romh, offs + i)); 295 if (rc != 0) 296 goto fail; 297 } 298 299 /* 300 * Find out where the STI ROM itself lies, and its size. 301 */ 302 303 offs = selected + 304 (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08); 305 stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, 306 offs + 0x18); 307 stiromsize = le32toh(stiromsize); 308 sti_pci_disable_rom_internal(spc); 309 310 /* 311 * Replace our mapping with a smaller mapping of only the area 312 * we are interested in. 313 */ 314 315 DPRINTF(("remapping rom @ %lx for %lx\n", 316 (long)(PCI_ROM_ADDR(address) + offs), (long)stiromsize)); 317 bus_space_unmap(pa->pa_memt, romh, romsize); 318 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs, 319 stiromsize, 0, &spc->sc_romh); 320 if (rc != 0) { 321 aprint_error_dev(sc->sc_dev, "can't map STI ROM (%d)\n", 322 rc); 323 goto fail2; 324 } 325 sti_pci_disable_rom_internal(spc); 326 sc->sc_flags &= ~STI_ROM_ENABLED; 327 328 return 0; 329 330 fail: 331 bus_space_unmap(pa->pa_memt, romh, romsize); 332 fail2: 333 sti_pci_disable_rom_internal(spc); 334 335 return rc; 336 } 337 338 /* 339 * Decode a BAR register. 340 */ 341 int 342 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region, 343 int bar) 344 { 345 bus_addr_t addr; 346 bus_size_t size; 347 uint32_t cf; 348 int rc; 349 350 if (bar == 0) { 351 sc->bases[region] = 0; 352 return (0); 353 } 354 355 #ifdef DIAGNOSTIC 356 if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) { 357 sti_pci_disable_rom(sc); 358 printf("%s: unexpected bar %02x for region %d\n", 359 device_xname(sc->sc_dev), bar, region); 360 sti_pci_enable_rom(sc); 361 } 362 #endif 363 364 cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar); 365 366 rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, PCI_MAPREG_TYPE(cf), 367 &addr, &size, NULL); 368 369 if (rc != 0) { 370 sti_pci_disable_rom(sc); 371 aprint_error_dev(sc->sc_dev, "invalid bar %02x for region %d\n", 372 bar, region); 373 sti_pci_enable_rom(sc); 374 return (rc); 375 } 376 377 sc->bases[region] = addr; 378 return (0); 379 } 380 381 /* 382 * Enable PCI ROM. 383 */ 384 void 385 sti_pci_enable_rom_internal(struct sti_pci_softc *spc) 386 { 387 pcireg_t address; 388 389 KASSERT(spc != NULL); 390 391 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM); 392 address |= PCI_ROM_ENABLE; 393 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address); 394 } 395 396 void 397 sti_pci_enable_rom(struct sti_softc *sc) 398 { 399 struct sti_pci_softc *spc = device_private(sc->sc_dev); 400 401 if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) { 402 sti_pci_enable_rom_internal(spc); 403 } 404 SET(sc->sc_flags, STI_ROM_ENABLED); 405 } 406 407 /* 408 * Disable PCI ROM. 409 */ 410 void 411 sti_pci_disable_rom_internal(struct sti_pci_softc *spc) 412 { 413 pcireg_t address; 414 415 KASSERT(spc != NULL); 416 417 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM); 418 address &= ~PCI_ROM_ENABLE; 419 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address); 420 } 421 422 void 423 sti_pci_disable_rom(struct sti_softc *sc) 424 { 425 struct sti_pci_softc *spc = device_private(sc->sc_dev); 426 427 if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) { 428 sti_pci_disable_rom_internal(spc); 429 } 430 CLR(sc->sc_flags, STI_ROM_ENABLED); 431 } 432