pci_link.c 16.6 KB
Newer Older
Andy Grover's avatar
Andy Grover committed
1
/*
2
 *  pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 34 $)
Andy Grover's avatar
Andy Grover committed
3 4 5
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
Andy Grover's avatar
Andy Grover committed
6
 *  Copyright (C) 2002       Dominik Brodowski <devel@brodo.de>
Andy Grover's avatar
Andy Grover committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  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: 
Andy Grover's avatar
Andy Grover committed
27
 *      1. Support more than one IRQ resource entry per link device (index).
Andy Grover's avatar
Andy Grover committed
28 29 30 31 32 33 34 35 36 37 38 39 40
 *	2. Implement start/stop mechanism and use ACPI Bus Driver facilities
 *	   for IRQ management (e.g. start()->_SRS).
 */

#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>

41 42
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
Andy Grover's avatar
Andy Grover committed
43 44


Andy Grover's avatar
Andy Grover committed
45 46
#define _COMPONENT		ACPI_PCI_COMPONENT
ACPI_MODULE_NAME		("pci_link")
Andy Grover's avatar
Andy Grover committed
47

48 49 50 51 52 53 54
#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"

Andy Grover's avatar
Andy Grover committed
55
#define ACPI_PCI_LINK_MAX_POSSIBLE 16
Andy Grover's avatar
Andy Grover committed
56 57 58 59 60

static 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 = {
61 62 63 64 65 66
	.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,
67
			},
Andy Grover's avatar
Andy Grover committed
68 69 70 71
};

struct acpi_pci_link_irq {
	u8			active;			/* Current IRQ */
72 73 74
	u8			edge_level;		/* All IRQs */
	u8			active_high_low;	/* All IRQs */
	u8			setonboot;
Andy Grover's avatar
Andy Grover committed
75
	u8			possible_count;
Andy Grover's avatar
Andy Grover committed
76
	u8			possible[ACPI_PCI_LINK_MAX_POSSIBLE];
Andy Grover's avatar
Andy Grover committed
77 78 79
};

struct acpi_pci_link {
Andy Grover's avatar
Andy Grover committed
80 81
	struct list_head	node;
	struct acpi_device	*device;
Andy Grover's avatar
Andy Grover committed
82 83 84 85
	acpi_handle		handle;
	struct acpi_pci_link_irq irq;
};

Andy Grover's avatar
Andy Grover committed
86 87 88 89 90
static struct {
	int			count;
	struct list_head	entries;
}				acpi_link;

Andy Grover's avatar
Andy Grover committed
91 92 93 94 95

/* --------------------------------------------------------------------------
                            PCI Link Device Management
   -------------------------------------------------------------------------- */

96 97 98 99
static acpi_status
acpi_pci_link_check_possible (
	struct acpi_resource	*resource,
	void			*context)
Andy Grover's avatar
Andy Grover committed
100
{
101
	struct acpi_pci_link	*link = (struct acpi_pci_link *) context;
Andy Grover's avatar
Andy Grover committed
102
	int			i = 0;
Andy Grover's avatar
Andy Grover committed
103

104
	ACPI_FUNCTION_TRACE("acpi_pci_link_check_possible");
105

Andy Grover's avatar
Andy Grover committed
106
	switch (resource->id) {
107 108
	case ACPI_RSTYPE_START_DPF:
		return AE_OK;
Andy Grover's avatar
Andy Grover committed
109 110
	case ACPI_RSTYPE_IRQ:
	{
111
		struct acpi_resource_irq *p = &resource->data.irq;
Andy Grover's avatar
Andy Grover committed
112
		if (!p || !p->number_of_interrupts) {
Andy Grover's avatar
Andy Grover committed
113
			ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n"));
114
			return AE_OK;
Andy Grover's avatar
Andy Grover committed
115
		}
Andy Grover's avatar
Andy Grover committed
116 117 118 119 120
		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;
			}
Andy Grover's avatar
Andy Grover committed
121 122 123
			link->irq.possible[i] = p->interrupts[i];
			link->irq.possible_count++;
		}
124 125
		link->irq.edge_level = p->edge_level;
		link->irq.active_high_low = p->active_high_low;
Andy Grover's avatar
Andy Grover committed
126 127 128 129
		break;
	}
	case ACPI_RSTYPE_EXT_IRQ:
	{
130
		struct acpi_resource_ext_irq *p = &resource->data.extended_irq;
Andy Grover's avatar
Andy Grover committed
131 132 133
		if (!p || !p->number_of_interrupts) {
			ACPI_DEBUG_PRINT((ACPI_DB_WARN, 
				"Blank IRQ resource\n"));
134
			return AE_OK;
Andy Grover's avatar
Andy Grover committed
135
		}
Andy Grover's avatar
Andy Grover committed
136 137 138 139 140
		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;
			}
