2015-11-10 02:06:34 +01:00
|
|
|
/*
|
|
|
|
* This file is part of the coreboot project.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2016 Intel Corp.
|
2018-12-14 13:26:04 +01:00
|
|
|
* Copyright (C) 2017-2019 Siemens AG
|
2015-11-11 04:00:18 +01:00
|
|
|
* (Written by Lance Zhao <lijian.zhao@intel.com> for Intel Corp.)
|
2015-11-10 02:06:34 +01:00
|
|
|
*
|
|
|
|
* 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.
|
2016-04-10 19:09:16 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2015-11-10 02:06:34 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <arch/acpi.h>
|
2016-04-20 03:04:21 +02:00
|
|
|
#include <arch/acpigen.h>
|
2019-03-03 07:01:05 +01:00
|
|
|
#include <device/mmio.h>
|
2015-11-17 03:33:21 +01:00
|
|
|
#include <arch/smp/mpspec.h>
|
2018-12-14 13:26:04 +01:00
|
|
|
#include <device/pci_ops.h>
|
2016-04-20 03:04:21 +02:00
|
|
|
#include <cbmem.h>
|
2015-11-11 04:00:18 +01:00
|
|
|
#include <cpu/x86/smm.h>
|
2017-07-18 09:19:33 +02:00
|
|
|
#include <gpio.h>
|
|
|
|
#include <intelblocks/acpi.h>
|
|
|
|
#include <intelblocks/pmclib.h>
|
2017-10-11 20:52:16 +02:00
|
|
|
#include <intelblocks/sgx.h>
|
2018-12-14 13:26:04 +01:00
|
|
|
#include <intelblocks/p2sb.h>
|
2015-11-11 04:00:18 +01:00
|
|
|
#include <soc/iomap.h>
|
|
|
|
#include <soc/pm.h>
|
2016-04-20 03:04:21 +02:00
|
|
|
#include <soc/nvs.h>
|
2016-07-12 10:22:33 +02:00
|
|
|
#include <soc/pci_devs.h>
|
2018-12-14 13:26:04 +01:00
|
|
|
#include <soc/systemagent.h>
|
2016-09-13 19:31:57 +02:00
|
|
|
#include <string.h>
|
2016-07-12 10:22:33 +02:00
|
|
|
#include "chip.h"
|
2015-11-10 02:06:34 +01:00
|
|
|
|
2016-04-18 22:47:08 +02:00
|
|
|
#define CSTATE_RES(address_space, width, offset, address) \
|
|
|
|
{ \
|
|
|
|
.space_id = address_space, \
|
|
|
|
.bit_width = width, \
|
|
|
|
.bit_offset = offset, \
|
|
|
|
.addrl = address, \
|
|
|
|
}
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
static acpi_cstate_t cstate_map[] = {
|
|
|
|
{
|
|
|
|
/* C1 */
|
|
|
|
.ctype = 1, /* ACPI C1 */
|
|
|
|
.latency = 1,
|
|
|
|
.power = 1000,
|
|
|
|
.resource = CSTATE_RES(ACPI_ADDRESS_SPACE_FIXED, 0, 0, 0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.ctype = 2, /* ACPI C2 */
|
|
|
|
.latency = 50,
|
|
|
|
.power = 10,
|
|
|
|
.resource = CSTATE_RES(ACPI_ADDRESS_SPACE_IO, 8, 0, 0x415),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.ctype = 3, /* ACPI C3 */
|
|
|
|
.latency = 150,
|
|
|
|
.power = 10,
|
|
|
|
.resource = CSTATE_RES(ACPI_ADDRESS_SPACE_IO, 8, 0, 0x419),
|
|
|
|
}
|
|
|
|
};
|
2015-11-11 04:00:18 +01:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
uint32_t soc_read_sci_irq_select(void)
|
2015-11-11 04:00:18 +01:00
|
|
|
{
|
2017-07-18 09:19:33 +02:00
|
|
|
uintptr_t pmc_bar = soc_read_pmc_base();
|
|
|
|
return read32((void *)pmc_bar + IRQ_REG);
|
2015-11-10 02:06:34 +01:00
|
|
|
}
|
2015-12-01 18:14:20 +01:00
|
|
|
|
2017-09-18 17:08:48 +02:00
|
|
|
void soc_write_sci_irq_select(uint32_t scis)
|
|
|
|
{
|
|
|
|
uintptr_t pmc_bar = soc_read_pmc_base();
|
|
|
|
write32((void *)pmc_bar + IRQ_REG, scis);
|
|
|
|
}
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
acpi_cstate_t *soc_get_cstate_map(size_t *entries)
|
2015-12-01 18:14:20 +01:00
|
|
|
{
|
2017-07-18 09:19:33 +02:00
|
|
|
*entries = ARRAY_SIZE(cstate_map);
|
|
|
|
return cstate_map;
|
2015-12-01 18:14:20 +01:00
|
|
|
}
|
2016-04-20 03:04:21 +02:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
void acpi_create_gnvs(struct global_nvs_t *gnvs)
|
2016-04-20 03:04:21 +02:00
|
|
|
{
|
2016-07-12 10:22:33 +02:00
|
|
|
struct soc_intel_apollolake_config *cfg;
|
2017-03-05 08:07:00 +01:00
|
|
|
struct device *dev = SA_DEV_ROOT;
|
2016-07-12 10:22:33 +02:00
|
|
|
|
2016-09-13 19:31:57 +02:00
|
|
|
/* Clear out GNVS. */
|
|
|
|
memset(gnvs, 0, sizeof(*gnvs));
|
2016-07-12 10:22:33 +02:00
|
|
|
|
2016-06-14 07:23:49 +02:00
|
|
|
if (IS_ENABLED(CONFIG_CONSOLE_CBMEM))
|
2017-07-18 09:19:33 +02:00
|
|
|
gnvs->cbmc = (uintptr_t) cbmem_find(CBMEM_ID_CONSOLE);
|
2016-06-14 07:23:49 +02:00
|
|
|
|
2016-04-20 03:04:21 +02:00
|
|
|
if (IS_ENABLED(CONFIG_CHROMEOS)) {
|
|
|
|
/* Initialize Verified Boot data */
|
2018-08-23 08:56:25 +02:00
|
|
|
chromeos_init_chromeos_acpi(&gnvs->chromeos);
|
2016-04-20 03:04:21 +02:00
|
|
|
gnvs->chromeos.vbt2 = ACTIVE_ECFW_RO;
|
|
|
|
}
|
2016-07-12 10:22:33 +02:00
|
|
|
|
2016-08-03 02:25:13 +02:00
|
|
|
/* Set unknown wake source */
|
|
|
|
gnvs->pm1i = ~0ULL;
|
2016-09-13 19:31:57 +02:00
|
|
|
|
2016-09-22 03:30:44 +02:00
|
|
|
/* CPU core count */
|
|
|
|
gnvs->pcnt = dev_count_cpu();
|
|
|
|
|
2016-09-13 19:31:57 +02:00
|
|
|
if (!dev || !dev->chip_info) {
|
|
|
|
printk(BIOS_ERR, "BUG! Could not find SOC devicetree config\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cfg = dev->chip_info;
|
|
|
|
|
|
|
|
/* Enable DPTF based on mainboard configuration */
|
|
|
|
gnvs->dpte = cfg->dptf_enable;
|
2016-08-24 02:56:17 +02:00
|
|
|
|
|
|
|
/* Assign address of PERST_0 if GPIO is defined in devicetree */
|
|
|
|
if (cfg->prt0_gpio != GPIO_PRT0_UDEF)
|
2017-07-18 09:19:33 +02:00
|
|
|
gnvs->prt0 = (uintptr_t) gpio_dwx_address(cfg->prt0_gpio);
|
2017-02-25 00:37:30 +01:00
|
|
|
|
2017-03-23 02:24:52 +01:00
|
|
|
/* Get sdcard cd GPIO portid if GPIO is defined in devicetree.
|
|
|
|
* Get offset of sdcard cd pin.
|
|
|
|
*/
|
|
|
|
if (cfg->sdcard_cd_gpio) {
|
|
|
|
gnvs->scdp = gpio_get_pad_portid(cfg->sdcard_cd_gpio);
|
|
|
|
gnvs->scdo = gpio_acpi_pin(cfg->sdcard_cd_gpio);
|
|
|
|
}
|
2017-10-11 20:52:16 +02:00
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_SOC_INTEL_COMMON_BLOCK_SGX))
|
|
|
|
sgx_fill_gnvs(gnvs);
|
2016-08-03 02:25:13 +02:00
|
|
|
}
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
uint32_t acpi_fill_soc_wake(uint32_t generic_pm1_en,
|
|
|
|
const struct chipset_power_state *ps)
|
2016-08-03 02:25:13 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* WAK_STS bit is set when the system is in one of the sleep states
|
|
|
|
* (via the SLP_EN bit) and an enabled wake event occurs. Upon setting
|
|
|
|
* this bit, the PMC will transition the system to the ON state and
|
|
|
|
* can only be set by hardware and can only be cleared by writing a one
|
|
|
|
* to this bit position.
|
|
|
|
*/
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
generic_pm1_en |= WAK_STS | RTC_EN | PWRBTN_EN;
|
|
|
|
return generic_pm1_en;
|
|
|
|
}
|
2016-08-03 02:25:13 +02:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
int soc_madt_sci_irq_polarity(int sci)
|
|
|
|
{
|
|
|
|
return MP_IRQ_POLARITY_LOW;
|
2016-04-20 03:04:21 +02:00
|
|
|
}
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
void soc_fill_fadt(acpi_fadt_t *fadt)
|
2016-04-20 03:04:21 +02:00
|
|
|
{
|
2017-12-13 18:37:05 +01:00
|
|
|
const struct soc_intel_apollolake_config *cfg;
|
|
|
|
struct device *dev = SA_DEV_ROOT;
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
fadt->pm_tmr_blk = ACPI_BASE_ADDRESS + PM1_TMR;
|
2016-04-20 03:04:21 +02:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
fadt->p_lvl2_lat = ACPI_FADT_C2_NOT_SUPPORTED;
|
|
|
|
fadt->p_lvl3_lat = ACPI_FADT_C3_NOT_SUPPORTED;
|
2016-04-20 03:04:21 +02:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
fadt->pm_tmr_len = 4;
|
|
|
|
fadt->duty_width = 3;
|
2016-04-20 03:04:21 +02:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042;
|
2016-04-18 22:47:08 +02:00
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
fadt->x_pm_tmr_blk.space_id = 1;
|
|
|
|
fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8;
|
|
|
|
fadt->x_pm_tmr_blk.addrl = ACPI_BASE_ADDRESS + PM1_TMR;
|
2017-12-13 18:37:05 +01:00
|
|
|
|
|
|
|
if (!dev || !dev->chip_info) {
|
|
|
|
printk(BIOS_ERR, "BUG! Could not find SOC devicetree config\n");
|
|
|
|
return;
|
|
|
|
}
|
2018-10-05 10:36:45 +02:00
|
|
|
cfg = dev->chip_info;
|
2017-12-13 18:37:05 +01:00
|
|
|
|
|
|
|
if(cfg->lpss_s0ix_enable)
|
|
|
|
fadt->flags |= ACPI_FADT_LOW_PWR_IDLE_S0;
|
2016-04-18 22:47:08 +02:00
|
|
|
}
|
|
|
|
|
2018-12-14 13:26:04 +01:00
|
|
|
static unsigned long soc_fill_dmar(unsigned long current)
|
|
|
|
{
|
|
|
|
struct device *const igfx_dev = dev_find_slot(0, SA_DEVFN_IGD);
|
|
|
|
uint64_t gfxvtbar = MCHBAR64(GFXVTBAR) & VTBAR_MASK;
|
|
|
|
uint64_t defvtbar = MCHBAR64(DEFVTBAR) & VTBAR_MASK;
|
|
|
|
bool gfxvten = MCHBAR32(GFXVTBAR) & VTBAR_ENABLED;
|
|
|
|
bool defvten = MCHBAR32(DEFVTBAR) & VTBAR_ENABLED;
|
|
|
|
unsigned long tmp;
|
|
|
|
|
|
|
|
/* IGD has to be enabled, GFXVTBAR set and enabled. */
|
|
|
|
if (igfx_dev && igfx_dev->enabled && gfxvtbar && gfxvten) {
|
|
|
|
tmp = current;
|
|
|
|
|
|
|
|
current += acpi_create_dmar_drhd(current, 0, 0, gfxvtbar);
|
|
|
|
current += acpi_create_dmar_ds_pci(current, 0, 2, 0);
|
|
|
|
acpi_dmar_drhd_fixup(tmp, current);
|
|
|
|
|
|
|
|
/* Add RMRR entry */
|
|
|
|
tmp = current;
|
|
|
|
current += acpi_create_dmar_rmrr(current, 0,
|
|
|
|
sa_get_gsm_base(), sa_get_tolud_base() - 1);
|
|
|
|
current += acpi_create_dmar_ds_pci(current, 0, 2, 0);
|
|
|
|
acpi_dmar_rmrr_fixup(tmp, current);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DEFVTBAR has to be set and enabled. */
|
|
|
|
if (defvtbar && defvten) {
|
|
|
|
tmp = current;
|
|
|
|
/*
|
|
|
|
* P2SB may already be hidden. There's no clear rule, when.
|
|
|
|
* It is needed to get bus, device and function for IOAPIC and
|
|
|
|
* HPET device which is stored in P2SB device. So unhide it to
|
|
|
|
* get the info and hide it again when done.
|
|
|
|
*/
|
|
|
|
p2sb_unhide();
|
|
|
|
struct device *p2sb_dev = dev_find_slot(0, PCH_DEVFN_P2SB);
|
|
|
|
uint16_t ibdf = pci_read_config16(p2sb_dev, PCH_P2SB_IBDF);
|
|
|
|
uint16_t hbdf = pci_read_config16(p2sb_dev, PCH_P2SB_HBDF);
|
|
|
|
p2sb_hide();
|
|
|
|
|
|
|
|
current += acpi_create_dmar_drhd(current,
|
|
|
|
DRHD_INCLUDE_PCI_ALL, 0, defvtbar);
|
|
|
|
current += acpi_create_dmar_ds_ioapic(current,
|
|
|
|
2, ibdf >> 8, PCI_SLOT(ibdf), PCI_FUNC(ibdf));
|
|
|
|
current += acpi_create_dmar_ds_msi_hpet(current,
|
|
|
|
0, hbdf >> 8, PCI_SLOT(hbdf), PCI_FUNC(hbdf));
|
|
|
|
acpi_dmar_drhd_fixup(tmp, current);
|
|
|
|
}
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long sa_write_acpi_tables(struct device *const dev,
|
|
|
|
unsigned long current,
|
|
|
|
struct acpi_rsdp *const rsdp)
|
|
|
|
{
|
|
|
|
acpi_dmar_t *const dmar = (acpi_dmar_t *)current;
|
|
|
|
|
|
|
|
/* Create DMAR table only if virtualization is enabled. Due to some
|
|
|
|
* constraints on Apollo Lake SoC (some stepping affected), VTD could
|
|
|
|
* not be enabled together with IPU. Doing so will override and disable
|
|
|
|
* VTD while leaving CAPID0_A still reporting that VTD is available.
|
|
|
|
* As in this case FSP will lock VTD to disabled state, we need to make
|
|
|
|
* sure that DMAR table generation only happens when at least DEFVTBAR
|
|
|
|
* is enabled. Otherwise the DMAR header will be generated while the
|
|
|
|
* content of the table will be missing.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((pci_read_config32(dev, CAPID0_A) & VTD_DISABLE) ||
|
|
|
|
!(MCHBAR32(DEFVTBAR) & VTBAR_ENABLED))
|
|
|
|
return current;
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * DMAR\n");
|
|
|
|
acpi_create_dmar(dmar, DMAR_INTR_REMAP, soc_fill_dmar);
|
|
|
|
current += dmar->header.length;
|
|
|
|
current = acpi_align_current(current);
|
|
|
|
acpi_add_table(rsdp, dmar);
|
|
|
|
current = acpi_align_current(current);
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2017-07-18 09:19:33 +02:00
|
|
|
void soc_power_states_generation(int core_id, int cores_per_package)
|
2016-04-18 22:47:08 +02:00
|
|
|
{
|
2017-07-18 09:19:33 +02:00
|
|
|
/* Generate P-state tables */
|
|
|
|
generate_p_state_entries(core_id, cores_per_package);
|
|
|
|
|
|
|
|
/* Generate T-state tables */
|
|
|
|
generate_t_state_entries(core_id, cores_per_package);
|
2016-04-18 22:47:08 +02:00
|
|
|
}
|
2016-10-21 07:45:26 +02:00
|
|
|
|
|
|
|
static void acpigen_soc_get_dw0_in_local5(uintptr_t addr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Store (\_SB.GPC0 (addr), Local5)
|
|
|
|
* \_SB.GPC0 is used to read cfg0 value from dw0. It is defined in
|
|
|
|
* gpiolib.asl.
|
|
|
|
*/
|
|
|
|
acpigen_write_store();
|
|
|
|
acpigen_emit_namestring("\\_SB.GPC0");
|
|
|
|
acpigen_write_integer(addr);
|
|
|
|
acpigen_emit_byte(LOCAL5_OP);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int acpigen_soc_get_gpio_val(unsigned int gpio_num, uint32_t mask)
|
|
|
|
{
|
2017-03-09 19:10:25 +01:00
|
|
|
assert(gpio_num < TOTAL_PADS);
|
2017-07-18 09:19:33 +02:00
|
|
|
uintptr_t addr = (uintptr_t) gpio_dwx_address(gpio_num);
|
2016-10-21 07:45:26 +02:00
|
|
|
|
|
|
|
acpigen_soc_get_dw0_in_local5(addr);
|
|
|
|
|
|
|
|
/* If (And (Local5, mask)) */
|
|
|
|
acpigen_write_if_and(LOCAL5_OP, mask);
|
|
|
|
|
|
|
|
/* Store (One, Local0) */
|
|
|
|
acpigen_write_store_ops(ONE_OP, LOCAL0_OP);
|
|
|
|
|
|
|
|
acpigen_pop_len(); /* If */
|
|
|
|
|
|
|
|
/* Else */
|
|
|
|
acpigen_write_else();
|
|
|
|
|
|
|
|
/* Store (Zero, Local0) */
|
|
|
|
acpigen_write_store_ops(ZERO_OP, LOCAL0_OP);
|
|
|
|
|
|
|
|
acpigen_pop_len(); /* Else */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int acpigen_soc_set_gpio_val(unsigned int gpio_num, uint32_t val)
|
|
|
|
{
|
2017-03-09 19:10:25 +01:00
|
|
|
assert(gpio_num < TOTAL_PADS);
|
2017-07-18 09:19:33 +02:00
|
|
|
uintptr_t addr = (uintptr_t) gpio_dwx_address(gpio_num);
|
2016-10-21 07:45:26 +02:00
|
|
|
|
|
|
|
acpigen_soc_get_dw0_in_local5(addr);
|
|
|
|
|
|
|
|
if (val) {
|
|
|
|
/* Or (Local5, PAD_CFG0_TX_STATE, Local5) */
|
|
|
|
acpigen_write_or(LOCAL5_OP, PAD_CFG0_TX_STATE, LOCAL5_OP);
|
|
|
|
} else {
|
|
|
|
/* Not (PAD_CFG0_TX_STATE, Local6) */
|
|
|
|
acpigen_write_not(PAD_CFG0_TX_STATE, LOCAL6_OP);
|
|
|
|
|
|
|
|
/* And (Local5, Local6, Local5) */
|
|
|
|
acpigen_write_and(LOCAL5_OP, LOCAL6_OP, LOCAL5_OP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* \_SB.SPC0 (addr, Local5)
|
|
|
|
* \_SB.SPC0 is used to write cfg0 value in dw0. It is defined in
|
|
|
|
* gpiolib.asl.
|
|
|
|
*/
|
|
|
|
acpigen_emit_namestring("\\_SB.SPC0");
|
|
|
|
acpigen_write_integer(addr);
|
|
|
|
acpigen_emit_byte(LOCAL5_OP);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int acpigen_soc_read_rx_gpio(unsigned int gpio_num)
|
|
|
|
{
|
|
|
|
return acpigen_soc_get_gpio_val(gpio_num, PAD_CFG0_RX_STATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
int acpigen_soc_get_tx_gpio(unsigned int gpio_num)
|
|
|
|
{
|
|
|
|
return acpigen_soc_get_gpio_val(gpio_num, PAD_CFG0_TX_STATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
int acpigen_soc_set_tx_gpio(unsigned int gpio_num)
|
|
|
|
{
|
|
|
|
return acpigen_soc_set_gpio_val(gpio_num, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int acpigen_soc_clear_tx_gpio(unsigned int gpio_num)
|
|
|
|
{
|
|
|
|
return acpigen_soc_set_gpio_val(gpio_num, 0);
|
|
|
|
}
|