soc/intel/broadwell: Enable VT-d and X2APIC

We use the usual static addresses 0xfed90000/0xfed91000 for the GFX
IOMMU and the general IOMMU respectively. These addresses have to be
configured in MCHBAR registers and reserved from the OS.

Change-Id: I7afcce0da028a160174db2cf6b4b6735bcd59165
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/23820
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Youness Alaoui <snifikino@gmail.com>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/src/soc/intel/broadwell/include/soc/iomap.h b/src/soc/intel/broadwell/include/soc/iomap.h
index fe8e78b..96b2c1f 100644
--- a/src/soc/intel/broadwell/include/soc/iomap.h
+++ b/src/soc/intel/broadwell/include/soc/iomap.h
@@ -39,6 +39,12 @@
 
 #define HPET_BASE_ADDRESS	0xfed00000
 
+#define GFXVT_BASE_ADDRESS	0xfed90000ULL
+#define GFXVT_BASE_SIZE		0x1000
+
+#define VTVC0_BASE_ADDRESS	0xfed91000ULL
+#define VTVC0_BASE_SIZE		0x1000
+
 #define ACPI_BASE_ADDRESS	0x1000
 #define ACPI_BASE_SIZE		0x100
 
diff --git a/src/soc/intel/broadwell/include/soc/lpc.h b/src/soc/intel/broadwell/include/soc/lpc.h
index 4fb0d90..5cca961 100644
--- a/src/soc/intel/broadwell/include/soc/lpc.h
+++ b/src/soc/intel/broadwell/include/soc/lpc.h
@@ -86,4 +86,7 @@
 #define  PMIR_CF9LOCK		(1 << 31)
 #define  PMIR_CF9GR		(1 << 20)
 
+#define LPC_IBDF		0x6C		/* I/O APIC bus/dev/fn */
+#define LPC_HnBDF(n)		(0x70 + n * 2)	/* HPET n bus/dev/fn */
+
 #endif
diff --git a/src/soc/intel/broadwell/include/soc/pci_devs.h b/src/soc/intel/broadwell/include/soc/pci_devs.h
index 59c64ce..0880353 100644
--- a/src/soc/intel/broadwell/include/soc/pci_devs.h
+++ b/src/soc/intel/broadwell/include/soc/pci_devs.h
@@ -112,4 +112,9 @@
 #define  PCH_DEV_SATA2		_PCH_DEV(LPC, 5)
 #define  PCH_DEV_THERMAL	_PCH_DEV(LPC, 6)
 
+#define PCH_IOAPIC_PCI_BUS	250
+#define PCH_IOAPIC_PCI_SLOT	31
+#define PCH_HPET_PCI_BUS	250
+#define PCH_HPET_PCI_SLOT	15
+
 #endif
diff --git a/src/soc/intel/broadwell/include/soc/systemagent.h b/src/soc/intel/broadwell/include/soc/systemagent.h
index 125ba47..92e79cc 100644
--- a/src/soc/intel/broadwell/include/soc/systemagent.h
+++ b/src/soc/intel/broadwell/include/soc/systemagent.h
@@ -74,6 +74,11 @@
 #define  D_LCK		(1 << 4)
 #define  G_SMRAME	(1 << 3)
 #define  C_BASE_SEG	((0 << 2) | (1 << 1) | (0 << 0))
+#define CAPID0_A	0xe4
+#define  VTD_DISABLE	(1 << 23)
+#define ARCHDIS		0xff0	/* DMA Remap Engine Policy Control */
+#define  DMAR_LCKDN	(1 << 31)
+#define  PRSCAPDIS	(1 << 2)
 
 #define MESEG_BASE	0x70	/* Management Engine Base. */
 #define MESEG_LIMIT	0x78	/* Management Engine Limit. */
@@ -95,7 +100,9 @@
 
 #define MCHBAR_PEI_VERSION	0x5034
 #define BIOS_RESET_CPL		0x5da8
+#define GFXVTBAR		0x5400
 #define EDRAMBAR		0x5408
+#define VTVC0BAR		0x5410
 #define MCH_PAIR		0x5418
 #define GDXCBAR			0x5420
 
diff --git a/src/soc/intel/broadwell/lpc.c b/src/soc/intel/broadwell/lpc.c
index d1c4242..8e7f4de 100644
--- a/src/soc/intel/broadwell/lpc.c
+++ b/src/soc/intel/broadwell/lpc.c
@@ -49,6 +49,10 @@
 {
 	u32 reg32;
 
+	/* Assign unique bus/dev/fn for I/O APIC */
+	pci_write_config16(dev, LPC_IBDF,
+		PCH_IOAPIC_PCI_BUS << 8 | PCH_IOAPIC_PCI_SLOT << 3);
+
 	set_ioapic_id(VIO_APIC_VADDR, 0x02);
 
 	/* affirm full set of redirection table entries ("write once") */
@@ -67,6 +71,16 @@
 	io_apic_write(VIO_APIC_VADDR, 0x03, 0x01);
 }
 