Andy Grover's avatar
Andy Grover committed
141 142 143
			link->irq.possible[i] = p->interrupts[i];
			link->irq.possible_count++;
		}
144 145
		link->irq.edge_level = p->edge_level;
		link->irq.active_high_low = p->active_high_low;
Andy Grover's avatar
Andy Grover committed
146 147 148 149
		break;
	}
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
Andy Grover's avatar
Andy Grover committed
150
			"Resource is not an IRQ entry\n"));
151
		return AE_OK;
Andy Grover's avatar
Andy Grover committed
152
	}
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

	return AE_CTRL_TERMINATE;
}


static int
acpi_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);
	}

Andy Grover's avatar
Andy Grover committed
176 177
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, 
		"Found %d possible IRQs\n", link->irq.possible_count));
Andy Grover's avatar
Andy Grover committed
178

179 180
	return_VALUE(0);
}
Andy Grover's avatar
Andy Grover committed
181

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

static acpi_status
acpi_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) {
			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
				"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) {
			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
				"Blank 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;
Andy Grover's avatar
Andy Grover committed
221 222 223
}

static int
Andy Grover's avatar
Andy Grover committed
224
acpi_pci_link_get_current (
Andy Grover's avatar
Andy Grover committed
225 226
	struct acpi_pci_link	*link)
{
Andy Grover's avatar
Andy Grover committed
227
	int			result = 0;
Andy Grover's avatar
Andy Grover committed
228
	acpi_status		status = AE_OK;
Andy Grover's avatar
Andy Grover committed
229
	int			irq = 0;
Andy Grover's avatar
Andy Grover committed
230

Andy Grover's avatar
Andy Grover committed
231
	ACPI_FUNCTION_TRACE("acpi_pci_link_get_current");
Andy Grover's avatar
Andy Grover committed
232

Andy Grover's avatar
Andy Grover committed
233
	if (!link || !link->handle)
Andy Grover's avatar
Andy Grover committed
234 235
		return_VALUE(-EINVAL);

Andy Grover's avatar
Andy Grover committed
236
	link->irq.active = 0;
Andy Grover's avatar
Andy Grover committed
237

Andy Grover's avatar
Andy Grover committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
	/* Make sure the link is enabled (no use querying if it isn't). */
	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. 
	 */

253 254
	status = acpi_walk_resources(link->handle, METHOD_NAME__CRS,
			acpi_pci_link_check_current, &irq);
Andy Grover's avatar
Andy Grover committed
255
	if (ACPI_FAILURE(status)) {
Andy Grover's avatar
Andy Grover committed
256
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _CRS\n"));
Andy Grover's avatar
Andy Grover committed
257 258 259 260
		result = -ENODEV;
		goto end;
	}

Andy Grover's avatar
Andy Grover committed
261
	if (!irq) {
262
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No IRQ resource found\n"));
Andy Grover's avatar
Andy Grover committed
263
		result = -ENODEV;
Andy Grover's avatar
Andy Grover committed
264
		goto end;
Andy Grover's avatar
Andy Grover committed
265 266
	}

Andy Grover's avatar
Andy Grover committed
267 268 269 270 271 272 273 274 275 276
	/*
	 * Note that we don't validate that the current IRQ (_CRS) exists
	 * within the possible IRQs (_PRS): we blindly assume that whatever
	 * IRQ a boot-enabled Link device is set to is the correct one.
	 * (Required to support systems such as the Toshiba 5005-S504.)
	 */
	link->irq.active = irq;

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active));

