# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1145  -> 1.1146 
#	Documentation/kernel-parameters.txt	1.11    -> 1.12   
#	drivers/acpi/pci_link.c	1.11    -> 1.12   
#	arch/i386/kernel/acpi.c	1.16    -> 1.17   
#	arch/x86_64/kernel/acpi.c	1.4     -> 1.5    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/11/18	len.brown@intel.com	1.1146
# [ACPI] 
#   Re-enable IRQ balacning if IOAPIC mode
#   http://bugzilla.kernel.org/show_bug.cgi?id=1440
# 
#   Also allow IRQ balancing in PIC mode if "acpi_irq_balance"
#   http://bugzilla.kernel.org/show_bug.cgi?id=1391
# --------------------------------------------
#
diff -Nru a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
--- a/Documentation/kernel-parameters.txt	Tue Nov 18 06:25:03 2003
+++ b/Documentation/kernel-parameters.txt	Tue Nov 18 06:25:03 2003
@@ -78,6 +78,11 @@
 		level	Force PIC-mode SCI to Level Trigger (default)
 		edge	Force PIC-mode SCI to Edge Trigger
  
+	acpi_irq_balance	ACPI will balance active IRQs
+	acpi_irq_nobalance	ACPI will not move active IRQs
+	acpi_irq_pci=	If irq_balance, Clear listed IRQs for use by PCI
+	acpi_irq_isa=	If irq_balance, Mark listed IRQs used by ISA
+
 	ad1816=		[HW,SOUND]
 
 	ad1848=		[HW,SOUND]
diff -Nru a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c
--- a/arch/i386/kernel/acpi.c	Tue Nov 18 06:25:03 2003
+++ b/arch/i386/kernel/acpi.c	Tue Nov 18 06:25:03 2003
@@ -459,6 +459,8 @@
 
 	acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC;
 
+	acpi_irq_balance_set(NULL);
+
 	acpi_ioapic = 1;
 
 	if (acpi_lapic && acpi_ioapic)
diff -Nru a/arch/x86_64/kernel/acpi.c b/arch/x86_64/kernel/acpi.c
--- a/arch/x86_64/kernel/acpi.c	Tue Nov 18 06:25:03 2003
+++ b/arch/x86_64/kernel/acpi.c	Tue Nov 18 06:25:03 2003
@@ -531,6 +531,8 @@
 
 	acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC;
 
+	acpi_irq_balance_set(NULL);
+
 	acpi_ioapic = 1;
 
 #endif /*CONFIG_X86_IO_APIC*/
diff -Nru a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
--- a/drivers/acpi/pci_link.c	Tue Nov 18 06:25:03 2003
+++ b/drivers/acpi/pci_link.c	Tue Nov 18 06:25:03 2003
@@ -95,7 +95,7 @@
 	void			*context)
 {
 	struct acpi_pci_link	*link = (struct acpi_pci_link *) context;
-	int			i = 0;
+	u32			i = 0;
 
 	ACPI_FUNCTION_TRACE("acpi_pci_link_check_possible");
 
@@ -290,7 +290,10 @@
 
 	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));
+		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;
 	}
 	
@@ -428,30 +431,67 @@
                             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:
+ * "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:
  *
- *   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)
+ * List interrupts that are free for PCI use.
+ * acpi_irq_pci=n[,m]
  *
- * Thus we're left with IRQs 5, 9, 10, 11, and everything above 15 (IO[S]APIC)
- * as 'best bets' for PCI use.
+ * 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] = {
-	1000000,  1000000,  1000000,    10000, 
-	  10000,        0,    10000,    10000,
-	  10000,        0,        0,        0, 
-	  10000,   100000,   100000,   100000,
+	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_TYPICAL,	/* IRQ12 mouse */
+	PIRQ_PENALTY_ISA_USED,	/* IRQ13 fpe, sometimes */
+	PIRQ_PENALTY_ISA_USED,	/* IRQ14 ide0 */
+	PIRQ_PENALTY_ISA_USED,	/* IRQ15 ide1 */
+			/* >IRQ15 */
 };
 
-
 int
 acpi_pci_link_check (void)
 {
@@ -472,20 +512,30 @@
 			continue;
 		}
 
-		if (link->irq.active)
-			acpi_irq_penalty[link->irq.active] += 100;
-		else if (link->irq.possible_count) {
-			int penalty = 100 / link->irq.possible_count;
-			for (i=0; i<link->irq.possible_count; i++) {
+		/*
+		 * reflect the possible and active irqs in the penalty table --
+		 * useful for breaking ties.
+		 */
+		if (link->irq.possible_count) {
+			int penalty = PIRQ_PENALTY_PCI_POSSIBLE / 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;
 			}
+
+		} else if (link->irq.active) {
+			acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_POSSIBLE;
 		}
 	}
+	/* Add a penalty for the SCI */
+	acpi_irq_penalty[acpi_fadt.sci_int] += PIRQ_PENALTY_PCI_USING;
 
 	return_VALUE(0);
 }
 
+static int acpi_irq_balance;	/* 0: static, 1: balance */
+
 static int acpi_pci_link_allocate(struct acpi_pci_link* link) {
 	int irq;
 	int i;
@@ -499,12 +549,14 @@
 		irq = link->irq.active;
 	} else {
 		irq = link->irq.possible[0];
+	}
 
+	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--) {
+		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];
 		}
@@ -517,13 +569,14 @@
 			acpi_device_bid(link->device));
 		return_VALUE(-ENODEV);
 	} else {
-		acpi_irq_penalty[link->irq.active] += 100;
+		acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_USING;
 		printk(PREFIX "%s [%s] enabled at IRQ %d\n", 
 			acpi_device_name(link->device),
 			acpi_device_bid(link->device), link->irq.active);
 	}
 
 	link->irq.setonboot = 1;
+
 	return_VALUE(0);
 }
 
@@ -606,9 +659,12 @@
 	if (result)
 		goto end;
 
+	/* query and set link->irq.active */
 	acpi_pci_link_get_current(link);
 
-	printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device), acpi_device_bid(device));
+//#ifdef CONFIG_ACPI_DEBUG
+	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]);
@@ -618,6 +674,7 @@
 			printk(" %d", link->irq.possible[i]);
 	}
 	printk(")\n");
+//#endif /* CONFIG_ACPI_DEBUG */
 
 	/* TBD: Acquire/release lock */
 	list_add_tail(&link->node, &acpi_link.entries);
@@ -652,6 +709,77 @@
 
 	return_VALUE(0);
 }
+
+/*
+ * 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;
+
+		retval = get_option(&str,&irq);
+
+		if (!retval)
+			break;	/* no number found */
+
+		if (irq < 0)
+			continue;
+		
+		if (irq >= ACPI_MAX_IRQS)
+			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;
+}
+
+/*
+ * 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));
+}
+__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));
+}
+__setup("acpi_irq_pci=", acpi_irq_pci);
+
+static int __init acpi_irq_nobalance_set(char *str)
+{
+printk("ACPI STATIC SET\n");
+	acpi_irq_balance = 0;
+	return(1);
+}
+__setup("acpi_irq_nobalance", acpi_irq_nobalance_set);
+
+int __init acpi_irq_balance_set(char *str)
+{
+printk("ACPI BALANCE SET\n");
+	acpi_irq_balance = 1;
+	return(1);
+}
+__setup("acpi_irq_balance", acpi_irq_balance_set);
 
 
 int __init
