1 /* $OpenBSD: sti_pci.c,v 1.6 2007/06/17 12:07:10 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2007 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 43 struct cfattach sti_pci_ca = { 44 sizeof(struct sti_pci_softc), sti_pci_match, sti_pci_attach 45 }; 46 47 const struct pci_matchid sti_pci_devices[] = { 48 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_EG }, 49 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX2 }, 50 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX4 }, 51 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX6 }, 52 { PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FXE }, 53 }; 54 55 int sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int); 56 int sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *); 57 void sti_pci_enable_rom(struct sti_softc *); 58 void sti_pci_disable_rom(struct sti_softc *); 59 60 int sti_pci_is_console(struct pci_attach_args *, bus_addr_t *); 61 62 int 63 sti_pci_match(struct device *parent, void *cf, void *aux) 64 { 65 struct pci_attach_args *paa = aux; 66 67 return (pci_matchbyid(paa, sti_pci_devices, 68 sizeof(sti_pci_devices) / sizeof(sti_pci_devices[0]))); 69 } 70 71 void 72 sti_pci_attach(struct device *parent, struct device *self, void *aux) 73 { 74 struct sti_pci_softc *spc = (void *)self; 75 struct pci_attach_args *paa = aux; 76 77 spc->sc_pc = paa->pa_pc; 78 spc->sc_tag = paa->pa_tag; 79 spc->sc_base.sc_enable_rom = sti_pci_enable_rom; 80 spc->sc_base.sc_disable_rom = sti_pci_disable_rom; 81 82 printf("\n"); 83 84 if (sti_check_rom(spc, paa) != 0) 85 return; 86 87 printf("%s", self->dv_xname); 88 if (sti_attach_common(&spc->sc_base, STI_CODEBASE_MAIN) == 0) { 89 if (sti_pci_is_console(paa, spc->sc_base.bases) != 0) 90 spc->sc_base.sc_flags |= STI_CONSOLE; 91 startuphook_establish(sti_end_attach, spc); 92 } 93 } 94 95 /* 96 * Grovel the STI ROM image. 97 */ 98 int 99 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa) 100 { 101 struct sti_softc *sc = &spc->sc_base; 102 pcireg_t address, mask; 103 bus_space_handle_t romh; 104 bus_size_t romsize, subsize, stiromsize; 105 bus_addr_t selected, offs, suboffs; 106 u_int32_t tmp; 107 int i; 108 int rc; 109 110 /* sort of inline sti_pci_enable_rom(sc) */ 111 address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG); 112 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, ~PCI_ROM_ENABLE); 113 mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG); 114 address |= PCI_ROM_ENABLE; 115 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, address); 116 sc->sc_flags |= STI_ROM_ENABLED; 117 118 /* 119 * Map the complete ROM for now. 120 */ 121 122 romsize = PCI_ROM_SIZE(mask); 123 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize, 124 0, &romh); 125 sti_pci_disable_rom(sc); 126 if (rc != 0) { 127 printf("%s: can't map PCI ROM (%d)\n", 128 sc->sc_dev.dv_xname, rc); 129 goto fail2; 130 } 131 132 /* 133 * Iterate over the ROM images, pick the best candidate. 134 */ 135 136 selected = (bus_addr_t)-1; 137 for (offs = 0; offs < romsize; offs += subsize) { 138 sti_pci_enable_rom(sc); 139 /* 140 * Check for a valid ROM header. 141 */ 142 tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0); 143 tmp = letoh32(tmp); 144 if (tmp != 0x55aa0000) { 145 sti_pci_disable_rom(sc); 146 if (offs == 0) { 147 printf("%s: invalid PCI ROM header signature" 148 " (%08x)\n", 149 sc->sc_dev.dv_xname, tmp); 150 rc = EINVAL; 151 } 152 break; 153 } 154 155 /* 156 * Check ROM type. 157 */ 158 tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4); 159 tmp = letoh32(tmp); 160 if (tmp != 0x00000001) { /* 1 == STI ROM */ 161 sti_pci_disable_rom(sc); 162 if (offs == 0) { 163 printf("%s: invalid PCI ROM type (%08x)\n", 164 sc->sc_dev.dv_xname, tmp); 165 rc = EINVAL; 166 } 167 break; 168 } 169 170 subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, 171 offs + 0x0c); 172 subsize <<= 9; 173 174 #ifdef STIDEBUG 175 sti_pci_disable_rom(sc); 176 printf("ROM offset %08x size %08x type %08x", 177 offs, subsize, tmp); 178 sti_pci_enable_rom(sc); 179 #endif 180 181 /* 182 * Check for a valid ROM data structure. 183 * We do not need it except to know what architecture the ROM 184 * code is for. 185 */ 186 187 suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh, 188 offs + 0x18); 189 tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0); 190 tmp = letoh32(tmp); 191 if (tmp != 0x50434952) { /* PCIR */ 192 sti_pci_disable_rom(sc); 193 if (offs == 0) { 194 printf("%s: invalid PCI data signature" 195 " (%08x)\n", 196 sc->sc_dev.dv_xname, tmp); 197 rc = EINVAL; 198 } else { 199 #ifdef STIDEBUG 200 printf(" invalid PCI data signature %08x\n", 201 tmp); 202 #endif 203 continue; 204 } 205 } 206 207 tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14); 208 sti_pci_disable_rom(sc); 209 #ifdef STIDEBUG 210 printf(" code %02x", tmp); 211 #endif 212 213 switch (tmp) { 214 #ifdef __hppa__ 215 case 0x10: 216 if (selected == (bus_addr_t)-1) 217 selected = offs; 218 break; 219 #endif 220 #ifdef __i386__ 221 case 0x00: 222 if (selected == (bus_addr_t)-1) 223 selected = offs; 224 break; 225 #endif 226 default: 227 #ifdef STIDEBUG 228 printf(" (wrong architecture)"); 229 #endif 230 break; 231 } 232 233 #ifdef STIDEBUG 234 if (selected == offs) 235 printf(" -> SELECTED"); 236 printf("\n"); 237 #endif 238 } 239 240 if (selected == (bus_addr_t)-1) { 241 if (rc == 0) { 242 printf("%s: found no ROM with correct microcode" 243 " architecture\n", sc->sc_dev.dv_xname); 244 rc = ENOEXEC; 245 } 246 goto fail; 247 } 248 249 /* 250 * Read the STI region BAR assignments. 251 */ 252 253 sti_pci_enable_rom(sc); 254 offs = selected + 255 (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e); 256 for (i = 0; i < STI_REGION_MAX; i++) { 257 rc = sti_readbar(sc, pa, i, 258 bus_space_read_1(pa->pa_memt, romh, offs + i)); 259 if (rc != 0) 260 goto fail; 261 } 262 263 /* 264 * Find out where the STI ROM itself lies, and its size. 265 */ 266 267 offs = selected + 268 (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08); 269 stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, 270 offs + 0x18); 271 stiromsize = letoh32(stiromsize); 272 sti_pci_disable_rom(sc); 273 274 /* 275 * Replace our mapping with a smaller mapping of only the area 276 * we are interested in. 277 */ 278 279 bus_space_unmap(pa->pa_memt, romh, romsize); 280 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs, 281 stiromsize, 0, &sc->romh); 282 if (rc != 0) { 283 printf("%s: can't map STI ROM (%d)\n", 284 sc->sc_dev.dv_xname, rc); 285 goto fail2; 286 } 287 sc->memt = pa->pa_memt; 288 289 return (0); 290 291 fail: 292 bus_space_unmap(pa->pa_memt, romh, romsize); 293 fail2: 294 sti_pci_disable_rom(sc); 295 296 return (rc); 297 } 298 299 /* 300 * Decode a BAR register. 301 */ 302 int 303 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region, 304 int bar) 305 { 306 bus_addr_t addr; 307 bus_size_t size; 308 u_int32_t cf; 309 int rc; 310 311 if (bar == 0) { 312 sc->bases[region] = 0; 313 return (0); 314 } 315 316 #ifdef DIAGNOSTIC 317 if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) { 318 sti_pci_disable_rom(sc); 319 printf("%s: unexpected bar %02x for region %d\n", 320 sc->sc_dev.dv_xname, bar, region); 321 sti_pci_enable_rom(sc); 322 } 323 #endif 324 325 cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar); 326 327 if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) 328 rc = pci_io_find(pa->pa_pc, pa->pa_tag, bar, &addr, &size); 329 else 330 rc = pci_mem_find(pa->pa_pc, pa->pa_tag, bar, &addr, &size, 331 NULL); 332 333 if (rc != 0) { 334 sti_pci_disable_rom(sc); 335 printf("%s: invalid bar %02x for region %d\n", 336 sc->sc_dev.dv_xname, bar, region); 337 sti_pci_enable_rom(sc); 338 return (rc); 339 } 340 341 sc->bases[region] = addr; 342 return (0); 343 } 344 345 /* 346 * Enable PCI ROM. 347 */ 348 void 349 sti_pci_enable_rom(struct sti_softc *sc) 350 { 351 struct sti_pci_softc *spc = (struct sti_pci_softc *)sc; 352 pcireg_t address; 353 354 if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) { 355 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG); 356 address |= PCI_ROM_ENABLE; 357 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address); 358 SET(sc->sc_flags, STI_ROM_ENABLED); 359 } 360 } 361 362 /* 363 * Disable PCI ROM. 364 */ 365 void 366 sti_pci_disable_rom(struct sti_softc *sc) 367 { 368 struct sti_pci_softc *spc = (struct sti_pci_softc *)sc; 369 pcireg_t address; 370 371 if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) { 372 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG); 373 address &= ~PCI_ROM_ENABLE; 374 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address); 375 376 CLR(sc->sc_flags, STI_ROM_ENABLED); 377 } 378 } 379