diff options
Diffstat (limited to 'src/soc/intel/common/block/systemagent')
5 files changed, 512 insertions, 32 deletions
diff --git a/src/soc/intel/common/block/systemagent/Kconfig b/src/soc/intel/common/block/systemagent/Kconfig index 773a56b831..01a4f8e201 100644 --- a/src/soc/intel/common/block/systemagent/Kconfig +++ b/src/soc/intel/common/block/systemagent/Kconfig @@ -24,3 +24,15 @@ config PCIEX_LENGTH_128MB config PCIEX_LENGTH_64MB bool "64MB" + +config SA_ENABLE_IMR + bool + default n + help + This option allows you to add the isolated memory ranges (IMRs). + +config SA_ENABLE_DPR + bool + default n + help + This option allows you to add the DMA Protected Range (DPR). diff --git a/src/soc/intel/common/block/systemagent/Makefile.inc b/src/soc/intel/common/block/systemagent/Makefile.inc index 75d5626cea..81e680b6f6 100644 --- a/src/soc/intel/common/block/systemagent/Makefile.inc +++ b/src/soc/intel/common/block/systemagent/Makefile.inc @@ -1 +1,4 @@ -bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SA) += systemagent.c +bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SA) += systemagent_early.c +romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SA) += systemagent_early.c +ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SA) += systemagent_early.c +ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SA) += systemagent.c 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 +}; diff --git a/src/soc/intel/common/block/systemagent/systemagent_def.h b/src/soc/intel/common/block/systemagent/systemagent_def.h new file mode 100644 index 0000000000..29ce9ecd76 --- /dev/null +++ b/src/soc/intel/common/block/systemagent/systemagent_def.h @@ -0,0 +1,82 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + */ + +#ifndef SOC_INTEL_COMMON_BLOCK_SA_DEF_H +#define SOC_INTEL_COMMON_BLOCK_SA_DEF_H + + +/* Device 0:0.0 PCI configuration space */ + +/* DPR register incase CONFIG_SA_ENABLE_DPR is selected by SoC */ +#define DPR 0x5c +#define DPR_EPM (1 << 2) +#define DPR_PRS (1 << 1) +#define DPR_SIZE_MASK 0xff0 + +#define PCIEXBAR_LENGTH_64MB 2 +#define PCIEXBAR_LENGTH_128MB 1 +#define PCIEXBAR_LENGTH_256MB 0 +#define PCIEXBAR_PCIEXBAREN (1 << 0) + +#define PAM0 0x80 +#define PAM1 0x81 +#define PAM2 0x82 +#define PAM3 0x83 +#define PAM4 0x84 +#define PAM5 0x85 +#define PAM6 0x86 + +/* Device 0:0.0 MMIO space */ +#define MCH_PAIR 0x5418 + +/* + * IMR register incase CONFIG_SA_ENABLE_IMR is selected by SoC. + * + * IMR registers are found under MCHBAR. + */ +#define MCH_IMR0_BASE 0x6870 +#define MCH_IMR0_MASK 0x6874 +#define MCH_IMR_PITCH 0x20 +#define MCH_NUM_IMRS 20 + +/* + * System Memory Map Registers + * - 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 + */ +enum { + SA_TOUUD_REG, + SA_TOLUD_REG, + SA_BGSM_REG, + SA_TSEG_REG, + /* Must be last. */ + MAX_MAP_ENTRIES +}; + +/* + * Set Fixed MMIO range + * REG = Either PCI configuration space registers. + * IS_64_BIT = If registers/offset is 64 bit. + * DESCRIPTION = Name of the register/offset. + */ +struct sa_mem_map_descriptor { + unsigned int reg; + bool is_64_bit; + const char *description; +}; + +#endif /* SOC_INTEL_COMMON_BLOCK_SA_DEF_H */ diff --git a/src/soc/intel/common/block/systemagent/systemagent_early.c b/src/soc/intel/common/block/systemagent/systemagent_early.c new file mode 100644 index 0000000000..7cf78e7352 --- /dev/null +++ b/src/soc/intel/common/block/systemagent/systemagent_early.c @@ -0,0 +1,134 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + */ + +#include <arch/io.h> +#include <delay.h> +#include <device/device.h> +#include <device/pci.h> +#include <intelblocks/systemagent.h> +#include <soc/iomap.h> +#include <soc/pci_devs.h> +#include <soc/systemagent.h> +#include "systemagent_def.h" +#include <timer.h> + +#if !ENV_RAMSTAGE +void bootblock_systemagent_early_init(void) +{ + uint32_t reg; + uint8_t pciexbar_length; + + /* + * The PCIEXBAR is assumed to live in the memory mapped IO space under + * 4GiB. + */ + 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); + + /* + * 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). + */ + pci_write_config32(SA_DEV_ROOT, TSEG, 0); +} +#endif + +void sa_set_pci_bar(const struct sa_mmio_descriptor *fixed_set_resources, + size_t count) +{ + int i; + + for (i = 0; i < count; i++) { + uintptr_t base; + unsigned int index; + + index = fixed_set_resources[i].index; + /* Check if PCI BAR already enabled */ + base = pci_read_config32(SA_DEV_ROOT, index); + + /* If enabled don't program it. */ + if (base & 0x1) + return; + + base = fixed_set_resources[i].base; + + pci_write_config32(SA_DEV_ROOT, index, base | 1); + } +} + +/* + * There are special BARs that actually are programmed in the MCHBAR. These + * Intel special features, but they do consume resources that need to be + * accounted for. + */ +void sa_set_mch_bar(const struct sa_mmio_descriptor *fixed_set_resources, + size_t count) +{ + int i; + + for (i = 0; i < count; i++) { + uintptr_t base; + unsigned int index; + + base = fixed_set_resources[i].base; + index = fixed_set_resources[i].index; + write32((void *)(MCH_BASE_ADDRESS + index), base | 1); + } +} + +void enable_pam_region(void) +{ + /* All read and writes in this region are serviced by DRAM */ + pci_write_config8(SA_DEV_ROOT, PAM0, 0x30); + pci_write_config8(SA_DEV_ROOT, PAM1, 0x33); + pci_write_config8(SA_DEV_ROOT, PAM2, 0x33); + pci_write_config8(SA_DEV_ROOT, PAM3, 0x33); + pci_write_config8(SA_DEV_ROOT, PAM4, 0x33); + pci_write_config8(SA_DEV_ROOT, PAM5, 0x33); + pci_write_config8(SA_DEV_ROOT, PAM6, 0x33); +} + +void enable_bios_reset_cpl(void) +{ + u8 bios_reset_cpl; + + /* + * Set bits 0+1 of BIOS_RESET_CPL to indicate to the CPU + * that BIOS has initialized memory and power management + */ + bios_reset_cpl = MCHBAR8(BIOS_RESET_CPL); + bios_reset_cpl |= 3; + MCHBAR8(BIOS_RESET_CPL) = bios_reset_cpl; +} |