|
@@ -325,8 +325,8 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- /* install notify handler */
|
|
|
- if (bridge->type != BRIDGE_TYPE_HOST) {
|
|
|
+ /* install notify handler for P2P bridges */
|
|
|
+ if (!pci_is_root_bus(bridge->pci_bus)) {
|
|
|
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
|
|
|
status = acpi_remove_notify_handler(bridge->func->handle,
|
|
|
ACPI_SYSTEM_NOTIFY,
|
|
@@ -369,27 +369,12 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle
|
|
|
static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)
|
|
|
{
|
|
|
acpi_handle dummy_handle;
|
|
|
+ struct acpiphp_func *func;
|
|
|
|
|
|
if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
|
|
|
- "_STA", &dummy_handle)))
|
|
|
- bridge->flags |= BRIDGE_HAS_STA;
|
|
|
-
|
|
|
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
|
|
|
- "_EJ0", &dummy_handle)))
|
|
|
+ "_EJ0", &dummy_handle))) {
|
|
|
bridge->flags |= BRIDGE_HAS_EJ0;
|
|
|
|
|
|
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
|
|
|
- "_PS0", &dummy_handle)))
|
|
|
- bridge->flags |= BRIDGE_HAS_PS0;
|
|
|
-
|
|
|
- if (ACPI_SUCCESS(acpi_get_handle(bridge->handle,
|
|
|
- "_PS3", &dummy_handle)))
|
|
|
- bridge->flags |= BRIDGE_HAS_PS3;
|
|
|
-
|
|
|
- /* is this ejectable p2p bridge? */
|
|
|
- if (bridge->flags & BRIDGE_HAS_EJ0) {
|
|
|
- struct acpiphp_func *func;
|
|
|
-
|
|
|
dbg("found ejectable p2p bridge\n");
|
|
|
|
|
|
/* make link between PCI bridge and PCI function */
|
|
@@ -412,7 +397,6 @@ static void add_host_bridge(struct acpi_pci_root *root)
|
|
|
if (bridge == NULL)
|
|
|
return;
|
|
|
|
|
|
- bridge->type = BRIDGE_TYPE_HOST;
|
|
|
bridge->handle = handle;
|
|
|
|
|
|
bridge->pci_bus = root->bus;
|
|
@@ -432,7 +416,6 @@ static void add_p2p_bridge(acpi_handle *handle)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- bridge->type = BRIDGE_TYPE_P2P;
|
|
|
bridge->handle = handle;
|
|
|
config_p2p_bridge_flags(bridge);
|
|
|
|
|
@@ -543,13 +526,15 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
|
|
|
acpi_status status;
|
|
|
acpi_handle handle = bridge->handle;
|
|
|
|
|
|
- status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
|
|
+ if (!pci_is_root_bus(bridge->pci_bus)) {
|
|
|
+ status = acpi_remove_notify_handler(handle,
|
|
|
+ ACPI_SYSTEM_NOTIFY,
|
|
|
handle_hotplug_event_bridge);
|
|
|
- if (ACPI_FAILURE(status))
|
|
|
- err("failed to remove notify handler\n");
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ err("failed to remove notify handler\n");
|
|
|
+ }
|
|
|
|
|
|
- if ((bridge->type != BRIDGE_TYPE_HOST) &&
|
|
|
- ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) {
|
|
|
+ if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
|
|
|
status = acpi_install_notify_handler(bridge->func->handle,
|
|
|
ACPI_SYSTEM_NOTIFY,
|
|
|
handle_hotplug_event_func,
|
|
@@ -630,9 +615,6 @@ static void remove_bridge(struct acpi_pci_root *root)
|
|
|
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 int power_on_slot(struct acpiphp_slot *slot)
|
|
@@ -793,6 +775,29 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
|
|
|
+{
|
|
|
+ struct acpiphp_func *func;
|
|
|
+
|
|
|
+ if (!dev->subordinate)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* quirk, or pcie could set it already */
|
|
|
+ if (dev->is_hotplug_bridge)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (PCI_SLOT(dev->devfn) != slot->device)
|
|
|
+ return;
|
|
|
+
|
|
|
+ list_for_each_entry(func, &slot->funcs, sibling) {
|
|
|
+ if (PCI_FUNC(dev->devfn) == func->function) {
|
|
|
+ /* check if this bridge has ejectable slots */
|
|
|
+ if ((detect_ejectable_slots(func->handle) > 0))
|
|
|
+ dev->is_hotplug_bridge = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
/**
|
|
|
* enable_device - enable, configure a slot
|
|
|
* @slot: slot to be enabled
|
|
@@ -812,6 +817,9 @@ static int __ref enable_device(struct acpiphp_slot *slot)
|
|
|
if (slot->flags & SLOT_ENABLED)
|
|
|
goto err_exit;
|
|
|
|
|
|
+ list_for_each_entry(func, &slot->funcs, sibling)
|
|
|
+ acpiphp_bus_add(func);
|
|
|
+
|
|
|
num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
|
|
|
if (num == 0) {
|
|
|
/* Maybe only part of funcs are added. */
|
|
@@ -827,15 +835,14 @@ static int __ref enable_device(struct acpiphp_slot *slot)
|
|
|
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
|
|
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
|
|
|
max = pci_scan_bridge(bus, dev, max, pass);
|
|
|
- if (pass && dev->subordinate)
|
|
|
+ if (pass && dev->subordinate) {
|
|
|
+ check_hotplug_bridge(slot, dev);
|
|
|
pci_bus_size_bridges(dev->subordinate);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- list_for_each_entry(func, &slot->funcs, sibling)
|
|
|
- acpiphp_bus_add(func);
|
|
|
-
|
|
|
pci_bus_assign_resources(bus);
|
|
|
acpiphp_sanitize_bus(bus);
|
|
|
acpiphp_set_hpp_values(bus);
|
|
@@ -1093,69 +1100,10 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* Program resources in newly inserted bridge */
|
|
|
-static int acpiphp_configure_bridge (acpi_handle handle)
|
|
|
-{
|
|
|
- struct pci_bus *bus;
|
|
|
-
|
|
|
- if (acpi_is_root_bridge(handle)) {
|
|
|
- struct acpi_pci_root *root = acpi_pci_find_root(handle);
|
|
|
- bus = root->bus;
|
|
|
- } else {
|
|
|
- struct pci_dev *pdev = acpi_get_pci_dev(handle);
|
|
|
- bus = pdev->subordinate;
|
|
|
- pci_dev_put(pdev);
|
|
|
- }
|
|
|
-
|
|
|
- pci_bus_size_bridges(bus);
|
|
|
- pci_bus_assign_resources(bus);
|
|
|
- acpiphp_sanitize_bus(bus);
|
|
|
- acpiphp_set_hpp_values(bus);
|
|
|
- pci_enable_bridges(bus);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void handle_bridge_insertion(acpi_handle handle, u32 type)
|
|
|
-{
|
|
|
- struct acpi_device *device;
|
|
|
-
|
|
|
- if ((type != ACPI_NOTIFY_BUS_CHECK) &&
|
|
|
- (type != ACPI_NOTIFY_DEVICE_CHECK)) {
|
|
|
- err("unexpected notification type %d\n", type);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (acpi_bus_scan(handle)) {
|
|
|
- err("cannot add bridge to acpi list\n");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (acpi_bus_get_device(handle, &device)) {
|
|
|
- err("ACPI device object missing\n");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (!acpiphp_configure_bridge(handle))
|
|
|
- add_bridge(handle);
|
|
|
- else
|
|
|
- err("cannot configure and start bridge\n");
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* ACPI event handlers
|
|
|
*/
|
|
|
|
|
|
-static acpi_status
|
|
|
-count_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
|
-{
|
|
|
- int *count = (int *)context;
|
|
|
- struct acpiphp_bridge *bridge;
|
|
|
-
|
|
|
- bridge = acpiphp_handle_to_bridge(handle);
|
|
|
- if (bridge)
|
|
|
- (*count)++;
|
|
|
- return AE_OK ;
|
|
|
-}
|
|
|
-
|
|
|
static acpi_status
|
|
|
check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
|
{
|
|
@@ -1174,83 +1122,33 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
|
return AE_OK ;
|
|
|
}
|
|
|
|
|
|
-struct acpiphp_hp_work {
|
|
|
- struct work_struct work;
|
|
|
- acpi_handle handle;
|
|
|
- u32 type;
|
|
|
- void *context;
|
|
|
-};
|
|
|
-
|
|
|
-static void alloc_acpiphp_hp_work(acpi_handle handle, u32 type,
|
|
|
- void *context,
|
|
|
- void (*func)(struct work_struct *work))
|
|
|
-{
|
|
|
- struct acpiphp_hp_work *hp_work;
|
|
|
- int ret;
|
|
|
-
|
|
|
- hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL);
|
|
|
- if (!hp_work)
|
|
|
- return;
|
|
|
-
|
|
|
- hp_work->handle = handle;
|
|
|
- hp_work->type = type;
|
|
|
- hp_work->context = context;
|
|
|
-
|
|
|
- INIT_WORK(&hp_work->work, func);
|
|
|
- ret = queue_work(kacpi_hotplug_wq, &hp_work->work);
|
|
|
- if (!ret)
|
|
|
- kfree(hp_work);
|
|
|
-}
|
|
|
-
|
|
|
static void _handle_hotplug_event_bridge(struct work_struct *work)
|
|
|
{
|
|
|
struct acpiphp_bridge *bridge;
|
|
|
char objname[64];
|
|
|
struct acpi_buffer buffer = { .length = sizeof(objname),
|
|
|
.pointer = objname };
|
|
|
- struct acpi_device *device;
|
|
|
- int num_sub_bridges = 0;
|
|
|
- struct acpiphp_hp_work *hp_work;
|
|
|
+ struct acpi_hp_work *hp_work;
|
|
|
acpi_handle handle;
|
|
|
u32 type;
|
|
|
|
|
|
- hp_work = container_of(work, struct acpiphp_hp_work, work);
|
|
|
+ hp_work = container_of(work, struct acpi_hp_work, work);
|
|
|
handle = hp_work->handle;
|
|
|
type = hp_work->type;
|
|
|
+ bridge = (struct acpiphp_bridge *)hp_work->context;
|
|
|
|
|
|
acpi_scan_lock_acquire();
|
|
|
|
|
|
- if (acpi_bus_get_device(handle, &device)) {
|
|
|
- /* This bridge must have just been physically inserted */
|
|
|
- handle_bridge_insertion(handle, type);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- bridge = acpiphp_handle_to_bridge(handle);
|
|
|
- if (type == ACPI_NOTIFY_BUS_CHECK) {
|
|
|
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX,
|
|
|
- count_sub_bridges, NULL, &num_sub_bridges, NULL);
|
|
|
- }
|
|
|
-
|
|
|
- if (!bridge && !num_sub_bridges) {
|
|
|
- err("cannot get bridge info\n");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
|
|
|
|
|
|
switch (type) {
|
|
|
case ACPI_NOTIFY_BUS_CHECK:
|
|
|
/* bus re-enumerate */
|
|
|
dbg("%s: Bus check notify on %s\n", __func__, objname);
|
|
|
- if (bridge) {
|
|
|
- dbg("%s: re-enumerating slots under %s\n",
|
|
|
- __func__, objname);
|
|
|
- acpiphp_check_bridge(bridge);
|
|
|
- }
|
|
|
- if (num_sub_bridges)
|
|
|
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
|
|
|
- ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
|
|
|
+ dbg("%s: re-enumerating slots under %s\n", __func__, objname);
|
|
|
+ acpiphp_check_bridge(bridge);
|
|
|
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
|
|
|
+ ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
|
|
|
break;
|
|
|
|
|
|
case ACPI_NOTIFY_DEVICE_CHECK:
|
|
@@ -1267,8 +1165,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
|
|
|
case ACPI_NOTIFY_EJECT_REQUEST:
|
|
|
/* request device eject */
|
|
|
dbg("%s: Device eject notify on %s\n", __func__, objname);
|
|
|
- if ((bridge->type != BRIDGE_TYPE_HOST) &&
|
|
|
- (bridge->flags & BRIDGE_HAS_EJ0)) {
|
|
|
+ if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
|
|
|
struct acpiphp_slot *slot;
|
|
|
slot = bridge->func->slot;
|
|
|
if (!acpiphp_disable_slot(slot))
|
|
@@ -1296,7 +1193,6 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
-out:
|
|
|
acpi_scan_lock_release();
|
|
|
kfree(hp_work); /* allocated in handle_hotplug_event_bridge */
|
|
|
}
|
|
@@ -1320,8 +1216,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type,
|
|
|
* For now just re-add this work to the kacpi_hotplug_wq so we
|
|
|
* don't deadlock on hotplug actions.
|
|
|
*/
|
|
|
- alloc_acpiphp_hp_work(handle, type, context,
|
|
|
- _handle_hotplug_event_bridge);
|
|
|
+ alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge);
|
|
|
}
|
|
|
|
|
|
static void _handle_hotplug_event_func(struct work_struct *work)
|
|
@@ -1330,22 +1225,19 @@ static void _handle_hotplug_event_func(struct work_struct *work)
|
|
|
char objname[64];
|
|
|
struct acpi_buffer buffer = { .length = sizeof(objname),
|
|
|
.pointer = objname };
|
|
|
- struct acpiphp_hp_work *hp_work;
|
|
|
+ struct acpi_hp_work *hp_work;
|
|
|
acpi_handle handle;
|
|
|
u32 type;
|
|
|
- void *context;
|
|
|
|
|
|
- hp_work = container_of(work, struct acpiphp_hp_work, work);
|
|
|
+ hp_work = container_of(work, struct acpi_hp_work, work);
|
|
|
handle = hp_work->handle;
|
|
|
type = hp_work->type;
|
|
|
- context = hp_work->context;
|
|
|
-
|
|
|
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
|
|
|
-
|
|
|
- func = (struct acpiphp_func *)context;
|
|
|
+ func = (struct acpiphp_func *)hp_work->context;
|
|
|
|
|
|
acpi_scan_lock_acquire();
|
|
|
|
|
|
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
|
|
|
+
|
|
|
switch (type) {
|
|
|
case ACPI_NOTIFY_BUS_CHECK:
|
|
|
/* bus re-enumerate */
|
|
@@ -1399,23 +1291,7 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type,
|
|
|
* For now just re-add this work to the kacpi_hotplug_wq so we
|
|
|
* don't deadlock on hotplug actions.
|
|
|
*/
|
|
|
- alloc_acpiphp_hp_work(handle, type, context,
|
|
|
- _handle_hotplug_event_func);
|
|
|
-}
|
|
|
-
|
|
|
-static acpi_status
|
|
|
-find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
|
-{
|
|
|
- int *count = (int *)context;
|
|
|
-
|
|
|
- if (!acpi_is_root_bridge(handle))
|
|
|
- return AE_OK;
|
|
|
-
|
|
|
- (*count)++;
|
|
|
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
|
|
- handle_hotplug_event_bridge, NULL);
|
|
|
-
|
|
|
- return AE_OK ;
|
|
|
+ alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func);
|
|
|
}
|
|
|
|
|
|
static struct acpi_pci_driver acpi_pci_hp_driver = {
|
|
@@ -1428,15 +1304,7 @@ static struct acpi_pci_driver acpi_pci_hp_driver = {
|
|
|
*/
|
|
|
int __init acpiphp_glue_init(void)
|
|
|
{
|
|
|
- int num = 0;
|
|
|
-
|
|
|
- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
|
|
- ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL);
|
|
|
-
|
|
|
- if (num <= 0)
|
|
|
- return -1;
|
|
|
- else
|
|
|
- acpi_pci_register_driver(&acpi_pci_hp_driver);
|
|
|
+ acpi_pci_register_driver(&acpi_pci_hp_driver);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1452,28 +1320,6 @@ void acpiphp_glue_exit(void)
|
|
|
acpi_pci_unregister_driver(&acpi_pci_hp_driver);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-/**
|
|
|
- * acpiphp_get_num_slots - count number of slots in a system
|
|
|
- */
|
|
|
-int __init acpiphp_get_num_slots(void)
|
|
|
-{
|
|
|
- struct acpiphp_bridge *bridge;
|
|
|
- int num_slots = 0;
|
|
|
-
|
|
|
- list_for_each_entry(bridge, &bridge_list, list) {
|
|
|
- dbg("Bus %04x:%02x has %d slot%s\n",
|
|
|
- pci_domain_nr(bridge->pci_bus),
|
|
|
- bridge->pci_bus->number, bridge->nr_slots,
|
|
|
- bridge->nr_slots == 1 ? "" : "s");
|
|
|
- num_slots += bridge->nr_slots;
|
|
|
- }
|
|
|
-
|
|
|
- dbg("Total %d slots\n", num_slots);
|
|
|
- return num_slots;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
/**
|
|
|
* acpiphp_enable_slot - power on slot
|
|
|
* @slot: ACPI PHP slot
|