Andy Grover's avatar
Andy Grover committed
277 278 279 280
end:
	return_VALUE(result);
}

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
static int
acpi_pci_link_try_get_current (
	struct acpi_pci_link *link,
	int irq)
{
	int result;

	ACPI_FUNCTION_TRACE("acpi_pci_link_try_get_current");

	result = acpi_pci_link_get_current(link);
	if (result && link->irq.active) {
 		return_VALUE(result);
 	}

	if (!link->irq.active) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No active IRQ resource found\n"));
		printk(KERN_WARNING "_CRS returns NULL! Using IRQ %d for device (%s [%s]).\n", irq, acpi_device_name(link->device), acpi_device_bid(link->device));
		link->irq.active = irq;
	}
	
	return 0;
}
Andy Grover's avatar
Andy Grover committed
303 304 305 306

static int
acpi_pci_link_set (
	struct acpi_pci_link	*link,
Andy Grover's avatar
Andy Grover committed
307
	int			irq)
Andy Grover's avatar
Andy Grover committed
308
{
Andy Grover's avatar
Andy Grover committed
309
	int			result = 0;
Andy Grover's avatar
Andy Grover committed
310 311
	acpi_status		status = AE_OK;
	struct {
312 313
		struct acpi_resource	res;
		struct acpi_resource	end;
Andy Grover's avatar
Andy Grover committed
314
	}                       resource;
315
	struct acpi_buffer	buffer = {sizeof(resource)+1, &resource};
Andy Grover's avatar
Andy Grover committed
316 317
	int			i = 0;
	int			valid = 0;
Andy Grover's avatar
Andy Grover committed
318 319 320 321 322 323

	ACPI_FUNCTION_TRACE("acpi_pci_link_set");

	if (!link || !irq)
		return_VALUE(-EINVAL);

324 325 326 327 328
	/* We don't check irqs the first time around */
	if (link->irq.setonboot) {
		/* See if we're already at the target IRQ. */
		if (irq == link->irq.active)
			return_VALUE(0);
Andy Grover's avatar
Andy Grover committed
329

330 331 332 333 334 335 336 337 338
		/* Make sure the target IRQ in the list of possible IRQs. */
		for (i=0; i<link->irq.possible_count; i++) {
			if (irq == link->irq.possible[i])
				valid = 1;
		}
		if (!valid) {
			ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ %d invalid\n", irq));
			return_VALUE(-EINVAL);
		}
Andy Grover's avatar
Andy Grover committed
339 340
	}

Andy Grover's avatar
Andy Grover committed
341 342
	memset(&resource, 0, sizeof(resource));

343 344 345
	/* NOTE: PCI interrupts are always level / active_low / shared. But not all
	   interrupts > 15 are PCI interrupts. Rely on the ACPI IRQ definition for 
	   parameters */
346 347 348
	if (irq <= 15) {
		resource.res.id = ACPI_RSTYPE_IRQ;
		resource.res.length = sizeof(struct acpi_resource);
349 350
		resource.res.data.irq.edge_level = link->irq.edge_level;
		resource.res.data.irq.active_high_low = link->irq.active_high_low;
351 352 353 354 355 356 357
		resource.res.data.irq.number_of_interrupts = 1;
		resource.res.data.irq.interrupts[0] = irq;
	}
	else {
		resource.res.id = ACPI_RSTYPE_EXT_IRQ;
		resource.res.length = sizeof(struct acpi_resource);
		resource.res.data.extended_irq.producer_consumer = ACPI_CONSUMER;
358 359
		resource.res.data.extended_irq.edge_level = link->irq.edge_level;
		resource.res.data.extended_irq.active_high_low = link->irq.active_high_low;
360 361 362 363
		resource.res.data.extended_irq.number_of_interrupts = 1;
		resource.res.data.extended_irq.interrupts[0] = irq;
		/* ignore resource_source, it's optional */
	}
364
	resource.end.id = ACPI_RSTYPE_END_TAG;
Andy Grover's avatar
Andy Grover committed
365

366
	/* Attempt to set the resource */
Andy Grover's avatar
Andy Grover committed
367 368 369 370 371 372
	status = acpi_set_current_resources(link->handle, &buffer);
	if (ACPI_FAILURE(status)) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n"));
		return_VALUE(-ENODEV);
	}

Andy Grover's avatar
Andy Grover committed
373 374 375 376 377
	/* Make sure the device is enabled. */
	result = acpi_bus_get_status(link->device);
	if (result) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n"));
		return_VALUE(result);
Andy Grover's avatar
Andy Grover committed
378
	}
Andy Grover's avatar
Andy Grover committed
379 380
	if (!link->device->status.enabled) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n"));
Andy Grover's avatar
Andy Grover committed
381 382 383
		return_VALUE(-ENODEV);
	}

Andy Grover's avatar
Andy Grover committed
384
	/* Make sure the active IRQ is the one we requested. */
385
	result = acpi_pci_link_try_get_current(link, irq);
Andy Grover's avatar
Andy Grover committed
386 387
	if (result) {
		return_VALUE(result);
Andy Grover's avatar
Andy Grover committed
388
	}
389
   
Andy Grover's avatar
Andy Grover committed
390
	if (link->irq.active != irq) {
Andy Grover's avatar
Andy Grover committed
391
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
Andy Grover's avatar
Andy Grover committed
392 393 394
			"Attempt to enable at IRQ %d resulted in IRQ %d\n", 
			irq, link->irq.active));
		link->irq.active = 0;
395
		acpi_ut_evaluate_object (link->handle, "_DIS", 0, NULL);	   
Andy Grover's avatar
Andy Grover committed
396
		return_VALUE(-ENODEV);
Andy Grover's avatar
Andy Grover committed
397 398
	}

Andy Grover's avatar
Andy Grover committed
399 400
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active));
	
Andy Grover's avatar
Andy Grover committed
401 402 403 404
	return_VALUE(0);
}


Andy Grover's avatar
Andy Grover committed
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
/* --------------------------------------------------------------------------
                            PCI Link IRQ Management
   -------------------------------------------------------------------------- */

#define ACPI_MAX_IRQS		256
#define ACPI_MAX_ISA_IRQ	16

/*
 * IRQ penalties are used to promote PCI IRQ balancing.  We set each ISA-
 * possible IRQ (0-15) with a default penalty relative to its feasibility
 * for PCI's use:
 *
 *   Never use:		0, 1, 2 (timer, keyboard, and cascade)
 *   Avoid using:	13, 14, and 15 (FP error and IDE)
 *   Penalize:		3, 4, 6, 7, 12 (known ISA uses)
 *
 * Thus we're left with IRQs 5, 9, 10, 11, and everything above 15 (IO[S]APIC)
 * as 'best bets' for PCI use.
 */

static int acpi_irq_penalty[ACPI_MAX_IRQS] = {
	1000000,  1000000,  1000000,    10000, 
	  10000,        0,    10000,    10000,
	  10000,        0,        0,        0, 
	  10000,   100000,   100000,   100000,
};


Andy Grover's avatar
Andy Grover committed
433
int
Andy Grover's avatar
Andy Grover committed
434
acpi_pci_link_check (void)
Andy Grover's avatar
Andy Grover committed
435
{
Andy Grover's avatar
Andy Grover committed
436 437
	struct list_head	*node = NULL;
	struct acpi_pci_link    *link = NULL;
Andy Grover's avatar
Andy Grover committed
438 439
	int			i = 0;

Andy Grover's avatar
Andy Grover committed
440
	ACPI_FUNCTION_TRACE("acpi_pci_link_check");
Andy Grover's avatar
Andy Grover committed
441

Andy Grover's avatar
Andy Grover committed
442
	/*
443
	 * Update penalties to facilitate IRQ balancing.
Andy Grover's avatar
Andy Grover committed
444 445
	 */
	list_for_each(node, &acpi_link.entries) {
Andy Grover's avatar
Andy Grover committed
446

Andy Grover's avatar
Andy Grover committed
447 448 449 450 451
		link = list_entry(node, struct acpi_pci_link, node);
		if (!link) {
			ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
			continue;
		}
Andy Grover's avatar
Andy Grover committed
452

Andy Grover's avatar
Andy Grover committed
453 454
		if (link->irq.active)
			acpi_irq_penalty[link->irq.active] += 100;
455
		else if (link->irq.possible_count) {
Andy Grover's avatar
Andy Grover committed
456 457 458 459 460 461
			int penalty = 100 / link->irq.possible_count;
			for (i=0; i<link->irq.possible_count; i++) {
				if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ)
					acpi_irq_penalty[link->irq.possible[i]] += penalty;
			}
		}
Andy Grover's avatar
Andy Grover committed
462 463
	}

464 465
	return_VALUE(0);
}
Andy Grover's avatar
Andy Grover committed
466

467 468 469
static int acpi_pci_link_allocate(struct acpi_pci_link* link) {
	int irq;
	int i;
Andy Grover's avatar
Andy Grover committed
470

471 472 473 474
	ACPI_FUNCTION_TRACE("acpi_pci_link_allocate");

	if (link->irq.setonboot)
		return_VALUE(0);
Andy Grover's avatar
Andy Grover committed
475

476 477 478
	if (link->irq.active) {
		irq = link->irq.active;
	} else {
Andy Grover's avatar
Andy Grover committed
479
		irq = link->irq.possible[0];
480
	}
Andy Grover's avatar
Andy Grover committed
481

Andy Grover's avatar
Andy Grover committed
482 483 484 485 486 487 488 489
		/* 
		 * 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--) {
			if (acpi_irq_penalty[irq] > acpi_irq_penalty[link->irq.possible[i]])
				irq = link->irq.possible[i];
		}
Andy Grover's avatar
Andy Grover committed
490

491 492 493 494 495 496 497
	/* Attempt to enable the link device at this IRQ. */
	if (acpi_pci_link_set(link, irq)) {
		printk(PREFIX "Unable to set IRQ for %s [%s] (likely buggy ACPI BIOS). Aborting ACPI-based IRQ routing. Try pci=noacpi or acpi=off\n",
			acpi_device_name(link->device),
			acpi_device_bid(link->device));
		return_VALUE(-ENODEV);
	} else {
Andy Grover's avatar
Andy Grover committed
498 499
		acpi_irq_penalty[link->irq.active] += 100;
		printk(PREFIX "%s [%s] enabled at IRQ %d\n", 
500 501
			acpi_device_name(link->device),
			acpi_device_bid(link->device), link->irq.active);
Andy Grover's avatar
Andy Grover committed
502 503
	}

504
	link->irq.setonboot = 1;
Andy Grover's avatar
Andy Grover committed
505
	return_VALUE(0);
Andy Grover's avatar
Andy Grover committed
506 507 508
}


Andy Grover's avatar
Andy Grover committed
509 510 511
int
acpi_pci_link_get_irq (
	acpi_handle		handle,
512 513 514
	int			index,
	int*			edge_level,
	int*			active_high_low)
Andy Grover's avatar
Andy Grover committed
515
{
Andy Grover's avatar
Andy Grover committed
516 517 518
	int                     result = 0;
	struct acpi_device	*device = NULL;
	struct acpi_pci_link	*link = NULL;
Andy Grover's avatar
Andy Grover committed
519

Andy Grover's avatar
Andy Grover committed
520
	ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq");
Andy Grover's avatar
Andy Grover committed
521

Andy Grover's avatar
Andy Grover committed
522 523 524 525
	result = acpi_bus_get_device(handle, &device);
	if (result) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n"));
		return_VALUE(0);
Andy Grover's avatar
Andy Grover committed
526 527
	}

Andy Grover's avatar
Andy Grover committed
528 529 530 531
	link = (struct acpi_pci_link *) acpi_driver_data(device);
	if (!link) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
		return_VALUE(0);
Andy Grover's avatar
Andy Grover committed
532 533
	}

Andy Grover's avatar
Andy Grover committed
534 535 536 537
	/* TBD: Support multiple index (IRQ) entries per Link Device */
	if (index) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid index %d\n", index));
		return_VALUE(0);
Andy Grover's avatar
Andy Grover committed
538 539
	}

Andrew Morton's avatar
Andrew Morton committed
540 541
	if (acpi_pci_link_allocate(link))
		return_VALUE(0);
542
	   
Andy Grover's avatar
Andy Grover committed
543 544 545 546
	if (!link->irq.active) {
		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n"));
		return_VALUE(0);
	}
Andy Grover's avatar
Andy Grover committed
547

548 549
	if (edge_level) *edge_level = link->irq.edge_level;
	if (active_high_low) *active_high_low = link->irq.active_high_low;
Andy Grover's avatar
Andy Grover committed
550
	return_VALUE(link->irq.active);
Andy Grover's avatar
Andy Grover committed
551 552 553
}


Andy Grover's avatar
Andy Grover committed
554 555 556 557 558 559 560 561 562 563 564
/* --------------------------------------------------------------------------
                                 Driver Interface
   -------------------------------------------------------------------------- */

static int
acpi_pci_link_add (
	struct acpi_device *device)
{
	int			result = 0;
	struct acpi_pci_link	*link = NULL;
	int			i = 0;
Andy Grover's avatar
Andy Grover committed
565
	int			found = 0;
Andy Grover's avatar
Andy Grover committed
566 567 568 569 570 571 572 573 574 575 576

	ACPI_FUNCTION_TRACE("acpi_pci_link_add");

	if (!device)
		return_VALUE(-EINVAL);

	link = kmalloc(sizeof(struct acpi_pci_link), GFP_KERNEL);
	if (!link)
		return_VALUE(-ENOMEM);
	memset(link, 0, sizeof(struct acpi_pci_link));

Andy Grover's avatar
Andy Grover committed
577
	link->device = device;
Andy Grover's avatar
Andy Grover committed
578 579 580 581 582
	link->handle = device->handle;
	sprintf(acpi_device_name(device), "%s", ACPI_PCI_LINK_DEVICE_NAME);
	sprintf(acpi_device_class(device), "%s", ACPI_PCI_LINK_CLASS);
	acpi_driver_data(device) = link;

Andy Grover's avatar
Andy Grover committed
583 584
	result = acpi_pci_link_get_possible(link);
	if (result)
Andy Grover's avatar
Andy Grover committed
585
		goto end;
Andy Grover's avatar
Andy Grover committed
586

Andy Grover's avatar
Andy Grover committed
587 588 589 590 591 592 593 594 595 596 597
	acpi_pci_link_get_current(link);

	printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device), acpi_device_bid(device));
	for (i = 0; i < link->irq.possible_count; i++) {
		if (link->irq.active == link->irq.possible[i]) {
			printk(" *%d", link->irq.possible[i]);
			found = 1;
		}
		else
			printk(" %d", link->irq.possible[i]);
	}
Andy Grover's avatar
Andy Grover committed
598 599
	printk(")\n");

Andy Grover's avatar
Andy Grover committed
600 601 602 603
	/* TBD: Acquire/release lock */
	list_add_tail(&link->node, &acpi_link.entries);
	acpi_link.count++;

Andy Grover's avatar
Andy Grover committed
604
end:
Andy Grover's avatar
Andy Grover committed
605
	if (result)
Andy Grover's avatar
Andy Grover committed
606 607 608
		kfree(link);

	return_VALUE(result);
Andy Grover's avatar
Andy Grover committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
}


static int
acpi_pci_link_remove (
	struct acpi_device	*device,
	int			type)
{
	struct acpi_pci_link *link = NULL;

	ACPI_FUNCTION_TRACE("acpi_pci_link_remove");

	if (!device || !acpi_driver_data(device))
		return_VALUE(-EINVAL);

	link = (struct acpi_pci_link *) acpi_driver_data(device);

Andy Grover's avatar
Andy Grover committed
626 627 628
	/* TBD: Acquire/release lock */
	list_del(&link->node);

Andy Grover's avatar
Andy Grover committed
629 630 631 632 633 634
	kfree(link);

	return_VALUE(0);
}


635
static int __init acpi_pci_link_init (void)
Andy Grover's avatar
Andy Grover committed
636 637 638
{
	ACPI_FUNCTION_TRACE("acpi_pci_link_init");

639 640 641
	if (acpi_disabled)
		return_VALUE(0);

Andy Grover's avatar
Andy Grover committed
642 643 644 645
	acpi_link.count = 0;
	INIT_LIST_HEAD(&acpi_link.entries);

	if (acpi_bus_register_driver(&acpi_pci_link_driver) < 0)
Andy Grover's avatar
Andy Grover committed
646 647 648 649
		return_VALUE(-ENODEV);

	return_VALUE(0);
}
650 651

subsys_initcall(acpi_pci_link_init);