pci_link.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 874 行 · 第 1/2 页

C
874
字号
/* *  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 <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_HID		"PNP0C0F"#define ACPI_PCI_LINK_DRIVER_NAME	"ACPI PCI Interrupt Link Driver"#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_driver acpi_pci_link_driver = {	.name =		ACPI_PCI_LINK_DRIVER_NAME,	.class =	ACPI_PCI_LINK_CLASS,	.ids =		ACPI_PCI_LINK_HID,	.ops =		{				.add =    acpi_pci_link_add,				.remove = acpi_pci_link_remove,			},};struct acpi_pci_link_irq {	u8			active;			/* Current IRQ */	u8			edge_level;		/* All IRQs */	u8			active_high_low;	/* All IRQs */	u8			initialized;	u8			resource_type;	u8			possible_count;	u8			possible[ACPI_PCI_LINK_MAX_POSSIBLE];};struct acpi_pci_link {	struct list_head	node;	struct acpi_device	*device;	acpi_handle		handle;	struct acpi_pci_link_irq irq;};static struct {	int			count;	struct list_head	entries;}				acpi_link;/* --------------------------------------------------------------------------                            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 = (struct acpi_pci_link *) context;	u32			i = 0;	ACPI_FUNCTION_TRACE("acpi_pci_link_check_possible");	switch (resource->id) {	case ACPI_RSTYPE_START_DPF:		return AE_OK;	case ACPI_RSTYPE_IRQ:	{		struct acpi_resource_irq *p = &resource->data.irq;		if (!p || !p->number_of_interrupts) {			ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n"));			return AE_OK;		}		for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_POSSIBLE); i++) {			if (!p->interrupts[i]) {				ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", p->interrupts[i]));				continue;			}			link->irq.possible[i] = p->interrupts[i];			link->irq.possible_count++;		}		link->irq.edge_level = p->edge_level;		link->irq.active_high_low = p->active_high_low;		link->irq.resource_type = ACPI_RSTYPE_IRQ;		break;	}	case ACPI_RSTYPE_EXT_IRQ:	{		struct acpi_resource_ext_irq *p = &resource->data.extended_irq;		if (!p || !p->number_of_interrupts) {			ACPI_DEBUG_PRINT((ACPI_DB_WARN, 				"Blank EXT IRQ resource\n"));			return AE_OK;		}		for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_POSSIBLE); i++) {			if (!p->interrupts[i]) {				ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", p->interrupts[i]));				continue;			}			link->irq.possible[i] = p->interrupts[i];			link->irq.possible_count++;		}		link->irq.edge_level = p->edge_level;		link->irq.active_high_low = p->active_high_low;		link->irq.resource_type = ACPI_RSTYPE_EXT_IRQ;		break;	}	default:		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 			"Resource is not an IRQ entry\n"));		return AE_OK;	}	return AE_CTRL_TERMINATE;}static intacpi_pci_link_get_possible (	struct acpi_pci_link	*link){	acpi_status		status;	ACPI_FUNCTION_TRACE("acpi_pci_link_get_possible");	if (!link)		return_VALUE(-EINVAL);	status = acpi_walk_resources(link->handle, METHOD_NAME__PRS,			acpi_pci_link_check_possible, link);	if (ACPI_FAILURE(status)) {		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRS\n"));		return_VALUE(-ENODEV);	}	ACPI_DEBUG_PRINT((ACPI_DB_INFO, 		"Found %d possible IRQs\n", link->irq.possible_count));	return_VALUE(0);}static acpi_statusacpi_pci_link_check_current (	struct acpi_resource	*resource,	void			*context){	int			*irq = (int *) context;	ACPI_FUNCTION_TRACE("acpi_pci_link_check_current");	switch (resource->id) {	case ACPI_RSTYPE_IRQ:	{		struct acpi_resource_irq *p = &resource->data.irq;		if (!p || !p->number_of_interrupts) {			/*			 * 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_RSTYPE_EXT_IRQ:	{		struct acpi_resource_ext_irq *p = &resource->data.extended_irq;		if (!p || !p->number_of_interrupts) {			/*			 * extended IRQ descriptors must			 * return at least 1 IRQ			 */			ACPI_DEBUG_PRINT((ACPI_DB_WARN,				"Blank EXT IRQ resource\n"));			return AE_OK;		}		*irq = p->interrupts[0];		break;	}	default:		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,			"Resource isn't an IRQ\n"));		return AE_OK;	}	return AE_CTRL_TERMINATE;}/* * Run _CRS and set link->irq.active * * return value: * 0 - success * !0 - failure */static intacpi_pci_link_get_current (	struct acpi_pci_link	*link){	int			result = 0;	acpi_status		status = AE_OK;	int			irq = 0;	ACPI_FUNCTION_TRACE("acpi_pci_link_get_current");	if (!link || !link->handle)		return_VALUE(-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) {			ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n"));			goto end;		}		if (!link->device->status.enabled) {			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n"));			return_VALUE(0);		}	}	/* 	 * Query and parse _CRS to get the current IRQ assignment. 	 */	status = acpi_walk_resources(link->handle, METHOD_NAME__CRS,			acpi_pci_link_check_current, &irq);	if (ACPI_FAILURE(status)) {		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _CRS\n"));		result = -ENODEV;		goto end;	}	if (acpi_strict && !irq) {		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "_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_VALUE(result);}static intacpi_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 = {sizeof(resource)+1, &resource};	ACPI_FUNCTION_TRACE("acpi_pci_link_set");	if (!link || !irq)		return_VALUE(-EINVAL);	memset(&resource, 0, sizeof(resource));	switch(link->irq.resource_type) {	case ACPI_RSTYPE_IRQ:		resource.res.id = ACPI_RSTYPE_IRQ;		resource.res.length = sizeof(struct acpi_resource);		resource.res.data.irq.edge_level = link->irq.edge_level;		resource.res.data.irq.active_high_low = link->irq.active_high_low;		if (link->irq.edge_level == ACPI_EDGE_SENSITIVE)			resource.res.data.irq.shared_exclusive = ACPI_EXCLUSIVE;		else			resource.res.data.irq.shared_exclusive = ACPI_SHARED;		resource.res.data.irq.number_of_interrupts = 1;		resource.res.data.irq.interrupts[0] = irq;		break;	   	case ACPI_RSTYPE_EXT_IRQ:		resource.res.id = ACPI_RSTYPE_EXT_IRQ;		resource.res.length = sizeof(struct acpi_resource);		resource.res.data.extended_irq.producer_consumer = ACPI_CONSUMER;		resource.res.data.extended_irq.edge_level = link->irq.edge_level;		resource.res.data.extended_irq.active_high_low = link->irq.active_high_low;		if (link->irq.edge_level == ACPI_EDGE_SENSITIVE)			resource.res.data.irq.shared_exclusive = ACPI_EXCLUSIVE;		else			resource.res.data.irq.shared_exclusive = ACPI_SHARED;		resource.res.data.extended_irq.number_of_interrupts = 1;		resource.res.data.extended_irq.interrupts[0] = irq;		/* ignore resource_source, it's optional */		break;	default:		printk("ACPI BUG: resource_type %d\n", link->irq.resource_type);		return_VALUE(-EINVAL);	}	resource.end.id = ACPI_RSTYPE_END_TAG;	/* Attempt to set the resource */	status = acpi_set_current_resources(link->handle, &buffer);	/* check for total failure */	if (ACPI_FAILURE(status)) {		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n"));		return_VALUE(-ENODEV);	}	/* Query _STA, set device->status */	result = acpi_bus_get_status(link->device);	if (result) {		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n"));		return_VALUE(result);	}	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) {		return_VALUE(result);	}	/*	 * 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));		return_VALUE(0);}/* --------------------------------------------------------------------------                            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

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?