Acked-By: Len Brown <len.brown@xxxxxxxxx> Tony, This should probably be sent via your tree. thanks, -Len On Tuesday 19 December 2006 15:56, akpm@xxxxxxxx wrote: > From: John Keller <jpk@xxxxxxx> > > 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 <jpk@xxxxxxx> > > 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 <ayoung@xxxxxxx> > Cc: "Brown, Len" <len.brown@xxxxxxxxx> > Cc: Greg KH <greg@xxxxxxxxx> > Cc: "Luck, Tony" <tony.luck@xxxxxxxxx> > Signed-off-by: Andrew Morton <akpm@xxxxxxxx> > --- > > drivers/pci/hotplug/sgi_hotplug.c | 155 ++++++++++++++++++++++++++-- > 1 files changed, 148 insertions(+), 7 deletions(-) > > diff -puN drivers/pci/hotplug/sgi_hotplug.c~altix-add-acpi-ssdt-pci-device-support-hotplug drivers/pci/hotplug/sgi_hotplug.c > --- a/drivers/pci/hotplug/sgi_hotplug.c~altix-add-acpi-ssdt-pci-device-support-hotplug > +++ a/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 (prarit@xxxxxxx, dickie@xxxxxxx, habeck@xxxxxxx)"); > 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(ACPI_TABLE_ID_SSDT, 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(); > > _ > - > To unsubscribe from this list: send the line "unsubscribe linux-acpi" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html