diff options
author | Shaunak Saha <shaunak.saha@intel.com> | 2017-07-18 00:19:33 -0700 |
---|---|---|
committer | Aaron Durbin <adurbin@chromium.org> | 2017-09-08 19:01:04 +0000 |
commit | bd427803ab18576462c2c179c94ec0fec819221c (patch) | |
tree | a441902068336e7d83bd2ad94efc2153e00bd1a1 /src/soc/intel/common/block | |
parent | af896d071b5d0c6ffabc1f4a5cda1429fb6754b6 (diff) |
soc/intel/common/block: Common ACPI
This patch adds the common acpi code.ACPI code is very similar
accross different intel chipsets.This patch is an effort to
move those code in common place so that it can be shared accross
different intel platforms instead of duplicating for each platform.
We are removing the common acpi files in src/soc/intel/common.
This removes the acpi.c file which was previously in
src/soc/common/acpi. The config for common acpi is
SOC_INTEL_COMMON_BLOCK_ACPI which can be defined in SOC's
Kconfig file in order to use the common ACPI code. This patch also
includes the changes in APL platform to use the common ACPI block.
TEST= Tested the patch as below:
1.Builds and system boots up with the patch.
2.Check all the ACPI tables are present in
/sys/firmware/acpi/tables
3.Check SCI's are properly working as we are
modifying the function to override madt.
4.Extract acpi tables like DSDT,APIC, FACP, FACS
and decompile the by iasl and compare with good
known tables.
5.Execute the extracted tables in aciexec to check
acpi methods are working properly.
Change-Id: Ib6eb6fd5366e6e28fd81bc22d050b0efa05a2e5d
Signed-off-by: Shaunak Saha <shaunak.saha@intel.com>
Reviewed-on: https://review.coreboot.org/20630
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Sumeet R Pawnikar <sumeet.r.pawnikar@intel.com>
Diffstat (limited to 'src/soc/intel/common/block')
-rw-r--r-- | src/soc/intel/common/block/acpi/Kconfig | 6 | ||||
-rw-r--r-- | src/soc/intel/common/block/acpi/Makefile.inc | 1 | ||||
-rw-r--r-- | src/soc/intel/common/block/acpi/acpi.c | 497 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/acpi.h | 87 | ||||
-rw-r--r-- | src/soc/intel/common/block/lpc/lpc.c | 2 |
5 files changed, 592 insertions, 1 deletions
diff --git a/src/soc/intel/common/block/acpi/Kconfig b/src/soc/intel/common/block/acpi/Kconfig new file mode 100644 index 0000000000..ff59172f3b --- /dev/null +++ b/src/soc/intel/common/block/acpi/Kconfig @@ -0,0 +1,6 @@ +config SOC_INTEL_COMMON_BLOCK_ACPI + depends on SOC_INTEL_COMMON_BLOCK_CPU + depends on SOC_INTEL_COMMON_BLOCK_PMC + bool + help + Intel Processor common code for ACPI diff --git a/src/soc/intel/common/block/acpi/Makefile.inc b/src/soc/intel/common/block/acpi/Makefile.inc new file mode 100644 index 0000000000..c6bdac5d3e --- /dev/null +++ b/src/soc/intel/common/block/acpi/Makefile.inc @@ -0,0 +1 @@ +ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_ACPI) += acpi.c diff --git a/src/soc/intel/common/block/acpi/acpi.c b/src/soc/intel/common/block/acpi/acpi.c new file mode 100644 index 0000000000..e216f2e001 --- /dev/null +++ b/src/soc/intel/common/block/acpi/acpi.c @@ -0,0 +1,497 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corp. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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/acpigen.h> +#include <arch/ioapic.h> +#include <arch/smp/mpspec.h> +#include <bootstate.h> +#include <cbmem.h> +#include <cpu/intel/reset.h> +#include <cpu/intel/turbo.h> +#include <cpu/x86/msr.h> +#include <cpu/x86/smm.h> +#include <intelblocks/acpi.h> +#include <intelblocks/msr.h> +#include <intelblocks/pmclib.h> +#include <soc/gpio.h> +#include <soc/iomap.h> +#include <soc/nvs.h> +#include <soc/pm.h> + +unsigned long acpi_fill_mcfg(unsigned long current) +{ + /* PCI Segment Group 0, Start Bus Number 0, End Bus Number is 255 */ + current += acpi_create_mcfg_mmconfig((void *)current, + CONFIG_MMCONF_BASE_ADDRESS, 0, 0, + 255); + return current; +} + +static int acpi_sci_irq(void) +{ + int sci_irq = 9; + uint32_t scis; + + scis = soc_read_sci_irq_select(); + scis &= SCI_IRQ_SEL; + scis >>= SCI_IRQ_ADJUST; + + /* Determine how SCI is routed. */ + switch (scis) { + case SCIS_IRQ9: + case SCIS_IRQ10: + case SCIS_IRQ11: + sci_irq = scis - SCIS_IRQ9 + 9; + break; + case SCIS_IRQ20: + case SCIS_IRQ21: + case SCIS_IRQ22: + case SCIS_IRQ23: + sci_irq = scis - SCIS_IRQ20 + 20; + break; + default: + printk(BIOS_DEBUG, "Invalid SCI route! Defaulting to IRQ9.\n"); + sci_irq = 9; + break; + } + + printk(BIOS_DEBUG, "SCI is IRQ%d\n", sci_irq); + return sci_irq; +} + +static unsigned long acpi_madt_irq_overrides(unsigned long current) +{ + int sci = acpi_sci_irq(); + uint16_t flags = MP_IRQ_TRIGGER_LEVEL; + + /* INT_SRC_OVR */ + current += acpi_create_madt_irqoverride((void *)current, 0, 0, 2, 0); + + flags |= soc_madt_sci_irq_polarity(sci); + + /* SCI */ + current += + acpi_create_madt_irqoverride((void *)current, 0, sci, sci, flags); + + return current; +} + +unsigned long acpi_fill_madt(unsigned long current) +{ + /* Local APICs */ + current = acpi_create_madt_lapics(current); + + /* IOAPIC */ + current += acpi_create_madt_ioapic((void *)current, 2, IO_APIC_ADDR, 0); + + return acpi_madt_irq_overrides(current); +} + +__attribute__ ((weak)) void soc_fill_fadt(acpi_fadt_t *fadt) +{ +} + +void acpi_fill_fadt(acpi_fadt_t *fadt) +{ + const uint16_t pmbase = ACPI_BASE_ADDRESS; + + /* Use ACPI 3.0 revision. */ + fadt->header.revision = ACPI_FADT_REV_ACPI_3_0; + + fadt->sci_int = acpi_sci_irq(); + fadt->smi_cmd = APM_CNT; + fadt->acpi_enable = APM_CNT_ACPI_ENABLE; + fadt->acpi_disable = APM_CNT_ACPI_DISABLE; + fadt->s4bios_req = 0x0; + fadt->pstate_cnt = 0; + + fadt->pm1a_evt_blk = pmbase + PM1_STS; + fadt->pm1b_evt_blk = 0x0; + fadt->pm1a_cnt_blk = pmbase + PM1_CNT; + fadt->pm1b_cnt_blk = 0x0; + + fadt->gpe0_blk = pmbase + GPE0_STS(0); + + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + + /* GPE0 STS/EN pairs each 32 bits wide. */ + fadt->gpe0_blk_len = 2 * GPE0_REG_MAX * sizeof(uint32_t); + + fadt->flush_size = 0x400; /* twice of cache size */ + fadt->flush_stride = 0x10; /* Cache line width */ + fadt->duty_offset = 1; + fadt->day_alrm = 0xd; + + fadt->flags = ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED | + ACPI_FADT_C2_MP_SUPPORTED | ACPI_FADT_SLEEP_BUTTON | + ACPI_FADT_RESET_REGISTER | ACPI_FADT_SEALED_CASE | + ACPI_FADT_S4_RTC_WAKE | ACPI_FADT_PLATFORM_CLOCK; + + fadt->reset_reg.space_id = 1; + fadt->reset_reg.bit_width = 8; + fadt->reset_reg.addrl = RST_CNT; + fadt->reset_value = RST_CPU | SYS_RST; + + fadt->x_pm1a_evt_blk.space_id = 1; + fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; + fadt->x_pm1a_evt_blk.addrl = pmbase + PM1_STS; + + fadt->x_pm1b_evt_blk.space_id = 1; + + fadt->x_pm1a_cnt_blk.space_id = 1; + fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8; + fadt->x_pm1a_cnt_blk.addrl = pmbase + PM1_CNT; + + fadt->x_pm1b_cnt_blk.space_id = 1; + + fadt->x_gpe1_blk.space_id = 1; + + soc_fill_fadt(fadt); +} + +unsigned long southbridge_write_acpi_tables(device_t device, + unsigned long current, + struct acpi_rsdp *rsdp) +{ + return acpi_write_hpet(device, current, rsdp); +} + +__attribute__ ((weak)) +uint32_t acpi_fill_soc_wake(uint32_t generic_pm1_en, + const struct chipset_power_state *ps) +{ + return generic_pm1_en; +} + +/* + * Save wake source information for calculating ACPI _SWS values + * + * @pm1: PM1_STS register with only enabled events set + * @gpe0: GPE0_STS registers with only enabled events set + * + * return the number of registers in the gpe0 array or -1 if nothing + * is provided by this function. + */ + +static int acpi_fill_wake(uint32_t *pm1, uint32_t **gpe0) +{ + struct chipset_power_state *ps; + static uint32_t gpe0_sts[GPE0_REG_MAX]; + uint32_t pm1_en; + int i; + + ps = cbmem_find(CBMEM_ID_POWER_STATE); + if (ps == NULL) + return -1; + + /* + * PM1_EN to check the basic wake events which can happen through + * powerbtn or any other wake source like lidopen, key board press etc. + */ + pm1_en = ps->pm1_en; + + pm1_en = acpi_fill_soc_wake(pm1_en, ps); + + *pm1 = ps->pm1_sts & pm1_en; + + /* Mask off GPE0 status bits that are not enabled */ + *gpe0 = &gpe0_sts[0]; + for (i = 0; i < GPE0_REG_MAX; i++) + gpe0_sts[i] = ps->gpe0_sts[i] & ps->gpe0_en[i]; + + return GPE0_REG_MAX; +} + +__attribute__ ((weak)) void acpi_create_gnvs(struct global_nvs_t *gnvs) +{ +} + +void southbridge_inject_dsdt(device_t device) +{ + struct global_nvs_t *gnvs; + + gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS); + if (!gnvs) { + gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(*gnvs)); + if (gnvs) + memset(gnvs, 0, sizeof(*gnvs)); + } + + if (gnvs) { + acpi_create_gnvs(gnvs); + acpi_save_gnvs((uintptr_t) gnvs); + /* And tell SMI about it */ + smm_setup_structures(gnvs, NULL, NULL); + + /* Add it to DSDT. */ + acpigen_write_scope("\\"); + acpigen_write_name_dword("NVSA", (uintptr_t) gnvs); + acpigen_pop_len(); + } +} + +static int calculate_power(int tdp, int p1_ratio, int ratio) +{ + u32 m; + u32 power; + + /* + * M = ((1.1 - ((p1_ratio - ratio) * 0.00625)) / 1.1) ^ 2 + * + * Power = (ratio / p1_ratio) * m * tdp + */ + + m = (110000 - ((p1_ratio - ratio) * 625)) / 11; + m = (m * m) / 1000; + + power = ((ratio * 100000 / p1_ratio) / 100); + power *= (m / 100) * (tdp / 1000); + power /= 1000; + + return power; +} + +static int get_cores_per_package(void) +{ + struct cpuinfo_x86 c; + struct cpuid_result result; + int cores = 1; + + get_fms(&c, cpuid_eax(1)); + if (c.x86 != 6) + return 1; + + result = cpuid_ext(0xb, 1); + cores = result.ebx & 0xff; + + return cores; +} + +static void generate_c_state_entries(void) +{ + acpi_cstate_t *c_state_map; + size_t entries; + + c_state_map = soc_get_cstate_map(&entries); + + /* Generate C-state tables */ + acpigen_write_CST_package(c_state_map, entries); +} + +void generate_p_state_entries(int core, int cores_per_package) +{ + int ratio_min, ratio_max, ratio_turbo, ratio_step; + int coord_type, power_max, num_entries; + int ratio, power, clock, clock_max; + + coord_type = cpu_get_coord_type(); + ratio_min = cpu_get_min_ratio(); + ratio_max = cpu_get_max_ratio(); + clock_max = (ratio_max * cpu_get_bus_clock()) / KHz; + + /* Calculate CPU TDP in mW */ + power_max = cpu_get_power_max(); + + /* Write _PCT indicating use of FFixedHW */ + acpigen_write_empty_PCT(); + + /* Write _PPC with no limit on supported P-state */ + acpigen_write_PPC_NVS(); + /* Write PSD indicating configured coordination type */ + acpigen_write_PSD_package(core, 1, coord_type); + + /* Add P-state entries in _PSS table */ + acpigen_write_name("_PSS"); + + /* Determine ratio points */ + ratio_step = PSS_RATIO_STEP; + num_entries = ((ratio_max - ratio_min) / ratio_step) + 1; + if (num_entries > PSS_MAX_ENTRIES) { + ratio_step += 1; + num_entries = ((ratio_max - ratio_min) / ratio_step) + 1; + } + + /* P[T] is Turbo state if enabled */ + if (get_turbo_state() == TURBO_ENABLED) { + /* _PSS package count including Turbo */ + acpigen_write_package(num_entries + 2); + ratio_turbo = cpu_get_max_turbo_ratio(); + + /* Add entry for Turbo ratio */ + acpigen_write_PSS_package(clock_max + 1, /* MHz */ + power_max, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio_turbo << 8, /* control */ + ratio_turbo << 8); /* status */ + } else { + /* _PSS package count without Turbo */ + acpigen_write_package(num_entries + 1); + } + + /* First regular entry is max non-turbo ratio */ + acpigen_write_PSS_package(clock_max, /* MHz */ + power_max, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio_max << 8, /* control */ + ratio_max << 8); /* status */ + + /* Generate the remaining entries */ + for (ratio = ratio_min + ((num_entries - 1) * ratio_step); + ratio >= ratio_min; ratio -= ratio_step) { + + /* Calculate power at this ratio */ + power = calculate_power(power_max, ratio_max, ratio); + clock = (ratio * cpu_get_bus_clock()) / KHz; + + acpigen_write_PSS_package(clock, /* MHz */ + power, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio << 8, /* control */ + ratio << 8); /* status */ + } + /* Fix package length */ + acpigen_pop_len(); +} + +static acpi_tstate_t *soc_get_tss_table(int *entries) +{ + *entries = 0; + return NULL; +} + +void generate_t_state_entries(int core, int cores_per_package) +{ + acpi_tstate_t *soc_tss_table; + int entries; + + soc_tss_table = soc_get_tss_table(&entries); + if (entries == 0) + return; + + /* Indicate SW_ALL coordination for T-states */ + acpigen_write_TSD_package(core, cores_per_package, SW_ALL); + + /* Indicate FixedHW so OS will use MSR */ + acpigen_write_empty_PTC(); + + /* Set NVS controlled T-state limit */ + acpigen_write_TPC("\\TLVL"); + + /* Write TSS table for MSR access */ + acpigen_write_TSS_package(entries, soc_tss_table); +} + +__attribute__ ((weak)) void soc_power_states_generation(int core_id, + int cores_per_package) +{ +} + +void generate_cpu_entries(device_t device) +{ + int core_id, cpu_id, pcontrol_blk = ACPI_BASE_ADDRESS; + int plen = 6; + int totalcores = dev_count_cpu(); + int cores_per_package = get_cores_per_package(); + int numcpus = totalcores / cores_per_package; + + printk(BIOS_DEBUG, "Found %d CPU(s) with %d core(s) each.\n", + numcpus, cores_per_package); + + for (cpu_id = 0; cpu_id < numcpus; cpu_id++) { + for (core_id = 0; core_id < cores_per_package; core_id++) { + if (core_id > 0) { + pcontrol_blk = 0; + plen = 0; + } + + /* Generate processor \_PR.CPUx */ + acpigen_write_processor((cpu_id) * cores_per_package + + core_id, pcontrol_blk, plen); + + /* Generate C-state tables */ + generate_c_state_entries(); + + /* Soc specific power states generation */ + soc_power_states_generation(core_id, cores_per_package); + + acpigen_pop_len(); + } + } +} + +#if IS_ENABLED(CONFIG_SOC_INTEL_COMMON_ACPI_WAKE_SOURCE) +/* Save wake source data for ACPI _SWS methods in NVS */ +static void acpi_save_wake_source(void *unused) +{ + global_nvs_t *gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS); + uint32_t pm1, *gpe0; + int gpe_reg, gpe_reg_count; + int reg_size = sizeof(uint32_t) * 8; + + if (!gnvs) + return; + + gnvs->pm1i = -1; + gnvs->gpei = -1; + + gpe_reg_count = acpi_fill_wake(&pm1, &gpe0); + if (gpe_reg_count < 0) + return; + + /* Scan for first set bit in PM1 */ + for (gnvs->pm1i = 0; gnvs->pm1i < reg_size; gnvs->pm1i++) { + if (pm1 & 1) + break; + pm1 >>= 1; + } + + /* If unable to determine then return -1 */ + if (gnvs->pm1i >= 16) + gnvs->pm1i = -1; + + /* Scan for first set bit in GPE registers */ + for (gpe_reg = 0; gpe_reg < gpe_reg_count; gpe_reg++) { + uint32_t gpe = gpe0[gpe_reg]; + int start = gpe_reg * reg_size; + int end = start + reg_size; + + if (gpe == 0) { + if (!gnvs->gpei) + gnvs->gpei = end; + continue; + } + + for (gnvs->gpei = start; gnvs->gpei < end; gnvs->gpei++) { + if (gpe & 1) + break; + gpe >>= 1; + } + } + + /* If unable to determine then return -1 */ + if (gnvs->gpei >= gpe_reg_count * reg_size) + gnvs->gpei = -1; + + printk(BIOS_DEBUG, "ACPI _SWS is PM1 Index %lld GPE Index %lld\n", + (long long)gnvs->pm1i, (long long)gnvs->gpei); +} + +BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, acpi_save_wake_source, NULL); + +#endif diff --git a/src/soc/intel/common/block/include/intelblocks/acpi.h b/src/soc/intel/common/block/include/intelblocks/acpi.h new file mode 100644 index 0000000000..010773b52a --- /dev/null +++ b/src/soc/intel/common/block/include/intelblocks/acpi.h @@ -0,0 +1,87 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corp. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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_ACPI_H +#define SOC_INTEL_COMMON_BLOCK_ACPI_H + +#include <arch/acpi.h> +#include <device/device.h> +#include <intelblocks/cpulib.h> +#include <stdint.h> + +/* Forward declare the power state struct here */ +struct chipset_power_state; + +/* Forward declare the global nvs structure here */ +struct global_nvs_t; + +/* Read the scis from soc specific register. Returns int scis value */ +uint32_t soc_read_sci_irq_select(void); + +/* + * Calls acpi_write_hpet which creates and fills HPET table and + * adds it to the RSDT (and XSDT) structure. + */ +unsigned long southbridge_write_acpi_tables(device_t device, + unsigned long current, + struct acpi_rsdp *rsdp); + +/* + * Craetes acpi gnvs and adds it to the DSDT table. + * GNVS creation is chipset specific and is done in soc specific acpi.c file. + */ +void southbridge_inject_dsdt(device_t device); + +/* + * This function populates the gnvs structure in acpi table. + * Defined as weak in common acpi as gnvs structure definition is + * chipset specific. + */ +void acpi_create_gnvs(struct global_nvs_t *gnvs); + +/* + * get_cstate_map returns a table of processor specific acpi_cstate_t entries + * and number of entries in the table + */ +acpi_cstate_t *soc_get_cstate_map(size_t *num_entries); + +/* + * Chipset specific quirks for the wake enable bits. + * Returns wake events for the soc. + */ +uint32_t acpi_fill_soc_wake(uint32_t generic_pm1_en, + const struct chipset_power_state *ps); + +/* Chipset specific settings for filling up fadt table */ +void soc_fill_fadt(acpi_fadt_t *fadt); + +/* Return the polarity flag for SCI IRQ */ +int soc_madt_sci_irq_polarity(int sci); + +/* Generate P-state tables */ +void generate_p_state_entries(int core, int cores_per_package); + +/* Generate T-state tables */ +void generate_t_state_entries(int core, int cores_per_package); + +/* + * soc specific power states generation. We need this to be defined by soc + * as the state generations varies in chipsets e.g. APL generates T and P + * states while SKL generates * P state only depening on a devicetree config + */ +void soc_power_states_generation(int core_id, int cores_per_package); + +#endif /* _SOC_INTEL_COMMON_BLOCK_ACPI_H_ */ diff --git a/src/soc/intel/common/block/lpc/lpc.c b/src/soc/intel/common/block/lpc/lpc.c index 0559d95399..1c4e2ce785 100644 --- a/src/soc/intel/common/block/lpc/lpc.c +++ b/src/soc/intel/common/block/lpc/lpc.c @@ -17,8 +17,8 @@ #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> +#include <intelblocks/acpi.h> #include <intelblocks/lpc_lib.h> -#include <soc/acpi.h> #include <soc/pm.h> /* Common weak definition, needs to be implemented in each soc LPC driver. */ |