pci_irq.c 12.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 *  pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $)
 *
 *  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.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */


28
#include <linux/dmi.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41
#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/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

#define _COMPONENT		ACPI_PCI_COMPONENT
42
ACPI_MODULE_NAME("pci_irq");
L
Linus Torvalds 已提交
43

44
struct acpi_prt_entry {
45
	struct list_head	list;
46 47
	struct acpi_pci_id	id;
	u8			pin;
48 49
	acpi_handle		link;
	u32			index;		/* GSI, or link _CRS index */
50 51
};

52
static LIST_HEAD(acpi_prt_list);
L
Linus Torvalds 已提交
53 54
static DEFINE_SPINLOCK(acpi_prt_lock);

55 56
static inline char pin_name(int pin)
{
57
	return 'A' + pin - 1;
58 59
}

L
Linus Torvalds 已提交
60 61 62 63
/* --------------------------------------------------------------------------
                         PCI IRQ Routing Table (PRT) Support
   -------------------------------------------------------------------------- */

64 65
static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(struct pci_dev *dev,
							  int pin)
L
Linus Torvalds 已提交
66
{
67
	struct acpi_prt_entry *entry;
68 69 70
	int segment = pci_domain_nr(dev->bus);
	int bus = dev->bus->number;
	int device = PCI_SLOT(dev->devfn);
L
Linus Torvalds 已提交
71 72

	spin_lock(&acpi_prt_lock);
73
	list_for_each_entry(entry, &acpi_prt_list, list) {
L
Len Brown 已提交
74 75 76 77
		if ((segment == entry->id.segment)
		    && (bus == entry->id.bus)
		    && (device == entry->id.device)
		    && (pin == entry->pin)) {
L
Linus Torvalds 已提交
78
			spin_unlock(&acpi_prt_lock);
79
			return entry;
L
Linus Torvalds 已提交
80 81 82
		}
	}
	spin_unlock(&acpi_prt_lock);
83
	return NULL;
L
Linus Torvalds 已提交
84 85
}

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */
static struct dmi_system_id medion_md9580[] = {
	{
		.ident = "Medion MD9580-F laptop",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
			DMI_MATCH(DMI_PRODUCT_NAME, "A555"),
		},
	},
	{ }
};

/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */
static struct dmi_system_id dell_optiplex[] = {
	{
		.ident = "Dell Optiplex GX1",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"),
		},
	},
	{ }
};

/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */
static struct dmi_system_id hp_t5710[] = {
	{
		.ident = "HP t5710",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
			DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"),
			DMI_MATCH(DMI_BOARD_NAME, "098Ch"),
		},
	},
	{ }
};

struct prt_quirk {
	struct dmi_system_id	*system;
	unsigned int		segment;
	unsigned int		bus;
	unsigned int		device;
	unsigned char		pin;
	char			*source;	/* according to BIOS */
	char			*actual_source;
};

133 134
#define PCI_INTX_PIN(c)		(c - 'A' + 1)

135 136 137 138 139 140
/*
 * These systems have incorrect _PRT entries.  The BIOS claims the PCI
 * interrupt at the listed segment/bus/device/pin is connected to the first
 * link device, but it is actually connected to the second.
 */
static struct prt_quirk prt_quirks[] = {
141
	{ medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'),
142 143
		"\\_SB_.PCI0.ISA_.LNKA",
		"\\_SB_.PCI0.ISA_.LNKB"},
144
	{ dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'),
145 146
		"\\_SB_.LNKB",
		"\\_SB_.LNKA"},
147
	{ hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'),
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
		"\\_SB_.PCI0.LNK1",
		"\\_SB_.PCI0.LNK3"},
};

