📄 acpiphp_glue.c
字号:
return; } bridge->pci_bus = bridge->pci_dev->subordinate; if (!bridge->pci_bus) { err("This is not a PCI-to-PCI bridge!\n"); kfree(bridge); return; } spin_lock_init(&bridge->res_lock); bridge->bus = bridge->pci_bus->number; bridge->sub = bridge->pci_bus->subordinate; /* * decode resources under this P2P bridge */ /* I/O resources */ pci_read_config_byte(bridge->pci_dev, PCI_IO_BASE, &tmp8); base = tmp8; pci_read_config_byte(bridge->pci_dev, PCI_IO_LIMIT, &tmp8); limit = tmp8; switch (base & PCI_IO_RANGE_TYPE_MASK) { case PCI_IO_RANGE_TYPE_16: base = (base << 8) & 0xf000; limit = ((limit << 8) & 0xf000) + 0xfff; bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); if (!bridge->io_head) { err("out of memory\n"); kfree(bridge); return; } dbg("16bit I/O range: %04x-%04x\n", (u32)bridge->io_head->base, (u32)(bridge->io_head->base + bridge->io_head->length - 1)); break; case PCI_IO_RANGE_TYPE_32: pci_read_config_word(bridge->pci_dev, PCI_IO_BASE_UPPER16, &tmp16); base = ((u32)tmp16 << 16) | ((base << 8) & 0xf000); pci_read_config_word(bridge->pci_dev, PCI_IO_LIMIT_UPPER16, &tmp16); limit = (((u32)tmp16 << 16) | ((limit << 8) & 0xf000)) + 0xfff; bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); if (!bridge->io_head) { err("out of memory\n"); kfree(bridge); return; } dbg("32bit I/O range: %08x-%08x\n", (u32)bridge->io_head->base, (u32)(bridge->io_head->base + bridge->io_head->length - 1)); break; case 0x0f: dbg("I/O space unsupported\n"); break; default: warn("Unknown I/O range type\n"); } /* Memory resources (mandatory for P2P bridge) */ pci_read_config_word(bridge->pci_dev, PCI_MEMORY_BASE, &tmp16); base = (tmp16 & 0xfff0) << 16; pci_read_config_word(bridge->pci_dev, PCI_MEMORY_LIMIT, &tmp16); limit = ((tmp16 & 0xfff0) << 16) | 0xfffff; bridge->mem_head = acpiphp_make_resource((u64)base, limit - base + 1); if (!bridge->mem_head) { err("out of memory\n"); kfree(bridge); return; } dbg("32bit Memory range: %08x-%08x\n", (u32)bridge->mem_head->base, (u32)(bridge->mem_head->base + bridge->mem_head->length-1)); /* Prefetchable Memory resources (optional) */ pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_BASE, &tmp16); base = tmp16; pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_LIMIT, &tmp16); limit = tmp16; switch (base & PCI_MEMORY_RANGE_TYPE_MASK) { case PCI_PREF_RANGE_TYPE_32: base = (base & 0xfff0) << 16; limit = ((limit & 0xfff0) << 16) | 0xfffff; bridge->p_mem_head = acpiphp_make_resource((u64)base, limit - base + 1); if (!bridge->p_mem_head) { err("out of memory\n"); kfree(bridge); return; } dbg("32bit Prefetchable memory range: %08x-%08x\n", (u32)bridge->p_mem_head->base, (u32)(bridge->p_mem_head->base + bridge->p_mem_head->length - 1)); break; case PCI_PREF_RANGE_TYPE_64: pci_read_config_dword(bridge->pci_dev, PCI_PREF_BASE_UPPER32, &base32u); pci_read_config_dword(bridge->pci_dev, PCI_PREF_LIMIT_UPPER32, &limit32u); base64 = ((u64)base32u << 32) | ((base & 0xfff0) << 16); limit64 = (((u64)limit32u << 32) | ((limit & 0xfff0) << 16)) + 0xfffff; bridge->p_mem_head = acpiphp_make_resource(base64, limit64 - base64 + 1); if (!bridge->p_mem_head) { err("out of memory\n"); kfree(bridge); return; } dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x\n", (u32)(bridge->p_mem_head->base >> 32), (u32)(bridge->p_mem_head->base & 0xffffffff), (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) >> 32), (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) & 0xffffffff)); break; case 0x0f: break; default: warn("Unknown prefetchale memory type\n"); } init_bridge_misc(bridge);}/* callback routine to find P2P bridges */static acpi_statusfind_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv){ acpi_status status; acpi_handle dummy_handle; unsigned long *segbus = context; unsigned long tmp; int seg, bus, device, function; struct pci_dev *dev; /* get PCI address */ seg = (*segbus >> 8) & 0xff; bus = *segbus & 0xff; status = acpi_get_handle(handle, "_ADR", &dummy_handle); if (ACPI_FAILURE(status)) return AE_OK; /* continue */ status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp); if (ACPI_FAILURE(status)) { dbg("%s: _ADR evaluation failure\n", __FUNCTION__); return AE_OK; } device = (tmp >> 16) & 0xffff; function = tmp & 0xffff; dev = pci_find_slot(bus, PCI_DEVFN(device, function)); if (!dev) return AE_OK; if (!dev->subordinate) return AE_OK; /* check if this bridge has ejectable slots */ if (detect_ejectable_slots(handle) > 0) { dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); add_p2p_bridge(handle, seg, bus, device, function); } return AE_OK;}/* find hot-pluggable slots, and then find P2P bridge */static int add_bridge(acpi_handle handle){ acpi_status status; unsigned long tmp; int seg, bus; acpi_handle dummy_handle; /* if the bridge doesn't have _STA, we assume it is always there */ status = acpi_get_handle(handle, "_STA", &dummy_handle); if (ACPI_SUCCESS(status)) { status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); if (ACPI_FAILURE(status)) { dbg("%s: _STA evaluation failure\n", __FUNCTION__); return 0; } if ((tmp & ACPI_STA_FUNCTIONING) == 0) /* don't register this object */ return 0; } /* get PCI segment number */ status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); seg = ACPI_SUCCESS(status) ? tmp : 0; /* get PCI bus number */ status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); if (ACPI_SUCCESS(status)) { bus = tmp; } else { warn("can't get bus number, assuming 0\n"); bus = 0; } /* check if this bridge has ejectable slots */ if (detect_ejectable_slots(handle) > 0) { dbg("found PCI host-bus bridge with hot-pluggable slots\n"); add_host_bridge(handle, seg, bus); return 0; } tmp = seg << 8 | bus; /* search P2P bridges under this host bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, find_p2p_bridge, &tmp, NULL); if (ACPI_FAILURE(status)) warn("find_p2p_bridge faied (error code = 0x%x)\n",status); return 0;}static void remove_bridge(acpi_handle handle){ /* No-op for now .. */}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", __FUNCTION__); status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); if (ACPI_FAILURE(status)) { warn("%s: _PS0 failed\n", __FUNCTION__); 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; struct acpi_object_list arg_list; union acpi_object arg; 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->pci_dev && (func->flags & FUNC_HAS_PS3)) { status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); if (ACPI_FAILURE(status)) { warn("%s: _PS3 failed\n", __FUNCTION__); retval = -1; goto err_exit; } else break; } } 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->pci_dev && (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", __FUNCTION__); 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;}/** * 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 enable_device(struct acpiphp_slot *slot){ u8 bus; struct pci_dev *dev; struct pci_bus *child; struct list_head *l; struct acpiphp_func *func; int retval = 0; int num; if (slot->flags & SLOT_ENABLED) goto err_exit; /* sanity check: dev should be NULL when hot-plugged in */ dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); if (dev) { /* This case shouldn't happen */ err("pci_dev structure already exists.\n"); retval = -1; goto err_exit; } /* allocate resources to device */ retval = acpiphp_configure_slot(slot); if (retval) goto err_exit; /* returned `dev' is the *first function* only! */ num = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0)); if (num) pci_bus_add_devices(slot->bridge->pci_bus); dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); if (!dev) { err("No new device found\n"); retval = -1; goto err_exit; } if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { pci_read_config_byte(dev, PCI_SECONDARY_BUS, &bus); child = (struct pci_bus*) pci_add_new_bus(dev->bus, dev, bus); pci_do_scan_bus(child); } /* associate pci_dev to our representation */ list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); func->pci_dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, func->function)); if (!func->pci_dev) continue; /* configure device */ retval = acpiphp_configure_function(func); if (retval) goto err_exit; } slot->flags |= SLOT_ENABLED; dbg("Available resources:\n"); acpiphp_dump_resource(slot->bridge); err_exit: return retval;}/** * disable_device - disable a 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->pci_dev) acpiphp_unconfigure_function(func); } slot->flags &= (~SLOT_ENABLED); err_exit: return retval;}/** * get_slot_status - get ACPI slot status * * if a slot has _STA for each function and if any one of them * returned non-zero status, return it * * 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 sta = 0; u32 dvid; struct list_head *l; struct acpiphp_func *func;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -