2006-11-15 15:25:10

by John Keller

[permalink] [raw]
Subject: [PATCH 1/3] - Altix: ACPI SSDT PCI device support

Add SN platform support for running with an ACPI
capable PROM that defines PCI devices in SSDT
tables. There is a SSDT table for every occupied
slot on a root bus, containing info for every
PPB and/or device on the bus. The SSDTs will be
dynamically loaded/unloaded at hotplug enable/disable.

Platform specific information that is currently
passed via a SAL call, will now be passed via the
Vendor resource in the ACPI Device object(s) defined
in each SSDT.

Signed-off-by: John Keller <[email protected]>

---

Note: This patch is dependent on a previous set of SN ACPI
patches.

altix-add-initial-acpi-io-support.patch
altix-sn-acpi-hotplug-support.patch
altix-initial-acpi-support-rom-shadowing.patch


arch/ia64/sn/kernel/io_acpi_init.c | 314 ++++++++++++++++++++--
arch/ia64/sn/kernel/io_common.c | 68 ----
arch/ia64/sn/kernel/io_init.c | 54 +++
arch/ia64/sn/pci/pcibr/pcibr_provider.c | 6
include/asm-ia64/sn/acpi.h | 5
include/asm-ia64/sn/pcibr_provider.h | 2
include/asm-ia64/sn/pcidev.h | 8
7 files changed, 362 insertions(+), 95 deletions(-)


Index: linux-2.6/arch/ia64/sn/kernel/io_acpi_init.c
===================================================================
--- linux-2.6.orig/arch/ia64/sn/kernel/io_acpi_init.c 2006-10-23 08:57:24.672453172 -0500
+++ linux-2.6/arch/ia64/sn/kernel/io_acpi_init.c 2006-10-23 09:16:25.245957610 -0500
@@ -13,6 +13,7 @@
#include <asm/sn/sn_sal.h>
#include "xtalk/hubdev.h"
#include <linux/acpi.h>
+#include <acpi/acnamesp.h>


/*
@@ -31,6 +32,12 @@ struct acpi_vendor_uuid sn_uuid = {
0xa2, 0x7c, 0x08, 0x00, 0x69, 0x13, 0xea, 0x51 },
};

+struct sn_pcidev_match {
+ u8 bus;
+ unsigned int devfn;
+ acpi_handle handle;
+};
+
/*
* Perform the early IO init in PROM.
*/
@@ -119,9 +126,11 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
&sn_uuid, &buffer);
if (ACPI_FAILURE(status)) {
- printk(KERN_ERR "get_acpi_pcibus_ptr: "
- "get_acpi_bussoft_info() failed: %d\n",
- status);
+ printk(KERN_ERR "%s: "
+ "acpi_get_vendor_resource() failed (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
return NULL;
}
resource = buffer.pointer;
@@ -130,8 +139,8 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) !=
sizeof(struct pcibus_bussoft *)) {
printk(KERN_ERR
- "get_acpi_bussoft_ptr: Invalid vendor data "
- "length %d\n", vendor->byte_length);
+ "%s: Invalid vendor data length %d\n",
+ __FUNCTION__, vendor->byte_length);
kfree(buffer.pointer);
return NULL;
}
@@ -143,34 +152,254 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
}

/*
- * sn_acpi_bus_fixup
+ * sn_extract_device_info - Extract the pcidev_info and the sn_irq_info
+ * pointers from the vendor resource using the
+ * provided acpi handle, and copy the structures
+ * into the argument buffers.
*/
-void
-sn_acpi_bus_fixup(struct pci_bus *bus)
+static int
+sn_extract_device_info(acpi_handle handle, struct pcidev_info **pcidev_info,
+ struct sn_irq_info **sn_irq_info)
{
- struct pci_dev *pci_dev = NULL;
- struct pcibus_bussoft *prom_bussoft_ptr;
- extern void sn_common_bus_fixup(struct pci_bus *,
- struct pcibus_bussoft *);
+ u64 addr;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct sn_irq_info *irq_info, *irq_info_prom;
+ struct pcidev_info *pcidev_ptr, *pcidev_prom_ptr;
+ struct acpi_resource *resource;
+ int ret = 0;
+ acpi_status status;
+ struct acpi_resource_vendor_typed *vendor;

- if (!bus->parent) { /* If root bus */
- prom_bussoft_ptr = sn_get_bussoft_ptr(bus);
- if (prom_bussoft_ptr == NULL) {
+ /*
+ * The pointer to this device's pcidev_info structure in
+ * the PROM, is in the vendor resource.
+ */
+ status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
+ &sn_uuid, &buffer);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR
+ "%s: acpi_get_vendor_resource() failed (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ return 1;
+ }
+
+ resource = buffer.pointer;
+ vendor = &resource->data.vendor_typed;
+ if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) !=
+ sizeof(struct pci_devdev_info *)) {
+ printk(KERN_ERR
+ "%s: Invalid vendor data length: %d for: ",
+ __FUNCTION__, vendor->byte_length);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ ret = 1;
+ goto exit;
+ }
+
+ pcidev_ptr = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
+ if (!pcidev_ptr)
+ panic("%s: Unable to alloc memory for pcidev_info", __FUNCTION__);
+
+ memcpy(&addr, vendor->byte_data, sizeof(struct pcidev_info *));
+ pcidev_prom_ptr = __va(addr);
+ memcpy(pcidev_ptr, pcidev_prom_ptr, sizeof(struct pcidev_info));
+
+ /* Get the IRQ info */
+ irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
+ if (!irq_info)
+ panic("%s: Unable to alloc memory for sn_irq_info", __FUNCTION__);
+
+ if (pcidev_ptr->pdi_sn_irq_info) {
+ irq_info_prom = __va(pcidev_ptr->pdi_sn_irq_info);
+ memcpy(irq_info, irq_info_prom, sizeof(struct sn_irq_info));
+ }
+
+ *pcidev_info = pcidev_ptr;
+ *sn_irq_info = irq_info;
+
+exit:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static unsigned int
+get_host_devfn(acpi_handle device_handle, acpi_handle rootbus_handle)
+{
+ unsigned long adr;
+ acpi_handle child;
+ unsigned int devfn;
+ int function;
+ acpi_handle parent;
+ int slot;
+ acpi_status status;
+
+ /*
+ * Do an upward search to find the root bus device, and
+ * obtain the host devfn from the previous child device.
+ */
+ child = device_handle;
+ while (child) {
+ status = acpi_get_parent(child, &parent);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "%s: acpi_get_parent() failed "
+ "(0x%x) for: ", __FUNCTION__, status);
+ acpi_ns_print_node_pathname(child, NULL);
+ printk("\n");
+ panic("%s: Unable to find host devfn\n", __FUNCTION__);
+ }
+ if (parent == rootbus_handle)
+ break;
+ child = parent;
+ }
+ if (!child) {
+ printk(KERN_ERR "%s: Unable to find root bus for: ",
+ __FUNCTION__);
+ acpi_ns_print_node_pathname(device_handle, NULL);
+ printk("\n");
+ BUG();
+ }
+
+ status = acpi_evaluate_integer(child, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "%s: Unable to get _ADR (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(child, NULL);
+ printk("\n");
+ panic("%s: Unable to find host devfn\n", __FUNCTION__);
+ }
+
+ slot = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+ devfn = PCI_DEVFN(slot, function);
+ return devfn;
+}
+
+/*
+ * find_matching_device - Callback routine to find the ACPI device
+ * that matches up with our pci_dev device.
+ * Matching is done on bus number and devfn.
+ * To find the bus number for a particular
+ * ACPI device, we must look at the _BBN method
+ * of its parent.
+ */
+static acpi_status
+find_matching_device(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ unsigned long bbn = -1;
+ unsigned long adr;
+ acpi_handle parent = NULL;
+ acpi_status status;
+ unsigned int devfn;
+ int function;
+ int slot;
+ struct sn_pcidev_match *info = context;
+
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL,
+ &adr);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_get_parent(handle, &parent);
+ if (ACPI_FAILURE(status)) {
printk(KERN_ERR
- "sn_pci_fixup_bus: 0x%04x:0x%02x Unable to "
- "obtain prom_bussoft_ptr\n",
- pci_domain_nr(bus), bus->number);
- return;
+ "%s: acpi_get_parent() failed (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ return AE_OK;
+ }
+ status = acpi_evaluate_integer(parent, METHOD_NAME__BBN,
+ NULL, &bbn);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR
+ "%s: Failed to find _BBN in parent of: ",
+ __FUNCTION__);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ return AE_OK;
+ }
+
+ slot = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+ devfn = PCI_DEVFN(slot, function);
+ if ((info->devfn == devfn) && (info->bus == bbn)) {
+ /* We have a match! */
+ info->handle = handle;
+ return 1;
}
- sn_common_bus_fixup(bus, prom_bussoft_ptr);
}
- list_for_each_entry(pci_dev, &bus->devices, bus_list) {
- sn_pci_fixup_slot(pci_dev);
+ return AE_OK;
+}
+
+/*
+ * sn_acpi_get_pcidev_info - Search ACPI namespace for the acpi
+ * device matching the specified pci_dev,
+ * and return the pcidev info and irq info.
+ */
+int
+sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info,
+ struct sn_irq_info **sn_irq_info)
+{
+ unsigned int host_devfn;
+ struct sn_pcidev_match pcidev_match;
+ acpi_handle rootbus_handle;
+ unsigned long segment;
+ acpi_status status;
+
+ rootbus_handle = PCI_CONTROLLER(dev)->acpi_handle;
+ status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL,
+ &segment);
+ if (ACPI_SUCCESS(status)) {
+ if (segment != pci_domain_nr(dev)) {
+ printk(KERN_ERR
+ "%s: Segment number mismatch, 0x%lx vs 0x%x for: ",
+ __FUNCTION__, segment, pci_domain_nr(dev));
+ acpi_ns_print_node_pathname(rootbus_handle, NULL);
+ printk("\n");
+ return 1;
+ }
+ } else {
+ printk(KERN_ERR "%s: Unable to get __SEG from: ",
+ __FUNCTION__);
+ acpi_ns_print_node_pathname(rootbus_handle, NULL);
+ printk("\n");
+ return 1;
}
+
+ /*
+ * We want to search all devices in this segment/domain
+ * of the ACPI namespace for the matching ACPI device,
+ * which holds the pcidev_info pointer in its vendor resource.
+ */
+ pcidev_match.bus = dev->bus->number;
+ pcidev_match.devfn = dev->devfn;
+ pcidev_match.handle = NULL;
+
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, rootbus_handle, ACPI_UINT32_MAX,
+ find_matching_device, &pcidev_match, NULL);
+
+ if (!pcidev_match.handle) {
+ printk(KERN_ERR
+ "%s: Could not find matching ACPI device for %s.\n",
+ __FUNCTION__, pci_name(dev));
+ return 1;
+ }
+
+ if (sn_extract_device_info(pcidev_match.handle, pcidev_info, sn_irq_info))
+ return 1;
+
+ /* Build up the pcidev_info.pdi_slot_host_handle */
+ host_devfn = get_host_devfn(pcidev_match.handle, rootbus_handle);
+ (*pcidev_info)->pdi_slot_host_handle =
+ ((unsigned long) pci_domain_nr(dev) << 40) |
+ /* bus == 0 */
+ host_devfn;
+ return 0;
}

