| Commit | Line | Data |
|---|---|---|
| 2ac05e91 SZ |
1 | /*- |
| 2 | * Copyright (c) 1997, Stefan Esser <se@kfreebsd.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: src/sys/i386/pci/pci_bus.c,v 1.128.8.1 2009/04/15 03:14:26 kensmith Exp $ | |
| 27 | */ | |
| 28 | ||
| 2ac05e91 SZ |
29 | #include <sys/param.h> |
| 30 | #include <sys/systm.h> | |
| 31 | #include <sys/bus.h> | |
| 32 | #include <sys/kernel.h> | |
| 33 | #include <sys/malloc.h> | |
| 34 | #include <sys/module.h> | |
| 35 | #include <sys/sysctl.h> | |
| 36 | ||
| 37 | #include <bus/pci/pcivar.h> | |
| 38 | #include <bus/pci/pcireg.h> | |
| 39 | #include <bus/pci/pcib_private.h> | |
| 40 | #include <bus/isa/isavar.h> | |
| 41 | #include <machine/md_var.h> | |
| 42 | ||
| 43 | #include "legacyvar.h" | |
| 44 | #include "pci_cfgreg.h" | |
| 45 | ||
| 46 | #include "pcib_if.h" | |
| 47 | ||
| 48 | #ifdef notyet | |
| 49 | static int pcibios_pcib_route_interrupt(device_t pcib, device_t dev, | |
| 50 | int pin); | |
| 51 | #endif | |
| 52 | ||
| 53 | int | |
| 54 | legacy_pcib_maxslots(device_t dev) | |
| 55 | { | |
| 56 | return 31; | |
| 57 | } | |
| 58 | ||
| 59 | /* read configuration space register */ | |
| 60 | ||
| 61 | u_int32_t | |
| 62 | legacy_pcib_read_config(device_t dev, int bus, int slot, int func, | |
| 63 | int reg, int bytes) | |
| 64 | { | |
| 65 | return(pci_cfgregread(bus, slot, func, reg, bytes)); | |
| 66 | } | |
| 67 | ||
| 68 | /* write configuration space register */ | |
| 69 | ||
| 70 | void | |
| 71 | legacy_pcib_write_config(device_t dev, int bus, int slot, int func, | |
| 72 | int reg, u_int32_t data, int bytes) | |
| 73 | { | |
| 74 | pci_cfgregwrite(bus, slot, func, reg, data, bytes); | |
| 75 | } | |
| 76 | ||
| 77 | /* Pass MSI requests up to the nexus. */ | |
| 78 | ||
| 79 | static int | |
| 80 | legacy_pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, | |
| 81 | int *irqs) | |
| 82 | { | |
| 83 | device_t bus; | |
| 84 | ||
| 85 | bus = device_get_parent(pcib); | |
| 86 | return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount, | |
| 87 | irqs)); | |
| 88 | } | |
| 89 | ||
| 90 | static int | |
| 91 | legacy_pcib_alloc_msix(device_t pcib, device_t dev, int *irq) | |
| 92 | { | |
| 93 | device_t bus; | |
| 94 | ||
| 95 | bus = device_get_parent(pcib); | |
| 96 | return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq)); | |
| 97 | } | |
| 98 | ||
| 99 | static int | |
| 100 | legacy_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, | |
| 101 | uint32_t *data) | |
| 102 | { | |
| 103 | device_t bus; | |
| 104 | ||
| 105 | bus = device_get_parent(pcib); | |
| 106 | return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data)); | |
| 107 | } | |
| 108 | ||
| 109 | static const char * | |
| 110 | legacy_pcib_is_host_bridge(int bus, int slot, int func, | |
| 111 | uint32_t id, uint8_t class, uint8_t subclass, | |
| 112 | uint8_t *busnum) | |
| 113 | { | |
| 114 | const char *s = NULL; | |
| 115 | static uint8_t pxb[4]; /* hack for 450nx */ | |
| 116 | ||
| 117 | *busnum = 0; | |
| 118 | ||
| 119 | switch (id) { | |
| 120 | case 0x12258086: | |
| 121 | s = "Intel 824?? host to PCI bridge"; | |
| 122 | /* XXX This is a guess */ | |
| 123 | /* *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x41, 1); */ | |
| 124 | *busnum = bus; | |
| 125 | break; | |
| 126 | case 0x71208086: | |
| 127 | s = "Intel 82810 (i810 GMCH) Host To Hub bridge"; | |
| 128 | break; | |
| 129 | case 0x71228086: | |
| 130 | s = "Intel 82810-DC100 (i810-DC100 GMCH) Host To Hub bridge"; | |
| 131 | break; | |
| 132 | case 0x71248086: | |
| 133 | s = "Intel 82810E (i810E GMCH) Host To Hub bridge"; | |
| 134 | break; | |
| 135 | case 0x11308086: | |
| 136 | s = "Intel 82815 (i815 GMCH) Host To Hub bridge"; | |
| 137 | break; | |
| 138 | case 0x71808086: | |
| 139 | s = "Intel 82443LX (440 LX) host to PCI bridge"; | |
| 140 | break; | |
| 141 | case 0x71908086: | |
| 142 | s = "Intel 82443BX (440 BX) host to PCI bridge"; | |
| 143 | break; | |
| 144 | case 0x71928086: | |
| 145 | s = "Intel 82443BX host to PCI bridge (AGP disabled)"; | |
| 146 | break; | |
| 147 | case 0x71948086: | |
| 148 | s = "Intel 82443MX host to PCI bridge"; | |
| 149 | break; | |
| 150 | case 0x71a08086: | |
| 151 | s = "Intel 82443GX host to PCI bridge"; | |
| 152 | break; | |
| 153 | case 0x71a18086: | |
| 154 | s = "Intel 82443GX host to AGP bridge"; | |
| 155 | break; | |
| 156 | case 0x71a28086: | |
| 157 | s = "Intel 82443GX host to PCI bridge (AGP disabled)"; | |
| 158 | break; | |
| 159 | case 0x84c48086: | |
| 160 | s = "Intel 82454KX/GX (Orion) host to PCI bridge"; | |
| 161 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x4a, 1); | |
| 162 | break; | |
| 163 | case 0x84ca8086: | |
| 164 | /* | |
| 165 | * For the 450nx chipset, there is a whole bundle of | |
| 166 | * things pretending to be host bridges. The MIOC will | |
| 167 | * be seen first and isn't really a pci bridge (the | |
| 168 | * actual busses are attached to the PXB's). We need to | |
| 169 | * read the registers of the MIOC to figure out the | |
| 170 | * bus numbers for the PXB channels. | |
| 171 | * | |
| 172 | * Since the MIOC doesn't have a pci bus attached, we | |
| 173 | * pretend it wasn't there. | |
| 174 | */ | |
| 175 | pxb[0] = legacy_pcib_read_config(0, bus, slot, func, | |
| 176 | 0xd0, 1); /* BUSNO[0] */ | |
| 177 | pxb[1] = legacy_pcib_read_config(0, bus, slot, func, | |
| 178 | 0xd1, 1) + 1; /* SUBA[0]+1 */ | |
| 179 | pxb[2] = legacy_pcib_read_config(0, bus, slot, func, | |
| 180 | 0xd3, 1); /* BUSNO[1] */ | |
| 181 | pxb[3] = legacy_pcib_read_config(0, bus, slot, func, | |
| 182 | 0xd4, 1) + 1; /* SUBA[1]+1 */ | |
| 183 | return NULL; | |
| 184 | case 0x84cb8086: | |
| 185 | switch (slot) { | |
| 186 | case 0x12: | |
| 187 | s = "Intel 82454NX PXB#0, Bus#A"; | |
| 188 | *busnum = pxb[0]; | |
| 189 | break; | |
| 190 | case 0x13: | |
| 191 | s = "Intel 82454NX PXB#0, Bus#B"; | |
| 192 | *busnum = pxb[1]; | |
| 193 | break; | |
| 194 | case 0x14: | |
| 195 | s = "Intel 82454NX PXB#1, Bus#A"; | |
| 196 | *busnum = pxb[2]; | |
| 197 | break; | |
| 198 | case 0x15: | |
| 199 | s = "Intel 82454NX PXB#1, Bus#B"; | |
| 200 | *busnum = pxb[3]; | |
| 201 | break; | |
| 202 | } | |
| 203 | break; | |
| 204 | ||
| 205 | /* AMD -- vendor 0x1022 */ | |
| 206 | case 0x30001022: | |
| 207 | s = "AMD Elan SC520 host to PCI bridge"; | |
| 208 | #ifdef CPU_ELAN | |
| 209 | init_AMD_Elan_sc520(); | |
| 210 | #else | |
| 211 | kprintf( | |
| 212 | "*** WARNING: missing CPU_ELAN -- timekeeping may be wrong\n"); | |
| 213 | #endif | |
| 214 | break; | |
| 215 | case 0x70061022: | |
| 216 | s = "AMD-751 host to PCI bridge"; | |
| 217 | break; | |
| 218 | case 0x700e1022: | |
| 219 | s = "AMD-761 host to PCI bridge"; | |
| 220 | break; | |
| 221 | ||
| 222 | /* SiS -- vendor 0x1039 */ | |
| 223 | case 0x04961039: | |
| 224 | s = "SiS 85c496"; | |
| 225 | break; | |
| 226 | case 0x04061039: | |
| 227 | s = "SiS 85c501"; | |
| 228 | break; | |
| 229 | case 0x06011039: | |
| 230 | s = "SiS 85c601"; | |
| 231 | break; | |
| 232 | case 0x55911039: | |
| 233 | s = "SiS 5591 host to PCI bridge"; | |
| 234 | break; | |
| 235 | case 0x00011039: | |
| 236 | s = "SiS 5591 host to AGP bridge"; | |
| 237 | break; | |
| 238 | ||
| 239 | /* VLSI -- vendor 0x1004 */ | |
| 240 | case 0x00051004: | |
| 241 | s = "VLSI 82C592 Host to PCI bridge"; | |
| 242 | break; | |
| 243 | ||
| 244 | /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ | |
| 245 | /* totally. Please let me know if anything wrong. -F */ | |
| 246 | /* XXX need info on the MVP3 -- any takers? */ | |
| 247 | case 0x05981106: | |
| 248 | s = "VIA 82C598MVP (Apollo MVP3) host bridge"; | |
| 249 | break; | |
| 250 | ||
| 251 | /* AcerLabs -- vendor 0x10b9 */ | |
| 252 | /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ | |
| 253 | /* id is '10b9" but the register always shows "10b9". -Foxfair */ | |
| 254 | case 0x154110b9: | |
| 255 | s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; | |
| 256 | break; | |
| 257 | ||
| 258 | /* OPTi -- vendor 0x1045 */ | |
| 259 | case 0xc7011045: | |
| 260 | s = "OPTi 82C700 host to PCI bridge"; | |
| 261 | break; | |
| 262 | case 0xc8221045: | |
| 263 | s = "OPTi 82C822 host to PCI Bridge"; | |
| 264 | break; | |
| 265 | ||
| 266 | /* ServerWorks -- vendor 0x1166 */ | |
| 267 | case 0x00051166: | |
| 268 | s = "ServerWorks NB6536 2.0HE host to PCI bridge"; | |
| 269 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1); | |
| 270 | break; | |
| 271 | ||
| 272 | case 0x00061166: | |
| 273 | /* FALLTHROUGH */ | |
| 274 | case 0x00081166: | |
| 275 | /* FALLTHROUGH */ | |
| 276 | case 0x02011166: | |
| 277 | /* FALLTHROUGH */ | |
| 278 | case 0x010f1014: /* IBM re-badged ServerWorks chipset */ | |
| 279 | s = "ServerWorks host to PCI bridge"; | |
| 280 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1); | |
| 281 | break; | |
| 282 | ||
| 283 | case 0x00091166: | |
| 284 | s = "ServerWorks NB6635 3.0LE host to PCI bridge"; | |
| 285 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1); | |
| 286 | break; | |
| 287 | ||
| 288 | case 0x00101166: | |
| 289 | s = "ServerWorks CIOB30 host to PCI bridge"; | |
| 290 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1); | |
| 291 | break; | |
| 292 | ||
| 293 | case 0x00111166: | |
| 294 | /* FALLTHROUGH */ | |
| 295 | case 0x03021014: /* IBM re-badged ServerWorks chipset */ | |
| 296 | s = "ServerWorks CMIC-HE host to PCI-X bridge"; | |
| 297 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1); | |
| 298 | break; | |
| 299 | ||
| 300 | /* XXX unknown chipset, but working */ | |
| 301 | case 0x00171166: | |
| 302 | /* FALLTHROUGH */ | |
| 303 | case 0x01011166: | |
| 304 | s = "ServerWorks host to PCI bridge(unknown chipset)"; | |
| 305 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1); | |
| 306 | break; | |
| 307 | ||
| 308 | /* Compaq/HP -- vendor 0x0e11 */ | |
| 309 | case 0x60100e11: | |
| 310 | s = "Compaq/HP Model 6010 HotPlug PCI Bridge"; | |
| 311 | *busnum = legacy_pcib_read_config(0, bus, slot, func, 0xc8, 1); | |
| 312 | break; | |
| 313 | ||
| 314 | /* Integrated Micro Solutions -- vendor 0x10e0 */ | |
| 315 | case 0x884910e0: | |
| 316 | s = "Integrated Micro Solutions VL Bridge"; | |
| 317 | break; | |
| 318 | ||
| 319 | default: | |
| 320 | if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) | |
| 321 | s = "Host to PCI bridge"; | |
| 322 | break; | |
| 323 | } | |
| 324 | ||
| 325 | return s; | |
| 326 | } | |
| 327 | ||
| 328 | /* | |
| 329 | * Scan the first pci bus for host-pci bridges and add pcib instances | |
| 330 | * to the nexus for each bridge. | |
| 331 | */ | |
| 332 | static int | |
| 333 | legacy_pcib_identify(driver_t *driver, device_t parent) | |
| 334 | { | |
| 335 | int bus, slot, func; | |
| 336 | u_int8_t hdrtype; | |
| 337 | int found = 0; | |
| 338 | int pcifunchigh; | |
| 339 | int found824xx = 0; | |
| 340 | int found_orion = 0; | |
| 341 | device_t child; | |
| 342 | devclass_t pci_devclass; | |
| 343 | ||
| 344 | if (pci_cfgregopen() == 0) | |
| 345 | return ENXIO; | |
| 346 | /* | |
| 347 | * Check to see if we haven't already had a PCI bus added | |
| 348 | * via some other means. If we have, bail since otherwise | |
| 349 | * we're going to end up duplicating it. | |
| 350 | */ | |
| 351 | if ((pci_devclass = devclass_find("pci")) && | |
| 352 | devclass_get_device(pci_devclass, 0)) | |
| 353 | return ENXIO; | |
| 354 | ||
| 355 | bus = 0; | |
| 356 | retry: | |
| 357 | for (slot = 0; slot <= PCI_SLOTMAX; slot++) { | |
| 358 | func = 0; | |
| 359 | hdrtype = legacy_pcib_read_config(0, bus, slot, func, | |
| 360 | PCIR_HDRTYPE, 1); | |
| 361 | /* | |
| 362 | * When enumerating bus devices, the standard says that | |
| 363 | * one should check the header type and ignore the slots whose | |
| 364 | * header types that the software doesn't know about. We use | |
| 365 | * this to filter out devices. | |
| 366 | */ | |
| 367 | if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) | |
| 368 | continue; | |
| 369 | if ((hdrtype & PCIM_MFDEV) && | |
| 370 | (!found_orion || hdrtype != 0xff)) | |
| 371 | pcifunchigh = PCI_FUNCMAX; | |
| 372 | else | |
| 373 | pcifunchigh = 0; | |
| 374 | for (func = 0; func <= pcifunchigh; func++) { | |
| 375 | /* | |
| 376 | * Read the IDs and class from the device. | |
| 377 | */ | |
| 378 | u_int32_t id; | |
| 379 | u_int8_t class, subclass, busnum; | |
| 380 | const char *s; | |
| 381 | device_t *devs; | |
| 382 | int ndevs, i; | |
| 383 | ||
| 384 | id = legacy_pcib_read_config(0, bus, slot, func, | |
| 385 | PCIR_DEVVENDOR, 4); | |
| 386 | if (id == -1) | |
| 387 | continue; | |
| 388 | class = legacy_pcib_read_config(0, bus, slot, func, | |
| 389 | PCIR_CLASS, 1); | |
| 390 | subclass = legacy_pcib_read_config(0, bus, slot, func, | |
| 391 | PCIR_SUBCLASS, 1); | |
| 392 | ||
| 393 | s = legacy_pcib_is_host_bridge(bus, slot, func, | |
| 394 | id, class, subclass, | |
| 395 | &busnum); | |
| 396 | if (s == NULL) | |
| 397 | continue; | |
| 398 | ||
| 399 | /* | |
| 400 | * Check to see if the physical bus has already | |
| 401 | * been seen. Eg: hybrid 32 and 64 bit host | |
| 402 | * bridges to the same logical bus. | |
| 403 | */ | |
| 404 | if (device_get_children(parent, &devs, &ndevs) == 0) { | |
| 405 | for (i = 0; s != NULL && i < ndevs; i++) { | |
| 406 | if (strcmp(device_get_name(devs[i]), | |
| 407 | "pcib") != 0) | |
| 408 | continue; | |
| 409 | if (legacy_get_pcibus(devs[i]) == busnum) | |
| 410 | s = NULL; | |
| 411 | } | |
| 412 | kfree(devs, M_TEMP); | |
| 413 | } | |
| 414 | ||
| 415 | if (s == NULL) | |
| 416 | continue; | |
| 417 | ||
| 418 | /* | |
| 419 | * Add at priority 100 to make sure we | |
| 420 | * go after any motherboard resources | |
| 421 | */ | |
| 422 | child = BUS_ADD_CHILD(parent, parent, 100 + busnum, | |
| 423 | "pcib", busnum); | |
| 424 | device_set_desc(child, s); | |
| 425 | legacy_set_pcibus(child, busnum); | |
| 426 | ||
| 427 | found = 1; | |
| 428 | if (id == 0x12258086) | |
| 429 | found824xx = 1; | |
| 430 | if (id == 0x84c48086) | |
| 431 | found_orion = 1; | |
| 432 | } | |
| 433 | } | |
| 434 | if (found824xx && bus == 0) { | |
| 435 | bus++; | |
| 436 | goto retry; | |
| 437 | } | |
| 438 | ||
| 439 | /* | |
| 440 | * Make sure we add at least one bridge since some old | |
| 441 | * hardware doesn't actually have a host-pci bridge device. | |
| 442 | * Note that pci_cfgregopen() thinks we have PCI devices.. | |
| 443 | */ | |
| 444 | if (!found) { | |
| 445 | if (bootverbose) | |
| 446 | kprintf( | |
| 447 | "legacy_pcib_identify: no bridge found, adding pcib0 anyway\n"); | |
| 448 | child = BUS_ADD_CHILD(parent, parent, 100, "pcib", 0); | |
| 449 | legacy_set_pcibus(child, 0); | |
| 450 | } | |
| 451 | return 0; | |
| 452 | } | |
| 453 | ||
| 454 | static int | |
| 455 | legacy_pcib_probe(device_t dev) | |
| 456 | { | |
| 457 | if (pci_cfgregopen() == 0) | |
| 458 | return ENXIO; | |
| 459 | return -100; | |
| 460 | } | |
| 461 | ||
| 462 | static int | |
| 463 | legacy_pcib_attach(device_t dev) | |
| 464 | { | |
| 465 | #ifdef notyet | |
| 466 | device_t pir; | |
| 467 | #endif | |
| 468 | int bus; | |
| 469 | ||
| 470 | /* | |
| 471 | * Look for a PCI BIOS interrupt routing table as that will be | |
| 472 | * our method of routing interrupts if we have one. | |
| 473 | */ | |
| 474 | bus = pcib_get_bus(dev); | |
| 475 | #ifdef notyet | |
| 476 | if (pci_pir_probe(bus, 0)) { | |
| 477 | pir = BUS_ADD_CHILD(device_get_parent(dev), device_get_parent(dev), 0, "pir", 0); | |
| 478 | if (pir != NULL) | |
| 479 | device_probe_and_attach(pir); | |
| 480 | } | |
| 481 | #endif | |
| 482 | device_add_child(dev, "pci", bus); | |
| 483 | return bus_generic_attach(dev); | |
| 484 | } | |
| 485 | ||
| 486 | int | |
| 487 | legacy_pcib_read_ivar(device_t dev, device_t child, int which, | |
| 488 | uintptr_t *result) | |
| 489 | { | |
| 490 | ||
| 491 | switch (which) { | |
| 492 | case PCIB_IVAR_DOMAIN: | |
| 493 | *result = 0; | |
| 494 | return 0; | |
| 495 | case PCIB_IVAR_BUS: | |
| 496 | *result = legacy_get_pcibus(dev); | |
| 497 | return 0; | |
| 498 | } | |
| 499 | return ENOENT; | |
| 500 | } | |
| 501 | ||
| 502 | int | |
| 503 | legacy_pcib_write_ivar(device_t dev, device_t child, int which, | |
| 504 | uintptr_t value) | |
| 505 | { | |
| 506 | ||
| 507 | switch (which) { | |
| 508 | case PCIB_IVAR_DOMAIN: | |
| 509 | return EINVAL; | |
| 510 | case PCIB_IVAR_BUS: | |
| 511 | legacy_set_pcibus(dev, value); | |
| 512 | return 0; | |
| 513 | } | |
| 514 | return ENOENT; | |
| 515 | } | |
| 516 | ||
| 517 | SYSCTL_DECL(_hw_pci); | |
| 518 | ||
| 519 | static unsigned long legacy_host_mem_start = 0x80000000; | |
| 520 | /* XXX need TUNABLE_ULONG */ | |
| 521 | TUNABLE_INT("hw.pci.host_mem_start", (int *)&legacy_host_mem_start); | |
| 522 | SYSCTL_ULONG(_hw_pci, OID_AUTO, host_mem_start, CTLFLAG_RD, | |
| 523 | &legacy_host_mem_start, 0x80000000, | |
| 524 | "Limit the host bridge memory to being above this address. Must be\n\ | |
| 525 | set at boot via a tunable."); | |
| 526 | ||
| 527 | struct resource * | |
| 528 | legacy_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, | |
| 529 | u_long start, u_long end, u_long count, u_int flags) | |
| 530 | { | |
| 531 | /* | |
| 532 | * If no memory preference is given, use upper 32MB slot most | |
| 533 | * bioses use for their memory window. Typically other bridges | |
| 534 | * before us get in the way to assert their preferences on memory. | |
| 535 | * Hardcoding like this sucks, so a more MD/MI way needs to be | |
| 536 | * found to do it. This is typically only used on older laptops | |
| 537 | * that don't have pci busses behind pci bridge, so assuming > 32MB | |
| 538 | * is liekly OK. | |
| 539 | * | |
| 540 | * However, this can cause problems for other chipsets, so we make | |
| 541 | * this tunable by hw.pci.host_mem_start. | |
| 542 | */ | |
| 543 | if (type == SYS_RES_MEMORY && start == 0UL && end == ~0UL) | |
| 544 | start = legacy_host_mem_start; | |
| 545 | if (type == SYS_RES_IOPORT && start == 0UL && end == ~0UL) | |
| 546 | start = 0x1000; | |
| 547 | return (bus_generic_alloc_resource(dev, child, type, rid, start, end, | |
| 548 | count, flags)); | |
| 549 | } | |
| 550 | ||
| 551 | static device_method_t legacy_pcib_methods[] = { | |
| 552 | /* Device interface */ | |
| 553 | DEVMETHOD(device_identify, legacy_pcib_identify), | |
| 554 | DEVMETHOD(device_probe, legacy_pcib_probe), | |
| 555 | DEVMETHOD(device_attach, legacy_pcib_attach), | |
| 556 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 557 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 558 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 559 | ||
| 560 | /* Bus interface */ | |
| 561 | DEVMETHOD(bus_print_child, bus_generic_print_child), | |
| 562 | DEVMETHOD(bus_read_ivar, legacy_pcib_read_ivar), | |
| 563 | DEVMETHOD(bus_write_ivar, legacy_pcib_write_ivar), | |
| 564 | DEVMETHOD(bus_alloc_resource, legacy_pcib_alloc_resource), | |
| 565 | DEVMETHOD(bus_release_resource, bus_generic_release_resource), | |
| 566 | DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), | |
| 567 | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | |
| 568 | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |
| 569 | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |
| 570 | ||
| 571 | /* pcib interface */ | |
| 572 | DEVMETHOD(pcib_maxslots, legacy_pcib_maxslots), | |
| 573 | DEVMETHOD(pcib_read_config, legacy_pcib_read_config), | |
| 574 | DEVMETHOD(pcib_write_config, legacy_pcib_write_config), | |
| 575 | #ifdef notyet | |
| 576 | DEVMETHOD(pcib_route_interrupt, pcibios_pcib_route_interrupt), | |
| 577 | #else | |
| 578 | DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), | |
| 579 | #endif | |
| 580 | DEVMETHOD(pcib_alloc_msi, legacy_pcib_alloc_msi), | |
| 581 | DEVMETHOD(pcib_release_msi, pcib_release_msi), | |
| 582 | DEVMETHOD(pcib_alloc_msix, legacy_pcib_alloc_msix), | |
| 583 | DEVMETHOD(pcib_release_msix, pcib_release_msix), | |
| 584 | DEVMETHOD(pcib_map_msi, legacy_pcib_map_msi), | |
| 585 | { 0, 0 } | |
| 586 | }; | |
| 587 | ||
| 588 | static devclass_t hostb_devclass; | |
| 589 | ||
| 590 | DEFINE_CLASS_0(pcib, legacy_pcib_driver, legacy_pcib_methods, 1); | |
| 591 | DRIVER_MODULE(pcib, legacy, legacy_pcib_driver, hostb_devclass, 0, 0); | |
| 592 | ||
| 593 | ||
| 594 | /* | |
| 595 | * Install placeholder to claim the resources owned by the | |
| 596 | * PCI bus interface. This could be used to extract the | |
| 597 | * config space registers in the extreme case where the PnP | |
| 598 | * ID is available and the PCI BIOS isn't, but for now we just | |
| 599 | * eat the PnP ID and do nothing else. | |
| 600 | * | |
| 601 | * XXX we should silence this probe, as it will generally confuse | |
| 602 | * people. | |
| 603 | */ | |
| 604 | static struct isa_pnp_id pcibus_pnp_ids[] = { | |
| 605 | { 0x030ad041 /* PNP0A03 */, "PCI Bus" }, | |
| 606 | { 0 } | |
| 607 | }; | |
| 608 | ||
| 609 | static int | |
| 610 | pcibus_pnp_probe(device_t dev) | |
| 611 | { | |
| 612 | int result; | |
| 613 | ||
| 614 | if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0) | |
| 615 | device_quiet(dev); | |
| 616 | return(result); | |
| 617 | } | |
| 618 | ||
| 619 | static int | |
| 620 | pcibus_pnp_attach(device_t dev) | |
| 621 | { | |
| 622 | return(0); | |
| 623 | } | |
| 624 | ||
| 625 | static device_method_t pcibus_pnp_methods[] = { | |
| 626 | /* Device interface */ | |
| 627 | DEVMETHOD(device_probe, pcibus_pnp_probe), | |
| 628 | DEVMETHOD(device_attach, pcibus_pnp_attach), | |
| 629 | DEVMETHOD(device_detach, bus_generic_detach), | |
| 630 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 631 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 632 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 633 | { 0, 0 } | |
| 634 | }; | |
| 635 | ||
| 636 | static devclass_t pcibus_pnp_devclass; | |
| 637 | ||
| 638 | DEFINE_CLASS_0(pcibus_pnp, pcibus_pnp_driver, pcibus_pnp_methods, 1); | |
| 639 | DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0); | |
| 640 | ||
| 641 | ||
| 642 | #ifdef notyet | |
| 643 | ||
| 644 | /* | |
| 645 | * Provide a PCI-PCI bridge driver for PCI busses behind PCI-PCI bridges | |
| 646 | * that appear in the PCIBIOS Interrupt Routing Table to use the routing | |
| 647 | * table for interrupt routing when possible. | |
| 648 | */ | |
| 649 | static int pcibios_pcib_probe(device_t bus); | |
| 650 | ||
| 651 | static device_method_t pcibios_pcib_pci_methods[] = { | |
| 652 | /* Device interface */ | |
| 653 | DEVMETHOD(device_probe, pcibios_pcib_probe), | |
| 654 | DEVMETHOD(device_attach, pcib_attach), | |
| 655 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 656 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 657 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 658 | ||
| 659 | /* Bus interface */ | |
| 660 | DEVMETHOD(bus_print_child, bus_generic_print_child), | |
| 661 | DEVMETHOD(bus_read_ivar, pcib_read_ivar), | |
| 662 | DEVMETHOD(bus_write_ivar, pcib_write_ivar), | |
| 663 | DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), | |
| 664 | DEVMETHOD(bus_release_resource, bus_generic_release_resource), | |
| 665 | DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), | |
| 666 | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | |
| 667 | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |
| 668 | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |
| 669 | ||
| 670 | /* pcib interface */ | |
| 671 | DEVMETHOD(pcib_maxslots, pcib_maxslots), | |
| 672 | DEVMETHOD(pcib_read_config, pcib_read_config), | |
| 673 | DEVMETHOD(pcib_write_config, pcib_write_config), | |
| 674 | DEVMETHOD(pcib_route_interrupt, pcibios_pcib_route_interrupt), | |
| 675 | DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi), | |
| 676 | DEVMETHOD(pcib_release_msi, pcib_release_msi), | |
| 677 | DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix), | |
| 678 | DEVMETHOD(pcib_release_msix, pcib_release_msix), | |
| 679 | DEVMETHOD(pcib_map_msi, pcib_map_msi), | |
| 680 | ||
| 681 | {0, 0} | |
| 682 | }; | |
| 683 | ||
| 684 | static devclass_t pcib_devclass; | |
| 685 | ||
| 686 | DEFINE_CLASS_0(pcib, pcibios_pcib_driver, pcibios_pcib_pci_methods, | |
| 687 | sizeof(struct pcib_softc)); | |
| 688 | DRIVER_MODULE(pcibios_pcib, pci, pcibios_pcib_driver, pcib_devclass, 0, 0); | |
| 689 | ||
| 690 | static int | |
| 691 | pcibios_pcib_probe(device_t dev) | |
| 692 | { | |
| 693 | int bus; | |
| 694 | ||
| 695 | if ((pci_get_class(dev) != PCIC_BRIDGE) || | |
| 696 | (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) | |
| 697 | return (ENXIO); | |
| 698 | bus = pci_read_config(dev, PCIR_SECBUS_1, 1); | |
| 699 | if (bus == 0) | |
| 700 | return (ENXIO); | |
| 701 | if (!pci_pir_probe(bus, 1)) | |
| 702 | return (ENXIO); | |
| 703 | device_set_desc(dev, "PCIBIOS PCI-PCI bridge"); | |
| 704 | return (-2000); | |
| 705 | } | |
| 706 | ||
| 707 | static int | |
| 708 | pcibios_pcib_route_interrupt(device_t pcib, device_t dev, int pin) | |
| 709 | { | |
| 710 | return (pci_pir_route_interrupt(pci_get_bus(dev), pci_get_slot(dev), | |
| 711 | pci_get_function(dev), pin)); | |
| 712 | } | |
| 713 | ||
| 714 | #endif /* notyet */ |