diff options
Diffstat (limited to 'src/soc/intel/snowridge/acpi.c')
-rw-r--r-- | src/soc/intel/snowridge/acpi.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/src/soc/intel/snowridge/acpi.c b/src/soc/intel/snowridge/acpi.c new file mode 100644 index 0000000000..552f7ba816 --- /dev/null +++ b/src/soc/intel/snowridge/acpi.c @@ -0,0 +1,481 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi.h> +#include <acpi/acpi_device.h> +#include <acpi/acpigen.h> +#include <acpi/acpigen_pci.h> +#include <arch/hpet.h> +#include <arch/ioapic.h> +#include <assert.h> +#include <cbmem.h> +#include <commonlib/bsd/helpers.h> +#include <console/console.h> +#include <device/mmio.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <device/pci_type.h> +#include <intelblocks/acpi.h> +#include <intelblocks/lpc_lib.h> +#include <intelblocks/p2sb.h> +#include <intelblocks/systemagent_server.h> +#include <soc/acpi.h> +#include <soc/iomap.h> +#include <soc/irq.h> +#include <soc/itss.h> +#include <soc/pci_devs.h> +#include <soc/pm.h> +#include <soc/systemagent.h> +#include <southbridge/intel/common/acpi_pirq_gen.h> +#include <static.h> +#include <string.h> + +uint32_t soc_read_sci_irq_select(void) +{ + return read32p(PCH_PWRM_BASE_ADDRESS + ACTL); +} + +int soc_madt_sci_irq_polarity(int sci) +{ + if (sci >= 20) + return MP_IRQ_POLARITY_LOW; + + return MP_IRQ_POLARITY_HIGH; +} + +void soc_fill_fadt(acpi_fadt_t *fadt) +{ + /** + * The default field value is 0 if it's not set when calling this function. + */ + + fadt->pm2_cnt_blk = ACPI_BASE_ADDRESS + PM2_CNT; + fadt->pm_tmr_blk = ACPI_BASE_ADDRESS + PM1_TMR; + fadt->pm2_cnt_len = 1; + fadt->pm_tmr_len = 4; + + fadt->duty_offset = 1; + fadt->duty_width = 0; + + fill_fadt_extended_pm_io(fadt); +} + +void soc_power_states_generation(int core_id, int cores_per_package) +{ + generate_p_state_entries(core_id, cores_per_package); +} + +static void acpigen_write_pci_prt(const struct device *dev) +{ + unsigned int map_count = 0; + struct slot_pin_irq_map *pin_irq_map = + calloc(MAX_SLOTS * PCI_INT_MAX, sizeof(struct slot_pin_irq_map)); + if (!pin_irq_map) + return; + + struct device *child = NULL; + while ((child = dev_bus_each_child(dev->downstream, child))) { + if (!is_enabled_pci(child)) + continue; + + enum pci_pin pin = pci_read_config8(child, PCI_INTERRUPT_PIN); + if (pin < PCI_INT_A || pin > PCI_INT_D) + continue; + + pin_irq_map[map_count].slot = PCI_SLOT(child->path.pci.devfn); + pin_irq_map[map_count].pin = pin; + + /* `pic_pirq` is actually `enum pirq` type. */ + pin_irq_map[map_count].pic_pirq = itss_soc_get_dev_pirq(child); + if (pin_irq_map[map_count].pic_pirq == PIRQ_INVALID) + continue; + + /* PIRQA-H is hardwired to IRQ16-23. */ + pin_irq_map[map_count].apic_gsi = + PCH_IRQ16 + pirq_idx(pin_irq_map[map_count].pic_pirq); + + printk(BIOS_SPEW, "%s, slot_pin_irq_map[%2d]: {0x%2x, %s, PIRQ%c, IRQ%d}\n", + dev_path(child), map_count, pin_irq_map[map_count].slot, + pin_to_str(pin_irq_map[map_count].pin), + (unsigned int)pirq_idx(pin_irq_map[map_count].pic_pirq) + 'A', + pin_irq_map[map_count].apic_gsi); + + map_count++; + } + + size_t pirq_routes; + const uint8_t *legacy_pirq_routing = lpc_get_pic_pirq_routing(&pirq_routes); + struct pic_pirq_map pirq_map = {.type = PIRQ_GSI}; + for (size_t i = 0; i < PIRQ_COUNT && i < pirq_routes; i++) + pirq_map.gsi[i] = legacy_pirq_routing[i]; + + intel_write_pci_PRT(acpi_device_path(dev), pin_irq_map, map_count, &pirq_map); + free(pin_irq_map); +} + +void domain_fill_ssdt(const struct device *dev) +{ + const char *acpi_scope = acpi_device_scope(dev); + const char *acpi_name = acpi_device_name(dev); + printk(BIOS_DEBUG, "%s ACPI scope: '%s', name: '%s'\n", __func__, acpi_scope, + acpi_name); + + /** + * PCH domain is defined in uncore.asl. + * + * Generating accelerator domains dynamically since they are SKU-dependent. + */ + if (!is_domain0(dev)) { + /** + * Device (PCIx) + * { + * Method (_STA) { Return (status) } + * Name (_HID, EISAID ("PNP0A08")) // PCI Express Bus + * Name (_CID, EISAID ("PNP0A03")) // PCI Bus + * Name (_UID, "PCIx") + * Name (_PXM, 0) + * Method (_OSC, 4) { Return (\_SB.DOSC (Arg0, Arg1, Arg2, Arg3)) } + * } + */ + acpigen_write_scope(acpi_scope); + acpigen_write_device(acpi_name); + + acpigen_write_STA(dev->enabled ? ACPI_STATUS_DEVICE_ALL_ON : + ACPI_STATUS_DEVICE_ALL_OFF); + + acpigen_write_name("_HID"); + acpigen_emit_eisaid("PNP0A08"); + + acpigen_write_name("_CID"); + acpigen_emit_eisaid("PNP0A03"); + + acpigen_write_name("_UID"); + acpigen_write_string(acpi_name); + + acpigen_write_name("_PXM"); + acpigen_write_integer(0); + + acpigen_write_method("_OSC", 4); + acpigen_write_return_namestr("\\_SB.DOSC"); + acpigen_emit_byte(ARG0_OP); + acpigen_emit_byte(ARG1_OP); + acpigen_emit_byte(ARG2_OP); + acpigen_emit_byte(ARG3_OP); + acpigen_write_method_end(); + + acpigen_write_device_end(); + acpigen_write_scope_end(); + } + + pci_domain_fill_ssdt(dev); + + acpigen_write_pci_prt(dev); +} + +void pcie_rp_fill_ssdt(const struct device *dev) +{ + const char *acpi_scope = acpi_device_scope(dev); + const char *acpi_name = acpi_device_name(dev); + printk(BIOS_DEBUG, "%s ACPI scope: '%s', name: '%s'\n", __func__, acpi_scope, + acpi_name); + + acpigen_write_scope(acpi_scope); + acpigen_write_device(acpi_name); + acpigen_write_STA(dev->enabled ? ACPI_STATUS_DEVICE_ALL_ON : + ACPI_STATUS_DEVICE_ALL_OFF); + acpigen_write_ADR_pci_device(dev); + acpigen_write_device_end(); + acpigen_write_scope_end(); + + acpigen_write_pci_prt(dev); +} + +unsigned long acpi_create_srat_lapics(unsigned long current) +{ + for (struct device *cpu = DEV_PTR(cpu_bus)->downstream->children; cpu != NULL; + cpu = cpu->next) { + if (!is_enabled_cpu(cpu)) + continue; + + printk(BIOS_DEBUG, + "SRAT: APIC ID = 0x%02x, package ID = 0x%02x, node ID = 0x%02x, core ID = 0x%02x, thread ID = 0x%02x\n", + cpu->path.apic.apic_id, cpu->path.apic.package_id, + cpu->path.apic.node_id, cpu->path.apic.core_id, + cpu->path.apic.thread_id); + current += acpi_create_srat_lapic((acpi_srat_lapic_t *)current, + cpu->path.apic.node_id, + cpu->path.apic.apic_id); + } + + return current; +} + +static unsigned long acpi_fill_srat(unsigned long current) +{ + const uint32_t low_mem_end = sa_server_get_tolud(); + const uint64_t hi_mem_end = sa_server_get_touud(), mem_4G = 4ull * GiB; + + current = acpi_create_srat_lapics(current); + + current += acpi_create_srat_mem((acpi_srat_mem_t *)current, 0, 0, low_mem_end >> 10, + ACPI_SRAT_MEMORY_ENABLED); + + if (hi_mem_end > mem_4G) + current += acpi_create_srat_mem((acpi_srat_mem_t *)current, 0, mem_4G >> 10, + (hi_mem_end - mem_4G) >> 10, + ACPI_SRAT_MEMORY_ENABLED); + + return current; +} + +static unsigned long acpi_create_drhd(unsigned long current, struct device *dev) +{ + unsigned long tmp = current; + const struct device *dev_domain = dev_get_domain(dev); + if (!dev_domain->enabled) + return current; + + /** + * Bit 0 of VTBAR is read-write and it indicates whether VT-d base address is enabled. + */ + uint32_t vtd_base = pci_read_config32(dev, VTBAR); + if (!(vtd_base & VTD_CHIPSET_BASE_ADDRESS_ENABLE)) + return current; + + vtd_base = ALIGN_DOWN(vtd_base, 4 * KiB); + + const uint32_t pcie_seg = dev->upstream->segment_group; + if (is_dev_on_domain0(dev)) { + printk(BIOS_DEBUG, + "[DMA Remapping Hardware Unit Definition] Flags: 0x%x, PCI Segment: 0x%x, Register Base Address: 0x%x\n", + DRHD_INCLUDE_PCI_ALL, pcie_seg, vtd_base); + current += acpi_create_dmar_drhd_4k(current, DRHD_INCLUDE_PCI_ALL, pcie_seg, + vtd_base); + + union p2sb_bdf ioapic_bdf = p2sb_get_ioapic_bdf(); + printk(BIOS_DEBUG, "[IOAPIC] PCI Bus: 0x%x, PCI Path: 0x%x, 0x%x\n", + ioapic_bdf.bus, ioapic_bdf.dev, ioapic_bdf.fn); + current += acpi_create_dmar_ds_ioapic_from_hw( + current, IO_APIC_ADDR, ioapic_bdf.bus, ioapic_bdf.dev, ioapic_bdf.fn); + + uint16_t num_hpets = (read32p(HPET_BASE_ADDRESS) >> HPET_NUM_TIM_CAP_SHIFT) & + HPET_NUM_TIM_CAP_MASK; + if (num_hpets && num_hpets != HPET_NUM_TIM_CAP_MASK) { + if (read32p(HPET_BASE_ADDRESS + HPET_TMR0_CNF_CAP) & + HPET_TIMER_FSB_EN_CNF_MASK) { + union p2sb_bdf hpet_bdf = p2sb_get_hpet_bdf(); + printk(BIOS_DEBUG, + "[MSI_CAPABLE_HPET] Enumeration ID: 0x%x, PCI Bus: 0x%x, PCI Path: 0x%x, 0x%x\n", + 0, hpet_bdf.bus, hpet_bdf.dev, hpet_bdf.fn); + current += acpi_create_dmar_ds_msi_hpet( + current, 0, hpet_bdf.bus, hpet_bdf.dev, hpet_bdf.fn); + } + } + } else { + printk(BIOS_DEBUG, + "[DMA Remapping Hardware Unit Definition] Flags: 0x%x, PCI Segment: 0x%x, Register Base Address: 0x%x\n", + 0, pcie_seg, vtd_base); + current += acpi_create_dmar_drhd_4k(current, 0, pcie_seg, vtd_base); + + if (dev == DEV_PTR(dlb_sa)) { + if (DEV_PTR(dlb)) { + printk(BIOS_DEBUG, "[PCI Endpoint Device] %s\n", + dev_path(DEV_PTR(dlb))); + current += acpi_create_dmar_ds_pci( + current, PCI_DEV2BUS(PCI_BDF(DEV_PTR(dlb))), + PCI_SLOT(DEV_PTR(dlb)->path.pci.devfn), + PCI_FUNC(DEV_PTR(dlb)->path.pci.devfn)); + } + } else { + struct device *pci_bridge = NULL; + while ((pci_bridge = dev_bus_each_child(dev_get_domain(dev)->downstream, + pci_bridge))) { + if (pci_bridge->vendor != PCI_VID_INTEL) + continue; + + switch (pci_bridge->device) { + case PCI_DID_INTEL_SNR_CPU_PCIE_RPA: + case PCI_DID_INTEL_SNR_CPU_PCIE_RPB: + case PCI_DID_INTEL_SNR_CPU_PCIE_RPC: + case PCI_DID_INTEL_SNR_CPU_PCIE_RPD: + case PCI_DID_INTEL_SNR_VRP4_NIS: + case PCI_DID_INTEL_SNR_VRP5_QAT_1_8: + printk(BIOS_DEBUG, "[PCI Sub-hierarchy] %s\n", + dev_path(pci_bridge)); + current += acpi_create_dmar_ds_pci_br( + current, dev->upstream->secondary, + PCI_SLOT(pci_bridge->path.pci.devfn), + PCI_FUNC(pci_bridge->path.pci.devfn)); + break; + default: + continue; + } + } + } + } + + if (current != tmp) + acpi_dmar_drhd_fixup(tmp, current); + + return current; +} + +static unsigned long acpi_create_rmrr(unsigned long current) +{ + const uint32_t MEM_BLK_COUNT = 0x140, MEM_BLK_SIZE = 32; + uint32_t size = ALIGN_UP(MEM_BLK_COUNT * MEM_BLK_SIZE, 0x1000); + const struct cbmem_entry *entry; + void *ptr; + unsigned long tmp = current; + struct device *dev = PCH_DEV_XHCI; + + entry = cbmem_entry_find(CBMEM_ID_STORAGE_DATA); + if (!entry) { + ptr = cbmem_add(CBMEM_ID_STORAGE_DATA, size); + if (!ptr) { + printk(BIOS_ERR, "Failed to allocate reserved memory in cbmem!\n"); + return current; + } + + memset(ptr, 0, size); + } else { + ptr = cbmem_entry_start(entry); + size = cbmem_entry_size(entry); + } + + printk(BIOS_DEBUG, + "[Reserved Memory Region Reporting] PCI Segment: 0x%x, Base: %p, Limit: %p\n", + dev->upstream->segment_group, ptr, ptr + size - 1); + current += acpi_create_dmar_rmrr(current, dev->upstream->segment_group, (uintptr_t)ptr, + (uintptr_t)(ptr + size - 1)); + + printk(BIOS_DEBUG, "[PCI Endpoint Device] %s\n", dev_path(dev)); + current += acpi_create_dmar_ds_pci(current, PCI_DEV2BUS(PCI_BDF(dev)), + PCI_SLOT(dev->path.pci.devfn), + PCI_FUNC(dev->path.pci.devfn)); + + if (current != tmp) + acpi_dmar_rmrr_fixup(tmp, current); + + return current; +} + +static unsigned long acpi_create_atsr(unsigned long current, struct device *dev) +{ + unsigned long tmp = current; + + /** + * Bit 0 of VTBAR is read-write and it indicates whether VT-d base address is enabled. + */ + uint32_t vtd_base = pci_read_config32(dev, VTBAR); + if (!(vtd_base & VTD_CHIPSET_BASE_ADDRESS_ENABLE)) + return current; + + vtd_base = ALIGN_DOWN(vtd_base, 4 * KiB); + + uint64_t vtd_ext_cap = read64p(vtd_base + VTD_ECAP); + if (!(vtd_ext_cap & DEVICE_TLB)) + return current; + + printk(BIOS_DEBUG, "Domain 1 VT-d BAR: 0x%x, Extended Capability: 0x%llx\n", vtd_base, + vtd_ext_cap); + + bool first = true; + struct device *cpu_pcie_rp = NULL; + while ((cpu_pcie_rp = + dev_bus_each_child(dev_get_domain(dev)->downstream, cpu_pcie_rp))) { + if (cpu_pcie_rp->vendor != PCI_VID_INTEL) + continue; + + switch (cpu_pcie_rp->device) { + case PCI_DID_INTEL_SNR_CPU_PCIE_RPA: + case PCI_DID_INTEL_SNR_CPU_PCIE_RPB: + case PCI_DID_INTEL_SNR_CPU_PCIE_RPC: + case PCI_DID_INTEL_SNR_CPU_PCIE_RPD: + break; + default: + continue; + } + + if (first) { + const uint32_t pcie_seg = dev->upstream->segment_group; + printk(BIOS_DEBUG, + "[Root Port ATS Capability] Flags: 0x%x, PCI Segment: 0x%x\n", 0, + pcie_seg); + current += acpi_create_dmar_atsr(current, 0, pcie_seg); + first = false; + } + + printk(BIOS_DEBUG, "[PCI Sub-hierarchy] %s\n", dev_path(cpu_pcie_rp)); + current += acpi_create_dmar_ds_pci_br(current, + PCI_DEV2BUS(PCI_BDF(cpu_pcie_rp)), + PCI_SLOT(cpu_pcie_rp->path.pci.devfn), + PCI_FUNC(cpu_pcie_rp->path.pci.devfn)); + } + + if (tmp != current) + acpi_dmar_atsr_fixup(tmp, current); + + return current; +} + +static unsigned long acpi_fill_dmar(unsigned long current) +{ + assert(DEV_PTR(pch_sa)); + + /** + * Domain 0 hosts all PCH peripherals, and DRHD entry for this domain should be at last, + * thus we search from `DEV_PTR(pch_sa)` here. + */ + struct device *dev = DEV_PTR(pch_sa); + while ((dev = dev_find_device(PCI_VID_INTEL, PCI_DID_INTEL_SNR_ID, dev)) != NULL) + current = acpi_create_drhd(current, dev); + + current = acpi_create_drhd(current, DEV_PTR(pch_sa)); + + current = acpi_create_rmrr(current); + + /* Only CPU PCIe root ports support address translation services (ATS). */ + assert(DEV_PTR(cpu_sa)); + current = acpi_create_atsr(current, DEV_PTR(cpu_sa)); + + return current; +} + +unsigned long sa_write_acpi_tables(const struct device *dev, unsigned long current, + struct acpi_rsdp *rsdp) +{ + acpi_srat_t *srat; + acpi_dmar_t *dmar; + + /** + * Write only when calling from system agent in domain 0. + */ + if (!sa_server_is_on_pch_domain(dev)) + return current; + + /* SRAT */ + printk(BIOS_DEBUG, "ACPI: * SRAT at 0x%08lx\n", current); + srat = (acpi_srat_t *)current; + acpi_create_srat(srat, acpi_fill_srat); + acpi_add_table(rsdp, srat); + current += srat->header.length; + current = acpi_align_current(current); + + printk(BIOS_DEBUG, "ACPI: * DMAR at 0x%08lx\n", current); + dmar = (acpi_dmar_t *)current; + acpi_create_dmar(dmar, DMAR_INTR_REMAP, acpi_fill_dmar); + acpi_add_table(rsdp, dmar); + current += dmar->header.length; + + return current; +} + +/** + * To use ACPI in common block, this function should be defined. + */ +const acpi_cstate_t *soc_get_cstate_map(size_t *entries) +{ + *entries = 0; + return NULL; +} |