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