#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
#include <linux/mutex.h>
#include "../pci.h"
static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus);
static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context);
-
-/*
- * initialization & terminatation routines
- */
-
-/**
- * is_ejectable - determine if a slot is ejectable
- * @handle: handle to acpi namespace
- *
- * Ejectable slot should satisfy at least these conditions:
- *
- * 1. has _ADR method
- * 2. has _EJ0 method
- *
- * optionally
- *
- * 1. has _STA method
- * 2. has _PS0 method
- * 3. has _PS3 method
- * 4. ..
- */
-static int is_ejectable(acpi_handle handle)
-{
- acpi_status status;
- acpi_handle tmp;
-
- status = acpi_get_handle(handle, "_ADR", &tmp);
- if (ACPI_FAILURE(status)) {
- return 0;
- }
-
- status = acpi_get_handle(handle, "_EJ0", &tmp);
- if (ACPI_FAILURE(status)) {
- return 0;
- }
-
- return 1;
-}
-
-
-/* callback routine to check for the existence of ejectable slots */
-static acpi_status
-is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
- int *count = (int *)context;
-
- if (is_ejectable(handle)) {
- (*count)++;
- /* only one ejectable slot is enough */
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
-}
-
/* callback routine to check for the existence of a pci dock device */
static acpi_status
is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
}
}
-
-
-
/*
* the _DCK method can do funny things... and sometimes not
* hah-hah funny.
if (((buses >> 8) & 0xff) != bus->secondary) {
buses = (buses & 0xff000000)
- | ((unsigned int)(bus->primary) << 0)
- | ((unsigned int)(bus->secondary) << 8)
- | ((unsigned int)(bus->subordinate) << 16);
+ | ((unsigned int)(bus->primary) << 0)
+ | ((unsigned int)(bus->secondary) << 8)
+ | ((unsigned int)(bus->subordinate) << 16);
pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
}
return NOTIFY_OK;
}
-
+static struct acpi_dock_ops acpiphp_dock_ops = {
+ .handler = handle_hotplug_event_func,
+};
/* callback routine to register each ACPI PCI slot object */
static acpi_status
struct acpiphp_func *newfunc;
acpi_handle tmp;
acpi_status status = AE_OK;
- unsigned long adr, sun;
+ unsigned long long adr, sun;
int device, function, retval;
+ struct pci_bus *pbus = bridge->pci_bus;
- status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
-
- if (ACPI_FAILURE(status))
- return AE_OK;
-
- status = acpi_get_handle(handle, "_EJ0", &tmp);
-
- if (ACPI_FAILURE(status) && !(is_dock_device(handle)))
+ if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle))
return AE_OK;
+ acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
device = (adr >> 16) & 0xffff;
function = adr & 0xffff;
INIT_LIST_HEAD(&newfunc->sibling);
newfunc->handle = handle;
newfunc->function = function;
- if (ACPI_SUCCESS(status))
+
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
newfunc->flags = FUNC_HAS_EJ0;
if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp)))
bridge->nr_slots++;
- dbg("found ACPI PCI Hotplug slot %d at PCI %04x:%02x:%02x\n",
- slot->sun, pci_domain_nr(bridge->pci_bus),
- bridge->pci_bus->number, slot->device);
+ dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
+ slot->sun, pci_domain_nr(pbus), pbus->number, device);
retval = acpiphp_register_hotplug_slot(slot);
if (retval) {
- warn("acpiphp_register_hotplug_slot failed(err code = 0x%x)\n", retval);
+ if (retval == -EBUSY)
+ warn("Slot %llu already registered by another "
+ "hotplug driver\n", slot->sun);
+ else
+ warn("acpiphp_register_hotplug_slot failed "
+ "(err code = 0x%x)\n", retval);
goto err_exit;
}
}
list_add_tail(&newfunc->sibling, &slot->funcs);
/* associate corresponding pci_dev */
- newfunc->pci_dev = pci_get_slot(bridge->pci_bus,
- PCI_DEVFN(device, function));
+ newfunc->pci_dev = pci_get_slot(pbus, PCI_DEVFN(device, function));
if (newfunc->pci_dev) {
slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
}
*/
newfunc->flags &= ~FUNC_HAS_EJ0;
if (register_hotplug_dock_device(handle,
- handle_hotplug_event_func, newfunc))
+ &acpiphp_dock_ops, newfunc))
dbg("failed to register dock device\n");
/* we need to be notified when dock events happen
/* see if it's worth looking at this bridge */
-static int detect_ejectable_slots(acpi_handle *bridge_handle)
+static int detect_ejectable_slots(struct pci_bus *pbus)
{
- acpi_status status;
- int count;
-
- count = 0;
-
- /* only check slots defined directly below bridge object */
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
- is_ejectable_slot, (void *)&count, NULL);
-
- /*
- * we also need to add this bridge if there is a dock bridge or
- * other pci device on a dock station (removable)
- */
- if (!count)
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
- (u32)1, is_pci_dock_device, (void *)&count,
- NULL);
-
- return count;
+ int found = acpi_pci_detect_ejectable(pbus);
+ if (!found) {
+ acpi_handle bridge_handle = acpi_pci_get_bridge_handle(pbus);
+ if (!bridge_handle)
+ return 0;
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
+ is_pci_dock_device, (void *)&found, NULL);
+ }
+ return found;
}
{
acpi_status status;
acpi_handle dummy_handle;
- unsigned long tmp;
+ unsigned long long tmp;
int device, function;
struct pci_dev *dev;
struct pci_bus *pci_bus = context;
goto out;
/* check if this bridge has ejectable slots */
- if ((detect_ejectable_slots(handle) > 0)) {
+ if ((detect_ejectable_slots(dev->subordinate) > 0)) {
dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
add_p2p_bridge(handle, dev);
}
static int add_bridge(acpi_handle handle)
{
acpi_status status;
- unsigned long tmp;
+ unsigned long long tmp;
int seg, bus;
acpi_handle dummy_handle;
struct pci_bus *pci_bus;
}
/* check if this bridge has ejectable slots */
- if (detect_ejectable_slots(handle) > 0) {
+ if (detect_ejectable_slots(pci_bus) > 0) {
dbg("found PCI host-bus bridge with hot-pluggable slots\n");
add_host_bridge(handle, pci_bus);
}
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
cleanup_p2p_bridge, NULL, NULL);
- if (!(bridge = acpiphp_handle_to_bridge(handle)))
- return AE_OK;
- cleanup_bridge(bridge);
+ bridge = acpiphp_handle_to_bridge(handle);
+ if (bridge)
+ cleanup_bridge(bridge);
+
return AE_OK;
}
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
(u32)1, cleanup_p2p_bridge, NULL, NULL);
+ /*
+ * On root bridges with hotplug slots directly underneath (ie,
+ * no p2p bridge inbetween), we call cleanup_bridge().
+ *
+ * The else clause cleans up root bridges that either had no
+ * hotplug slots at all, or had a p2p bridge underneath.
+ */
bridge = acpiphp_handle_to_bridge(handle);
if (bridge)
cleanup_bridge(bridge);
+ else
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_bridge);
}
static struct pci_dev * get_apic_pci_info(acpi_handle handle)
{
acpi_status status;
int result = -1;
- unsigned long gsb;
+ unsigned long long gsb;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
void *table;
ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status;
- unsigned long sta;
+ unsigned long long sta;
acpi_handle tmp;
struct pci_dev *pdev;
u32 gsi_base;
ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status;
- unsigned long sta;
+ unsigned long long sta;
acpi_handle tmp;
u32 gsi_base;
struct acpiphp_ioapic *pos, *n, *ioapic = NULL;
static unsigned int get_slot_status(struct acpiphp_slot *slot)
{
acpi_status status;
- unsigned long sta = 0;
+ unsigned long long sta = 0;
u32 dvid;
struct list_head *l;
struct acpiphp_func *func;
return (sta == 0) ? 0 : 1;
}
-
-
-/*
- * pci address (seg/bus/dev)
- */
-u32 acpiphp_get_address(struct acpiphp_slot *slot)
-{
- u32 address;
- struct pci_bus *pci_bus = slot->bridge->pci_bus;
-
- address = (pci_domain_nr(pci_bus) << 16) |
- (pci_bus->number << 8) |
- slot->device;
-
- return address;
-}