sandy/ivy/nehalem: Remerge interrupt handling

On those chipsets the pins are just a legacy concept. Real interrupts are
messages on corresponding busses or some internal logic of chipset.
Hence interrupt routing isn't anymore board-specific (dependent on layout) but
depends only on configuration.
Rather than attempting to sync real config, ACPI and legacy descriptors, just
use the same interrupt routing per chipset covering all possible devices.

The only part which remains board-specific are LPC and PCI interrupts.

Interrupt balancing may suffer from such merge but:
a) Doesn't seem to be the case of this map on current systems
b) Almost all OS use MSI nowadays bypassing this stuff completely
c) If we want a good balancing we need to take into account that e.g.
   wlan card may be placed in a different slot and so would require complicated
   balancing on runtime. It's difficult to maintain with almost no benefit.

Change-Id: I9f63d1d338c5587ebac7a52093e5b924f6e5ca2d
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-on: http://review.coreboot.org/7130
Tested-by: build bot (Jenkins)
Reviewed-by: Edward O'Callaghan <eocallaghan@alterapraxis.com>
Reviewed-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
diff --git a/src/southbridge/intel/bd82x6x/Makefile.inc b/src/southbridge/intel/bd82x6x/Makefile.inc
index 621a743..83de051 100644
--- a/src/southbridge/intel/bd82x6x/Makefile.inc
+++ b/src/southbridge/intel/bd82x6x/Makefile.inc
@@ -51,12 +51,15 @@
 romstage-y += early_smbus.c me_status.c gpio.c
 romstage-y += reset.c
 romstage-y += early_spi.c early_pch.c
+romstage-y += early_rcba.c
 
 romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += early_me.c early_usb.c
 romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += early_me.c early_usb.c
 romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c early_usb_native.c
 romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c early_usb_native.c
 
+ramstage-y += madt.c
+
 ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y)
 IFD_BIN_PATH := $(objgenerated)/ifdfake.bin
 IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \
diff --git a/src/southbridge/intel/bd82x6x/acpi/irq.asl b/src/southbridge/intel/bd82x6x/acpi/irq.asl
new file mode 100644
index 0000000..61e3335
--- /dev/null
+++ b/src/southbridge/intel/bd82x6x/acpi/irq.asl
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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; version 2 of
+ * the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+/* PCI Interrupt Routing */
+Method(_PRT)
+{
+	If (PICM) {
+		Return (Package() {
+			/* Onboard graphics (IGD)	0:2.0 */
+			Package() { 0x0002ffff, 0, 0, 16 },/*              GFX    INTA -> PIRQA (MSI) */
+			/* XHCI	0:14.0 (ivy only) */
+			Package() { 0x0014ffff, 0, 0, 19 },
+			/* High Definition Audio	0:1b.0 */
+			Package() { 0x001bffff, 0, 0, 16 },/*  D27IP_ZIP   HDA    INTA -> PIRQA (MSI) */
+			/* PCIe Root Ports		0:1c.x */
+			Package() { 0x001cffff, 0, 0, 17 },/*  D28IP_P1IP  PCIe   INTA -> PIRQB */
+			Package() { 0x001cffff, 1, 0, 21 },/*  D28IP_P2IP  PCIe   INTB -> PIRQF */
+			Package() { 0x001cffff, 2, 0, 19 },/*  D28IP_P3IP  PCIe   INTC -> PIRQD */
+			Package() { 0x001cffff, 3, 0, 20 },/*  D28IP_P3IP  PCIe   INTD -> PIRQE */
+			/* EHCI	#1			0:1d.0 */
+			Package() { 0x001dffff, 0, 0, 19 },/*  D29IP_E1P   EHCI1  INTA -> PIRQD */
+			/* EHCI	#2			0:1a.0 */
+			Package() { 0x001affff, 0, 0, 21 },/*  D26IP_E2P   EHCI2  INTA -> PIRQF */
+			/* LPC devices			0:1f.0 */
+			Package() { 0x001fffff, 0, 0, 17 }, /* D31IP_SIP   SATA   INTA -> PIRQB (MSI) */
+			Package() { 0x001fffff, 1, 0, 23 }, /* D31IP_SMIP  SMBUS  INTB -> PIRQH */
+			Package() { 0x001fffff, 2, 0, 16 }, /* D31IP_TTIP  THRT   INTC -> PIRQA */
+			Package() { 0x001fffff, 3, 0, 18 },
+		})
+	} Else {
+		Return (Package() {
+			/* Onboard graphics (IGD)	0:2.0 */
+			Package() { 0x0002ffff, 0, \_SB.PCI0.LPCB.LNKA, 0 },
+			/* XHCI   0:14.0 (ivy only) */
+			Package() { 0x0014ffff, 0, \_SB.PCI0.LPCB.LNKD, 0 },
+			/* High Definition Audio	0:1b.0 */
+			Package() { 0x001bffff, 0, \_SB.PCI0.LPCB.LNKA, 0 },
+			/* PCIe Root Ports		0:1c.x */
+			Package() { 0x001cffff, 0, \_SB.PCI0.LPCB.LNKB, 0 },
+			Package() { 0x001cffff, 1, \_SB.PCI0.LPCB.LNKF, 0 },
+			Package() { 0x001cffff, 2, \_SB.PCI0.LPCB.LNKD, 0 },
+			Package() { 0x001cffff, 3, \_SB.PCI0.LPCB.LNKE, 0 },
+			/* EHCI	#1			0:1d.0 */
+			Package() { 0x001dffff, 0, \_SB.PCI0.LPCB.LNKD, 0 },
+			/* EHCI	#2			0:1a.0 */
+			Package() { 0x001affff, 0, \_SB.PCI0.LPCB.LNKF, 0 },
+			/* LPC device			0:1f.0 */
+			Package() { 0x001fffff, 0, \_SB.PCI0.LPCB.LNKB, 0 },
+			Package() { 0x001fffff, 1, \_SB.PCI0.LPCB.LNKH, 0 },
+			Package() { 0x001fffff, 2, \_SB.PCI0.LPCB.LNKA, 0 },
+			Package() { 0x001fffff, 3, \_SB.PCI0.LPCB.LNKC, 0 },
+		})
+	}
+}
diff --git a/src/southbridge/intel/bd82x6x/acpi/pch.asl b/src/southbridge/intel/bd82x6x/acpi/pch.asl
index 27f08e2..73fcfcc 100644
--- a/src/southbridge/intel/bd82x6x/acpi/pch.asl
+++ b/src/southbridge/intel/bd82x6x/acpi/pch.asl
@@ -257,6 +257,8 @@
 // SMBus 0:1f.3
 #include "smbus.asl"
 
+#include "irq.asl"
+
 Method (_OSC, 4)
 {
 	/* Check for XHCI */
diff --git a/src/southbridge/intel/bd82x6x/chip.h b/src/southbridge/intel/bd82x6x/chip.h
index d4adfd5..290bb05 100644
--- a/src/southbridge/intel/bd82x6x/chip.h
+++ b/src/southbridge/intel/bd82x6x/chip.h
@@ -22,19 +22,6 @@
 
 struct southbridge_intel_bd82x6x_config {
 	/**
-	 * Interrupt Routing configuration
-	 * If bit7 is 1, the interrupt is disabled.
-	 */
-	uint8_t pirqa_routing;
-	uint8_t pirqb_routing;
-	uint8_t pirqc_routing;
-	uint8_t pirqd_routing;
-	uint8_t pirqe_routing;
-	uint8_t pirqf_routing;
-	uint8_t pirqg_routing;
-	uint8_t pirqh_routing;
-
-	/**
 	 * GPI Routing configuration
 	 *
 	 * Only the lower two bits have a meaning:
diff --git a/src/southbridge/intel/bd82x6x/early_rcba.c b/src/southbridge/intel/bd82x6x/early_rcba.c
new file mode 100644
index 0000000..114b174
--- /dev/null
+++ b/src/southbridge/intel/bd82x6x/early_rcba.c
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2010 coresystems GmbH
+ * Copyright (C) 2011 The ChromiumOS Authors.  All rights reserved.
+ * Copyright (C) 2014 Vladimir Serbinenko
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include "pch.h"
+#include "northbridge/intel/sandybridge/sandybridge.h"
+
+void
+southbridge_configure_default_intmap(void)
+{
+	/*
+	 *             GFX    INTA -> PIRQA (MSI)
+	 * D28IP_P1IP  SLOT1  INTA -> PIRQB
+	 * D28IP_P2IP  SLOT2  INTB -> PIRQF
+	 * D28IP_P3IP  SLOT3  INTC -> PIRQD
+	 * D28IP_P5IP  SLOT5  INTC -> PIRQD
+	 * D29IP_E1P   EHCI1  INTA -> PIRQD
+	 * D26IP_E2P   EHCI2  INTA -> PIRQF
+	 * D31IP_SIP   SATA   INTA -> PIRQB (MSI)
+	 * D31IP_SMIP  SMBUS  INTB -> PIRQH
+	 * D31IP_TTIP  THRT   INTC -> PIRQA
+	 * D27IP_ZIP   HDA    INTA -> PIRQA (MSI)
+	 *
+
+	 */
+
+	RCBA32(D31IP) = (INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) |
+			(INTB << D31IP_SMIP) | (INTA << D31IP_SIP);
+	RCBA32(D30IP) = (NOINT << D30IP_PIP);
+	RCBA32(D29IP) = (INTA << D29IP_E1P);
+	RCBA32(D28IP) = (INTA << D28IP_P1IP) | (INTB << D28IP_P2IP) |
+			(INTC << D28IP_P3IP) | (INTC << D28IP_P5IP);
+	RCBA32(D27IP) = (INTA << D27IP_ZIP);
+	RCBA32(D26IP) = (INTA << D26IP_E2P);
+	RCBA32(D25IP) = (NOINT << D25IP_LIP);
+	RCBA32(D22IP) = (NOINT << D22IP_MEI1IP);
+
+	/* Device interrupt route registers */
+	DIR_ROUTE(D31IR, PIRQB, PIRQH, PIRQA, PIRQC);
+	DIR_ROUTE(D29IR, PIRQD, PIRQE, PIRQF, PIRQG);
+	DIR_ROUTE(D28IR, PIRQB, PIRQF, PIRQD, PIRQE);
+	DIR_ROUTE(D27IR, PIRQA, PIRQH, PIRQA, PIRQB);
+	DIR_ROUTE(D26IR, PIRQF, PIRQE, PIRQG, PIRQH);
+	DIR_ROUTE(D25IR, PIRQA, PIRQB, PIRQC, PIRQD);
+	DIR_ROUTE(D22IR, PIRQA, PIRQB, PIRQC, PIRQD);
+
+	/* Enable IOAPIC (generic) */
+	RCBA16(OIC) = 0x0100;
+	/* PCH BWG says to read back the IOAPIC enable register */
+	(void) RCBA16(OIC);
+}
diff --git a/src/southbridge/intel/bd82x6x/lpc.c b/src/southbridge/intel/bd82x6x/lpc.c
index 3c55946..11b765a 100644
--- a/src/southbridge/intel/bd82x6x/lpc.c
+++ b/src/southbridge/intel/bd82x6x/lpc.c
@@ -107,42 +107,37 @@
 static void pch_pirq_init(device_t dev)
 {
 	device_t irq_dev;
-	/* Get the chip configuration */
-	config_t *config = dev->chip_info;
-
-	pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing);
-	pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing);
-	pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing);
-	pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing);
-
-	pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing);
-	pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing);
-	pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing);
-	pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing);
-
-	/* Eric Biederman once said we should let the OS do this.
-	 * I am not so sure anymore he was right.
+	/* Interrupt 11 is not used by legacy devices and so can always be used for
+	   PCI interrupts. Full legacy IRQ routing is complicated and hard to
+	   get right. Fortunately all modern OS use MSI and so it's not that big of
+	   an issue anyway. Still we have to provide a reasonable default. Using
+	   interrupt 11 for it everywhere is a working default. ACPI-aware OS can
+	   move it to any interrupt and others will just leave them at default.
 	 */
