Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/acpi From Gregoire Sutre: rework the ACPI PCI suppor...



details:   https://anonhg.NetBSD.org/src/rev/1a20d6e2e073
branches:  trunk
changeset: 754166:1a20d6e2e073
user:      jruoho <jruoho%NetBSD.org@localhost>
date:      Thu Apr 22 14:50:30 2010 +0000

description:
>From Gregoire Sutre: rework the ACPI PCI support. This makes ACPI to
correctly pick PCI segment groups, PCI bus numbers, PCI root bridges,
PCI-to-PCI bridges, and PCI devices, among other things. In short: it is
more robust than the old code or anything in sys/arch/x86/x86/mpacpi.c.

ok cegger@, jmcneill@

diffstat:

 sys/dev/acpi/acpi.c     |    5 +-
 sys/dev/acpi/acpi_pci.c |  387 ++++++++++++++++++++++++++++++++---------------
 sys/dev/acpi/acpi_pci.h |    9 +-
 sys/dev/acpi/acpivar.h  |   24 ++-
 4 files changed, 293 insertions(+), 132 deletions(-)

diffs (truncated from 552 to 300 lines):

diff -r 6ef1312568d1 -r 1a20d6e2e073 sys/dev/acpi/acpi.c
--- a/sys/dev/acpi/acpi.c       Thu Apr 22 14:37:06 2010 +0000
+++ b/sys/dev/acpi/acpi.c       Thu Apr 22 14:50:30 2010 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: acpi.c,v 1.179 2010/04/20 04:57:04 jruoho Exp $        */
+/*     $NetBSD: acpi.c,v 1.180 2010/04/22 14:50:30 jruoho Exp $        */
 
 /*-
  * Copyright (c) 2003, 2007 The NetBSD Foundation, Inc.
@@ -65,7 +65,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.179 2010/04/20 04:57:04 jruoho Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.180 2010/04/22 14:50:30 jruoho Exp $");
 
 #include "opt_acpi.h"
 #include "opt_pcifixup.h"
@@ -662,6 +662,7 @@
 
                ad->ad_device = NULL;
                ad->ad_notify = NULL;
+               ad->ad_pciinfo = NULL;
 
                ad->ad_type = type;
                ad->ad_handle = handle;
diff -r 6ef1312568d1 -r 1a20d6e2e073 sys/dev/acpi/acpi_pci.c
--- a/sys/dev/acpi/acpi_pci.c   Thu Apr 22 14:37:06 2010 +0000
+++ b/sys/dev/acpi/acpi_pci.c   Thu Apr 22 14:50:30 2010 +0000
@@ -1,11 +1,11 @@
-/* $NetBSD: acpi_pci.c,v 1.5 2010/04/18 14:05:26 jruoho Exp $ */
+/* $NetBSD: acpi_pci.c,v 1.6 2010/04/22 14:50:31 jruoho Exp $ */
 
 /*
- * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
- * by Christoph Egger.
+ * by Christoph Egger and Gregoire Sutre.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -33,147 +33,287 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi_pci.c,v 1.5 2010/04/18 14:05:26 jruoho Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi_pci.c,v 1.6 2010/04/22 14:50:31 jruoho Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
 #include <sys/kmem.h>
-#include <sys/queue.h>
 #include <sys/systm.h>
 
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/ppbreg.h>
+
 #include <dev/acpi/acpireg.h>
 #include <dev/acpi/acpivar.h>
 #include <dev/acpi/acpi_pci.h>
 
-struct acpi_pcidev;
+#define _COMPONENT     ACPI_BUS_COMPONENT
+ACPI_MODULE_NAME       ("acpi_pci")
 
-static TAILQ_HEAD(, acpi_pcidev) acpi_pcidevlist =
-    TAILQ_HEAD_INITIALIZER(acpi_pcidevlist);
+static ACPI_STATUS     acpi_pcidev_pciroot_bus(ACPI_HANDLE, uint16_t *);
+static ACPI_STATUS     acpi_pcidev_pciroot_bus_callback(ACPI_RESOURCE *,
+                           void *);
+static ACPI_STATUS     acpi_pcidev_scan_rec(struct acpi_devnode *);
+
 
-struct acpi_pcidev {
-       struct acpi_devnode *ap_node;
-       uint32_t ap_pciseg;
-       uint32_t ap_pcibus;
-       uint32_t ap_pcidev;
-       uint32_t ap_pcifunc;
-       bool ap_pcihost;
-       TAILQ_ENTRY(acpi_pcidev) ap_list;
-};
+/*
+ * Regarding PCI Segment Groups, the ACPI spec says (cf. ACPI 4.0, p. 277):
+ *
+ * "The optional _SEG object is located under a PCI host bridge and
+ * evaluates to an integer that describes the PCI Segment Group (see PCI
+ * Firmware Specification v3.0)."
+ *
+ * "PCI Segment Group supports more than 256 buses in a system by allowing
+ * the reuse of the PCI bus numbers.  Within each PCI Segment Group, the bus
+ * numbers for the PCI buses must be unique. PCI buses in different PCI
+ * Segment Group are permitted to have the same bus number."
+ *
+ * "If _SEG does not exist, OSPM assumes that all PCI bus segments are in
+ * PCI Segment Group 0."
+ *
+ * "The lower 16 bits of _SEG returned integer is the PCI Segment Group
+ * number.  Other bits are reserved."
+ */
+
+/*
+ * Regarding PCI Base Bus Numbers, the ACPI spec says (cf. ACPI 4.0, p. 277):
+ *
+ * "For multi-root PCI platforms, the _BBN object evaluates to the PCI bus
+ * number that the BIOS assigns.  This is needed to access a PCI_Config
+ * operation region for the specified bus.  The _BBN object is located under
+ * a PCI host bridge and must be unique for every host bridge within a
+ * segment since it is the PCI bus number."
+ *
+ * Moreover, the ACPI FAQ (http://www.acpi.info/acpi_faq.htm) says:
+ *
+ * "For a multiple root bus machine, _BBN is required for each bus.  _BBN
+ * should provide the bus number assigned to this bus by the BIOS at boot
+ * time."
+ */
 
 
-static const char * const acpi_pcidev_ids[] = {
-       "PNP0A??",      /* ACPI PCI host controllers */
-       NULL
-};
-
-static bool
-acpi_pcidev_add(struct acpi_softc *sc, struct acpi_devnode *ad)
+/*
+ * acpi_pcidev_pciroot_bus:
+ *
+ *     Derive the PCI bus number of a PCI root bridge from its resources.
+ *     If successful, return AE_OK and fill *busp.  Otherwise, return an
+ *     exception code and leave *busp unchanged.
+ *
+ * XXX Use ACPI resource parsing functions (acpi_resource.c) once bus number
+ *     ranges are implemented there.
+ */
+static ACPI_STATUS
+acpi_pcidev_pciroot_bus(ACPI_HANDLE handle, uint16_t *busp)
 {
-       struct acpi_pcidev *ap;
        ACPI_STATUS rv;
-       ACPI_INTEGER seg, bus, addr;
+       int32_t bus;
 
-       /*
-        * ACPI spec: "The _BBN object is located under a
-        * PCI host bridge and must be unique for every
-        * host bridge within a segment since it is the PCI bus number."
-        */
-       rv = acpi_eval_integer(ad->ad_handle, "_BBN", &bus);
+       bus = -1;
+       rv = AcpiWalkResources(handle, METHOD_NAME__CRS,
+           acpi_pcidev_pciroot_bus_callback, &bus);
+
        if (ACPI_FAILURE(rv))
-               return false;
-       /*
-         * The ACPI address (_ADR) is equal to: (device << 16) | function.
-        */
-       rv = acpi_eval_integer(ad->ad_handle, "_ADR", &addr);
-       if (ACPI_FAILURE(rv))
-               return false;
+               return rv;
+
+       if (bus < 0 || bus > 0xFFFF)
+               return AE_NOT_EXIST;
+
+       *busp = (uint16_t)bus;
+       return rv;
+}
+
+static ACPI_STATUS
+acpi_pcidev_pciroot_bus_callback(ACPI_RESOURCE *res, void *context)
+{
+       int32_t *bus = context;
+       ACPI_RESOURCE_ADDRESS64 addr64;
+
+       if ((res->Type != ACPI_RESOURCE_TYPE_ADDRESS16) &&
+           (res->Type != ACPI_RESOURCE_TYPE_ADDRESS32) &&
+           (res->Type != ACPI_RESOURCE_TYPE_ADDRESS64))
+               return AE_OK;   /* continue the walk */
+
+       if (ACPI_FAILURE(AcpiResourceToAddress64(res, &addr64)))
+               return AE_OK;   /* continue the walk */
+
+       if (addr64.ResourceType != ACPI_BUS_NUMBER_RANGE)
+               return AE_OK;   /* continue the walk */
 
-       /*
-        * ACPI spec: "The optional _SEG object is located under a PCI host
-        * bridge and evaluates to an integer that describes the
-        * PCI Segment Group (see PCI Firmware Specification v3.0)."
-        *
-        * "PCI Segment Group supports more than 256 buses
-        * in a system by allowing the reuse of the PCI bus numbers.
-        * Within each PCI Segment Group, the bus numbers for the PCI
-        * buses must be unique. PCI buses in different PCI Segment
-        * Group are permitted to have the same bus number."
-        */
-       rv = acpi_eval_integer(ad->ad_handle, "_SEG", &seg);
-       if (ACPI_FAILURE(rv)) {
-               /*
-                * ACPI spec: "If _SEG does not exist, OSPM assumes that all
-                * PCI bus segments are in PCI Segment Group 0."
-                */
-               seg = 0;
+       if (*bus != -1)
+               return AE_ALREADY_EXISTS;
+
+       *bus = addr64.Minimum;
+       return AE_OK;           /* continue the walk */
+}
+
+/*
+ * acpi_pcidev_scan_rec:
+ *
+ *     Scan the ACPI device tree for PCI devices.  A node is detected as a
+ *     PCI device if it has an ancestor that is a PCI root bridge and such
+ *     that all intermediate nodes are PCI-to-PCI bridges.  Depth-first
+ *     recursive implementation.
+ */
+static ACPI_STATUS
+acpi_pcidev_scan_rec(struct acpi_devnode *ad)
+{
+       struct acpi_devnode *child;
+       struct acpi_pci_info *ap;
+       ACPI_INTEGER val;
+       ACPI_STATUS rv;
+
+       if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
+           !(ad->ad_devinfo->Valid & ACPI_VALID_ADR)) {
+               ad->ad_pciinfo = NULL;
+               goto rec;
        }
 
-       ap = kmem_alloc(sizeof(*ap), KM_SLEEP);
-       if (ap == NULL) {
-               aprint_error("%s: kmem_alloc failed\n", __func__);
-               return false;
+       if (ad->ad_devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) {
+               ap = kmem_zalloc(sizeof(*ap), KM_SLEEP);
+               if (ap == NULL)
+                       return AE_NO_MEMORY;
+
+               rv = acpi_eval_integer(ad->ad_handle, METHOD_NAME__SEG, &val);
+               if (ACPI_SUCCESS(rv))
+                       ap->ap_segment = ACPI_LOWORD(val);
+               else
+                       ap->ap_segment = 0;
+
+               /* try to get bus number using _CRS first */
+               rv = acpi_pcidev_pciroot_bus(ad->ad_handle, &ap->ap_bus);
+               if (ACPI_FAILURE(rv)) {
+                       rv = acpi_eval_integer(ad->ad_handle, METHOD_NAME__BBN, &val);
+                       if (ACPI_SUCCESS(rv))
+                               ap->ap_bus = ACPI_LOWORD(val);
+                       else
+                               ap->ap_bus = 0;
+               }
+
+               ap->ap_device = ACPI_HIWORD(ACPI_LODWORD(ad->ad_devinfo->Address));
+               ap->ap_function = ACPI_LOWORD(ACPI_LODWORD(ad->ad_devinfo->Address));
+
+               ap->ap_bridge = true;
+               ap->ap_downbus = ap->ap_bus;
+
+               ad->ad_pciinfo = ap;
+               goto rec;
        }
 
-       if (acpi_match_hid(ad->ad_devinfo, acpi_pcidev_ids))
-               ap->ap_pcihost = true;
-       else
-               ap->ap_pcihost = false;
+       if ((ad->ad_parent != NULL) &&
+           (ad->ad_parent->ad_pciinfo != NULL) &&
+           (ad->ad_parent->ad_pciinfo->ap_bridge)) {
+               /*
+                * Our parent is a PCI root bridge or a PCI-to-PCI bridge.  We
+                * have the same PCI segment#, and our bus# is its downstream
+                * bus number.
+                */
+               ap = kmem_zalloc(sizeof(*ap), KM_SLEEP);
+               if (ap == NULL)
+                       return AE_NO_MEMORY;
+
+               ap->ap_segment = ad->ad_parent->ad_pciinfo->ap_segment;
+               ap->ap_bus = ad->ad_parent->ad_pciinfo->ap_downbus;
+               ap->ap_device = ACPI_HIWORD(ACPI_LODWORD(ad->ad_devinfo->Address));
+               ap->ap_function = ACPI_LOWORD(ACPI_LODWORD(ad->ad_devinfo->Address));
+
+               /*
+                * Check whether this device is a PCI-to-PCI bridge and get its
+                * secondary bus#.
+                */
+               rv = acpi_pcidev_ppb_downbus(ap->ap_segment, ap->ap_bus,
+                   ap->ap_device, ap->ap_function, &ap->ap_downbus);



Home | Main Index | Thread Index | Old Index