static void
do_prt_fixups(struct acpi_prt_entry *entry, struct acpi_pci_routing_table *prt)
{
	int i;
	struct prt_quirk *quirk;

	for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) {
		quirk = &prt_quirks[i];

		/* All current quirks involve link devices, not GSIs */
		if (!prt->source)
			continue;

		if (dmi_check_system(quirk->system) &&
		    entry->id.segment == quirk->segment &&
		    entry->id.bus == quirk->bus &&
		    entry->id.device == quirk->device &&
169
		    entry->pin == quirk->pin &&
170 171 172
		    !strcmp(prt->source, quirk->source) &&
		    strlen(prt->source) >= strlen(quirk->actual_source)) {
			printk(KERN_WARNING PREFIX "firmware reports "
173
				"%04x:%02x:%02x PCI INT %c connected to %s; "
174 175
				"changing to %s\n",
				entry->id.segment, entry->id.bus,
176
				entry->id.device, pin_name(entry->pin),
177 178 179 180 181 182
				prt->source, quirk->actual_source);
			strcpy(prt->source, quirk->actual_source);
		}
	}
}

L
Linus Torvalds 已提交
183
static int
L
Len Brown 已提交
184 185
acpi_pci_irq_add_entry(acpi_handle handle,
		       int segment, int bus, struct acpi_pci_routing_table *prt)
L
Linus Torvalds 已提交
186
{
187
	struct acpi_prt_entry *entry;
L
Linus Torvalds 已提交
188

189
	entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
L
Linus Torvalds 已提交
190
	if (!entry)
191
		return -ENOMEM;
L
Linus Torvalds 已提交
192

193 194 195 196 197
	/*
	 * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses
	 * 1=INTA, 2=INTB.  We use the PCI encoding throughout, so convert
	 * it here.
	 */
L
Linus Torvalds 已提交
198 199 200
	entry->id.segment = segment;
	entry->id.bus = bus;
	entry->id.device = (prt->address >> 16) & 0xFFFF;
201
	entry->pin = prt->pin + 1;
L
Linus Torvalds 已提交
202

203 204
	do_prt_fixups(entry, prt);

205 206
	entry->index = prt->source_index;

L
Linus Torvalds 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219
	/*
	 * Type 1: Dynamic
	 * ---------------
	 * The 'source' field specifies the PCI interrupt link device used to
	 * configure the IRQ assigned to this slot|dev|pin.  The 'source_index'
	 * indicates which resource descriptor in the resource template (of
	 * the link device) this interrupt is allocated from.
	 * 
	 * NOTE: Don't query the Link Device for IRQ information at this time
	 *       because Link Device enumeration may not have occurred yet
	 *       (e.g. exists somewhere 'below' this _PRT entry in the ACPI
	 *       namespace).
	 */
220 221 222
	if (prt->source[0])
		acpi_get_handle(handle, prt->source, &entry->link);

L
Linus Torvalds 已提交
223 224 225 226 227 228 229 230 231
	/*
	 * Type 2: Static
	 * --------------
	 * The 'source' field is NULL, and the 'source_index' field specifies
	 * the IRQ value, which is hardwired to specific interrupt inputs on
	 * the interrupt controller.
	 */

	ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
232
			      "      %04x:%02x:%02x[%c] -> %s[%d]\n",
L
Len Brown 已提交
233
			      entry->id.segment, entry->id.bus,
234
			      entry->id.device, pin_name(entry->pin),
235
			      prt->source, entry->index));
L
Linus Torvalds 已提交
236 237

	spin_lock(&acpi_prt_lock);
238
	list_add_tail(&entry->list, &acpi_prt_list);
L
Linus Torvalds 已提交
239 240
	spin_unlock(&acpi_prt_lock);

241
	return 0;
L
Linus Torvalds 已提交
242 243
}

L
Len Brown 已提交
244
int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
L
Linus Torvalds 已提交
245
{
246 247 248
	acpi_status status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	struct acpi_pci_routing_table *entry;
L
Linus Torvalds 已提交
249

250 251 252 253
	/* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */
	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
	if (ACPI_FAILURE(status))
		return -ENODEV;
L
Linus Torvalds 已提交
254 255

	printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n",
256
	       (char *) buffer.pointer);
L
Linus Torvalds 已提交
257

258
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
259

260
	buffer.length = ACPI_ALLOCATE_BUFFER;
L
Linus Torvalds 已提交
261 262 263 264
	buffer.pointer = NULL;

	status = acpi_get_irq_routing_table(handle, &buffer);
	if (ACPI_FAILURE(status)) {
265 266
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]",
				acpi_format_exception(status)));
L
Linus Torvalds 已提交
267
		kfree(buffer.pointer);