+	const u8 pirq_routing = 11;
+
+	pci_write_config8(dev, PIRQA_ROUT, pirq_routing);
+	pci_write_config8(dev, PIRQB_ROUT, pirq_routing);
+	pci_write_config8(dev, PIRQC_ROUT, pirq_routing);
+	pci_write_config8(dev, PIRQD_ROUT, pirq_routing);
+
+	pci_write_config8(dev, PIRQE_ROUT, pirq_routing);
+	pci_write_config8(dev, PIRQF_ROUT, pirq_routing);
+	pci_write_config8(dev, PIRQG_ROUT, pirq_routing);
+	pci_write_config8(dev, PIRQH_ROUT, pirq_routing);
 
 	for(irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) {
-		u8 int_pin=0, int_line=0;
+		u8 int_pin=0;
 
 		if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI)
 			continue;
 
 		int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN);
 
-		switch (int_pin) {
-		case 1: /* INTA# */ int_line = config->pirqa_routing; break;
-		case 2: /* INTB# */ int_line = config->pirqb_routing; break;
-		case 3: /* INTC# */ int_line = config->pirqc_routing; break;
-		case 4: /* INTD# */ int_line = config->pirqd_routing; break;
-		}
-
-		if (!int_line)
+		if (int_pin == 0)
 			continue;
 
-		pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line);
+		pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, pirq_routing);
 	}
 }
 
diff --git a/src/southbridge/intel/bd82x6x/madt.c b/src/southbridge/intel/bd82x6x/madt.c
new file mode 100644
index 0000000..45b652b
--- /dev/null
+++ b/src/southbridge/intel/bd82x6x/madt.c
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <types.h>
+#include <string.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <arch/acpi.h>
+#include <arch/ioapic.h>
+#include <arch/acpigen.h>
+#include <arch/smp/mpspec.h>
+
+unsigned long acpi_fill_madt(unsigned long current)
+{
+	/* Local APICs */
+	current = acpi_create_madt_lapics(current);
+
+	/* IOAPIC */
+	current += acpi_create_madt_ioapic((acpi_madt_ioapic_t *) current,
+				2, IO_APIC_ADDR, 0);
+
+	/* INT_SRC_OVR */
+	current += acpi_create_madt_irqoverride((acpi_madt_irqoverride_t *)
+		 current, 0, 0, 2, 0);
+	current += acpi_create_madt_irqoverride((acpi_madt_irqoverride_t *)
+		 current, 0, 9, 9, MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH);
+
+	return current;
+}
diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h
index 9b84c67..7cde7bc 100644
--- a/src/southbridge/intel/bd82x6x/pch.h
+++ b/src/southbridge/intel/bd82x6x/pch.h
@@ -75,6 +75,7 @@
 int smbus_read_byte(unsigned device, unsigned address);
 int early_spi_read(u32 offset, u32 size, u8 *buffer);
 void early_thermal_init(void);
+void southbridge_configure_default_intmap(void);
 void early_pch_init_native(void);
 int southbridge_detect_s3_resume(void);