/*
- * sn_acpi_slot_fixup - Perform any SN specific slot fixup.
+ * sn_acpi_slot_fixup - Obtain the pcidev_info and sn_irq_info.
+ * Perform any SN specific slot fixup.
* At present there does not appear to be
* any generic way to handle a ROM image
* that has been shadowed by the PROM, so
@@ -179,11 +408,18 @@ sn_acpi_bus_fixup(struct pci_bus *bus)
*/

void
-sn_acpi_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info)
+sn_acpi_slot_fixup(struct pci_dev *dev)
{
void __iomem *addr;
+ struct pcidev_info *pcidev_info = NULL;
+ struct sn_irq_info *sn_irq_info = NULL;
size_t size;

+ if (sn_acpi_get_pcidev_info(dev, &pcidev_info, &sn_irq_info)) {
+ panic("%s: Failure obtaining pcidev_info for %s\n",
+ __FUNCTION__, pci_name(dev));
+ }
+
if (pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE]) {
/*
* A valid ROM image exists and has been shadowed by the
@@ -200,8 +436,11 @@ sn_acpi_slot_fixup(struct pci_dev *dev,
(unsigned long) addr + size;
dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY;
}
+ sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
}

+EXPORT_SYMBOL(sn_acpi_slot_fixup);
+
static struct acpi_driver acpi_sn_hubdev_driver = {
.name = "SGI HUBDEV Driver",
.ids = "SGIHUB,SGITIO",
@@ -212,6 +451,33 @@ static struct acpi_driver acpi_sn_hubdev


/*
+ * sn_acpi_bus_fixup - Perform SN specific setup of software structs
+ * (pcibus_bussoft, pcidev_info) and hardware
+ * registers, for the specified bus and devices under it.
+ */
+void
+sn_acpi_bus_fixup(struct pci_bus *bus)
+{
+ struct pci_dev *pci_dev = NULL;
+ struct pcibus_bussoft *prom_bussoft_ptr;
+
+ if (!bus->parent) { /* If root bus */
+ prom_bussoft_ptr = sn_get_bussoft_ptr(bus);
+ if (prom_bussoft_ptr == NULL) {
+ printk(KERN_ERR
+ "%s: 0x%04x:0x%02x Unable to "
+ "obtain prom_bussoft_ptr\n",
+ __FUNCTION__, pci_domain_nr(bus), bus->number);
+ return;
+ }
+ sn_common_bus_fixup(bus, prom_bussoft_ptr);
+ }
+ list_for_each_entry(pci_dev, &bus->devices, bus_list) {
+ sn_acpi_slot_fixup(pci_dev);
+ }
+}
+
+/*
* sn_io_acpi_init - PROM has ACPI support for IO, defining at a minimum the
* nodes and root buses in the DSDT. As a result, bus scanning
* will be initiated by the Linux ACPI code.
Index: linux-2.6/arch/ia64/sn/kernel/io_common.c
===================================================================
--- linux-2.6.orig/arch/ia64/sn/kernel/io_common.c 2006-10-23 08:57:24.668452675 -0500
+++ linux-2.6/arch/ia64/sn/kernel/io_common.c 2006-10-26 11:04:30.120075293 -0500
@@ -26,14 +26,10 @@
#include <linux/acpi.h>
#include <asm/sn/sn2/sn_hwperf.h>
#include <asm/sn/acpi.h>
+#include "acpi/acglobal.h"

extern void sn_init_cpei_timer(void);
extern void register_sn_procfs(void);
-extern void sn_acpi_bus_fixup(struct pci_bus *);
-extern void sn_bus_fixup(struct pci_bus *);
-extern void sn_acpi_slot_fixup(struct pci_dev *, struct pcidev_info *);
-extern void sn_more_slot_fixup(struct pci_dev *, struct pcidev_info *);
-extern void sn_legacy_pci_window_fixup(struct pci_controller *, u64, u64);
extern void sn_io_acpi_init(void);
extern void sn_io_init(void);

@@ -48,6 +44,9 @@ struct sysdata_el {

int sn_ioif_inited; /* SN I/O infrastructure initialized? */

+int sn_acpi_rev; /* SN ACPI revision */
+EXPORT_SYMBOL_GPL(sn_acpi_rev);
+
struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */

/*
@@ -99,25 +98,6 @@ sal_get_device_dmaflush_list(u64 nasid,
}

/*
- * Retrieve the pci device information given the bus and device|function number.
- */
-static inline u64
-sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
- u64 sn_irq_info)
-{
- struct ia64_sal_retval ret_stuff;
- ret_stuff.status = 0;
- ret_stuff.v0 = 0;
-
- SAL_CALL_NOLOCK(ret_stuff,
- (u64) SN_SAL_IOIF_GET_PCIDEV_INFO,
- (u64) segment, (u64) bus_number, (u64) devfn,
- (u64) pci_dev,
- sn_irq_info, 0, 0);
- return ret_stuff.v0;
-}
-
-/*
* sn_pcidev_info_get() - Retrieve the pcidev_info struct for the specified
* device.
*/
@@ -249,47 +229,22 @@ void sn_pci_unfixup_slot(struct pci_dev
}

/*
- * sn_pci_fixup_slot() - This routine sets up a slot's resources consistent
- * with the Linux PCI abstraction layer. Resources
- * acquired from our PCI provider include PIO maps
- * to BAR space and interrupt objects.
+ * sn_pci_fixup_slot()
*/
-void sn_pci_fixup_slot(struct pci_dev *dev)
+void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *pcidev_info,
+ struct sn_irq_info *sn_irq_info)
{
int segment = pci_domain_nr(dev->bus);
- int status = 0;
struct pcibus_bussoft *bs;
struct pci_bus *host_pci_bus;
struct pci_dev *host_pci_dev;
- struct pcidev_info *pcidev_info;
- struct sn_irq_info *sn_irq_info;
unsigned int bus_no, devfn;

pci_dev_get(dev); /* for the sysdata pointer */
- pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
- if (!pcidev_info)
- BUG(); /* Cannot afford to run out of memory */
-
- sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
- if (!sn_irq_info)
- BUG(); /* Cannot afford to run out of memory */
-
- /* Call to retrieve pci device information needed by kernel. */
- status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number,
- dev->devfn,
- (u64) __pa(pcidev_info),
- (u64) __pa(sn_irq_info));
- if (status)
- BUG(); /* Cannot get platform pci device information */

/* Add pcidev_info to list in pci_controller.platform_data */
list_add_tail(&pcidev_info->pdi_list,
&(SN_PLATFORM_DATA(dev->bus)->pcidev_info));
-
- if (SN_ACPI_BASE_SUPPORT())
- sn_acpi_slot_fixup(dev, pcidev_info);
- else
- sn_more_slot_fixup(dev, pcidev_info);
/*
* Using the PROMs values for the PCI host bus, get the Linux
* PCI host_pci_dev struct and set up host bus linkages
@@ -489,11 +444,6 @@ void sn_generate_path(struct pci_bus *pc
sprintf(address, "%s^%d", address, geo_slot(geoid));
}

-/*
- * sn_pci_fixup_bus() - Perform SN specific setup of software structs
- * (pcibus_bussoft, pcidev_info) and hardware
- * registers, for the specified bus and devices under it.
- */
void __devinit
sn_pci_fixup_bus(struct pci_bus *bus)
{
@@ -519,6 +469,9 @@ sn_io_early_init(void)
if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM())
return 0;

+ /* we set the acpi revision to that of the DSDT table OEM rev. */
+ sn_acpi_rev = acpi_gbl_DSDT->oem_revision;
+
/*
* prime sn_pci_provider[]. Individial provider init routines will
* override their respective default entries.
@@ -605,7 +558,6 @@ sn_io_late_init(void)

fs_initcall(sn_io_late_init);

-EXPORT_SYMBOL(sn_pci_fixup_slot);
EXPORT_SYMBOL(sn_pci_unfixup_slot);
EXPORT_SYMBOL(sn_bus_store_sysdata);
EXPORT_SYMBOL(sn_bus_free_sysdata);
Index: linux-2.6/arch/ia64/sn/kernel/io_init.c
===================================================================
--- linux-2.6.orig/arch/ia64/sn/kernel/io_init.c 2006-10-23 08:57:24.672453172 -0500
+++ linux-2.6/arch/ia64/sn/kernel/io_init.c 2006-10-23 09:16:25.245957610 -0500
@@ -56,6 +56,25 @@ static inline u64 sal_get_pcibus_info(u6
return ret_stuff.v0;
}

+/*
+ * Retrieve the pci device information given the bus and device|function number.
+ */
+static inline u64
+sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
+ u64 sn_irq_info)
+{
+ struct ia64_sal_retval ret_stuff;
+ ret_stuff.status = 0;
+ ret_stuff.v0 = 0;
+
+ SAL_CALL_NOLOCK(ret_stuff,
+ (u64) SN_SAL_IOIF_GET_PCIDEV_INFO,
+ (u64) segment, (u64) bus_number, (u64) devfn,
+ (u64) pci_dev,
+ sn_irq_info, 0, 0);
+ return ret_stuff.v0;
+}
+

/*
* sn_fixup_ionodes() - This routine initializes the HUB data structure for
@@ -172,18 +191,40 @@ sn_pci_window_fixup(struct pci_dev *dev,
}

/*
- * sn_more_slot_fixup() - We are not running with an ACPI capable PROM,
+ * sn_io_slot_fixup() - We are not running with an ACPI capable PROM,
* and need to convert the pci_dev->resource
* 'start' and 'end' addresses to mapped addresses,
* and setup the pci_controller->window array entries.
*/
void
-sn_more_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info)
+sn_io_slot_fixup(struct pci_dev *dev)
{
unsigned int count = 0;
int idx;
s64 pci_addrs[PCI_ROM_RESOURCE + 1];
unsigned long addr, end, size, start;
+ struct pcidev_info *pcidev_info;
+ struct sn_irq_info *sn_irq_info;
+ int status;
+
+ pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
+ if (!pcidev_info)
+ panic("%s: Unable to alloc memory for pcidev_info", __FUNCTION__);
+
+ sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
+ if (!sn_irq_info)
+ panic("%s: Unable to alloc memory for sn_irq_info", __FUNCTION__);
+
+ /* Call to retrieve pci device information needed by kernel. */
+ status = sal_get_pcidev_info((u64) pci_domain_nr(dev),
+ (u64) dev->bus->number,
+ dev->devfn,
+ (u64) __pa(pcidev_info),
+ (u64) __pa(sn_irq_info));
+
+ if (status)
+ BUG(); /* Cannot get platform pci device information */
+

/* Copy over PIO Mapped Addresses */
for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
@@ -219,8 +260,12 @@ sn_more_slot_fixup(struct pci_dev *dev,
*/
if (count > 0)
sn_pci_window_fixup(dev, count, pci_addrs);
+
+ sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
}

+EXPORT_SYMBOL(sn_io_slot_fixup);
+
/*
* sn_pci_controller_fixup() - This routine sets up a bus's resources
* consistent with the Linux PCI abstraction layer.
@@ -272,9 +317,6 @@ sn_bus_fixup(struct pci_bus *bus)
{
struct pci_dev *pci_dev = NULL;
struct pcibus_bussoft *prom_bussoft_ptr;
- extern void sn_common_bus_fixup(struct pci_bus *,
- struct pcibus_bussoft *);
-

if (!bus->parent) { /* If root bus */
prom_bussoft_ptr = PCI_CONTROLLER(bus)->platform_data;
@@ -291,7 +333,7 @@ sn_bus_fixup(struct pci_bus *bus)
prom_bussoft_ptr->bs_legacy_mem);
}
list_for_each_entry(pci_dev, &bus->devices, bus_list) {
- sn_pci_fixup_slot(pci_dev);
+ sn_io_slot_fixup(pci_dev);
}

}
Index: linux-2.6/arch/ia64/sn/pci/pcibr/pcibr_provider.c
===================================================================
--- linux-2.6.orig/arch/ia64/sn/pci/pcibr/pcibr_provider.c 2006-10-23 08:56:18.684259160 -0500
+++ linux-2.6/arch/ia64/sn/pci/pcibr/pcibr_provider.c 2006-10-23 09:16:25.249958106 -0500
@@ -20,7 +20,8 @@
#include "xtalk/hubdev.h"

int
-sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp)
+sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp,
+ char **ssdt)
{
struct ia64_sal_retval ret_stuff;
u64 busnum;
@@ -32,7 +33,8 @@ sal_pcibr_slot_enable(struct pcibus_info
segment = soft->pbi_buscommon.bs_persist_segment;
busnum = soft->pbi_buscommon.bs_persist_busnum;
SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_ENABLE, segment,
- busnum, (u64) device, (u64) resp, 0, 0, 0);
+ busnum, (u64) device, (u64) resp, (u64)ia64_tpa(ssdt),
+ 0, 0);

return (int)ret_stuff.v0;
}
Index: linux-2.6/include/asm-ia64/sn/acpi.h
===================================================================
--- linux-2.6.orig/include/asm-ia64/sn/acpi.h 2006-10-23 08:56:18.688259657 -0500
+++ linux-2.6/include/asm-ia64/sn/acpi.h 2006-10-23 09:16:25.249958106 -0500
@@ -9,8 +9,7 @@
#ifndef _ASM_IA64_SN_ACPI_H
#define _ASM_IA64_SN_ACPI_H

-#include "acpi/acglobal.h"
-
-#define SN_ACPI_BASE_SUPPORT() (acpi_gbl_DSDT->oem_revision >= 0x20101)
+extern int sn_acpi_rev;
+#define SN_ACPI_BASE_SUPPORT() (sn_acpi_rev >= 0x20101)

#endif /* _ASM_IA64_SN_ACPI_H */
Index: linux-2.6/include/asm-ia64/sn/pcibr_provider.h
===================================================================
--- linux-2.6.orig/include/asm-ia64/sn/pcibr_provider.h 2006-10-23 08:48:18.740640326 -0500
+++ linux-2.6/include/asm-ia64/sn/pcibr_provider.h 2006-10-23 09:16:25.249958106 -0500
@@ -142,7 +142,7 @@ extern int pcibr_ate_alloc(struct pcib
extern void pcibr_ate_free(struct pcibus_info *, int);
extern void ate_write(struct pcibus_info *, int, int, u64);
extern int sal_pcibr_slot_enable(struct pcibus_info *soft, int device,
- void *resp);
+ void *resp, char **ssdt);
extern int sal_pcibr_slot_disable(struct pcibus_info *soft, int device,
int action, void *resp);
extern u16 sn_ioboard_to_pci_bus(struct pci_bus *pci_bus);
Index: linux-2.6/include/asm-ia64/sn/pcidev.h
===================================================================
--- linux-2.6.orig/include/asm-ia64/sn/pcidev.h 2006-10-23 08:56:18.688259657 -0500
+++ linux-2.6/include/asm-ia64/sn/pcidev.h 2006-10-23 09:16:25.249958106 -0500
@@ -70,10 +70,16 @@ extern void sn_irq_fixup(struct pci_dev
struct sn_irq_info *sn_irq_info);
extern void sn_irq_unfixup(struct pci_dev *pci_dev);
extern struct pcidev_info * sn_pcidev_info_get(struct pci_dev *);
+extern void sn_bus_fixup(struct pci_bus *);
+extern void sn_acpi_bus_fixup(struct pci_bus *);
+extern void sn_common_bus_fixup(struct pci_bus *, struct pcibus_bussoft *);
extern void sn_bus_store_sysdata(struct pci_dev *dev);
extern void sn_bus_free_sysdata(void);
extern void sn_generate_path(struct pci_bus *pci_bus, char *address);
-extern void sn_pci_fixup_slot(struct pci_dev *dev);
+extern void sn_io_slot_fixup(struct pci_dev *);
+extern void sn_acpi_slot_fixup(struct pci_dev *);
+extern void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *,
+ struct sn_irq_info *);
extern void sn_pci_unfixup_slot(struct pci_dev *dev);
extern void sn_irq_lh_init(void);
#endif /* _ASM_IA64_SN_PCI_PCIDEV_H */


2007-01-27 01:10:59

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/3] - Altix: ACPI SSDT PCI device support

On Wed, 15 Nov 2006 09:24:54 -0600
John Keller <[email protected]> wrote:

> Add SN platform support for running with an ACPI
> capable PROM that defines PCI devices in SSDT
> tables. There is a SSDT table for every occupied
> slot on a root bus, containing info for every
> PPB and/or device on the bus. The SSDTs will be
> dynamically loaded/unloaded at hotplug enable/disable.

An additional 600kb of diff has just popped up in the ACPI tree, so none of
this patch applies any more. I'll need to drop it again, sorry.

It's been over two months - I'm not sure why things are so difficult for
this patchset.

2007-01-27 07:02:47

by Len Brown

[permalink] [raw]
Subject: Re: [PATCH 1/3] - Altix: ACPI SSDT PCI device support

On Friday 26 January 2007 20:10, Andrew Morton wrote:
> On Wed, 15 Nov 2006 09:24:54 -0600
> John Keller <[email protected]> wrote:
>
> > Add SN platform support for running with an ACPI
> > capable PROM that defines PCI devices in SSDT
> > tables. There is a SSDT table for every occupied
> > slot on a root bus, containing info for every
> > PPB and/or device on the bus. The SSDTs will be
> > dynamically loaded/unloaded at hotplug enable/disable.
>
> An additional 600kb of diff has just popped up in the ACPI tree, so none of
> this patch applies any more. I'll need to drop it again, sorry.
>
> It's been over two months - I'm not sure why things are so difficult for
> this patchset.

it is because
1. this didn't make it into 2.6.20 before -rc1, and Linus prohibits adding features thereafter
2. these patches simultaneously depend on ACPI, hotplug, and IA64 platform dependent code,
so it isn't always clear who owns them. However, if Greg and Tony Ack them,
I'll be happy to carry them upstream with the ACPI tree.

I'll reply to this note with a shim patch to help these apply, and a pair of refreshed SGI patches
that apply on top of the ACPI tree. Build tested vs sn2_defconfig , but SGI will have to test it on hardware.

thanks,
-Len

2007-01-27 07:06:09

by Len Brown

[permalink] [raw]
Subject: [PATCH 1/3] - ACPICA: reduce conflicts with Altix patch series

Syntax only -- no functional changes.

Signed-off-by: Len Brown <[email protected]
---
arch/ia64/sn/kernel/io_common.c | 10 ++++++----
arch/ia64/sn/kernel/iomv.c | 5 ++---
include/asm-ia64/sn/acpi.h | 18 ++++++++++++++++++
3 files changed, 26 insertions(+), 7 deletions(-)

Index: acpi/arch/ia64/sn/kernel/io_common.c
===================================================================
--- acpi.orig/arch/ia64/sn/kernel/io_common.c
+++ acpi/arch/ia64/sn/kernel/io_common.c
@@ -25,6 +25,7 @@
#include "xtalk/xwidgetdev.h"
#include <linux/acpi.h>
#include <asm/sn/sn2/sn_hwperf.h>
+#include <asm/sn/acpi.h>

extern void sn_init_cpei_timer(void);
extern void register_sn_procfs(void);
@@ -36,6 +37,7 @@ extern void sn_legacy_pci_window_fixup(s
extern void sn_io_acpi_init(void);
extern void sn_io_init(void);

+
static struct list_head sn_sysdata_list;

/* sysdata list struct */
@@ -48,7 +50,7 @@ int sn_ioif_inited; /* SN I/O infrastru

struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */

-int sn_acpi_base_support()
+int sn_acpi_base_support(void)
{
struct acpi_table_header *header;
(void)acpi_get_table_by_index(ACPI_TABLE_INDEX_DSDT, &header);
@@ -293,7 +295,7 @@ void sn_pci_fixup_slot(struct pci_dev *d
list_add_tail(&pcidev_info->pdi_list,
&(SN_PLATFORM_DATA(dev->bus)->pcidev_info));

- if (sn_acpi_base_support())
+ if (SN_ACPI_BASE_SUPPORT())
sn_acpi_slot_fixup(dev, pcidev_info);
else
sn_more_slot_fixup(dev, pcidev_info);
@@ -505,7 +507,7 @@ void __devinit
sn_pci_fixup_bus(struct pci_bus *bus)
{

- if (sn_acpi_base_support())
+ if (SN_ACPI_BASE_SUPPORT())
sn_acpi_bus_fixup(bus);
else
sn_bus_fixup(bus);
@@ -557,7 +559,7 @@ sn_io_early_init(void)
printk(KERN_INFO "ACPI DSDT OEM Rev 0x%x\n",
header->oem_revision);
}
- if (sn_acpi_base_support())
+ if (SN_ACPI_BASE_SUPPORT())
sn_io_acpi_init();
else
sn_io_init();
Index: acpi/arch/ia64/sn/kernel/iomv.c
===================================================================
--- acpi.orig/arch/ia64/sn/kernel/iomv.c
+++ acpi/arch/ia64/sn/kernel/iomv.c
@@ -16,6 +16,7 @@
#include <asm/sn/pda.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/shub_mmr.h>
+#include <asm/sn/acpi.h>

#define IS_LEGACY_VGA_IOPORT(p) \
(((p) >= 0x3b0 && (p) <= 0x3bb) || ((p) >= 0x3c0 && (p) <= 0x3df))
@@ -29,8 +30,6 @@
* SN i/o address. Used by sn_in*() and sn_out*().
*/

-extern int sn_acpi_base_support();
-
void *sn_io_addr(unsigned long port)
{
if (!IS_RUNNING_ON_SIMULATOR()) {
@@ -39,7 +38,7 @@ void *sn_io_addr(unsigned long port)
/* On sn2, legacy I/O ports don't point at anything */
if (port < (64 * 1024))
return NULL;
- if (sn_acpi_base_support())
+ if (SN_ACPI_BASE_SUPPORT())
return (__ia64_mk_io_addr(port));
else
return ((void *)(port | __IA64_UNCACHED_OFFSET));
Index: acpi/include/asm-ia64/sn/acpi.h
===================================================================
--- /dev/null
+++ acpi/include/asm-ia64/sn/acpi.h
@@ -0,0 +1,18 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2006 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_ACPI_H
+#define _ASM_IA64_SN_ACPI_H
+
+#include "acpi/acglobal.h"
+
+#define SN_ACPI_BASE_SUPPORT() sn_acpi_base_support()
+
+extern int sn_acpi_base_support(void);
+
+#endif /* _ASM_IA64_SN_ACPI_H */

2007-01-27 07:10:44

by Len Brown

[permalink] [raw]
Subject: [PATCH 2/3] Altix: ACPI SSDT PCI device support

From: John Keller <[email protected]>

Add SN platform support for running with an ACPI
capable PROM that defines PCI devices in SSDT
tables. There is a SSDT table for every occupied
slot on a root bus, containing info for every
PPB and/or device on the bus. The SSDTs will be
dynamically loaded/unloaded at hotplug enable/disable.

Platform specific information that is currently
passed via a SAL call, will now be passed via the
Vendor resource in the ACPI Device object(s) defined
in each SSDT.

Signed-off-by: John Keller <[email protected]>
Cc: Greg KH <[email protected]>
Cc: "Luck, Tony" <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Len Brown <[email protected]>
---

arch/ia64/sn/kernel/io_acpi_init.c | 314 +++++++++++++++++++++++++++++---
arch/ia64/sn/kernel/io_common.c | 83 +-------
arch/ia64/sn/kernel/io_init.c | 54 ++++-
arch/ia64/sn/pci/pcibr/pcibr_provider.c | 6
include/asm-ia64/sn/acpi.h | 5
include/asm-ia64/sn/pcibr_provider.h | 2
include/asm-ia64/sn/pcidev.h | 8
7 files changed, 368 insertions(+), 104 deletions(-)

Index: acpi/arch/ia64/sn/kernel/io_acpi_init.c
===================================================================
--- acpi.orig/arch/ia64/sn/kernel/io_acpi_init.c
+++ acpi/arch/ia64/sn/kernel/io_acpi_init.c
@@ -13,6 +13,7 @@
#include <asm/sn/sn_sal.h>
#include "xtalk/hubdev.h"
#include <linux/acpi.h>
+#include <acpi/acnamesp.h>


/*
@@ -31,6 +32,12 @@ struct acpi_vendor_uuid sn_uuid = {
0xa2, 0x7c, 0x08, 0x00, 0x69, 0x13, 0xea, 0x51 },
};

+struct sn_pcidev_match {
+ u8 bus;
+ unsigned int devfn;
+ acpi_handle handle;
+};
+
/*
* Perform the early IO init in PROM.
*/
@@ -119,9 +126,11 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
&sn_uuid, &buffer);
if (ACPI_FAILURE(status)) {
- printk(KERN_ERR "get_acpi_pcibus_ptr: "
- "get_acpi_bussoft_info() failed: %d\n",
- status);
+ printk(KERN_ERR "%s: "
+ "acpi_get_vendor_resource() failed (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
return NULL;
}
resource = buffer.pointer;
@@ -130,8 +139,8 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) !=
sizeof(struct pcibus_bussoft *)) {
printk(KERN_ERR
- "get_acpi_bussoft_ptr: Invalid vendor data "
- "length %d\n", vendor->byte_length);
+ "%s: Invalid vendor data length %d\n",
+ __FUNCTION__, vendor->byte_length);
kfree(buffer.pointer);
return NULL;
}
@@ -143,34 +152,254 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
}

/*
- * sn_acpi_bus_fixup
+ * sn_extract_device_info - Extract the pcidev_info and the sn_irq_info
+ * pointers from the vendor resource using the
+ * provided acpi handle, and copy the structures
+ * into the argument buffers.
*/
-void
-sn_acpi_bus_fixup(struct pci_bus *bus)
+static int
+sn_extract_device_info(acpi_handle handle, struct pcidev_info **pcidev_info,
+ struct sn_irq_info **sn_irq_info)
{
- struct pci_dev *pci_dev = NULL;
- struct pcibus_bussoft *prom_bussoft_ptr;
- extern void sn_common_bus_fixup(struct pci_bus *,
- struct pcibus_bussoft *);
+ u64 addr;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct sn_irq_info *irq_info, *irq_info_prom;
+ struct pcidev_info *pcidev_ptr, *pcidev_prom_ptr;
+ struct acpi_resource *resource;
+ int ret = 0;
+ acpi_status status;
+ struct acpi_resource_vendor_typed *vendor;

- if (!bus->parent) { /* If root bus */
- prom_bussoft_ptr = sn_get_bussoft_ptr(bus);
- if (prom_bussoft_ptr == NULL) {
+ /*
+ * The pointer to this device's pcidev_info structure in
+ * the PROM, is in the vendor resource.
+ */
+ status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
+ &sn_uuid, &buffer);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR
+ "%s: acpi_get_vendor_resource() failed (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ return 1;
+ }
+
+ resource = buffer.pointer;
+ vendor = &resource->data.vendor_typed;
+ if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) !=
+ sizeof(struct pci_devdev_info *)) {
+ printk(KERN_ERR
+ "%s: Invalid vendor data length: %d for: ",
+ __FUNCTION__, vendor->byte_length);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ ret = 1;
+ goto exit;
+ }
+
+ pcidev_ptr = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
+ if (!pcidev_ptr)
+ panic("%s: Unable to alloc memory for pcidev_info", __FUNCTION__);
+
+ memcpy(&addr, vendor->byte_data, sizeof(struct pcidev_info *));
+ pcidev_prom_ptr = __va(addr);
+ memcpy(pcidev_ptr, pcidev_prom_ptr, sizeof(struct pcidev_info));
+
+ /* Get the IRQ info */
+ irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
+ if (!irq_info)
+ panic("%s: Unable to alloc memory for sn_irq_info", __FUNCTION__);
+
+ if (pcidev_ptr->pdi_sn_irq_info) {
+ irq_info_prom = __va(pcidev_ptr->pdi_sn_irq_info);
+ memcpy(irq_info, irq_info_prom, sizeof(struct sn_irq_info));
+ }
+
+ *pcidev_info = pcidev_ptr;
+ *sn_irq_info = irq_info;
+
+exit:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static unsigned int
+get_host_devfn(acpi_handle device_handle, acpi_handle rootbus_handle)
+{
+ unsigned long adr;
+ acpi_handle child;
+ unsigned int devfn;
+ int function;
+ acpi_handle parent;
+ int slot;
+ acpi_status status;
+
+ /*
+ * Do an upward search to find the root bus device, and
+ * obtain the host devfn from the previous child device.
+ */
+ child = device_handle;
+ while (child) {
+ status = acpi_get_parent(child, &parent);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "%s: acpi_get_parent() failed "
+ "(0x%x) for: ", __FUNCTION__, status);
+ acpi_ns_print_node_pathname(child, NULL);
+ printk("\n");
+ panic("%s: Unable to find host devfn\n", __FUNCTION__);
+ }
+ if (parent == rootbus_handle)
+ break;
+ child = parent;
+ }
+ if (!child) {
+ printk(KERN_ERR "%s: Unable to find root bus for: ",
+ __FUNCTION__);
+ acpi_ns_print_node_pathname(device_handle, NULL);
+ printk("\n");
+ BUG();
+ }
+
+ status = acpi_evaluate_integer(child, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "%s: Unable to get _ADR (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(child, NULL);
+ printk("\n");
+ panic("%s: Unable to find host devfn\n", __FUNCTION__);
+ }
+
+ slot = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+ devfn = PCI_DEVFN(slot, function);
+ return devfn;
+}
+
+/*
+ * find_matching_device - Callback routine to find the ACPI device
+ * that matches up with our pci_dev device.
+ * Matching is done on bus number and devfn.
+ * To find the bus number for a particular
+ * ACPI device, we must look at the _BBN method
+ * of its parent.
+ */
+static acpi_status
+find_matching_device(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ unsigned long bbn = -1;
+ unsigned long adr;
+ acpi_handle parent = NULL;
+ acpi_status status;
+ unsigned int devfn;
+ int function;
+ int slot;
+ struct sn_pcidev_match *info = context;
+
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL,
+ &adr);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_get_parent(handle, &parent);
+ if (ACPI_FAILURE(status)) {
printk(KERN_ERR
- "sn_pci_fixup_bus: 0x%04x:0x%02x Unable to "
- "obtain prom_bussoft_ptr\n",
- pci_domain_nr(bus), bus->number);
- return;
+ "%s: acpi_get_parent() failed (0x%x) for: ",
+ __FUNCTION__, status);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ return AE_OK;
+ }
+ status = acpi_evaluate_integer(parent, METHOD_NAME__BBN,
+ NULL, &bbn);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR
+ "%s: Failed to find _BBN in parent of: ",
+ __FUNCTION__);
+ acpi_ns_print_node_pathname(handle, NULL);
+ printk("\n");
+ return AE_OK;
+ }
+
+ slot = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+ devfn = PCI_DEVFN(slot, function);
+ if ((info->devfn == devfn) && (info->bus == bbn)) {
+ /* We have a match! */
+ info->handle = handle;
+ return 1;
}
- sn_common_bus_fixup(bus, prom_bussoft_ptr);
}
- list_for_each_entry(pci_dev, &bus->devices, bus_list) {
- sn_pci_fixup_slot(pci_dev);
+ return AE_OK;
+}
+
+/*
+ * sn_acpi_get_pcidev_info - Search ACPI namespace for the acpi
+ * device matching the specified pci_dev,
+ * and return the pcidev info and irq info.
+ */
+int
+sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info,
+ struct sn_irq_info **sn_irq_info)
+{
+ unsigned int host_devfn;
+ struct sn_pcidev_match pcidev_match;
+ acpi_handle rootbus_handle;
+ unsigned long segment;
+ acpi_status status;
+
+ rootbus_handle = PCI_CONTROLLER(dev)->acpi_handle;
+ status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL,
+ &segment);
+ if (ACPI_SUCCESS(status)) {
+ if (segment != pci_domain_nr(dev)) {
+ printk(KERN_ERR
+ "%s: Segment number mismatch, 0x%lx vs 0x%x for: ",
+ __FUNCTION__, segment, pci_domain_nr(dev));
+ acpi_ns_print_node_pathname(rootbus_handle, NULL);
+ printk("\n");
+ return 1;
+ }
+ } else {
+ printk(KERN_ERR "%s: Unable to get __SEG from: ",
+ __FUNCTION__);
+ acpi_ns_print_node_pathname(rootbus_handle, NULL);
+ printk("\n");
+ return 1;
}
+
+ /*
+ * We want to search all devices in this segment/domain
+ * of the ACPI namespace for the matching ACPI device,
+ * which holds the pcidev_info pointer in its vendor resource.
+ */
+ pcidev_match.bus = dev->bus->number;
+ pcidev_match.devfn = dev->devfn;
+ pcidev_match.handle = NULL;
+
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, rootbus_handle, ACPI_UINT32_MAX,
+ find_matching_device, &pcidev_match, NULL);
+
+ if (!pcidev_match.handle) {
+ printk(KERN_ERR
+ "%s: Could not find matching ACPI device for %s.\n",
+ __FUNCTION__, pci_name(dev));
+ return 1;
+ }
+
+ if (sn_extract_device_info(pcidev_match.handle, pcidev_info, sn_irq_info))
+ return 1;
+
+ /* Build up the pcidev_info.pdi_slot_host_handle */
+ host_devfn = get_host_devfn(pcidev_match.handle, rootbus_handle);
+ (*pcidev_info)->pdi_slot_host_handle =
+ ((unsigned long) pci_domain_nr(dev) << 40) |
+ /* bus == 0 */
+ host_devfn;
+ return 0;
}

/*
- * sn_acpi_slot_fixup - Perform any SN specific slot fixup.
+ * sn_acpi_slot_fixup - Obtain the pcidev_info and sn_irq_info.
+ * Perform any SN specific slot fixup.
* At present there does not appear to be
* any generic way to handle a ROM image
* that has been shadowed by the PROM, so
@@ -179,11 +408,18 @@ sn_acpi_bus_fixup(struct pci_bus *bus)
*/