268
		return -ENODEV;
L
Linus Torvalds 已提交
269 270
	}

271
	entry = buffer.pointer;
L
Linus Torvalds 已提交
272 273 274
	while (entry && (entry->length > 0)) {
		acpi_pci_irq_add_entry(handle, segment, bus, entry);
		entry = (struct acpi_pci_routing_table *)
L
Len Brown 已提交
275
		    ((unsigned long)entry + entry->length);
L
Linus Torvalds 已提交
276 277
	}

278
	kfree(buffer.pointer);
279
	return 0;
L
Linus Torvalds 已提交
280 281
}

L
Len Brown 已提交
282
void acpi_pci_irq_del_prt(int segment, int bus)
L
Linus Torvalds 已提交
283
{
284
	struct acpi_prt_entry *entry, *tmp;
L
Linus Torvalds 已提交
285

L
Len Brown 已提交
286
	printk(KERN_DEBUG
287 288
	       "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n",
	       segment, bus);
L
Linus Torvalds 已提交
289
	spin_lock(&acpi_prt_lock);
290 291 292 293 294
	list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) {
		if (segment == entry->id.segment && bus == entry->id.bus) {
			list_del(&entry->list);
			kfree(entry);
		}
L
Linus Torvalds 已提交
295 296 297
	}
	spin_unlock(&acpi_prt_lock);
}
L
Len Brown 已提交
298

L
Linus Torvalds 已提交
299 300 301
/* --------------------------------------------------------------------------
                          PCI Interrupt Routing Support
   -------------------------------------------------------------------------- */
302 303
static struct acpi_prt_entry *
acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
L
Linus Torvalds 已提交
304
{
305
	struct acpi_prt_entry *entry;
306 307
	struct pci_dev *bridge;
	u8 bridge_pin, orig_pin = pin;
L
Linus Torvalds 已提交
308

309
	entry = acpi_pci_irq_find_prt_entry(dev, pin);
310 311
	if (entry) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n",
B
Bjorn Helgaas 已提交
312
				  pci_name(dev), pin_name(pin)));
313
		return entry;
L
Linus Torvalds 已提交
314
	}
L
Len Brown 已提交
315

L
Linus Torvalds 已提交
316 317 318 319
	/* 
	 * Attempt to derive an IRQ for this device from a parent bridge's
	 * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge).
	 */
320 321 322
	bridge = dev->bus->self;
	while (bridge) {
		pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1;
L
Linus Torvalds 已提交
323 324 325

		if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) {
			/* PC card has the same IRQ as its cardbridge */
326
			bridge_pin = bridge->pin;
L
Linus Torvalds 已提交
327
			if (!bridge_pin) {
L
Len Brown 已提交
328 329 330
				ACPI_DEBUG_PRINT((ACPI_DB_INFO,
						  "No interrupt pin configured for device %s\n",
						  pci_name(bridge)));
331
				return NULL;
L
Linus Torvalds 已提交
332 333 334 335
			}
			pin = bridge_pin;
		}

336
		entry = acpi_pci_irq_find_prt_entry(bridge, pin);
337 338 339 340 341 342 343
		if (entry) {
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
					 "Derived GSI for %s INT %c from %s\n",
					 pci_name(dev), pin_name(orig_pin),
					 pci_name(bridge)));
			return entry;
		}
344 345 346

		dev = bridge;
		bridge = dev->bus->self;
L
Linus Torvalds 已提交
347 348
	}

349 350 351
	dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n",
		 pin_name(orig_pin));
	return NULL;
L
Linus Torvalds 已提交
352 353 354 355 356 357 358 359
}

/*
 * acpi_pci_irq_enable
 * success: return 0
 * failure: return < 0
 */

L
Len Brown 已提交
360
int acpi_pci_irq_enable(struct pci_dev *dev)
L
Linus Torvalds 已提交
361
{
362
	struct acpi_prt_entry *entry;
363
	int gsi = 0;
L
Len Brown 已提交
364
	u8 pin = 0;
B
Bob Moore 已提交
365 366
	int triggering = ACPI_LEVEL_SENSITIVE;
	int polarity = ACPI_ACTIVE_LOW;
L
Len Brown 已提交
367
	char *link = NULL;
368
	char link_desc[16];
L
Len Brown 已提交
369
	int rc;
L
Linus Torvalds 已提交
370 371


372
	pin = dev->pin;
L
Linus Torvalds 已提交
373
	if (!pin) {
L
Len Brown 已提交
374 375 376
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "No interrupt pin configured for device %s\n",
				  pci_name(dev)));
377
		return 0;
L
Linus Torvalds 已提交
378 379
	}

380
	entry = acpi_pci_irq_lookup(dev, pin);
381
	if (!entry) {
382 383 384 385 386 387 388 389
		/*
		 * IDE legacy mode controller IRQs are magic. Why do compat
		 * extensions always make such a nasty mess.
		 */
		if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE &&
				(dev->class & 0x05) == 0)
			return 0;
	}
390

391 392 393 394 395 396 397 398 399
	if (entry) {
		if (entry->link)
			gsi = acpi_pci_link_allocate_irq(entry->link,
							 entry->index,
							 &triggering, &polarity,
							 &link);
		else
			gsi = entry->index;
	} else
400 401
		gsi = -1;

L
Linus Torvalds 已提交
402 403 404 405
	/*
	 * No IRQ known to the ACPI subsystem - maybe the BIOS / 
	 * driver reported one, then use it. Exit in any case.
	 */
406
	if (gsi < 0) {
407
		dev_warn(&dev->dev, "PCI INT %c: no GSI", pin_name(pin));
L
Linus Torvalds 已提交
408
		/* Interrupt Line values above 0xF are forbidden */
409
		if (dev->irq > 0 && (dev->irq <= 0xF)) {
L
Linus Torvalds 已提交
410
			printk(" - using IRQ %d\n", dev->irq);
L
Len Brown 已提交
411 412
			acpi_register_gsi(dev->irq, ACPI_LEVEL_SENSITIVE,
					  ACPI_ACTIVE_LOW);
413
			return 0;
L
Len Brown 已提交
414
		} else {
L
Linus Torvalds 已提交
415
			printk("\n");
416
			return 0;
L
Linus Torvalds 已提交
417
		}
L
Len Brown 已提交
418
	}
L
Linus Torvalds 已提交
419

420
	rc = acpi_register_gsi(gsi, triggering, polarity);
421
	if (rc < 0) {
422
		dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
423
			 pin_name(pin));
424
		return rc;
425 426
	}
	dev->irq = rc;
L
Linus Torvalds 已提交
427 428

	if (link)
429 430 431
		snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
	else
		link_desc[0] = '\0';
L
Linus Torvalds 已提交
432

433
	dev_info(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n",
434
		 pin_name(pin), link_desc, gsi,
435 436
		 (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
		 (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
L
Linus Torvalds 已提交
437

438
	return 0;
L
Linus Torvalds 已提交
439 440
}

441
/* FIXME: implement x86/x86_64 version */
L
Len Brown 已提交
442 443 444
void __attribute__ ((weak)) acpi_unregister_gsi(u32 i)
{
}
445

L
Len Brown 已提交
446
void acpi_pci_irq_disable(struct pci_dev *dev)
L
Linus Torvalds 已提交
447
{
448
	struct acpi_prt_entry *entry;
L
Len Brown 已提交
449 450
	int gsi = 0;
	u8 pin = 0;
L
Linus Torvalds 已提交
451 452


453
	pin = dev->pin;
L
Linus Torvalds 已提交
454
	if (!pin)
455
		return;
L
Linus Torvalds 已提交
456

457 458
	entry = acpi_pci_irq_lookup(dev, pin);
	if (!entry)
459
		return;
L
Linus Torvalds 已提交
460

461 462 463 464
	if (entry->link)
		gsi = acpi_pci_link_free_irq(entry->link);
	else
		gsi = entry->index;
465

L
Linus Torvalds 已提交
466 467 468 469 470
	/*
	 * TBD: It might be worth clearing dev->irq by magic constant
	 * (e.g. PCI_UNDEFINED_IRQ).
	 */

471
	dev_info(&dev->dev, "PCI INT %c disabled\n", pin_name(pin));
L
Linus Torvalds 已提交
472 473
	acpi_unregister_gsi(gsi);
}