📄 acpiphp_glue.c
字号:
* If a slot doesn't have _STA and if any one of its functions' * configuration space is configured, return 0x0f as a _STA. * * Otherwise return 0. */static unsigned int get_slot_status(struct acpiphp_slot *slot){ acpi_status status; unsigned long long sta = 0; u32 dvid; struct list_head *l; struct acpiphp_func *func; list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); if (func->flags & FUNC_HAS_STA) { status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta); if (ACPI_SUCCESS(status) && sta) break; } else { pci_bus_read_config_dword(slot->bridge->pci_bus, PCI_DEVFN(slot->device, func->function), PCI_VENDOR_ID, &dvid); if (dvid != 0xffffffff) { sta = ACPI_STA_ALL; break; } } } return (unsigned int)sta;}/** * acpiphp_eject_slot - physically eject the slot * @slot: ACPI PHP slot */int acpiphp_eject_slot(struct acpiphp_slot *slot){ acpi_status status; struct acpiphp_func *func; struct list_head *l; struct acpi_object_list arg_list; union acpi_object arg; list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); /* We don't want to call _EJ0 on non-existing functions. */ if ((func->flags & FUNC_HAS_EJ0)) { /* _EJ0 method take one argument */ arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; arg.integer.value = 1; status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) { warn("%s: _EJ0 failed\n", __func__); return -1; } else break; } } return 0;}/** * acpiphp_check_bridge - re-enumerate devices * @bridge: where to begin re-enumeration * * Iterate over all slots under this bridge and make sure that if a * card is present they are enabled, and if not they are disabled. */static int acpiphp_check_bridge(struct acpiphp_bridge *bridge){ struct acpiphp_slot *slot; int retval = 0; int enabled, disabled; enabled = disabled = 0; for (slot = bridge->slots; slot; slot = slot->next) { unsigned int status = get_slot_status(slot); if (slot->flags & SLOT_ENABLED) { if (status == ACPI_STA_ALL) continue; retval = acpiphp_disable_slot(slot); if (retval) { err("Error occurred in disabling\n"); goto err_exit; } else { acpiphp_eject_slot(slot); } disabled++; } else { if (status != ACPI_STA_ALL) continue; retval = acpiphp_enable_slot(slot); if (retval) { err("Error occurred in enabling\n"); goto err_exit; } enabled++; } } dbg("%s: %d enabled, %d disabled\n", __func__, enabled, disabled); err_exit: return retval;}static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge){ u16 pci_cmd, pci_bctl; struct pci_dev *cdev; /* Program hpp values for this device */ if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) return; if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) return; pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, bridge->hpp.t0->cache_line_size); pci_write_config_byte(dev, PCI_LATENCY_TIMER, bridge->hpp.t0->latency_timer); pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); if (bridge->hpp.t0->enable_serr) pci_cmd |= PCI_COMMAND_SERR; else pci_cmd &= ~PCI_COMMAND_SERR; if (bridge->hpp.t0->enable_perr) pci_cmd |= PCI_COMMAND_PARITY; else pci_cmd &= ~PCI_COMMAND_PARITY; pci_write_config_word(dev, PCI_COMMAND, pci_cmd); /* 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.t0->latency_timer); pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); if (bridge->hpp.t0->enable_serr) pci_bctl |= PCI_BRIDGE_CTL_SERR; else pci_bctl &= ~PCI_BRIDGE_CTL_SERR; if (bridge->hpp.t0->enable_perr) pci_bctl |= PCI_BRIDGE_CTL_PARITY; else pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); if (dev->subordinate) { list_for_each_entry(cdev, &dev->subordinate->devices, bus_list) program_hpp(cdev, bridge); } }}static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus){ struct acpiphp_bridge bridge; struct pci_dev *dev; 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) program_hpp(dev, &bridge);}/* * Remove devices for which we could not assign resources, call * arch specific code to fix-up the bus */static void acpiphp_sanitize_bus(struct pci_bus *bus){ struct pci_dev *dev; int i; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; list_for_each_entry(dev, &bus->devices, bus_list) { for (i=0; i<PCI_BRIDGE_RESOURCES; i++) { struct resource *res = &dev->resource[i]; if ((res->flags & type_mask) && !res->start && res->end) { /* Could not assign a required resources * for this device, remove it */ pci_remove_bus_device(dev); break; } } }}/* Program resources in newly inserted bridge */static int acpiphp_configure_bridge (acpi_handle handle){ struct acpi_pci_id pci_id; struct pci_bus *bus; if (ACPI_FAILURE(acpi_get_pci_id(handle, &pci_id))) { err("cannot get PCI domain and bus number for bridge\n"); return -EINVAL; } bus = pci_find_bus(pci_id.segment, pci_id.bus); if (!bus) { err("cannot find bus %d:%d\n", pci_id.segment, pci_id.bus); return -EINVAL; } pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); acpiphp_sanitize_bus(bus); acpiphp_set_hpp_values(handle, bus); pci_enable_bridges(bus); acpiphp_configure_ioapics(handle); return 0;}static void handle_bridge_insertion(acpi_handle handle, u32 type){ struct acpi_device *device, *pdevice; acpi_handle phandle; if ((type != ACPI_NOTIFY_BUS_CHECK) && (type != ACPI_NOTIFY_DEVICE_CHECK)) { err("unexpected notification type %d\n", type); return; } acpi_get_parent(handle, &phandle); if (acpi_bus_get_device(phandle, &pdevice)) { dbg("no parent device, assuming NULL\n"); pdevice = NULL; } if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) { err("cannot add bridge to acpi list\n"); return; } if (!acpiphp_configure_bridge(handle) && !acpi_bus_start(device)) add_bridge(handle); else err("cannot configure and start bridge\n");}/* * ACPI event handlers */static acpi_statuscount_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_statuscheck_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv){ struct acpiphp_bridge *bridge; char objname[64]; struct acpi_buffer buffer = { .length = sizeof(objname), .pointer = objname }; bridge = acpiphp_handle_to_bridge(handle); if (bridge) { acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); dbg("%s: re-enumerating slots under %s\n", __func__, objname); acpiphp_check_bridge(bridge); } return AE_OK ;}/** * handle_hotplug_event_bridge - handle ACPI event on bridges * @handle: Notify()'ed acpi_handle * @type: Notify code * @context: pointer to acpiphp_bridge structure * * Handles ACPI event notification on {host,p2p} bridges. */static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context){ 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; if (acpi_bus_get_device(handle, &device)) { /* This bridge must have just been physically inserted */ handle_bridge_insertion(handle, type); return; } 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, &num_sub_bridges, NULL); } if (!bridge && !num_sub_bridges) { err("cannot get bridge info\n"); return; } 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); break; case ACPI_NOTIFY_DEVICE_CHECK: /* device check */ dbg("%s: Device check notify on %s\n", __func__, objname); acpiphp_check_bridge(bridge); break; case ACPI_NOTIFY_DEVICE_WAKE: /* wake event */ dbg("%s: Device wake notify on %s\n", __func__, objname); break; 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)) { struct acpiphp_slot *slot; slot = bridge->func->slot; if (!acpiphp_disable_slot(slot)) acpiphp_eject_slot(slot); } break; case ACPI_NOTIFY_FREQUENCY_MISMATCH: printk(KERN_ERR "Device %s cannot be configured due" " to a frequency mismatch\n", objname); break; case ACPI_NOTIFY_BUS_MODE_MISMATCH: printk(KERN_ERR "Device %s cannot be configured due" " to a bus mode mismatch\n", objname); break; case ACPI_NOTIFY_POWER_FAULT: printk(KERN_ERR "Device %s has suffered a power fault\n", objname); break; default: warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); break; }}/** * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) * @handle: Notify()'ed acpi_handle * @type: Notify code * @context: pointer to acpiphp_func structure * * Handles ACPI event notification on slots. */static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context){ struct acpiphp_func *func; char objname[64]; struct acpi_buffer buffer = { .length = sizeof(objname), .pointer = objname }; acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); func = (struct acpiphp_func *)context; switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* bus re-enumerate */ dbg("%s: Bus check notify on %s\n", __func__, objname); acpiphp_enable_slot(func->slot); break; case ACPI_NOTIFY_DEVICE_CHECK: /* device check : re-enumerate from parent bus */ dbg("%s: Device check notify on %s\n", __func__, objname); acpiphp_check_bridge(func->slot->bridge); break; case ACPI_NOTIFY_DEVICE_WAKE: /* wake event */ dbg("%s: Device wake notify on %s\n", __func__, objname); break; case ACPI_NOTIFY_EJECT_REQUEST: /* request device eject */ dbg("%s: Device eject notify on %s\n", __func__, objname); if (!(acpiphp_disable_slot(func->slot))) acpiphp_eject_slot(func->slot); break; default: warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); break; }}static acpi_statusfind_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv){ int *count = (int *)context; if (acpi_root_bridge(handle)) { acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, handle_hotplug_event_bridge, NULL); (*count)++; } return AE_OK ;}static struct acpi_pci_driver acpi_pci_hp_driver = { .add = add_bridge, .remove = remove_bridge,};/** * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures */int __init acpiphp_glue_init(void){ int num = 0; acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_root_bridges, &num, NULL); if (num <= 0) return -1; else acpi_pci_register_driver(&acpi_pci_hp_driver); return 0;}/** * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures * * This function frees all data allocated in acpiphp_glue_init(). */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;}#if 0/** * acpiphp_for_each_slot - call function for each slot * @fn: callback function * @data: context to be passed to callback function */static int acpiphp_for_each_slot(acpiphp_callback fn, void *data){ struct list_head *node; struct acpiphp_bridge *bridge; struct acpiphp_slot *slot; int retval = 0; list_for_each (node, &bridge_list) { bridge = (struct acpiphp_bridge *)node; for (slot = bridge->slots; slot; slot = slot->next) { retval = fn(slot, data); if (!retval) goto err_exit; } } err_exit: return retval;}#endif/** * acpiphp_enable_slot - power on slot * @slot: ACPI PHP slot */int acpiphp_enable_slot(struct acpiphp_slot *slot){ int retval; mutex_lock(&slot->crit_sect); /* wake up all functions */ retval = power_on_slot(slot); if (retval) goto err_exit; 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", __func__); power_off_slot(slot); } err_exit: mutex_unlock(&slot->crit_sect); return retval;}/** * acpiphp_disable_slot - power off slot * @slot: ACPI PHP slot */int acpiphp_disable_slot(struct acpiphp_slot *slot){ int retval = 0; mutex_lock(&slot->crit_sect); /* unconfigure all functions */ retval = disable_device(slot); if (retval) goto err_exit; /* power off all functions */ retval = power_off_slot(slot); if (retval) goto err_exit; err_exit: mutex_unlock(&slot->crit_sect); return retval;}/* * slot enabled: 1 * slot disabled: 0 */u8 acpiphp_get_power_status(struct acpiphp_slot *slot){ return (slot->flags & SLOT_POWEREDON);}/* * latch open: 1 * latch closed: 0 */u8 acpiphp_get_latch_status(struct acpiphp_slot *slot){ unsigned int sta; sta = get_slot_status(slot); return (sta & ACPI_STA_SHOW_IN_UI) ? 0 : 1;}/* * adapter presence : 1 * absence : 0 */u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot){ unsigned int sta; sta = get_slot_status(slot); return (sta == 0) ? 0 : 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -