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 + -
显示快捷键?