void
-sn_acpi_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info)
+sn_acpi_slot_fixup(struct pci_dev *dev)
{
void __iomem *addr;
+ struct pcidev_info *pcidev_info = NULL;
+ struct sn_irq_info *sn_irq_info = NULL;
size_t size;

+ if (sn_acpi_get_pcidev_info(dev, &pcidev_info, &sn_irq_info)) {
+ panic("%s: Failure obtaining pcidev_info for %s\n",
+ __FUNCTION__, pci_name(dev));
+ }
+
if (pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE]) {
/*
* A valid ROM image exists and has been shadowed by the
@@ -200,8 +436,11 @@ sn_acpi_slot_fixup(struct pci_dev *dev,
(unsigned long) addr + size;
dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY;
}
+ sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
}

+EXPORT_SYMBOL(sn_acpi_slot_fixup);
+
static struct acpi_driver acpi_sn_hubdev_driver = {
.name = "SGI HUBDEV Driver",
.ids = "SGIHUB,SGITIO",
@@ -212,6 +451,33 @@ static struct acpi_driver acpi_sn_hubdev


/*
+ * sn_acpi_bus_fixup - Perform SN specific setup of software structs
+ * (pcibus_bussoft, pcidev_info) and hardware
+ * registers, for the specified bus and devices under it.
+ */
+void
+sn_acpi_bus_fixup(struct pci_bus *bus)
+{
+ struct pci_dev *pci_dev = NULL;
+ struct pcibus_bussoft *prom_bussoft_ptr;
+
+ if (!bus->parent) { /* If root bus */
+ prom_bussoft_ptr = sn_get_bussoft_ptr(bus);
+ if (prom_bussoft_ptr == NULL) {
+ printk(KERN_ERR
+ "%s: 0x%04x:0x%02x Unable to "
+ "obtain prom_bussoft_ptr\n",
+ __FUNCTION__, pci_domain_nr(bus), bus->number);
+ return;
+ }
+ sn_common_bus_fixup(bus, prom_bussoft_ptr);
+ }
+ list_for_each_entry(pci_dev, &bus->devices, bus_list) {
+ sn_acpi_slot_fixup(pci_dev);
+ }
+}
+
+/*
* sn_io_acpi_init - PROM has ACPI support for IO, defining at a minimum the
* nodes and root buses in the DSDT. As a result, bus scanning
* will be initiated by the Linux ACPI code.
Index: acpi/arch/ia64/sn/kernel/io_common.c
===================================================================
--- acpi.orig/arch/ia64/sn/kernel/io_common.c
+++ acpi/arch/ia64/sn/kernel/io_common.c
@@ -26,14 +26,10 @@
#include <linux/acpi.h>
#include <asm/sn/sn2/sn_hwperf.h>
#include <asm/sn/acpi.h>
+#include "acpi/acglobal.h"

extern void sn_init_cpei_timer(void);
extern void register_sn_procfs(void);
-extern void sn_acpi_bus_fixup(struct pci_bus *);
-extern void sn_bus_fixup(struct pci_bus *);
-extern void sn_acpi_slot_fixup(struct pci_dev *, struct pcidev_info *);
-extern void sn_more_slot_fixup(struct pci_dev *, struct pcidev_info *);
-extern void sn_legacy_pci_window_fixup(struct pci_controller *, u64, u64);
extern void sn_io_acpi_init(void);
extern void sn_io_init(void);

@@ -48,16 +44,10 @@ struct sysdata_el {

int sn_ioif_inited; /* SN I/O infrastructure initialized? */

-struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */
+int sn_acpi_rev; /* SN ACPI revision */
+EXPORT_SYMBOL_GPL(sn_acpi_rev);

-int sn_acpi_base_support(void)
-{
- struct acpi_table_header *header;
- (void)acpi_get_table_by_index(ACPI_TABLE_INDEX_DSDT, &header);
- if (header && header->oem_revision >= 0x20101)
- return 1;
- return 0;
-}
+struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */

/*
* Hooks and struct for unsupported pci providers
@@ -108,25 +98,6 @@ sal_get_device_dmaflush_list(u64 nasid,
}

/*
- * Retrieve the pci device information given the bus and device|function number.
- */
-static inline u64
-sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
- u64 sn_irq_info)
-{
- struct ia64_sal_retval ret_stuff;
- ret_stuff.status = 0;
- ret_stuff.v0 = 0;
-
- SAL_CALL_NOLOCK(ret_stuff,
- (u64) SN_SAL_IOIF_GET_PCIDEV_INFO,
- (u64) segment, (u64) bus_number, (u64) devfn,
- (u64) pci_dev,
- sn_irq_info, 0, 0);
- return ret_stuff.v0;
-}
-
-/*
* sn_pcidev_info_get() - Retrieve the pcidev_info struct for the specified
* device.
*/
@@ -258,47 +229,22 @@ void sn_pci_unfixup_slot(struct pci_dev
}

/*
- * sn_pci_fixup_slot() - This routine sets up a slot's resources consistent
- * with the Linux PCI abstraction layer. Resources
- * acquired from our PCI provider include PIO maps
- * to BAR space and interrupt objects.
+ * sn_pci_fixup_slot()
*/
-void sn_pci_fixup_slot(struct pci_dev *dev)
+void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *pcidev_info,
+ struct sn_irq_info *sn_irq_info)
{
int segment = pci_domain_nr(dev->bus);
- int status = 0;
struct pcibus_bussoft *bs;
struct pci_bus *host_pci_bus;
struct pci_dev *host_pci_dev;
- struct pcidev_info *pcidev_info;
- struct sn_irq_info *sn_irq_info;
unsigned int bus_no, devfn;

pci_dev_get(dev); /* for the sysdata pointer */
- pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
- if (!pcidev_info)
- BUG(); /* Cannot afford to run out of memory */
-
- sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
- if (!sn_irq_info)
- BUG(); /* Cannot afford to run out of memory */
-
- /* Call to retrieve pci device information needed by kernel. */
- status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number,
- dev->devfn,
- (u64) __pa(pcidev_info),
- (u64) __pa(sn_irq_info));
- if (status)
- BUG(); /* Cannot get platform pci device information */

/* Add pcidev_info to list in pci_controller.platform_data */
list_add_tail(&pcidev_info->pdi_list,
&(SN_PLATFORM_DATA(dev->bus)->pcidev_info));
-
- if (SN_ACPI_BASE_SUPPORT())
- sn_acpi_slot_fixup(dev, pcidev_info);
- else
- sn_more_slot_fixup(dev, pcidev_info);
/*
* Using the PROMs values for the PCI host bus, get the Linux
* PCI host_pci_dev struct and set up host bus linkages
@@ -498,11 +444,6 @@ void sn_generate_path(struct pci_bus *pc
sprintf(address, "%s^%d", address, geo_slot(geoid));
}

-/*
- * sn_pci_fixup_bus() - Perform SN specific setup of software structs
- * (pcibus_bussoft, pcidev_info) and hardware
- * registers, for the specified bus and devices under it.
- */
void __devinit
sn_pci_fixup_bus(struct pci_bus *bus)
{
@@ -528,6 +469,15 @@ sn_io_early_init(void)
if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM())
return 0;

+ /* we set the acpi revision to that of the DSDT table OEM rev. */
+ {
+ struct acpi_table_header *header = NULL;
+
+ acpi_get_table_by_index(ACPI_TABLE_INDEX_DSDT, &header);
+ BUG_ON(header == NULL);
+ sn_acpi_rev = header->oem_revision;
+ }
+
/*
* prime sn_pci_provider[]. Individial provider init routines will
* override their respective default entries.
@@ -618,7 +568,6 @@ sn_io_late_init(void)

fs_initcall(sn_io_late_init);

-EXPORT_SYMBOL(sn_pci_fixup_slot);
EXPORT_SYMBOL(sn_pci_unfixup_slot);
EXPORT_SYMBOL(sn_bus_store_sysdata);
EXPORT_SYMBOL(sn_bus_free_sysdata);
Index: acpi/arch/ia64/sn/kernel/io_init.c
===================================================================
--- acpi.orig/arch/ia64/sn/kernel/io_init.c
+++ acpi/arch/ia64/sn/kernel/io_init.c
@@ -56,6 +56,25 @@ static inline u64 sal_get_pcibus_info(u6
return ret_stuff.v0;
}

+/*
+ * Retrieve the pci device information given the bus and device|function number.
+ */
+static inline u64
+sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
+ u64 sn_irq_info)
+{
+ struct ia64_sal_retval ret_stuff;
+ ret_stuff.status = 0;
+ ret_stuff.v0 = 0;
+
+ SAL_CALL_NOLOCK(ret_stuff,
+ (u64) SN_SAL_IOIF_GET_PCIDEV_INFO,
+ (u64) segment, (u64) bus_number, (u64) devfn,
+ (u64) pci_dev,
+ sn_irq_info, 0, 0);
+ return ret_stuff.v0;
+}
+

