📄 acpiphp_glue.c
字号:
}static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle){ struct list_head *head; list_for_each(head, &bridge_list) { struct acpiphp_bridge *bridge = list_entry(head, struct acpiphp_bridge, list); if (bridge->handle == handle) return bridge; } return NULL;}static void cleanup_bridge(struct acpiphp_bridge *bridge){ struct list_head *list, *tmp; struct acpiphp_slot *slot; acpi_status status; acpi_handle handle = bridge->handle; 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 ((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; list_for_each_safe (list, tmp, &slot->funcs) { struct acpiphp_func *func; func = list_entry(list, struct acpiphp_func, sibling); if (is_dock_device(func->handle)) { unregister_hotplug_dock_device(func->handle); unregister_dock_notifier(&func->nb); } if (!(func->flags & FUNC_HAS_DCK)) { status = acpi_remove_notify_handler(func->handle, ACPI_SYSTEM_NOTIFY, handle_hotplug_event_func); if (ACPI_FAILURE(status)) err("failed to remove notify handler\n"); } pci_dev_put(func->pci_dev); list_del(list); kfree(func); } acpiphp_unregister_hotplug_slot(slot); list_del(&slot->funcs); kfree(slot); slot = next; } pci_dev_put(bridge->pci_dev); list_del(&bridge->list); kfree(bridge);}static acpi_statuscleanup_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); bridge = acpiphp_handle_to_bridge(handle); if (bridge) cleanup_bridge(bridge); return AE_OK;}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); /* * 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){ struct acpi_pci_id id; struct pci_bus *bus; struct pci_dev *dev; if (ACPI_FAILURE(acpi_get_pci_id(handle, &id))) return NULL; bus = pci_find_bus(id.segment, id.bus); if (!bus) return NULL; dev = pci_get_slot(bus, PCI_DEVFN(id.device, id.function)); if (!dev) return NULL; if ((dev->class != PCI_CLASS_SYSTEM_PIC_IOAPIC) && (dev->class != PCI_CLASS_SYSTEM_PIC_IOXAPIC)) { pci_dev_put(dev); return NULL; } return dev;}static int get_gsi_base(acpi_handle handle, u32 *gsi_base){ acpi_status status; int result = -1; unsigned long long gsb; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; void *table; status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb); if (ACPI_SUCCESS(status)) { *gsi_base = (u32)gsb; return 0; } status = acpi_evaluate_object(handle, "_MAT", NULL, &buffer); if (ACPI_FAILURE(status) || !buffer.length || !buffer.pointer) return -1; obj = buffer.pointer; if (obj->type != ACPI_TYPE_BUFFER) goto out; table = obj->buffer.pointer; switch (((struct acpi_subtable_header *)table)->type) { case ACPI_MADT_TYPE_IO_SAPIC: *gsi_base = ((struct acpi_madt_io_sapic *)table)->global_irq_base; result = 0; break; case ACPI_MADT_TYPE_IO_APIC: *gsi_base = ((struct acpi_madt_io_apic *)table)->global_irq_base; result = 0; break; default: break; } out: kfree(buffer.pointer); return result;}static acpi_statusioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv){ acpi_status status; unsigned long long sta; acpi_handle tmp; struct pci_dev *pdev; u32 gsi_base; u64 phys_addr; struct acpiphp_ioapic *ioapic; /* Evaluate _STA if present */ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL) return AE_CTRL_DEPTH; /* Scan only PCI bus scope */ status = acpi_get_handle(handle, "_HID", &tmp); if (ACPI_SUCCESS(status)) return AE_CTRL_DEPTH; if (get_gsi_base(handle, &gsi_base)) return AE_OK; ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL); if (!ioapic) return AE_NO_MEMORY; pdev = get_apic_pci_info(handle); if (!pdev) goto exit_kfree; if (pci_enable_device(pdev)) goto exit_pci_dev_put; pci_set_master(pdev); if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) goto exit_pci_disable_device; phys_addr = pci_resource_start(pdev, 0); if (acpi_register_ioapic(handle, phys_addr, gsi_base)) goto exit_pci_release_region; ioapic->gsi_base = gsi_base; ioapic->dev = pdev; spin_lock(&ioapic_list_lock); list_add_tail(&ioapic->list, &ioapic_list); spin_unlock(&ioapic_list_lock); return AE_OK; exit_pci_release_region: pci_release_region(pdev, 0); exit_pci_disable_device: pci_disable_device(pdev); exit_pci_dev_put: pci_dev_put(pdev); exit_kfree: kfree(ioapic); return AE_OK;}static acpi_statusioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv){ acpi_status status; unsigned long long sta; acpi_handle tmp; u32 gsi_base; struct acpiphp_ioapic *pos, *n, *ioapic = NULL; /* Evaluate _STA if present */ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL) return AE_CTRL_DEPTH; /* Scan only PCI bus scope */ status = acpi_get_handle(handle, "_HID", &tmp); if (ACPI_SUCCESS(status)) return AE_CTRL_DEPTH; if (get_gsi_base(handle, &gsi_base)) return AE_OK; acpi_unregister_ioapic(handle, gsi_base); spin_lock(&ioapic_list_lock); list_for_each_entry_safe(pos, n, &ioapic_list, list) { if (pos->gsi_base != gsi_base) continue; ioapic = pos; list_del(&ioapic->list); break; } spin_unlock(&ioapic_list_lock); if (!ioapic) return AE_OK; pci_release_region(ioapic->dev, 0); pci_disable_device(ioapic->dev); pci_dev_put(ioapic->dev); kfree(ioapic); return AE_OK;}static int acpiphp_configure_ioapics(acpi_handle handle){ ioapic_add(handle, 0, NULL, NULL); acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX, ioapic_add, NULL, NULL); return 0;}static int acpiphp_unconfigure_ioapics(acpi_handle handle){ ioapic_remove(handle, 0, NULL, NULL); acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX, ioapic_remove, NULL, NULL); return 0;}static int power_on_slot(struct acpiphp_slot *slot){ acpi_status status; struct acpiphp_func *func; struct list_head *l; int retval = 0; /* if already enabled, just skip */ if (slot->flags & SLOT_POWEREDON) goto err_exit; list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); if (func->flags & FUNC_HAS_PS0) { dbg("%s: executing _PS0\n", __func__); status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); if (ACPI_FAILURE(status)) { warn("%s: _PS0 failed\n", __func__); retval = -1; goto err_exit; } else break; } } /* TBD: evaluate _STA to check if the slot is enabled */ slot->flags |= SLOT_POWEREDON; err_exit: return retval;}static int power_off_slot(struct acpiphp_slot *slot){ acpi_status status; struct acpiphp_func *func; struct list_head *l; int retval = 0; /* if already disabled, just skip */ if ((slot->flags & SLOT_POWEREDON) == 0) goto err_exit; list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); if (func->flags & FUNC_HAS_PS3) { status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); if (ACPI_FAILURE(status)) { warn("%s: _PS3 failed\n", __func__); retval = -1; goto err_exit; } else break; } } /* TBD: evaluate _STA to check if the slot is disabled */ slot->flags &= (~SLOT_POWEREDON); err_exit: return retval;}/** * acpiphp_max_busnr - return the highest reserved bus number under the given bus. * @bus: bus to start search with */static unsigned char acpiphp_max_busnr(struct pci_bus *bus){ struct list_head *tmp; unsigned char max, n; /* * pci_bus_max_busnr will return the highest * reserved busnr for all these children. * that is equivalent to the bus->subordinate * value. We don't want to use the parent's * bus->subordinate value because it could have * padding in it. */ max = bus->secondary; list_for_each(tmp, &bus->children) { n = pci_bus_max_busnr(pci_bus_b(tmp)); if (n > max) max = n; } return max;}/** * acpiphp_bus_add - add a new bus to acpi subsystem * @func: acpiphp_func of the bridge */static int acpiphp_bus_add(struct acpiphp_func *func){ acpi_handle phandle; struct acpi_device *device, *pdevice; int ret_val; acpi_get_parent(func->handle, &phandle); if (acpi_bus_get_device(phandle, &pdevice)) { dbg("no parent device, assuming NULL\n"); pdevice = NULL; } if (!acpi_bus_get_device(func->handle, &device)) { dbg("bus exists... trim\n"); /* this shouldn't be in here, so remove * the bus then re-add it... */ ret_val = acpi_bus_trim(device, 1); dbg("acpi_bus_trim return %x\n", ret_val); } ret_val = acpi_bus_add(&device, pdevice, func->handle, ACPI_BUS_TYPE_DEVICE); if (ret_val) { dbg("error adding bus, %x\n", -ret_val); goto acpiphp_bus_add_out; } /* * try to start anyway. We could have failed to add * simply because this bus had previously been added * on another add. Don't bother with the return value * we just keep going. */ ret_val = acpi_bus_start(device);acpiphp_bus_add_out: return ret_val;}/** * acpiphp_bus_trim - trim a bus from acpi subsystem * @handle: handle to acpi namespace */static 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 * @slot: slot to be enabled * * This function should be called per *physical slot*, * not per each slot object in ACPI namespace. */static int __ref enable_device(struct acpiphp_slot *slot){ struct pci_dev *dev; struct pci_bus *bus = slot->bridge->pci_bus; struct list_head *l; struct acpiphp_func *func; int retval = 0; int num, max, pass; acpi_status status; if (slot->flags & SLOT_ENABLED) goto err_exit; /* sanity check: dev should be NULL when hot-plugged in */ dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); if (dev) { /* This case shouldn't happen */ err("pci_dev structure already exists.\n"); pci_dev_put(dev); retval = -1; goto err_exit; } num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0)); if (num == 0) { err("No new device found\n"); retval = -1; goto err_exit; } max = acpiphp_max_busnr(bus); for (pass = 0; pass < 2; pass++) { list_for_each_entry(dev, &bus->devices, bus_list) { if (PCI_SLOT(dev->devfn) != slot->device) continue; 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) pci_bus_size_bridges(dev->subordinate); } } } 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); acpiphp_set_hpp_values(slot->bridge->handle, bus); list_for_each_entry(func, &slot->funcs, sibling) acpiphp_configure_ioapics(func->handle); pci_enable_bridges(bus); pci_bus_add_devices(bus); /* associate pci_dev to our representation */ list_for_each (l, &slot->funcs) { 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; err_exit: return retval;}static void disable_bridges(struct pci_bus *bus){ struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->subordinate) { disable_bridges(dev->subordinate); pci_disable_device(dev); } }}/** * disable_device - disable a slot * @slot: ACPI PHP slot */static int disable_device(struct acpiphp_slot *slot){ int retval = 0; struct acpiphp_func *func; struct list_head *l; /* is this slot already disabled? */ if (!(slot->flags & SLOT_ENABLED)) goto err_exit; 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; } if (func->pci_dev) { pci_stop_bus_device(func->pci_dev); if (func->pci_dev->subordinate) { disable_bridges(func->pci_dev->subordinate); pci_disable_device(func->pci_dev); } } } list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); acpiphp_unconfigure_ioapics(func->handle); acpiphp_bus_trim(func->handle); /* try to remove anyway. * acpiphp_bus_add might have been failed */ if (!func->pci_dev) continue; pci_remove_bus_device(func->pci_dev); pci_dev_put(func->pci_dev); func->pci_dev = NULL; } slot->flags &= (~SLOT_ENABLED); err_exit: return retval;}/** * get_slot_status - get ACPI slot status * @slot: ACPI PHP slot * * If a slot has _STA for each function and if any one of them * returned non-zero status, return it. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -