summaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/systemagent/systemagent.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel/common/block/systemagent/systemagent.c')
-rw-r--r--src/soc/intel/common/block/systemagent/systemagent.c311
1 files changed, 280 insertions, 31 deletions
diff --git a/src/soc/intel/common/block/systemagent/systemagent.c b/src/soc/intel/common/block/systemagent/systemagent.c
index 58e2c7e054..f2e74cc29f 100644
--- a/src/soc/intel/common/block/systemagent/systemagent.c
+++ b/src/soc/intel/common/block/systemagent/systemagent.c
@@ -14,46 +14,295 @@
*/
#include <arch/io.h>
-#include <commonlib/helpers.h>
+#include <cbmem.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
#include <intelblocks/systemagent.h>
+#include <soc/iomap.h>
#include <soc/pci_devs.h>
+#include <soc/systemagent.h>
+#include "systemagent_def.h"
-void bootblock_systemagent_early_init(void)
+/* SoC override function */
+__attribute__((weak)) void soc_systemagent_init(struct device *dev)
{
- uint32_t reg;
- uint8_t pciexbar_length;
+ /* no-op */
+}
+
+__attribute__((weak)) void soc_add_fixed_mmio_resources(struct device *dev,
+ int *resource_cnt)
+{
+ /* no-op */
+}
+/*
+ * Add all known fixed MMIO ranges that hang off the host bridge/memory
+ * controller device.
+ */
+void sa_add_fixed_mmio_resources(struct device *dev, int *resource_cnt,
+ const struct sa_mmio_descriptor *sa_fixed_resources, size_t count)
+{
+ int i;
+ int index = *resource_cnt;
+
+ for (i = 0; i < count; i++) {
+ uintptr_t base;
+ size_t size;
+
+ size = sa_fixed_resources[i].size;
+ base = sa_fixed_resources[i].base;
+
+ mmio_resource(dev, index++, base / KiB, size / KiB);
+ }
+
+ *resource_cnt = index;
+}
+
+/*
+ * DRAM memory mapped register
+ *
+ * TOUUD: This 64 bit register defines the Top of Upper Usable DRAM
+ * TOLUD: This 32 bit register defines the Top of Low Usable DRAM
+ * BGSM: This register contains the base address of stolen DRAM memory for GTT
+ * TSEG: This register contains the base address of TSEG DRAM memory
+ */
+static const struct sa_mem_map_descriptor sa_memory_map[MAX_MAP_ENTRIES] = {
+ { TOUUD, true, "TOUUD" },
+ { TOLUD, false, "TOLUD" },
+ { BGSM, false, "BGSM" },
+ { TSEG, false, "TSEG" },
+};
+
+/* Read DRAM memory map register value through PCI configuration space */
+static void sa_read_map_entry(device_t dev,
+ const struct sa_mem_map_descriptor *entry, uint64_t *result)
+{
+ uint64_t value = 0;
+
+ if (entry->is_64_bit) {
+ value = pci_read_config32(dev, entry->reg + 4);
+ value <<= 32;
+ }
+
+ value |= pci_read_config32(dev, entry->reg);
+ /* All registers are on a 1MiB granularity. */
+ value = ALIGN_DOWN(value, 1 * MiB);
+
+ *result = value;
+}
+
+static void sa_get_mem_map(struct device *dev, uint64_t *values)
+{
+ int i;
+ for (i = 0; i < MAX_MAP_ENTRIES; i++)
+ sa_read_map_entry(dev, &sa_memory_map[i], &values[i]);
+}
+
+/*
+ * Get DPR size incase CONFIG_SA_ENABLE_DPR is selected by SoC.
+ */
+static size_t get_dpr_size(void)
+{
+ uintptr_t dpr_reg;
+ size_t size = 0;
/*
- * The PCIEXBAR is assumed to live in the memory mapped IO space under
- * 4GiB.
+ * DMA Protected Range can be reserved below TSEG for PCODE patch
+ * or TXT/BootGuard related data. Rather than report a base address
+ * the DPR register reports the TOP of the region, which is the same
+ * as TSEG base. The region size is reported in MiB in bits 11:4.
*/
- reg = 0;
- pci_io_write_config32(SA_DEV_ROOT, PCIEXBAR + 4, reg);
-
- /* Get PCI Express Region Length */
- switch (CONFIG_SA_PCIEX_LENGTH) {
- case 256 * MiB:
- pciexbar_length = PCIEXBAR_LENGTH_256MB;
- break;
- case 128 * MiB:
- pciexbar_length = PCIEXBAR_LENGTH_128MB;
- break;
- case 64 * MiB:
- pciexbar_length = PCIEXBAR_LENGTH_64MB;
- break;
- default:
- pciexbar_length = PCIEXBAR_LENGTH_256MB;
- }
- reg = CONFIG_MMCONF_BASE_ADDRESS | (pciexbar_length << 1)
- | PCIEXBAR_PCIEXBAREN;
- pci_io_write_config32(SA_DEV_ROOT, PCIEXBAR, reg);
+ dpr_reg = pci_read_config32(SA_DEV_ROOT, DPR);
+ if (dpr_reg & DPR_EPM)
+ size = (dpr_reg & DPR_SIZE_MASK) << 16;
+
+ return size;
+}
+/*
+ * These are the host memory ranges that should be added:
+ * - 0 -> 0xa0000: cacheable
+ * - 0xc0000 -> top_of_ram : cacheable
+ * - top_of_ram -> TSEG - DPR: uncacheable
+ * - TESG - DPR -> BGSM: cacheable with standard MTRRs and reserved
+ * - BGSM -> TOLUD: not cacheable with standard MTRRs and reserved
+ * - 4GiB -> TOUUD: cacheable
+ *
+ * The default SMRAM space is reserved so that the range doesn't
+ * have to be saved during S3 Resume. Once marked reserved the OS
+ * cannot use the memory. This is a bit of an odd place to reserve
+ * the region, but the CPU devices don't have dev_ops->read_resources()
+ * called on them.
+ *
+ * The range 0xa0000 -> 0xc0000 does not have any resources
+ * associated with it to handle legacy VGA memory. If this range
+ * is not omitted the mtrr code will setup the area as cacheable
+ * causing VGA access to not work.
+ *
+ * The TSEG region is mapped as cacheable so that one can perform
+ * SMRAM relocation faster. Once the SMRR is enabled the SMRR takes
+ * precedence over the existing MTRRs covering this region.
+ *
+ * It should be noted that cacheable entry types need to be added in
+ * order. The reason is that the current MTRR code assumes this and
+ * falls over itself if it isn't.
+ *
+ * The resource index starts low and should not meet or exceed
+ * PCI_BASE_ADDRESS_0.
+ */
+static void sa_add_dram_resources(struct device *dev, int *resource_count)
+{
+ uintptr_t base_k, touud_k;
+ size_t dpr_size = 0, size_k;
+ uint64_t sa_map_values[MAX_MAP_ENTRIES];
+ uintptr_t top_of_ram;
+ int index = *resource_count;
+
+ if (IS_ENABLED(CONFIG_SA_ENABLE_DPR))
+ dpr_size = get_dpr_size();
+
+ top_of_ram = (uintptr_t)cbmem_top();
+
+ /* 0 - > 0xa0000 */
+ base_k = 0;
+ size_k = (0xa0000 / KiB) - base_k;
+ ram_resource(dev, index++, base_k, size_k);
+
+ /* 0xc0000 -> top_of_ram */
+ base_k = 0xc0000 / KiB;
+ size_k = (top_of_ram / KiB) - base_k;
+ ram_resource(dev, index++, base_k, size_k);
+
+ sa_get_mem_map(dev, &sa_map_values[0]);
+
+ /* top_of_ram -> TSEG - DPR */
+ base_k = top_of_ram;
+ size_k = sa_map_values[SA_TSEG_REG] - dpr_size - base_k;
+ mmio_resource(dev, index++, base_k / KiB, size_k / KiB);
+
+ /* TSEG - DPR -> BGSM */
+ base_k = sa_map_values[SA_TSEG_REG] - dpr_size;
+ size_k = sa_map_values[SA_BGSM_REG] - base_k;
+ reserved_ram_resource(dev, index++, base_k / KiB, size_k / KiB);
+
+ /* BGSM -> TOLUD */
+ base_k = sa_map_values[SA_BGSM_REG];
+ size_k = sa_map_values[SA_TOLUD_REG] - base_k;
+ mmio_resource(dev, index++, base_k / KiB, size_k / KiB);
+
+ /* 4GiB -> TOUUD */
+ base_k = 4 * (GiB / KiB); /* 4GiB */
+ touud_k = sa_map_values[SA_TOUUD_REG] / KiB;
+ size_k = touud_k - base_k;
+ if (touud_k > base_k)
+ ram_resource(dev, index++, base_k, size_k);
+
+ /*
+ * Reserve everything between A segment and 1MB:
+ *
+ * 0xa0000 - 0xbffff: legacy VGA
+ * 0xc0000 - 0xfffff: RAM
+ */
+ mmio_resource(dev, index++, 0xa0000 / KiB, (0xc0000 - 0xa0000) / KiB);
+ reserved_ram_resource(dev, index++, 0xc0000 / KiB,
+ (1*MiB - 0xc0000) / KiB);
+
+ *resource_count = index;
+}
+
+static bool is_imr_enabled(uint32_t imr_base_reg)
+{
+ return !!(imr_base_reg & (1 << 31));
+}
+
+static void imr_resource(device_t dev, int idx, uint32_t base, uint32_t mask)
+{
+ uint32_t base_k, size_k;
+ /* Bits 28:0 encode the base address bits 38:10, hence the KiB unit. */
+ base_k = (base & 0x0fffffff);
+ /* Bits 28:0 encode the AND mask used for comparison, in KiB. */
+ size_k = ((~mask & 0x0fffffff) + 1);
/*
- * TSEG defines the base of SMM range. BIOS determines the base
- * of TSEG memory which must be at or below Graphics base of GTT
- * Stolen memory, hence its better to clear TSEG register early
- * to avoid power on default non-zero value (if any).
+ * IMRs sit in lower DRAM. Mark them cacheable, otherwise we run
+ * out of MTRRs. Memory reserved by IMRs is not usable for host
+ * so mark it reserved.
*/
- pci_write_config32(SA_DEV_ROOT, TSEG, 0);
+ reserved_ram_resource(dev, idx, base_k, size_k);
}
+/*
+ * Add IMR ranges that hang off the host bridge/memory
+ * controller device in case CONFIG_SA_ENABLE_IMR is selected by SoC.
+ */
+static void sa_add_imr_resources(struct device *dev, int *resource_cnt)
+{
+ size_t i, imr_offset;
+ uint32_t base, mask;
+ int index = *resource_cnt;
+
+ for (i = 0; i < MCH_NUM_IMRS; i++) {
+ imr_offset = i * MCH_IMR_PITCH;
+ base = MCHBAR32(imr_offset + MCH_IMR0_BASE);
+ mask = MCHBAR32(imr_offset + MCH_IMR0_MASK);
+
+ if (is_imr_enabled(base))
+ imr_resource(dev, index++, base, mask);
+ }
+
+ *resource_cnt = index;
+}
+
+static void systemagent_read_resources(struct device *dev)
+{
+ int index = 0;
+
+ /* Read standard PCI resources. */
+ pci_dev_read_resources(dev);
+
+ /* Add all fixed MMIO resources. */
+ soc_add_fixed_mmio_resources(dev, &index);
+ /* Calculate and add DRAM resources. */
+ sa_add_dram_resources(dev, &index);
+ if (IS_ENABLED(CONFIG_SA_ENABLE_IMR))
+ /* Add the isolated memory ranges (IMRs). */
+ sa_add_imr_resources(dev, &index);
+}
+
+void enable_power_aware_intr(void)
+{
+ uint8_t pair;
+
+ /* Enable Power Aware Interrupt Routing */
+ pair = MCHBAR8(MCH_PAIR);
+ pair &= ~0x7; /* Clear 2:0 */
+ pair |= 0x4; /* Fixed Priority */
+ MCHBAR8(MCH_PAIR) = pair;
+}
+
+static struct device_operations systemagent_ops = {
+ .read_resources = &systemagent_read_resources,
+ .set_resources = &pci_dev_set_resources,
+ .enable_resources = &pci_dev_enable_resources,
+ .init = soc_systemagent_init,
+};
+
+static const unsigned short systemagent_ids[] = {
+ PCI_DEVICE_ID_INTEL_GLK_NB,
+ PCI_DEVICE_ID_INTEL_APL_NB,
+ PCI_DEVICE_ID_INTEL_SKL_ID_U,
+ PCI_DEVICE_ID_INTEL_SKL_ID_Y,
+ PCI_DEVICE_ID_INTEL_SKL_ID_ULX,
+ PCI_DEVICE_ID_INTEL_SKL_ID_H,
+ PCI_DEVICE_ID_INTEL_SKL_ID_H_EM,
+ PCI_DEVICE_ID_INTEL_KBL_ID_U,
+ PCI_DEVICE_ID_INTEL_KBL_ID_Y,
+ PCI_DEVICE_ID_INTEL_KBL_ID_H,
+ PCI_DEVICE_ID_INTEL_KBL_U_R,
+ 0
+};
+
+static const struct pci_driver systemagent_driver __pci_driver = {
+ .ops = &systemagent_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = systemagent_ids
+};