|
@@ -286,13 +286,19 @@ static void decode_hpp(struct acpiphp_bridge *bridge)
|
|
|
{
|
|
|
acpi_status status;
|
|
|
|
|
|
- status = acpi_get_hp_params_from_firmware(bridge->pci_dev, &bridge->hpp);
|
|
|
- if (ACPI_FAILURE(status)) {
|
|
|
+ status = acpi_get_hp_params_from_firmware(bridge->pci_bus, &bridge->hpp);
|
|
|
+ if (ACPI_FAILURE(status) ||
|
|
|
+ !bridge->hpp.t0 || (bridge->hpp.t0->revision > 1)) {
|
|
|
/* use default numbers */
|
|
|
- bridge->hpp.cache_line_size = 0x10;
|
|
|
- bridge->hpp.latency_timer = 0x40;
|
|
|
- bridge->hpp.enable_serr = 0;
|
|
|
- bridge->hpp.enable_perr = 0;
|
|
|
+ printk(KERN_WARNING
|
|
|
+ "%s: Could not get hotplug parameters. Use defaults\n",
|
|
|
+ __FUNCTION__);
|
|
|
+ bridge->hpp.t0 = &bridge->hpp.type0_data;
|
|
|
+ bridge->hpp.t0->revision = 0;
|
|
|
+ bridge->hpp.t0->cache_line_size = 0x10;
|
|
|
+ bridge->hpp.t0->latency_timer = 0x40;
|
|
|
+ bridge->hpp.t0->enable_serr = 0;
|
|
|
+ bridge->hpp.t0->enable_perr = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -319,6 +325,13 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
|
|
|
|
|
|
/* install notify handler */
|
|
|
if (bridge->type != BRIDGE_TYPE_HOST) {
|
|
|
+ if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
|
|
|
+ status = acpi_remove_notify_handler(bridge->func->handle,
|
|
|
+ ACPI_SYSTEM_NOTIFY,
|
|
|
+ handle_hotplug_event_func);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ err("failed to remove notify handler\n");
|
|
|
+ }
|
|
|
status = acpi_install_notify_handler(bridge->handle,
|
|
|
ACPI_SYSTEM_NOTIFY,
|
|
|
handle_hotplug_event_bridge,
|
|
@@ -331,6 +344,66 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
|
|
|
}
|
|
|
|
|
|
|
|
|
+/* find acpiphp_func from acpiphp_bridge */
|
|
|
+static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle)
|
|
|
+{
|
|
|
+ struct list_head *node, *l;
|
|
|
+ struct acpiphp_bridge *bridge;
|
|
|
+ struct acpiphp_slot *slot;
|
|
|
+ struct acpiphp_func *func;
|
|
|
+
|
|
|
+ list_for_each(node, &bridge_list) {
|
|
|
+ bridge = list_entry(node, struct acpiphp_bridge, list);
|
|
|
+ for (slot = bridge->slots; slot; slot = slot->next) {
|
|
|
+ list_for_each(l, &slot->funcs) {
|
|
|
+ func = list_entry(l, struct acpiphp_func,
|
|
|
+ sibling);
|
|
|
+ if (func->handle == handle)
|
|
|
+ return func;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)
|
|
|
+{
|
|
|
+ acpi_handle dummy_handle;
|
|
|
+
|
|
|
+ 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)))
|
|
|
+ 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 */
|
|
|
+ func = acpiphp_bridge_handle_to_function(bridge->handle);
|
|
|
+ if (!func)
|
|
|
+ return;
|
|
|
+ bridge->func = func;
|
|
|
+ func->bridge = bridge;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* allocate and initialize host bridge data structure */
|
|
|
static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus)
|
|
|
{
|
|
@@ -364,6 +437,7 @@ static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev)
|
|
|
|
|
|
bridge->type = BRIDGE_TYPE_P2P;
|
|
|
bridge->handle = handle;
|
|
|
+ config_p2p_bridge_flags(bridge);
|
|
|
|
|
|
bridge->pci_dev = pci_dev_get(pci_dev);
|
|
|
bridge->pci_bus = pci_dev->subordinate;
|
|
@@ -423,7 +497,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
|
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
|
|
|
find_p2p_bridge, dev->subordinate, NULL);
|
|
|
if (ACPI_FAILURE(status))
|
|
|
- warn("find_p2p_bridge faied (error code = 0x%x)\n", status);
|
|
|
+ warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
|
|
|
|
|
|
out:
|
|
|
pci_dev_put(dev);
|
|
@@ -478,7 +552,6 @@ static int add_bridge(acpi_handle handle)
|
|
|
if (detect_ejectable_slots(handle) > 0) {
|
|
|
dbg("found PCI host-bus bridge with hot-pluggable slots\n");
|
|
|
add_host_bridge(handle, pci_bus);
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
/* search P2P bridges under this host bridge */
|
|
@@ -486,7 +559,7 @@ static int add_bridge(acpi_handle handle)
|
|
|
find_p2p_bridge, pci_bus, NULL);
|
|
|
|
|
|
if (ACPI_FAILURE(status))
|
|
|
- warn("find_p2p_bridge faied (error code = 0x%x)\n",status);
|
|
|
+ warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -516,6 +589,16 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
|
|
|
if (ACPI_FAILURE(status))
|
|
|
err("failed to remove notify handler\n");
|
|
|
|
|
|
+ if ((bridge->type != BRIDGE_TYPE_HOST) &&
|
|
|
+ ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) {
|
|
|
+ status = acpi_install_notify_handler(bridge->func->handle,
|
|
|
+ ACPI_SYSTEM_NOTIFY,
|
|
|
+ handle_hotplug_event_func,
|
|
|
+ bridge->func);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ err("failed to install interrupt notify handler\n");
|
|
|
+ }
|
|
|
+
|
|
|
slot = bridge->slots;
|
|
|
while (slot) {
|
|
|
struct acpiphp_slot *next = slot->next;
|
|
@@ -549,6 +632,11 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
|
{
|
|
|
struct acpiphp_bridge *bridge;
|
|
|
|
|
|
+ /* cleanup p2p bridges under this P2P bridge
|
|
|
+ in a depth-first manner */
|
|
|
+ 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);
|
|
@@ -559,15 +647,14 @@ static void remove_bridge(acpi_handle handle)
|
|
|
{
|
|
|
struct acpiphp_bridge *bridge;
|
|
|
|
|
|
+ /* cleanup p2p bridges under this host bridge
|
|
|
+ in a depth-first manner */
|
|
|
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
|
|
|
+ (u32)1, cleanup_p2p_bridge, NULL, NULL);
|
|
|
+
|
|
|
bridge = acpiphp_handle_to_bridge(handle);
|
|
|
- if (bridge) {
|
|
|
+ if (bridge)
|
|
|
cleanup_bridge(bridge);
|
|
|
- } else {
|
|
|
- /* clean-up p2p bridges under this host bridge */
|
|
|
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
|
|
|
- ACPI_UINT32_MAX, cleanup_p2p_bridge,
|
|
|
- NULL, NULL);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
static struct pci_dev * get_apic_pci_info(acpi_handle handle)
|
|
@@ -634,7 +721,7 @@ static int get_gsi_base(acpi_handle handle, u32 *gsi_base)
|
|
|
break;
|
|
|
}
|
|
|
out:
|
|
|
- acpi_os_free(buffer.pointer);
|
|
|
+ kfree(buffer.pointer);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
@@ -797,36 +884,6 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
-/**
|
|
|
- * get_func - get a pointer to acpiphp_func given a slot, device
|
|
|
- * @slot: slot to search
|
|
|
- * @dev: pci_dev struct to match.
|
|
|
- *
|
|
|
- * This function will increase the reference count of pci_dev,
|
|
|
- * so callers should call pci_dev_put when complete.
|
|
|
- *
|
|
|
- */
|
|
|
-static struct acpiphp_func *
|
|
|
-get_func(struct acpiphp_slot *slot, struct pci_dev *dev)
|
|
|
-{
|
|
|
- struct acpiphp_func *func = NULL;
|
|
|
- struct pci_bus *bus = slot->bridge->pci_bus;
|
|
|
- struct pci_dev *pdev;
|
|
|
-
|
|
|
- list_for_each_entry(func, &slot->funcs, sibling) {
|
|
|
- pdev = pci_get_slot(bus, PCI_DEVFN(slot->device,
|
|
|
- func->function));
|
|
|
- if (pdev) {
|
|
|
- if (pdev == dev)
|
|
|
- break;
|
|
|
- pci_dev_put(pdev);
|
|
|
- }
|
|
|
- }
|
|
|
- return func;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
/**
|
|
|
* acpiphp_bus_add - add a new bus to acpi subsystem
|
|
|
* @func: acpiphp_func of the bridge
|
|
@@ -872,6 +929,28 @@ acpiphp_bus_add_out:
|
|
|
}
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * acpiphp_bus_trim - trim a bus from acpi subsystem
|
|
|
+ * @handle: handle to acpi namespace
|
|
|
+ *
|
|
|
+ */
|
|
|
+int acpiphp_bus_trim(acpi_handle handle)
|
|
|
+{
|
|
|
+ struct acpi_device *device;
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ retval = acpi_bus_get_device(handle, &device);
|
|
|
+ if (retval) {
|
|
|
+ dbg("acpi_device not found\n");
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval = acpi_bus_trim(device, 1);
|
|
|
+ if (retval)
|
|
|
+ err("cannot remove from acpi list\n");
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
* enable_device - enable, configure a slot
|
|
@@ -889,6 +968,7 @@ static int enable_device(struct acpiphp_slot *slot)
|
|
|
struct acpiphp_func *func;
|
|
|
int retval = 0;
|
|
|
int num, max, pass;
|
|
|
+ acpi_status status;
|
|
|
|
|
|
if (slot->flags & SLOT_ENABLED)
|
|
|
goto err_exit;
|
|
@@ -918,19 +998,17 @@ static int 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)
|
|
|
pci_bus_size_bridges(dev->subordinate);
|
|
|
- func = get_func(slot, dev);
|
|
|
- if (func) {
|
|
|
- acpiphp_bus_add(func);
|
|
|
- /* side effect of get_func */
|
|
|
- pci_dev_put(dev);
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ list_for_each (l, &slot->funcs) {
|
|
|
+ func = list_entry(l, struct acpiphp_func, sibling);
|
|
|
+ acpiphp_bus_add(func);
|
|
|
+ }
|
|
|
+
|
|
|
pci_bus_assign_resources(bus);
|
|
|
acpiphp_sanitize_bus(bus);
|
|
|
pci_enable_bridges(bus);
|
|
@@ -943,6 +1021,17 @@ static int enable_device(struct acpiphp_slot *slot)
|
|
|
func = list_entry(l, struct acpiphp_func, sibling);
|
|
|
func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
|
|
|
func->function));
|
|
|
+ if (!func->pci_dev)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (func->pci_dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
|
|
|
+ func->pci_dev->hdr_type != PCI_HEADER_TYPE_CARDBUS)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ status = find_p2p_bridge(func->handle, (u32)1, bus, NULL);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ warn("find_p2p_bridge failed (error code = 0x%x)\n",
|
|
|
+ status);
|
|
|
}
|
|
|
|
|
|
slot->flags |= SLOT_ENABLED;
|
|
@@ -967,6 +1056,18 @@ static int disable_device(struct acpiphp_slot *slot)
|
|
|
|
|
|
list_for_each (l, &slot->funcs) {
|
|
|
func = list_entry(l, struct acpiphp_func, sibling);
|
|
|
+
|
|
|
+ if (func->bridge) {
|
|
|
+ /* cleanup p2p bridges under this P2P bridge */
|
|
|
+ cleanup_p2p_bridge(func->bridge->handle,
|
|
|
+ (u32)1, NULL, NULL);
|
|
|
+ func->bridge = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ acpiphp_bus_trim(func->handle);
|
|
|
+ /* try to remove anyway.
|
|
|
+ * acpiphp_bus_add might have been failed */
|
|
|
+
|
|
|
if (!func->pci_dev)
|
|
|
continue;
|
|
|
|
|
@@ -1111,16 +1212,17 @@ static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge)
|
|
|
(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
|
|
|
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
|
|
|
return;
|
|
|
+
|
|
|
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
|
|
|
- bridge->hpp.cache_line_size);
|
|
|
+ bridge->hpp.t0->cache_line_size);
|
|
|
pci_write_config_byte(dev, PCI_LATENCY_TIMER,
|
|
|
- bridge->hpp.latency_timer);
|
|
|
+ bridge->hpp.t0->latency_timer);
|
|
|
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
|
|
|
- if (bridge->hpp.enable_serr)
|
|
|
+ if (bridge->hpp.t0->enable_serr)
|
|
|
pci_cmd |= PCI_COMMAND_SERR;
|
|
|
else
|
|
|
pci_cmd &= ~PCI_COMMAND_SERR;
|
|
|
- if (bridge->hpp.enable_perr)
|
|
|
+ if (bridge->hpp.t0->enable_perr)
|
|
|
pci_cmd |= PCI_COMMAND_PARITY;
|
|
|
else
|
|
|
pci_cmd &= ~PCI_COMMAND_PARITY;
|
|
@@ -1129,13 +1231,13 @@ static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge)
|
|
|
/* Program bridge control value and child devices */
|
|
|
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
|
|
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
|
|
|
- bridge->hpp.latency_timer);
|
|
|
+ bridge->hpp.t0->latency_timer);
|
|
|
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
|
|
|
- if (bridge->hpp.enable_serr)
|
|
|
+ if (bridge->hpp.t0->enable_serr)
|
|
|
pci_bctl |= PCI_BRIDGE_CTL_SERR;
|
|
|
else
|
|
|
pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
|
|
|
- if (bridge->hpp.enable_perr)
|
|
|
+ if (bridge->hpp.t0->enable_perr)
|
|
|
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
|
|
|
else
|
|
|
pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
|
|
@@ -1155,6 +1257,7 @@ static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus)
|
|
|
|
|
|
memset(&bridge, 0, sizeof(bridge));
|
|
|
bridge.handle = handle;
|
|
|
+ bridge.pci_bus = bus;
|
|
|
bridge.pci_dev = bus->self;
|
|
|
decode_hpp(&bridge);
|
|
|
list_for_each_entry(dev, &bus->devices, bus_list)
|
|
@@ -1297,6 +1400,13 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont
|
|
|
case ACPI_NOTIFY_EJECT_REQUEST:
|
|
|
/* request device eject */
|
|
|
dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
|
|
|
+ if ((bridge->type != BRIDGE_TYPE_HOST) &&
|
|
|
+ (bridge->flags & BRIDGE_HAS_EJ0)) {
|
|
|
+ struct acpiphp_slot *slot;
|
|
|
+ slot = bridge->func->slot;
|
|
|
+ if (!acpiphp_disable_slot(slot))
|
|
|
+ acpiphp_eject_slot(slot);
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
|
|
@@ -1490,9 +1600,15 @@ int acpiphp_enable_slot(struct acpiphp_slot *slot)
|
|
|
if (retval)
|
|
|
goto err_exit;
|
|
|
|
|
|
- if (get_slot_status(slot) == ACPI_STA_ALL)
|
|
|
+ if (get_slot_status(slot) == ACPI_STA_ALL) {
|
|
|
/* configure all functions */
|
|
|
retval = enable_device(slot);
|
|
|
+ if (retval)
|
|
|
+ power_off_slot(slot);
|
|
|
+ } else {
|
|
|
+ dbg("%s: Slot status is not ACPI_STA_ALL\n", __FUNCTION__);
|
|
|
+ power_off_slot(slot);
|
|
|
+ }
|
|
|
|
|
|
err_exit:
|
|
|
mutex_unlock(&slot->crit_sect);
|