+static void enable_hpet(struct device *dev)
+{
+	size_t i;
+
+	/* Assign unique bus/dev/fn for each HPET */
+	for (i = 0; i < 8; ++i)
+		pci_write_config16(dev, LPC_HnBDF(i),
+			PCH_HPET_PCI_BUS << 8 | PCH_HPET_PCI_SLOT << 3 | i);
+}
+
 /* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control
  * 0x00 - 0000 = Reserved
  * 0x01 - 0001 = Reserved
@@ -436,6 +450,7 @@
 	pch_pirq_init(dev);
 	setup_i8259();
 	i8259_configure_irq_trigger(9, 1);
+	enable_hpet(dev);
 
 	/* Initialize power management */
 	pch_power_options(dev);
diff --git a/src/soc/intel/broadwell/romstage/systemagent.c b/src/soc/intel/broadwell/romstage/systemagent.c
index e7dbbc8..153dfdf 100644
--- a/src/soc/intel/broadwell/romstage/systemagent.c
+++ b/src/soc/intel/broadwell/romstage/systemagent.c
@@ -47,5 +47,26 @@
 
 void systemagent_early_init(void)
 {
+	const bool vtd_capable =
+		!(pci_read_config32(SA_DEV_ROOT, CAPID0_A) & VTD_DISABLE);
+
 	reg_script_run_on_dev(SA_DEV_ROOT, systemagent_early_init_script);
+
+	if (vtd_capable) {
+		/* setup BARs: zeroize top 32 bits; set enable bit */
+		MCHBAR32(GFXVTBAR + 4) = GFXVT_BASE_ADDRESS >> 32;
+		MCHBAR32(GFXVTBAR) = GFXVT_BASE_ADDRESS | 1;
+		MCHBAR32(VTVC0BAR + 4) = VTVC0_BASE_ADDRESS >> 32;
+		MCHBAR32(VTVC0BAR) = VTVC0_BASE_ADDRESS | 1;
+
+		/* set PRSCAPDIS, lock GFXVTBAR policy cfg registers */
+		u32 reg32;
+		reg32 = read32((void *)(GFXVT_BASE_ADDRESS + ARCHDIS));
+		write32((void *)(GFXVT_BASE_ADDRESS + ARCHDIS),
+				reg32 | DMAR_LCKDN | PRSCAPDIS);
+		/* lock VTVC0BAR policy cfg registers */
+		reg32 = read32((void *)(VTVC0_BASE_ADDRESS + ARCHDIS));
+		write32((void *)(VTVC0_BASE_ADDRESS + ARCHDIS),
+				reg32 | DMAR_LCKDN);
+	}
 }
diff --git a/src/soc/intel/broadwell/systemagent.c b/src/soc/intel/broadwell/systemagent.c
index f46f5ea..d9dbfe9 100644
--- a/src/soc/intel/broadwell/systemagent.c
+++ b/src/soc/intel/broadwell/systemagent.c
@@ -271,7 +271,7 @@
 	printk(BIOS_DEBUG, "MC MAP: GGC: 0x%x\n", pci_read_config16(dev, GGC));
 }
 
-static void mc_add_dram_resources(device_t dev)
+static void mc_add_dram_resources(device_t dev, int *resource_cnt)
 {
 	unsigned long base_k, size_k;
 	unsigned long touud_k;
@@ -327,7 +327,7 @@
 	 * The resource index starts low and should not meet or exceed
 	 * PCI_BASE_ADDRESS_0.
 	 */
-	index = 0;
+	index = *resource_cnt;
 
 	/* 0 - > 0xa0000 */
 	base_k = 0;
@@ -373,18 +373,32 @@
 				(0x100000 - 0xc0000) >> 10);
 
 	chromeos_reserve_ram_oops(dev, index++);
+
+	*resource_cnt = index;
 }
 
 static void systemagent_read_resources(device_t dev)
 {
+	int index = 0;
+	const bool vtd_capable =
+		!(pci_read_config32(dev, CAPID0_A) & VTD_DISABLE);
+
 	/* Read standard PCI resources. */
 	pci_dev_read_resources(dev);
 
 	/* Add all fixed MMIO resources. */
 	mc_add_fixed_mmio_resources(dev);
 
+	/* Add VT-d MMIO resources if capable */
+	if (vtd_capable) {
+		mmio_resource(dev, index++, GFXVT_BASE_ADDRESS / KiB,
+			GFXVT_BASE_SIZE / KiB);
+		mmio_resource(dev, index++, VTVC0_BASE_ADDRESS / KiB,
+			VTVC0_BASE_SIZE / KiB);
+	}
+
 	/* Calculate and add DRAM resources. */
-	mc_add_dram_resources(dev);
+	mc_add_dram_resources(dev, &index);
 }
 
 static void systemagent_init(struct device *dev)