pci_link.c
来自「linux 内核源代码」· C语言 代码 · 共 956 行 · 第 1/2 页
C
956 行
/* * pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 34 $) * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * TBD: * 1. Support more than one IRQ resource entry per link device (index). * 2. Implement start/stop mechanism and use ACPI Bus Driver facilities * for IRQ management (e.g. start()->_SRS). */#include <linux/sysdev.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <linux/pm.h>#include <linux/pci.h>#include <linux/mutex.h>#include <acpi/acpi_bus.h>#include <acpi/acpi_drivers.h>#define _COMPONENT ACPI_PCI_COMPONENTACPI_MODULE_NAME("pci_link");#define ACPI_PCI_LINK_CLASS "pci_irq_routing"#define ACPI_PCI_LINK_DEVICE_NAME "PCI Interrupt Link"#define ACPI_PCI_LINK_FILE_INFO "info"#define ACPI_PCI_LINK_FILE_STATUS "state"#define ACPI_PCI_LINK_MAX_POSSIBLE 16static int acpi_pci_link_add(struct acpi_device *device);static int acpi_pci_link_remove(struct acpi_device *device, int type);static struct acpi_device_id link_device_ids[] = { {"PNP0C0F", 0}, {"", 0},};MODULE_DEVICE_TABLE(acpi, link_device_ids);static struct acpi_driver acpi_pci_link_driver = { .name = "pci_link", .class = ACPI_PCI_LINK_CLASS, .ids = link_device_ids, .ops = { .add = acpi_pci_link_add, .remove = acpi_pci_link_remove, },};/* * If a link is initialized, we never change its active and initialized * later even the link is disable. Instead, we just repick the active irq */struct acpi_pci_link_irq { u8 active; /* Current IRQ */ u8 triggering; /* All IRQs */ u8 polarity; /* All IRQs */ u8 resource_type; u8 possible_count; u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; u8 initialized:1; u8 reserved:7;};struct acpi_pci_link { struct list_head node; struct acpi_device *device; struct acpi_pci_link_irq irq; int refcnt;};static struct { int count; struct list_head entries;} acpi_link;DEFINE_MUTEX(acpi_link_lock);/* -------------------------------------------------------------------------- PCI Link Device Management -------------------------------------------------------------------------- *//* * set context (link) possible list from resource list */static acpi_statusacpi_pci_link_check_possible(struct acpi_resource *resource, void *context){ struct acpi_pci_link *link = context; u32 i = 0; switch (resource->type) { case ACPI_RESOURCE_TYPE_START_DEPENDENT: return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; if (!p || !p->interrupt_count) { printk(KERN_WARNING PREFIX "Blank IRQ resource\n"); return AE_OK; } for (i = 0; (i < p->interrupt_count && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { if (!p->interrupts[i]) { printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", p->interrupts[i]); continue; } link->irq.possible[i] = p->interrupts[i]; link->irq.possible_count++; } link->irq.triggering = p->triggering; link->irq.polarity = p->polarity; link->irq.resource_type = ACPI_RESOURCE_TYPE_IRQ; break; } case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: { struct acpi_resource_extended_irq *p = &resource->data.extended_irq; if (!p || !p->interrupt_count) { printk(KERN_WARNING PREFIX "Blank EXT IRQ resource\n"); return AE_OK; } for (i = 0; (i < p->interrupt_count && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { if (!p->interrupts[i]) { printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", p->interrupts[i]); continue; } link->irq.possible[i] = p->interrupts[i]; link->irq.possible_count++; } link->irq.triggering = p->triggering; link->irq.polarity = p->polarity; link->irq.resource_type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ; break; } default: printk(KERN_ERR PREFIX "Resource is not an IRQ entry\n"); return AE_OK; } return AE_CTRL_TERMINATE;}static int acpi_pci_link_get_possible(struct acpi_pci_link *link){ acpi_status status; if (!link) return -EINVAL; status = acpi_walk_resources(link->device->handle, METHOD_NAME__PRS, acpi_pci_link_check_possible, link); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRS")); return -ENODEV; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d possible IRQs\n", link->irq.possible_count)); return 0;}static acpi_statusacpi_pci_link_check_current(struct acpi_resource *resource, void *context){ int *irq = (int *)context; switch (resource->type) { case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; if (!p || !p->interrupt_count) { /* * IRQ descriptors may have no IRQ# bits set, * particularly those those w/ _STA disabled */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Blank IRQ resource\n")); return AE_OK; } *irq = p->interrupts[0]; break; } case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: { struct acpi_resource_extended_irq *p = &resource->data.extended_irq; if (!p || !p->interrupt_count) { /* * extended IRQ descriptors must * return at least 1 IRQ */ printk(KERN_WARNING PREFIX "Blank EXT IRQ resource\n"); return AE_OK; } *irq = p->interrupts[0]; break; } break; default: printk(KERN_ERR PREFIX "Resource %d isn't an IRQ\n", resource->type); case ACPI_RESOURCE_TYPE_END_TAG: return AE_OK; } return AE_CTRL_TERMINATE;}/* * Run _CRS and set link->irq.active * * return value: * 0 - success * !0 - failure */static int acpi_pci_link_get_current(struct acpi_pci_link *link){ int result = 0; acpi_status status = AE_OK; int irq = 0; if (!link) return -EINVAL; link->irq.active = 0; /* in practice, status disabled is meaningless, ignore it */ if (acpi_strict) { /* Query _STA, set link->device->status */ result = acpi_bus_get_status(link->device); if (result) { printk(KERN_ERR PREFIX "Unable to read status\n"); goto end; } if (!link->device->status.enabled) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n")); return 0; } } /* * Query and parse _CRS to get the current IRQ assignment. */ status = acpi_walk_resources(link->device->handle, METHOD_NAME__CRS, acpi_pci_link_check_current, &irq); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _CRS")); result = -ENODEV; goto end; } if (acpi_strict && !irq) { printk(KERN_ERR PREFIX "_CRS returned 0\n"); result = -ENODEV; } link->irq.active = irq; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active)); end: return result;}static int acpi_pci_link_set(struct acpi_pci_link *link, int irq){ int result = 0; acpi_status status = AE_OK; struct { struct acpi_resource res; struct acpi_resource end; } *resource; struct acpi_buffer buffer = { 0, NULL }; if (!link || !irq) return -EINVAL; resource = kzalloc(sizeof(*resource) + 1, irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL); if (!resource) return -ENOMEM; buffer.length = sizeof(*resource) + 1; buffer.pointer = resource; switch (link->irq.resource_type) { case ACPI_RESOURCE_TYPE_IRQ: resource->res.type = ACPI_RESOURCE_TYPE_IRQ; resource->res.length = sizeof(struct acpi_resource); resource->res.data.irq.triggering = link->irq.triggering; resource->res.data.irq.polarity = link->irq.polarity; if (link->irq.triggering == ACPI_EDGE_SENSITIVE) resource->res.data.irq.sharable = ACPI_EXCLUSIVE; else resource->res.data.irq.sharable = ACPI_SHARED; resource->res.data.irq.interrupt_count = 1; resource->res.data.irq.interrupts[0] = irq; break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: resource->res.type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ; resource->res.length = sizeof(struct acpi_resource); resource->res.data.extended_irq.producer_consumer = ACPI_CONSUMER; resource->res.data.extended_irq.triggering = link->irq.triggering; resource->res.data.extended_irq.polarity = link->irq.polarity; if (link->irq.triggering == ACPI_EDGE_SENSITIVE) resource->res.data.irq.sharable = ACPI_EXCLUSIVE; else resource->res.data.irq.sharable = ACPI_SHARED; resource->res.data.extended_irq.interrupt_count = 1; resource->res.data.extended_irq.interrupts[0] = irq; /* ignore resource_source, it's optional */ break; default: printk(KERN_ERR PREFIX "Invalid Resource_type %d\n", link->irq.resource_type); result = -EINVAL; goto end; } resource->end.type = ACPI_RESOURCE_TYPE_END_TAG; /* Attempt to set the resource */ status = acpi_set_current_resources(link->device->handle, &buffer); /* check for total failure */ if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SRS")); result = -ENODEV; goto end; } /* Query _STA, set device->status */ result = acpi_bus_get_status(link->device); if (result) { printk(KERN_ERR PREFIX "Unable to read status\n"); goto end; } if (!link->device->status.enabled) { printk(KERN_WARNING PREFIX "%s [%s] disabled and referenced, BIOS bug\n", acpi_device_name(link->device), acpi_device_bid(link->device)); } /* Query _CRS, set link->irq.active */ result = acpi_pci_link_get_current(link); if (result) { goto end; } /* * Is current setting not what we set? * set link->irq.active */ if (link->irq.active != irq) { /* * policy: when _CRS doesn't return what we just _SRS * assume _SRS worked and override _CRS value. */ printk(KERN_WARNING PREFIX "%s [%s] BIOS reported IRQ %d, using IRQ %d\n", acpi_device_name(link->device), acpi_device_bid(link->device), link->irq.active, irq); link->irq.active = irq; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active)); end: kfree(resource); return result;}/* -------------------------------------------------------------------------- PCI Link IRQ Management -------------------------------------------------------------------------- *//* * "acpi_irq_balance" (default in APIC mode) enables ACPI to use PIC Interrupt * Link Devices to move the PIRQs around to minimize sharing. * * "acpi_irq_nobalance" (default in PIC mode) tells ACPI not to move any PIC IRQs * that the BIOS has already set to active. This is necessary because * ACPI has no automatic means of knowing what ISA IRQs are used. Note that * if the BIOS doesn't set a Link Device active, ACPI needs to program it * even if acpi_irq_nobalance is set. * * A tables of penalties avoids directing PCI interrupts to well known * ISA IRQs. Boot params are available to over-ride the default table: * * List interrupts that are free for PCI use. * acpi_irq_pci=n[,m] * * List interrupts that should not be used for PCI: * acpi_irq_isa=n[,m] * * Note that PCI IRQ routers have a list of possible IRQs, * which may not include the IRQs this table says are available. * * Since this heuristic can't tell the difference between a link * that no device will attach to, vs. a link which may be shared * by multiple active devices -- it is not optimal. * * If interrupt performance is that important, get an IO-APIC system * with a pin dedicated to each device. Or for that matter, an MSI * enabled system. */#define ACPI_MAX_IRQS 256#define ACPI_MAX_ISA_IRQ 16#define PIRQ_PENALTY_PCI_AVAILABLE (0)#define PIRQ_PENALTY_PCI_POSSIBLE (16*16)#define PIRQ_PENALTY_PCI_USING (16*16*16)#define PIRQ_PENALTY_ISA_TYPICAL (16*16*16*16)#define PIRQ_PENALTY_ISA_USED (16*16*16*16*16)#define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16)static int acpi_irq_penalty[ACPI_MAX_IRQS] = { PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ3 serial */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ4 serial */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ5 sometimes SoundBlaster */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ6 */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ7 parallel, spurious */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ8 rtc, sometimes */ PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ9 PCI, often acpi */ PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ10 PCI */ PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ11 PCI */ PIRQ_PENALTY_ISA_USED, /* IRQ12 mouse */ PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */ PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */ PIRQ_PENALTY_ISA_USED, /* IRQ15 ide1 */ /* >IRQ15 */};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?