pci_cfgreg.c revision 55590
1/* 2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 55590 2000-01-08 08:31:24Z peter $ 27 * 28 */ 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/kernel.h> 34 35#include <pci/pcivar.h> 36#include <pci/pcireg.h> 37#include <i386/isa/pcibus.h> 38 39static int cfgmech; 40static int devmax; 41 42/* enable configuration space accesses and return data port address */ 43 44static int 45pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 46{ 47 int dataport = 0; 48 49 if (bus <= PCI_BUSMAX 50 && slot < devmax 51 && func <= PCI_FUNCMAX 52 && reg <= PCI_REGMAX 53 && bytes != 3 54 && (unsigned) bytes <= 4 55 && (reg & (bytes -1)) == 0) { 56 switch (cfgmech) { 57 case 1: 58 outl(CONF1_ADDR_PORT, (1 << 31) 59 | (bus << 16) | (slot << 11) 60 | (func << 8) | (reg & ~0x03)); 61 dataport = CONF1_DATA_PORT + (reg & 0x03); 62 break; 63 case 2: 64 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 65 outb(CONF2_FORWARD_PORT, bus); 66 dataport = 0xc000 | (slot << 8) | reg; 67 break; 68 } 69 } 70 return (dataport); 71} 72 73/* disable configuration space accesses */ 74 75static void 76pci_cfgdisable(void) 77{ 78 switch (cfgmech) { 79 case 1: 80 outl(CONF1_ADDR_PORT, 0); 81 break; 82 case 2: 83 outb(CONF2_ENABLE_PORT, 0); 84 outb(CONF2_FORWARD_PORT, 0); 85 break; 86 } 87} 88 89/* read configuration space register */ 90 91int 92pci_cfgread(pcicfgregs *cfg, int reg, int bytes) 93{ 94 int data = -1; 95 int port; 96 97 port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 98 99 if (port != 0) { 100 switch (bytes) { 101 case 1: 102 data = inb(port); 103 break; 104 case 2: 105 data = inw(port); 106 break; 107 case 4: 108 data = inl(port); 109 break; 110 } 111 pci_cfgdisable(); 112 } 113 return (data); 114} 115 116/* write configuration space register */ 117 118void 119pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) 120{ 121 int port; 122 123 port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 124 if (port != 0) { 125 switch (bytes) { 126 case 1: 127 outb(port, data); 128 break; 129 case 2: 130 outw(port, data); 131 break; 132 case 4: 133 outl(port, data); 134 break; 135 } 136 pci_cfgdisable(); 137 } 138} 139 140/* check whether the configuration mechanism has been correct identified */ 141 142static int 143pci_cfgcheck(int maxdev) 144{ 145 u_char device; 146 147 if (bootverbose) 148 printf("pci_cfgcheck:\tdevice "); 149 150 for (device = 0; device < maxdev; device++) { 151 unsigned id, class, header; 152 if (bootverbose) 153 printf("%d ", device); 154 155 id = inl(pci_cfgenable(0, device, 0, 0, 4)); 156 if (id == 0 || id == -1) 157 continue; 158 159 class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 160 if (bootverbose) 161 printf("[class=%06x] ", class); 162 if (class == 0 || (class & 0xf870ff) != 0) 163 continue; 164 165 header = inb(pci_cfgenable(0, device, 0, 14, 1)); 166 if (bootverbose) 167 printf("[hdr=%02x] ", header); 168 if ((header & 0x7e) != 0) 169 continue; 170 171 if (bootverbose) 172 printf("is there (id=%08x)\n", id); 173 174 pci_cfgdisable(); 175 return (1); 176 } 177 if (bootverbose) 178 printf("-- nothing found\n"); 179 180 pci_cfgdisable(); 181 return (0); 182} 183 184static int 185pci_cfgopen(void) 186{ 187 unsigned long mode1res,oldval1; 188 unsigned char mode2res,oldval2; 189 190 oldval1 = inl(CONF1_ADDR_PORT); 191 192 if (bootverbose) { 193 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 194 oldval1); 195 } 196 197 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 198 199 cfgmech = 1; 200 devmax = 32; 201 202 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 203 outb(CONF1_ADDR_PORT +3, 0); 204 mode1res = inl(CONF1_ADDR_PORT); 205 outl(CONF1_ADDR_PORT, oldval1); 206 207 if (bootverbose) 208 printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 209 mode1res, CONF1_ENABLE_CHK); 210 211 if (mode1res) { 212 if (pci_cfgcheck(32)) 213 return (cfgmech); 214 } 215 216 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 217 mode1res = inl(CONF1_ADDR_PORT); 218 outl(CONF1_ADDR_PORT, oldval1); 219 220 if (bootverbose) 221 printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 222 mode1res, CONF1_ENABLE_CHK1); 223 224 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 225 if (pci_cfgcheck(32)) 226 return (cfgmech); 227 } 228 } 229 230 oldval2 = inb(CONF2_ENABLE_PORT); 231 232 if (bootverbose) { 233 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 234 oldval2); 235 } 236 237 if ((oldval2 & 0xf0) == 0) { 238 239 cfgmech = 2; 240 devmax = 16; 241 242 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 243 mode2res = inb(CONF2_ENABLE_PORT); 244 outb(CONF2_ENABLE_PORT, oldval2); 245 246 if (bootverbose) 247 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 248 mode2res, CONF2_ENABLE_CHK); 249 250 if (mode2res == CONF2_ENABLE_RES) { 251 if (bootverbose) 252 printf("pci_open(2a):\tnow trying mechanism 2\n"); 253 254 if (pci_cfgcheck(16)) 255 return (cfgmech); 256 } 257 } 258 259 cfgmech = 0; 260 devmax = 0; 261 return (cfgmech); 262} 263 264static devclass_t pcib_devclass; 265 266static const char * 267nexus_pcib_is_host_bridge(pcicfgregs *cfg, 268 u_int32_t id, u_int8_t class, u_int8_t subclass, 269 u_int8_t *busnum) 270{ 271 const char *s = NULL; 272 static u_int8_t pxb[4]; /* hack for 450nx */ 273 274 *busnum = 0; 275 276 switch (id) { 277 case 0x12258086: 278 s = "Intel 824?? host to PCI bridge"; 279 /* XXX This is a guess */ 280 *busnum = pci_cfgread(cfg, 0x41, 1); 281 break; 282 case 0x71808086: 283 s = "Intel 82443LX (440 LX) host to PCI bridge"; 284 break; 285 case 0x71908086: 286 s = "Intel 82443BX (440 BX) host to PCI bridge"; 287 break; 288 case 0x71928086: 289 s = "Intel 82443BX host to PCI bridge (AGP disabled)"; 290 break; 291 case 0x71a08086: 292 s = "Intel 82443GX host to PCI bridge"; 293 break; 294 case 0x71a18086: 295 s = "Intel 82443GX host to AGP bridge"; 296 break; 297 case 0x71a28086: 298 s = "Intel 82443GX host to PCI bridge (AGP disabled)"; 299 break; 300 case 0x84c48086: 301 s = "Intel 82454KX/GX (Orion) host to PCI bridge"; 302 *busnum = pci_cfgread(cfg, 0x4a, 1); 303 break; 304 case 0x84ca8086: 305 /* 306 * For the 450nx chipset, there is a whole bundle of 307 * things pretending to be host bridges. The MIOC will 308 * be seen first and isn't really a pci bridge (the 309 * actual busses are attached to the PXB's). We need to 310 * read the registers of the MIOC to figure out the 311 * bus numbers for the PXB channels. 312 * 313 * Since the MIOC doesn't have a pci bus attached, we 314 * pretend it wasn't there. 315 */ 316 pxb[0] = pci_cfgread(cfg, 0xd0, 1); /* BUSNO[0] */ 317 pxb[1] = pci_cfgread(cfg, 0xd1, 1) + 1; /* SUBA[0]+1 */ 318 pxb[2] = pci_cfgread(cfg, 0xd3, 1); /* BUSNO[1] */ 319 pxb[3] = pci_cfgread(cfg, 0xd4, 1) + 1; /* SUBA[1]+1 */ 320 return NULL; 321 case 0x84cb8086: 322 switch (cfg->slot) { 323 case 0x12: 324 s = "Intel 82454NX PXB#0, Bus#A"; 325 *busnum = pxb[0]; 326 break; 327 case 0x13: 328 s = "Intel 82454NX PXB#0, Bus#B"; 329 *busnum = pxb[1]; 330 break; 331 case 0x14: 332 s = "Intel 82454NX PXB#1, Bus#A"; 333 *busnum = pxb[2]; 334 break; 335 case 0x15: 336 s = "Intel 82454NX PXB#1, Bus#B"; 337 *busnum = pxb[3]; 338 break; 339 } 340 break; 341 342 /* AMD -- vendor 0x1022 */ 343 case 0x70061022: 344 s = "AMD-751 host to PCI bridge"; 345 break; 346 347 /* SiS -- vendor 0x1039 */ 348 case 0x04961039: 349 s = "SiS 85c496"; 350 break; 351 case 0x04061039: 352 s = "SiS 85c501"; 353 break; 354 case 0x06011039: 355 s = "SiS 85c601"; 356 break; 357 case 0x55911039: 358 s = "SiS 5591 host to PCI bridge"; 359 break; 360 case 0x00011039: 361 s = "SiS 5591 host to AGP bridge"; 362 break; 363 364 /* VLSI -- vendor 0x1004 */ 365 case 0x00051004: 366 s = "VLSI 82C592 Host to PCI bridge"; 367 break; 368 369 /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ 370 /* totally. Please let me know if anything wrong. -F */ 371 /* XXX need info on the MVP3 -- any takers? */ 372 case 0x05981106: 373 s = "VIA 82C598MVP (Apollo MVP3) host bridge"; 374 break; 375 376 /* AcerLabs -- vendor 0x10b9 */ 377 /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ 378 /* id is '10b9" but the register always shows "10b9". -Foxfair */ 379 case 0x154110b9: 380 s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; 381 break; 382 383 /* OPTi -- vendor 0x1045 */ 384 case 0xc8221045: 385 s = "OPTi 82C822 host to PCI Bridge"; 386 break; 387 388 /* Ross (?) -- vendor 0x1166 */ 389 case 0x00051166: 390 s = "Ross (?) host to PCI bridge"; 391 /* just guessing the secondary bus register number ... */ 392#if 0 393 *busnum = pci_cfgread(cfg, 0x45, 1); 394#endif 395 break; 396 397 /* Integrated Micro Solutions -- vendor 0x10e0 */ 398 case 0x884910e0: 399 s = "Integrated Micro Solutions VL Bridge"; 400 break; 401 402 default: 403 if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) 404 s = "Host to PCI bridge"; 405 break; 406 } 407 408 return s; 409} 410 411/* 412 * Scan the first pci bus for host-pci bridges and add pcib instances 413 * to the nexus for each bridge. 414 */ 415static void 416nexus_pcib_identify(driver_t *driver, device_t parent) 417{ 418 pcicfgregs probe; 419 int found = 0; 420 421 if (pci_cfgopen() == 0) 422 return; 423 probe.hose = 0; 424 probe.bus = 0; 425 for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { 426 int pcifunchigh = 0; 427 for (probe.func = 0; 428 probe.func <= pcifunchigh; 429 probe.func++) { 430 /* 431 * Read the IDs and class from the device. 432 */ 433 u_int32_t id; 434 u_int8_t class, subclass, busnum; 435 device_t child; 436 const char *s; 437 438 id = pci_cfgread(&probe, PCIR_DEVVENDOR, 4); 439 if (id == -1) 440 continue; 441 class = pci_cfgread(&probe, PCIR_CLASS, 1); 442 subclass = pci_cfgread(&probe, PCIR_SUBCLASS, 1); 443 444 s = nexus_pcib_is_host_bridge(&probe, id, 445 class, subclass, 446 &busnum); 447 if (s) { 448 /* 449 * Add at priority 100 to make sure we 450 * go after any motherboard resources 451 */ 452 child = BUS_ADD_CHILD(parent, 100, 453 "pcib", busnum); 454 device_set_desc(child, s); 455 found = 1; 456 } 457 } 458 } 459 460 /* 461 * Make sure we add at least one bridge since some old 462 * hardware doesn't actually have a host-pci bridge device. 463 * Note that pci_cfgopen() thinks we have PCI devices.. 464 */ 465 if (!found) { 466 if (bootverbose) 467 printf( 468 "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); 469 BUS_ADD_CHILD(parent, 100, "pcib", 0); 470 } 471} 472 473static int 474nexus_pcib_probe(device_t dev) 475{ 476 if (pci_cfgopen() != 0) { 477 device_add_child(dev, "pci", device_get_unit(dev)); 478 return 0; 479 } 480 return ENXIO; 481} 482 483static device_method_t nexus_pcib_methods[] = { 484 /* Device interface */ 485 DEVMETHOD(device_identify, nexus_pcib_identify), 486 DEVMETHOD(device_probe, nexus_pcib_probe), 487 DEVMETHOD(device_attach, bus_generic_attach), 488 DEVMETHOD(device_shutdown, bus_generic_shutdown), 489 DEVMETHOD(device_suspend, bus_generic_suspend), 490 DEVMETHOD(device_resume, bus_generic_resume), 491 492 /* Bus interface */ 493 DEVMETHOD(bus_print_child, bus_generic_print_child), 494 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 495 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 496 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 497 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 498 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 499 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 500 501 { 0, 0 } 502}; 503 504static driver_t nexus_pcib_driver = { 505 "pcib", 506 nexus_pcib_methods, 507 1, 508}; 509 510DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); 511