acpi/pcib: Rework the _BBN handling for Host-PCI bridges.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 7 Apr 2011 10:11:16 +0000 (18:11 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 7 Apr 2011 10:27:14 +0000 (18:27 +0800)
Obtained-from: FreeBSD (Revision 193829 by jhb@freebsd.org)

  Previously we only trusted a _BBN value of 0 if it was for the first
  bridge encountered since some older systems returned _BBN of 0 for
  all bridges.  However, some newer systems enumerate bridges with
  non-zero _BBN before bus 0 which is perfectly valid.  Handle both
  cases by trusting the first bridge that has a _BBN of 0 and falling
  back to reading from non-standard config registers only for
  subsequent bridges with a _BBN of 0.  We also only perform this
  check for segment (domain) 0.  We assume that _BBN is always correct
  for segments other than 0.

sys/dev/acpica5/acpi_pcib_acpi.c

index 7144815..0bea1d6 100644 (file)
@@ -147,6 +147,7 @@ acpi_pcib_acpi_attach(device_t dev)
 {
     struct acpi_hpcib_softc    *sc;
     ACPI_STATUS                        status;
+    static int bus0_seen = 0;
     u_int addr, slot, func, busok;
     uint8_t busno;
 
@@ -156,6 +157,21 @@ acpi_pcib_acpi_attach(device_t dev)
     sc->ap_dev = dev;
     sc->ap_handle = acpi_get_handle(dev);
 
+    /*
+     * Get our segment number by evaluating _SEG
+     * It's OK for this to not exist.
+     */
+    status = acpi_GetInteger(sc->ap_handle, "_SEG", &sc->ap_segment);
+    if (ACPI_FAILURE(status)) {
+       if (status != AE_NOT_FOUND) {
+           device_printf(dev, "could not evaluate _SEG - %s\n",
+               AcpiFormatException(status));
+           return_VALUE (ENXIO);
+       }
+       /* If it's not found, assume 0. */
+       sc->ap_segment = 0;
+    }
+
     /*
      * Get our base bus number by evaluating _BBN.
      * If this doesn't work, we assume we're bus number 0.
@@ -170,8 +186,10 @@ acpi_pcib_acpi_attach(device_t dev)
      * XXX invoke _REG on this for the PCI config space address space?
      * XXX It seems many BIOS's with multiple Host-PCI bridges do not set
      *     _BBN correctly.  They set _BBN to zero for all bridges.  Thus,
-     *     if _BBN is zero and pcib0 already exists, we try to read our
+     *     if _BBN is zero and PCI bus 0 already exists, we try to read our
      *     bus number from the configuration registers at address _ADR.
+     *     We only do this for domain/segment 0 in the hopes that this is
+     *     only needed for old single-domain machines.
      */
     status = acpi_GetInteger(sc->ap_handle, "_BBN", &sc->ap_bus);
     if (ACPI_FAILURE(status)) {
@@ -186,11 +204,11 @@ acpi_pcib_acpi_attach(device_t dev)
     }
 
     /*
-     * If the bus is zero and pcib0 already exists, read the bus number
-     * via PCI config space.
+     * If this is segment 0, the bus is zero, and PCI bus 0 already
+     * exists, read the bus number via PCI config space.
      */
     busok = 1;
-    if (sc->ap_bus == 0 && devclass_get_device(pcib_devclass, 0) != dev) {
+    if (sc->ap_segment == 0 && sc->ap_bus == 0 && bus0_seen) {
        busok = 0;
        status = acpi_GetInteger(sc->ap_handle, "_ADR", &addr);
        if (ACPI_FAILURE(status)) {
@@ -227,20 +245,10 @@ acpi_pcib_acpi_attach(device_t dev)
        device_printf(dev, "trying bus number %d\n", sc->ap_bus);
     }
 
-    /*
-     * Get our segment number by evaluating _SEG
-     * It's OK for this to not exist.
-     */
-    status = acpi_GetInteger(sc->ap_handle, "_SEG", &sc->ap_segment);
-    if (ACPI_FAILURE(status)) {
-       if (status != AE_NOT_FOUND) {
-           device_printf(dev, "could not evaluate _SEG - %s\n",
-               AcpiFormatException(status));
-           return_VALUE (ENXIO);
-       }
-       /* If it's not found, assume 0. */
-       sc->ap_segment = 0;
-    }
+    /* If this is bus 0 on segment 0, note that it has been seen already. */
+    if (sc->ap_segment == 0 && sc->ap_bus == 0)
+           bus0_seen = 1;
+
     return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_bus));
 }