Files
linux-apfs/drivers/acpi/pci_link.c
T

907 lines
23 KiB
C
Raw Normal View History

2005-04-16 15:20:36 -07:00
/*
* 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/syscore_ops.h>
2005-04-16 15:20:36 -07:00
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/pci.h>
2006-04-27 05:25:00 -04:00
#include <linux/mutex.h>
#include <linux/slab.h>
2005-04-16 15:20:36 -07:00
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#define PREFIX "ACPI: "
2009-02-17 14:00:40 -07:00
#define _COMPONENT ACPI_PCI_COMPONENT
2007-02-12 22:42:12 -05:00
ACPI_MODULE_NAME("pci_link");
2005-04-16 15:20:36 -07:00
#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"
2009-02-17 14:00:40 -07:00
#define ACPI_PCI_LINK_MAX_POSSIBLE 16
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_add(struct acpi_device *device);
static int acpi_pci_link_remove(struct acpi_device *device, int type);
2005-04-16 15:20:36 -07:00
2010-01-10 17:15:36 +01:00
static const struct acpi_device_id link_device_ids[] = {
{"PNP0C0F", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, link_device_ids);
2005-04-16 15:20:36 -07:00
static struct acpi_driver acpi_pci_link_driver = {
2007-02-12 23:33:40 -05:00
.name = "pci_link",
2005-08-05 00:44:28 -04:00
.class = ACPI_PCI_LINK_CLASS,
.ids = link_device_ids,
2005-08-05 00:44:28 -04:00
.ops = {
.add = acpi_pci_link_add,
.remove = acpi_pci_link_remove,
2009-02-17 14:00:40 -07:00
},
2005-04-16 15:20:36 -07:00
};
/*
* 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
*/
2005-04-16 15:20:36 -07:00
struct acpi_pci_link_irq {
2005-08-05 00:44:28 -04:00
u8 active; /* Current IRQ */
2005-09-30 19:03:00 -04:00
u8 triggering; /* All IRQs */
2009-02-17 14:00:40 -07:00
u8 polarity; /* All IRQs */
2005-08-05 00:44:28 -04:00
u8 resource_type;
u8 possible_count;
u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
u8 initialized:1;
u8 reserved:7;
2005-04-16 15:20:36 -07:00
};
struct acpi_pci_link {
struct list_head list;
2009-02-17 14:00:40 -07:00
struct acpi_device *device;
struct acpi_pci_link_irq irq;
int refcnt;
2005-04-16 15:20:36 -07:00
};
static LIST_HEAD(acpi_link_list);
2007-10-24 18:24:42 +02:00
static DEFINE_MUTEX(acpi_link_lock);
2005-04-16 15:20:36 -07:00
/* --------------------------------------------------------------------------
PCI Link Device Management
-------------------------------------------------------------------------- */
/*
* set context (link) possible list from resource list
*/
2009-02-17 14:00:40 -07:00
static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource,
void *context)
2005-04-16 15:20:36 -07:00
{
struct acpi_pci_link *link = context;
u32 i;
2005-04-16 15:20:36 -07:00
switch (resource->type) {
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_IRQ:
2005-08-05 00:44:28 -04:00
{
struct acpi_resource_irq *p = &resource->data.irq;
2005-09-30 19:03:00 -04:00
if (!p || !p->interrupt_count) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Blank _PRS IRQ resource\n"));
return AE_OK;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
for (i = 0;
2005-09-30 19:03:00 -04:00
(i < p->interrupt_count
2005-08-05 00:44:28 -04:00
&& i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) {
printk(KERN_WARNING PREFIX
"Invalid _PRS IRQ %d\n",
p->interrupts[i]);
2005-08-05 00:44:28 -04:00
continue;
}
link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++;
}
2005-09-30 19:03:00 -04:00
link->irq.triggering = p->triggering;
link->irq.polarity = p->polarity;
link->irq.resource_type = ACPI_RESOURCE_TYPE_IRQ;
2005-08-05 00:44:28 -04:00
break;
2005-04-16 15:20:36 -07:00
}
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
2005-08-05 00:44:28 -04:00
{
2005-09-30 19:03:00 -04:00
struct acpi_resource_extended_irq *p =
2005-08-05 00:44:28 -04:00
&resource->data.extended_irq;
2005-09-30 19:03:00 -04:00
if (!p || !p->interrupt_count) {
printk(KERN_WARNING PREFIX
"Blank _PRS EXT IRQ resource\n");
return AE_OK;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
for (i = 0;
2005-09-30 19:03:00 -04:00
(i < p->interrupt_count
2005-08-05 00:44:28 -04:00
&& i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) {
printk(KERN_WARNING PREFIX
"Invalid _PRS IRQ %d\n",
p->interrupts[i]);
2005-08-05 00:44:28 -04:00
continue;
}
link->irq.possible[i] = p->interrupts[i];
link->irq.possible_count++;
}
2005-09-30 19:03:00 -04:00
link->irq.triggering = p->triggering;
link->irq.polarity = p->polarity;
link->irq.resource_type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
2005-08-05 00:44:28 -04:00
break;
2005-04-16 15:20:36 -07:00
}
default:
printk(KERN_ERR PREFIX "_PRS resource type 0x%x isn't an IRQ\n",
resource->type);
return AE_OK;
2005-04-16 15:20:36 -07:00
}
return AE_CTRL_TERMINATE;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_get_possible(struct acpi_pci_link *link)
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
acpi_status status;
2005-04-16 15:20:36 -07:00
status = acpi_walk_resources(link->device->handle, METHOD_NAME__PRS,
2005-08-05 00:44:28 -04:00
acpi_pci_link_check_possible, link);
2005-04-16 15:20:36 -07:00
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRS"));
return -ENODEV;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Found %d possible IRQs\n",
link->irq.possible_count));
2005-04-16 15:20:36 -07:00
return 0;
2005-04-16 15:20:36 -07:00
}
2009-02-17 14:00:40 -07:00
static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource,
void *context)
2005-04-16 15:20:36 -07:00
{
int *irq = context;
2005-04-16 15:20:36 -07:00
switch (resource->type) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_IRQ:
2005-08-05 00:44:28 -04:00
{
struct acpi_resource_irq *p = &resource->data.irq;
2005-09-30 19:03:00 -04:00
if (!p || !p->interrupt_count) {
2005-08-05 00:44:28 -04:00
/*
* IRQ descriptors may have no IRQ# bits set,
* particularly those those w/ _STA disabled
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Blank _CRS IRQ resource\n"));
return AE_OK;
2005-08-05 00:44:28 -04:00
}
*irq = p->interrupts[0];
break;
2005-04-16 15:20:36 -07:00
}
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
2005-08-05 00:44:28 -04:00
{
2005-09-30 19:03:00 -04:00
struct acpi_resource_extended_irq *p =
2005-08-05 00:44:28 -04:00
&resource->data.extended_irq;
2005-09-30 19:03:00 -04:00
if (!p || !p->interrupt_count) {
2005-08-05 00:44:28 -04:00
/*
* extended IRQ descriptors must
* return at least 1 IRQ
*/
printk(KERN_WARNING PREFIX
"Blank _CRS EXT IRQ resource\n");
return AE_OK;
2005-08-05 00:44:28 -04:00
}
*irq = p->interrupts[0];
break;
2005-04-16 15:20:36 -07:00
}
break;
2005-04-16 15:20:36 -07:00
default:
printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n",
resource->type);
return AE_OK;
2005-04-16 15:20:36 -07:00
}
return AE_CTRL_TERMINATE;
2005-04-16 15:20:36 -07:00
}
/*
* Run _CRS and set link->irq.active
*
* return value:
* 0 - success
* !0 - failure
*/
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_get_current(struct acpi_pci_link *link)
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0;
acpi_status status;
2005-08-05 00:44:28 -04:00
int irq = 0;
2005-04-16 15:20:36 -07:00
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");
2005-04-16 15:20:36 -07:00
goto end;
}
if (!link->device->status.enabled) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n"));
return 0;
2005-04-16 15:20:36 -07:00
}
}
/*
* Query and parse _CRS to get the current IRQ assignment.
*/
status = acpi_walk_resources(link->device->handle, METHOD_NAME__CRS,
2005-08-05 00:44:28 -04:00
acpi_pci_link_check_current, &irq);
2005-04-16 15:20:36 -07:00
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _CRS"));
2005-04-16 15:20:36 -07:00
result = -ENODEV;
goto end;
}
if (acpi_strict && !irq) {
printk(KERN_ERR PREFIX "_CRS returned 0\n");
2005-04-16 15:20:36 -07:00
result = -ENODEV;
}
link->irq.active = irq;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active));
2005-08-05 00:44:28 -04:00
end:
return result;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
2005-04-16 15:20:36 -07:00
{
int result;
acpi_status status;
2005-04-16 15:20:36 -07:00
struct {
2005-08-05 00:44:28 -04:00
struct acpi_resource res;
struct acpi_resource end;
} *resource;
struct acpi_buffer buffer = { 0, NULL };
2005-04-16 15:20:36 -07:00
if (!irq)
return -EINVAL;
2005-04-16 15:20:36 -07:00
2006-12-19 12:56:11 -08:00
resource = kzalloc(sizeof(*resource) + 1, irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL);
2005-08-05 00:44:28 -04:00
if (!resource)
return -ENOMEM;
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
buffer.length = sizeof(*resource) + 1;
2005-04-16 15:20:36 -07:00
buffer.pointer = resource;
2005-08-05 00:44:28 -04:00
switch (link->irq.resource_type) {
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_IRQ:
resource->res.type = ACPI_RESOURCE_TYPE_IRQ;
2005-04-16 15:20:36 -07:00
resource->res.length = sizeof(struct acpi_resource);
2005-09-30 19:03:00 -04:00
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 =
2005-08-05 00:44:28 -04:00
ACPI_EXCLUSIVE;
2005-04-16 15:20:36 -07:00
else
2005-09-30 19:03:00 -04:00
resource->res.data.irq.sharable = ACPI_SHARED;
resource->res.data.irq.interrupt_count = 1;
2005-04-16 15:20:36 -07:00
resource->res.data.irq.interrupts[0] = irq;
break;
2005-08-05 00:44:28 -04:00
2005-09-30 19:03:00 -04:00
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
resource->res.type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
2005-04-16 15:20:36 -07:00
resource->res.length = sizeof(struct acpi_resource);
2005-08-05 00:44:28 -04:00
resource->res.data.extended_irq.producer_consumer =
ACPI_CONSUMER;
2005-09-30 19:03:00 -04:00
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 =
2005-08-05 00:44:28 -04:00
ACPI_EXCLUSIVE;
2005-04-16 15:20:36 -07:00
else
2005-09-30 19:03:00 -04:00
resource->res.data.irq.sharable = ACPI_SHARED;
resource->res.data.extended_irq.interrupt_count = 1;
2005-04-16 15:20:36 -07:00
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);
2005-04-16 15:20:36 -07:00
result = -EINVAL;
goto end;
}
2005-09-30 19:03:00 -04:00
resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
2005-04-16 15:20:36 -07:00
/* Attempt to set the resource */
status = acpi_set_current_resources(link->device->handle, &buffer);
2005-04-16 15:20:36 -07:00
/* check for total failure */
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SRS"));
2005-04-16 15:20:36 -07:00
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");
2005-04-16 15:20:36 -07:00
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));
2005-04-16 15:20:36 -07:00
}
/* 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);
2005-04-16 15:20:36 -07:00
link->irq.active = irq;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active));
2005-08-05 00:44:28 -04:00
end:
2005-04-16 15:20:36 -07:00
kfree(resource);
return result;
2005-04-16 15:20:36 -07:00
}
/* --------------------------------------------------------------------------
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 */
2005-08-05 00:44:28 -04:00
PIRQ_PENALTY_ISA_TYPICAL, /* IRQ3 serial */
PIRQ_PENALTY_ISA_TYPICAL, /* IRQ4 serial */
2005-04-16 15:20:36 -07:00
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 */
2009-02-17 14:00:40 -07:00
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 */
2005-08-05 00:44:28 -04:00
/* >IRQ15 */
2005-04-16 15:20:36 -07:00
};
2005-08-05 00:44:28 -04:00
int __init acpi_irq_penalty_init(void)
2005-04-16 15:20:36 -07:00
{
struct acpi_pci_link *link;
int i;
2005-04-16 15:20:36 -07:00
/*
* Update penalties to facilitate IRQ balancing.
*/
list_for_each_entry(link, &acpi_link_list, list) {
2005-04-16 15:20:36 -07:00
/*
* reflect the possible and active irqs in the penalty table --
* useful for breaking ties.
*/
if (link->irq.possible_count) {
2005-08-05 00:44:28 -04:00
int penalty =
PIRQ_PENALTY_PCI_POSSIBLE /
link->irq.possible_count;
2005-04-16 15:20:36 -07:00
for (i = 0; i < link->irq.possible_count; i++) {
if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ)
2005-08-05 00:44:28 -04:00
acpi_irq_penalty[link->irq.
possible[i]] +=
penalty;
2005-04-16 15:20:36 -07:00
}
} else if (link->irq.active) {
2005-08-05 00:44:28 -04:00
acpi_irq_penalty[link->irq.active] +=
PIRQ_PENALTY_PCI_POSSIBLE;
2005-04-16 15:20:36 -07:00
}
}
/* Add a penalty for the SCI */
2007-02-02 19:48:22 +03:00
acpi_irq_penalty[acpi_gbl_FADT.sci_interrupt] += PIRQ_PENALTY_PCI_USING;
return 0;
2005-04-16 15:20:36 -07:00
}
static int acpi_irq_balance = -1; /* 0: static, 1: balance */
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_allocate(struct acpi_pci_link *link)
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int irq;
int i;
2005-04-16 15:20:36 -07:00
if (link->irq.initialized) {
if (link->refcnt == 0)
/* This means the link is disabled but initialized */
acpi_pci_link_set(link, link->irq.active);
return 0;
}
2005-04-16 15:20:36 -07:00
/*
* search for active IRQ in list of possible IRQs.
*/
for (i = 0; i < link->irq.possible_count; ++i) {
if (link->irq.active == link->irq.possible[i])
break;
}
/*
* forget active IRQ that is not in possible list
*/
if (i == link->irq.possible_count) {
if (acpi_strict)
printk(KERN_WARNING PREFIX "_CRS %d not found"
" in _PRS\n", link->irq.active);
2005-04-16 15:20:36 -07:00
link->irq.active = 0;
}
/*
* if active found, use it; else pick entry from end of possible list.
*/
2009-02-17 14:00:40 -07:00
if (link->irq.active)
2005-04-16 15:20:36 -07:00
irq = link->irq.active;
2009-02-17 14:00:40 -07:00
else
2005-04-16 15:20:36 -07:00
irq = link->irq.possible[link->irq.possible_count - 1];
if (acpi_irq_balance || !link->irq.active) {
/*
* Select the best IRQ. This is done in reverse to promote
* the use of IRQs 9, 10, 11, and >15.
*/
for (i = (link->irq.possible_count - 1); i >= 0; i--) {
2005-08-05 00:44:28 -04:00
if (acpi_irq_penalty[irq] >
acpi_irq_penalty[link->irq.possible[i]])
2005-04-16 15:20:36 -07:00
irq = link->irq.possible[i];
}
}
/* Attempt to enable the link device at this IRQ. */
if (acpi_pci_link_set(link, irq)) {
printk(KERN_ERR PREFIX "Unable to set IRQ for %s [%s]. "
"Try pci=noacpi or acpi=off\n",
acpi_device_name(link->device),
acpi_device_bid(link->device));
return -ENODEV;
2005-04-16 15:20:36 -07:00
} else {
acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_USING;
printk(KERN_WARNING PREFIX "%s [%s] enabled at IRQ %d\n",
2005-08-05 00:44:28 -04:00
acpi_device_name(link->device),
acpi_device_bid(link->device), link->irq.active);
2005-04-16 15:20:36 -07:00
}
link->irq.initialized = 1;
return 0;
2005-04-16 15:20:36 -07:00
}
/*
* acpi_pci_link_allocate_irq
2005-04-16 15:20:36 -07:00
* success: return IRQ >= 0
* failure: return -1
*/
2009-02-17 14:00:40 -07:00
int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering,
int *polarity, char **name)
2005-04-16 15:20:36 -07:00
{
int result;
struct acpi_device *device;
struct acpi_pci_link *link;
2005-04-16 15:20:36 -07:00
result = acpi_bus_get_device(handle, &device);
if (result) {
printk(KERN_ERR PREFIX "Invalid link device\n");
return -1;
2005-04-16 15:20:36 -07:00
}
link = acpi_driver_data(device);
2005-04-16 15:20:36 -07:00
if (!link) {
printk(KERN_ERR PREFIX "Invalid link context\n");
return -1;
2005-04-16 15:20:36 -07:00
}
/* TBD: Support multiple index (IRQ) entries per Link Device */
if (index) {
printk(KERN_ERR PREFIX "Invalid index %d\n", index);
return -1;
2005-04-16 15:20:36 -07:00
}
2006-04-27 05:25:00 -04:00
mutex_lock(&acpi_link_lock);
if (acpi_pci_link_allocate(link)) {
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
return -1;
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
if (!link->irq.active) {
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
printk(KERN_ERR PREFIX "Link active IRQ is 0!\n");
return -1;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
link->refcnt++;
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
2005-04-16 15:20:36 -07:00
2005-09-30 19:03:00 -04:00
if (triggering)
*triggering = link->irq.triggering;
if (polarity)
*polarity = link->irq.polarity;
2005-08-05 00:44:28 -04:00
if (name)
*name = acpi_device_bid(link->device);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
2005-08-05 00:44:28 -04:00
"Link %s is referenced\n",
acpi_device_bid(link->device)));
return (link->irq.active);
2005-04-16 15:20:36 -07:00
}
/*
* We don't change link's irq information here. After it is reenabled, we
* continue use the info
*/
2005-08-05 00:44:28 -04:00
int acpi_pci_link_free_irq(acpi_handle handle)
{
struct acpi_device *device;
struct acpi_pci_link *link;
2005-08-05 00:44:28 -04:00
acpi_status result;
2005-04-16 15:20:36 -07:00
result = acpi_bus_get_device(handle, &device);
if (result) {
printk(KERN_ERR PREFIX "Invalid link device\n");
return -1;
}
link = acpi_driver_data(device);
if (!link) {
printk(KERN_ERR PREFIX "Invalid link context\n");
return -1;
}
2006-04-27 05:25:00 -04:00
mutex_lock(&acpi_link_lock);
if (!link->irq.initialized) {
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
printk(KERN_ERR PREFIX "Link isn't initialized\n");
return -1;
}
#ifdef FUTURE_USE
/*
* The Link reference count allows us to _DISable an unused link
* and suspend time, and set it again on resume.
* However, 2.6.12 still has irq_router.resume
* which blindly restores the link state.
* So we disable the reference count method
* to prevent duplicate acpi_pci_link_set()
* which would harm some systems
*/
2005-08-05 00:44:28 -04:00
link->refcnt--;
#endif
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
2005-08-05 00:44:28 -04:00
"Link %s is dereferenced\n",
acpi_device_bid(link->device)));
2009-02-17 14:00:40 -07:00
if (link->refcnt == 0)
2008-10-17 07:49:50 -07:00
acpi_evaluate_object(link->device->handle, "_DIS", NULL, NULL);
2009-02-17 14:00:40 -07:00
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
return (link->irq.active);
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_add(struct acpi_device *device)
2005-04-16 15:20:36 -07:00
{
int result;
struct acpi_pci_link *link;
int i;
2005-08-05 00:44:28 -04:00
int found = 0;
2005-04-16 15:20:36 -07:00
2006-12-19 12:56:11 -08:00
link = kzalloc(sizeof(struct acpi_pci_link), GFP_KERNEL);
2005-04-16 15:20:36 -07:00
if (!link)
return -ENOMEM;
2005-04-16 15:20:36 -07:00
link->device = device;
strcpy(acpi_device_name(device), ACPI_PCI_LINK_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
device->driver_data = link;
2005-04-16 15:20:36 -07:00
2006-04-27 05:25:00 -04:00
mutex_lock(&acpi_link_lock);
2005-04-16 15:20:36 -07:00
result = acpi_pci_link_get_possible(link);
if (result)
goto end;
/* query and set link->irq.active */
acpi_pci_link_get_current(link);
printk(KERN_INFO PREFIX "%s [%s] (IRQs", acpi_device_name(device),
2005-08-05 00:44:28 -04:00
acpi_device_bid(device));
2005-04-16 15:20:36 -07:00
for (i = 0; i < link->irq.possible_count; i++) {
if (link->irq.active == link->irq.possible[i]) {
printk(KERN_CONT " *%d", link->irq.possible[i]);
2005-04-16 15:20:36 -07:00
found = 1;
2005-08-05 00:44:28 -04:00
} else
printk(KERN_CONT " %d", link->irq.possible[i]);
2005-04-16 15:20:36 -07:00
}
printk(KERN_CONT ")");
2005-04-16 15:20:36 -07:00
if (!found)
printk(KERN_CONT " *%d", link->irq.active);
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
if (!link->device->status.enabled)
printk(KERN_CONT ", disabled.");
2005-04-16 15:20:36 -07:00
printk(KERN_CONT "\n");
2005-04-16 15:20:36 -07:00
list_add_tail(&link->list, &acpi_link_list);
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
end:
2005-04-16 15:20:36 -07:00
/* disable all links -- to be activated on use */
2008-10-17 07:49:50 -07:00
acpi_evaluate_object(device->handle, "_DIS", NULL, NULL);
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
2005-04-16 15:20:36 -07:00
if (result)
kfree(link);
return result;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_resume(struct acpi_pci_link *link)
2005-08-01 12:37:54 -07:00
{
if (link->refcnt && link->irq.active && link->irq.initialized)
return (acpi_pci_link_set(link, link->irq.active));
2009-02-17 14:00:40 -07:00
return 0;
2005-08-01 12:37:54 -07:00
}
static void irqrouter_resume(void)
2005-04-16 15:20:36 -07:00
{
struct acpi_pci_link *link;
2005-04-16 15:20:36 -07:00
list_for_each_entry(link, &acpi_link_list, list) {
2005-08-01 12:37:54 -07:00
acpi_pci_link_resume(link);
2005-04-16 15:20:36 -07:00
}
}
2005-08-05 00:44:28 -04:00
static int acpi_pci_link_remove(struct acpi_device *device, int type)
2005-04-16 15:20:36 -07:00
{
struct acpi_pci_link *link;
2005-04-16 15:20:36 -07:00
link = acpi_driver_data(device);
2005-04-16 15:20:36 -07:00
2006-04-27 05:25:00 -04:00
mutex_lock(&acpi_link_lock);
list_del(&link->list);
2006-04-27 05:25:00 -04:00
mutex_unlock(&acpi_link_lock);
2005-04-16 15:20:36 -07:00
kfree(link);
return 0;
2005-04-16 15:20:36 -07:00
}
/*
* modify acpi_irq_penalty[] from cmdline
*/
static int __init acpi_irq_penalty_update(char *str, int used)
{
int i;
for (i = 0; i < 16; i++) {
int retval;
int irq;
2005-08-05 00:44:28 -04:00
retval = get_option(&str, &irq);
2005-04-16 15:20:36 -07:00
if (!retval)
break; /* no number found */
if (irq < 0)
continue;
2005-08-05 00:44:28 -04:00
if (irq >= ARRAY_SIZE(acpi_irq_penalty))
2005-04-16 15:20:36 -07:00
continue;
if (used)
acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED;
else
acpi_irq_penalty[irq] = PIRQ_PENALTY_PCI_AVAILABLE;
if (retval != 2) /* no next number */
break;
}
return 1;
}
/*
* We'd like PNP to call this routine for the
* single ISA_USED value for each legacy device.
* But instead it calls us with each POSSIBLE setting.
* There is no ISA_POSSIBLE weight, so we simply use
* the (small) PCI_USING penalty.
*/
2005-04-01 00:07:31 -05:00
void acpi_penalize_isa_irq(int irq, int active)
2005-04-16 15:20:36 -07:00
{
if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) {
if (active)
acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED;
else
acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING;
}
2005-04-16 15:20:36 -07:00
}
/*
* Over-ride default table to reserve additional IRQs for use by ISA
* e.g. acpi_irq_isa=5
* Useful for telling ACPI how not to interfere with your ISA sound card.
*/
static int __init acpi_irq_isa(char *str)
{
return acpi_irq_penalty_update(str, 1);
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
__setup("acpi_irq_isa=", acpi_irq_isa);
/*
* Over-ride default table to free additional IRQs for use by PCI
* e.g. acpi_irq_pci=7,15
* Used for acpi_irq_balance to free up IRQs to reduce PCI IRQ sharing.
*/
static int __init acpi_irq_pci(char *str)
{
return acpi_irq_penalty_update(str, 0);
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
__setup("acpi_irq_pci=", acpi_irq_pci);
static int __init acpi_irq_nobalance_set(char *str)
{
acpi_irq_balance = 0;
return 1;
}
2005-08-05 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
__setup("acpi_irq_nobalance", acpi_irq_nobalance_set);
static int __init acpi_irq_balance_set(char *str)
2005-04-16 15:20:36 -07:00
{
acpi_irq_balance = 1;
return 1;
}
2005-08-05 00:44:28 -04:00
__setup("acpi_irq_balance", acpi_irq_balance_set);
2005-04-16 15:20:36 -07:00
static struct syscore_ops irqrouter_syscore_ops = {
2005-08-05 00:44:28 -04:00
.resume = irqrouter_resume,
2005-04-16 15:20:36 -07:00
};
static int __init irqrouter_init_ops(void)
2005-04-16 15:20:36 -07:00
{
if (!acpi_disabled && !acpi_noirq)
register_syscore_ops(&irqrouter_syscore_ops);
2005-04-16 15:20:36 -07:00
return 0;
2005-08-05 00:44:28 -04:00
}
2005-04-16 15:20:36 -07:00
device_initcall(irqrouter_init_ops);
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
static int __init acpi_pci_link_init(void)
2005-04-16 15:20:36 -07:00
{
if (acpi_noirq)
return 0;
2005-04-16 15:20:36 -07:00
if (acpi_irq_balance == -1) {
/* no command line switch: enable balancing in IOAPIC mode */
if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC)
acpi_irq_balance = 1;
else
acpi_irq_balance = 0;
}
2005-04-16 15:20:36 -07:00
if (acpi_bus_register_driver(&acpi_pci_link_driver) < 0)
return -ENODEV;
2005-04-16 15:20:36 -07:00
return 0;
2005-04-16 15:20:36 -07:00
}
subsys_initcall(acpi_pci_link_init);