/*
* sn_fixup_ionodes() - This routine initializes the HUB data structure for
@@ -172,18 +191,40 @@ sn_pci_window_fixup(struct pci_dev *dev,
}

/*
- * sn_more_slot_fixup() - We are not running with an ACPI capable PROM,
+ * sn_io_slot_fixup() - We are not running with an ACPI capable PROM,
* and need to convert the pci_dev->resource
* 'start' and 'end' addresses to mapped addresses,
* and setup the pci_controller->window array entries.
*/
void
-sn_more_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info)
+sn_io_slot_fixup(struct pci_dev *dev)
{
unsigned int count = 0;
int idx;
s64 pci_addrs[PCI_ROM_RESOURCE + 1];
unsigned long addr, end, size, start;
+ struct pcidev_info *pcidev_info;
+ struct sn_irq_info *sn_irq_info;
+ int status;
+
+ pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
+ if (!pcidev_info)
+ panic("%s: Unable to alloc memory for pcidev_info", __FUNCTION__);
+
+ sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
+ if (!sn_irq_info)
+ panic("%s: Unable to alloc memory for sn_irq_info", __FUNCTION__);
+
+ /* Call to retrieve pci device information needed by kernel. */
+ status = sal_get_pcidev_info((u64) pci_domain_nr(dev),
+ (u64) dev->bus->number,
+ dev->devfn,
+ (u64) __pa(pcidev_info),
+ (u64) __pa(sn_irq_info));
+
+ if (status)
+ BUG(); /* Cannot get platform pci device information */
+

/* Copy over PIO Mapped Addresses */
for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
@@ -219,8 +260,12 @@ sn_more_slot_fixup(struct pci_dev *dev,
*/
if (count > 0)
sn_pci_window_fixup(dev, count, pci_addrs);
+
+ sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
}

+EXPORT_SYMBOL(sn_io_slot_fixup);
+
/*
* sn_pci_controller_fixup() - This routine sets up a bus's resources
* consistent with the Linux PCI abstraction layer.
@@ -272,9 +317,6 @@ sn_bus_fixup(struct pci_bus *bus)
{
struct pci_dev *pci_dev = NULL;
struct pcibus_bussoft *prom_bussoft_ptr;
- extern void sn_common_bus_fixup(struct pci_bus *,
- struct pcibus_bussoft *);
-

if (!bus->parent) { /* If root bus */
prom_bussoft_ptr = PCI_CONTROLLER(bus)->platform_data;
@@ -291,7 +333,7 @@ sn_bus_fixup(struct pci_bus *bus)
prom_bussoft_ptr->bs_legacy_mem);
}
list_for_each_entry(pci_dev, &bus->devices, bus_list) {
- sn_pci_fixup_slot(pci_dev);
+ sn_io_slot_fixup(pci_dev);
}

}
Index: acpi/arch/ia64/sn/pci/pcibr/pcibr_provider.c
===================================================================
--- acpi.orig/arch/ia64/sn/pci/pcibr/pcibr_provider.c
+++ acpi/arch/ia64/sn/pci/pcibr/pcibr_provider.c
@@ -20,7 +20,8 @@
#include "xtalk/hubdev.h"

int
-sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp)
+sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp,
+ char **ssdt)
{
struct ia64_sal_retval ret_stuff;
u64 busnum;
@@ -32,7 +33,8 @@ sal_pcibr_slot_enable(struct pcibus_info
segment = soft->pbi_buscommon.bs_persist_segment;
busnum = soft->pbi_buscommon.bs_persist_busnum;
SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_ENABLE, segment,
- busnum, (u64) device, (u64) resp, 0, 0, 0);
+ busnum, (u64) device, (u64) resp, (u64)ia64_tpa(ssdt),
+ 0, 0);

return (int)ret_stuff.v0;
}
Index: acpi/include/asm-ia64/sn/acpi.h
===================================================================
--- acpi.orig/include/asm-ia64/sn/acpi.h
+++ acpi/include/asm-ia64/sn/acpi.h
@@ -11,8 +11,7 @@

#include "acpi/acglobal.h"

-#define SN_ACPI_BASE_SUPPORT() sn_acpi_base_support()
-
-extern int sn_acpi_base_support(void);
+extern int sn_acpi_rev;
+#define SN_ACPI_BASE_SUPPORT() (sn_acpi_rev >= 0x20101)

#endif /* _ASM_IA64_SN_ACPI_H */
Index: acpi/include/asm-ia64/sn/pcibr_provider.h
===================================================================
--- acpi.orig/include/asm-ia64/sn/pcibr_provider.h
+++ acpi/include/asm-ia64/sn/pcibr_provider.h
@@ -142,7 +142,7 @@ extern int pcibr_ate_alloc(struct pcib
extern void pcibr_ate_free(struct pcibus_info *, int);
extern void ate_write(struct pcibus_info *, int, int, u64);
extern int sal_pcibr_slot_enable(struct pcibus_info *soft, int device,
- void *resp);
+ void *resp, char **ssdt);
extern int sal_pcibr_slot_disable(struct pcibus_info *soft, int device,
int action, void *resp);
extern u16 sn_ioboard_to_pci_bus(struct pci_bus *pci_bus);
Index: acpi/include/asm-ia64/sn/pcidev.h
===================================================================
--- acpi.orig/include/asm-ia64/sn/pcidev.h
+++ acpi/include/asm-ia64/sn/pcidev.h
@@ -70,10 +70,16 @@ extern void sn_irq_fixup(struct pci_dev
struct sn_irq_info *sn_irq_info);
extern void sn_irq_unfixup(struct pci_dev *pci_dev);
extern struct pcidev_info * sn_pcidev_info_get(struct pci_dev *);
+extern void sn_bus_fixup(struct pci_bus *);
+extern void sn_acpi_bus_fixup(struct pci_bus *);
+extern void sn_common_bus_fixup(struct pci_bus *, struct pcibus_bussoft *);
extern void sn_bus_store_sysdata(struct pci_dev *dev);
extern void sn_bus_free_sysdata(void);
extern void sn_generate_path(struct pci_bus *pci_bus, char *address);
-extern void sn_pci_fixup_slot(struct pci_dev *dev);
+extern void sn_io_slot_fixup(struct pci_dev *);
+extern void sn_acpi_slot_fixup(struct pci_dev *);
+extern void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *,
+ struct sn_irq_info *);
extern void sn_pci_unfixup_slot(struct pci_dev *dev);
extern void sn_irq_lh_init(void);
#endif /* _ASM_IA64_SN_PCI_PCIDEV_H */

2007-01-27 07:12:12

by Len Brown

[permalink] [raw]
Subject: [PATCH 3/3] Altix: Add ACPI SSDT PCI device support (hotplug)

From: John Keller <[email protected]>

Support for dynamic loading and unloading of ACPI SSDT tables upon slot
hotplugs and unplugs.

On SN platforms, we now represent every populated root bus slot with a single
ACPI SSDT table containing info for every device and PPB attached to the slot.
These SSDTs are generated by the prom at initial boot and hotplug time. The
info in these SSDT tables is used by the SN kernel IO "fixup" code (which is
called at boot and hotplug time).

On hotplugs (i.e. enable_slot()), if running with an ACPI capable prom,
attempt to obtain a new ACPI SSDT table for the slot being hotplugged. If
successful, add the table to the ACPI namespace (acpi_load_table()) and then
walk the new devices and add them to the ACPI infrastructure (acpi_bus_add()).

On hot unplugs (i.e. disable_slot()), if running with an ACPI capable prom,
attempt to remove the SSDT table associated with the slot from the ACPI
namespace (acpi_unload_table_id()) and infastructure (acpi_bus_trim()).

From: John Keller <[email protected]>

A bug was fixed where the sgi hotplug driver was removing
the slot's SSDT table from the ACPI namespace a bit too early in
disable_slot(). Also, we now call acpi_bus_start() subsequent
to acpi_bus_add().

Signed-off-by: Aaron Young <[email protected]>
Cc: Greg KH <[email protected]>
Cc: "Luck, Tony" <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Len Brown<[email protected]>
---

drivers/pci/hotplug/sgi_hotplug.c | 155 ++++++++++++++++++++++++++--
1 files changed, 148 insertions(+), 7 deletions(-)

Index: acpi/drivers/pci/hotplug/sgi_hotplug.c
===================================================================
--- acpi.orig/drivers/pci/hotplug/sgi_hotplug.c
+++ acpi/drivers/pci/hotplug/sgi_hotplug.c
@@ -28,6 +28,8 @@
#include <asm/sn/sn_feature_sets.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/types.h>
+#include <linux/acpi.h>
+#include <asm/sn/acpi.h>

#include "../pci.h"

@@ -35,14 +37,17 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("SGI ([email protected], [email protected], [email protected])");
MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver");

-#define PCIIO_ASIC_TYPE_TIOCA 4
+
+/* SAL call error codes. Keep in sync with prom header io/include/pcibr.h */
#define PCI_SLOT_ALREADY_UP 2 /* slot already up */
#define PCI_SLOT_ALREADY_DOWN 3 /* slot already down */
#define PCI_L1_ERR 7 /* L1 console command error */
#define PCI_EMPTY_33MHZ 15 /* empty 33 MHz bus */
+
+
+#define PCIIO_ASIC_TYPE_TIOCA 4
#define PCI_L1_QSIZE 128 /* our L1 message buffer size */
#define SN_MAX_HP_SLOTS 32 /* max hotplug slots */
-#define SGI_HOTPLUG_PROM_REV 0x0430 /* Min. required PROM version */
#define SN_SLOT_NAME_SIZE 33 /* size of name string */

/* internal list head */
@@ -227,7 +232,7 @@ static void sn_bus_free_data(struct pci_
}

static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
- int device_num)
+ int device_num, char **ssdt)
{
struct slot *slot = bss_hotplug_slot->private;
struct pcibus_info *pcibus_info;
@@ -240,7 +245,8 @@ static int sn_slot_enable(struct hotplug
* Power-on and initialize the slot in the SN
* PCI infrastructure.
*/
- rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp);
+ rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp, ssdt);
+

if (rc == PCI_SLOT_ALREADY_UP) {
dev_dbg(slot->pci_bus->self, "is already active\n");
@@ -335,6 +341,7 @@ static int enable_slot(struct hotplug_sl
int func, num_funcs;
int new_ppb = 0;
int rc;
+ char *ssdt = NULL;
void pcibios_fixup_device_resources(struct pci_dev *);

/* Serialize the Linux PCI infrastructure */
@@ -342,14 +349,29 @@ static int enable_slot(struct hotplug_sl

/*
* Power-on and initialize the slot in the SN
- * PCI infrastructure.
+ * PCI infrastructure. Also, retrieve the ACPI SSDT
+ * table for the slot (if ACPI capable PROM).
*/
- rc = sn_slot_enable(bss_hotplug_slot, slot->device_num);
+ rc = sn_slot_enable(bss_hotplug_slot, slot->device_num, &ssdt);
if (rc) {
mutex_unlock(&sn_hotplug_mutex);
return rc;
}

+ if (ssdt)
+ ssdt = __va(ssdt);
+ /* Add the new SSDT for the slot to the ACPI namespace */
+ if (SN_ACPI_BASE_SUPPORT() && ssdt) {
+ acpi_status ret;
+
+ ret = acpi_load_table((struct acpi_table_header *)ssdt);
+ if (ACPI_FAILURE(ret)) {
+ printk(KERN_ERR "%s: acpi_load_table failed (0x%x)\n",
+ __FUNCTION__, ret);
+ /* try to continue on */
+ }
+ }
+
num_funcs = pci_scan_slot(slot->pci_bus,
PCI_DEVFN(slot->device_num + 1, 0));
if (!num_funcs) {
@@ -374,7 +396,10 @@ static int enable_slot(struct hotplug_sl
* pdi_host_pcidev_info).
*/
pcibios_fixup_device_resources(dev);
- sn_pci_fixup_slot(dev);
+ if (SN_ACPI_BASE_SUPPORT())
+ sn_acpi_slot_fixup(dev);
+ else
+ sn_io_slot_fixup(dev);
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
unsigned char sec_bus;
pci_read_config_byte(dev, PCI_SECONDARY_BUS,
@@ -388,6 +413,63 @@ static int enable_slot(struct hotplug_sl
}
}

+ /*
+ * Add the slot's devices to the ACPI infrastructure */
+ if (SN_ACPI_BASE_SUPPORT() && ssdt) {
+ unsigned long adr;
+ struct acpi_device *pdevice;
+ struct acpi_device *device;
+ acpi_handle phandle;
+ acpi_handle chandle = NULL;
+ acpi_handle rethandle;
+ acpi_status ret;
+
+ phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
+
+ if (acpi_bus_get_device(phandle, &pdevice)) {
+ dev_dbg(slot->pci_bus->self,
+ "no parent device, assuming NULL\n");
+ pdevice = NULL;
+ }
+
+ /*
+ * Walk the rootbus node's immediate children looking for
+ * the slot's device node(s). There can be more than
+ * one for multifunction devices.
+ */
+ for (;;) {
+ rethandle = NULL;
+ ret = acpi_get_next_object(ACPI_TYPE_DEVICE,
+ phandle, chandle,
+ &rethandle);
+
+ if (ret == AE_NOT_FOUND || rethandle == NULL)
+ break;
+
+ chandle = rethandle;
+
+ ret = acpi_evaluate_integer(chandle, METHOD_NAME__ADR,
+ NULL, &adr);
+
+ if (ACPI_SUCCESS(ret) &&
+ (adr>>16) == (slot->device_num + 1)) {
+
+ ret = acpi_bus_add(&device, pdevice, chandle,
+ ACPI_BUS_TYPE_DEVICE);
+ if (ACPI_FAILURE(ret)) {
+ printk(KERN_ERR "%s: acpi_bus_add "
+ "failed (0x%x) for slot %d "
+ "func %d\n", __FUNCTION__,
+ ret, (int)(adr>>16),
+ (int)(adr&0xffff));
+ /* try to continue on */
+ } else {
+ acpi_bus_start(device);
+ }
+ }
+ }
+ }
+
/* Call the driver for the new device */
pci_bus_add_devices(slot->pci_bus);
/* Call the drivers for the new devices subordinate to PPB */
@@ -412,6 +494,7 @@ static int disable_slot(struct hotplug_s
struct pci_dev *dev;
int func;
int rc;
+ acpi_owner_id ssdt_id = 0;

/* Acquire update access to the bus */
mutex_lock(&sn_hotplug_mutex);
@@ -422,6 +505,52 @@ static int disable_slot(struct hotplug_s
if (rc)
goto leaving;

+ /* free the ACPI resources for the slot */
+ if (SN_ACPI_BASE_SUPPORT() &&
+ PCI_CONTROLLER(slot->pci_bus)->acpi_handle) {
+ unsigned long adr;
+ struct acpi_device *device;
+ acpi_handle phandle;
+ acpi_handle chandle = NULL;
+ acpi_handle rethandle;
+ acpi_status ret;
+
+ /* Get the rootbus node pointer */
+ phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
+
+ /*
+ * Walk the rootbus node's immediate children looking for
+ * the slot's device node(s). There can be more than
+ * one for multifunction devices.
+ */
+ for (;;) {
+ rethandle = NULL;
+ ret = acpi_get_next_object(ACPI_TYPE_DEVICE,
+ phandle, chandle,
+ &rethandle);
+
+ if (ret == AE_NOT_FOUND || rethandle == NULL)
+ break;
+
+ chandle = rethandle;
+
+ ret = acpi_evaluate_integer(chandle,
+ METHOD_NAME__ADR,
+ NULL, &adr);
+ if (ACPI_SUCCESS(ret) &&
+ (adr>>16) == (slot->device_num + 1)) {
+ /* retain the owner id */
+ acpi_get_id(chandle, &ssdt_id);
+
+ ret = acpi_bus_get_device(chandle,
+ &device);
+ if (ACPI_SUCCESS(ret))
+ acpi_bus_trim(device, 1);
+ }
+ }
+
+ }
+
/* Free the SN resources assigned to the Linux device.*/
for (func = 0; func < 8; func++) {
dev = pci_get_slot(slot->pci_bus,
@@ -434,6 +563,18 @@ static int disable_slot(struct hotplug_s
}
}

+ /* Remove the SSDT for the slot from the ACPI namespace */
+ if (SN_ACPI_BASE_SUPPORT() && ssdt_id) {
+ acpi_status ret;
+ ret = acpi_unload_table_id(ssdt_id);
+ if (ACPI_FAILURE(ret)) {
+ printk(KERN_ERR "%s: acpi_unload_table_id "
+ "failed (0x%x) for id %d\n",
+ __FUNCTION__, ret, ssdt_id);
+ /* try to continue on */
+ }
+ }
+
/* free the collected sysdata pointers */
sn_bus_free_sysdata();

2007-01-30 00:13:25

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 3/3] Altix: Add ACPI SSDT PCI device support (hotplug)

On Sat, Jan 27, 2007 at 02:04:43AM -0500, Len Brown wrote:
> From: John Keller <[email protected]>
>
> Support for dynamic loading and unloading of ACPI SSDT tables upon slot
> hotplugs and unplugs.
>
> On SN platforms, we now represent every populated root bus slot with a single
> ACPI SSDT table containing info for every device and PPB attached to the slot.
> These SSDTs are generated by the prom at initial boot and hotplug time. The
> info in these SSDT tables is used by the SN kernel IO "fixup" code (which is
> called at boot and hotplug time).
>
> On hotplugs (i.e. enable_slot()), if running with an ACPI capable prom,
> attempt to obtain a new ACPI SSDT table for the slot being hotplugged. If
> successful, add the table to the ACPI namespace (acpi_load_table()) and then
> walk the new devices and add them to the ACPI infrastructure (acpi_bus_add()).
>
> On hot unplugs (i.e. disable_slot()), if running with an ACPI capable prom,
> attempt to remove the SSDT table associated with the slot from the ACPI
> namespace (acpi_unload_table_id()) and infastructure (acpi_bus_trim()).
>
> From: John Keller <[email protected]>
>
> A bug was fixed where the sgi hotplug driver was removing
> the slot's SSDT table from the ACPI namespace a bit too early in
> disable_slot(). Also, we now call acpi_bus_start() subsequent
> to acpi_bus_add().
>
> Signed-off-by: Aaron Young <[email protected]>
> Cc: Greg KH <[email protected]>

This should go through Kristen, as she is now the pci hotplug
maintainer, not I.

thanks,

greg k-h