diff options
author | Simon Glass <sjg@chromium.org> | 2019-12-06 21:42:57 -0700 |
---|---|---|
committer | Bin Meng <bmeng.cn@gmail.com> | 2019-12-15 11:44:26 +0800 |
commit | 28eefefccfaa695fb44935ce8b04d50548d78b13 (patch) | |
tree | 39b1d8aa7406cd5beb2c360445f1e9cb1d9e52dd /arch/x86/cpu/apollolake | |
parent | 5690d5c8f878846d862bd4a0256c2e8285413bff (diff) | |
download | u-boot-28eefefccfaa695fb44935ce8b04d50548d78b13.tar.gz u-boot-28eefefccfaa695fb44935ce8b04d50548d78b13.tar.xz u-boot-28eefefccfaa695fb44935ce8b04d50548d78b13.zip |
x86: apl: Add PMC driver
Add a driver for the Apollo Lake SoC. It supports the basic operations and
can use device tree or of-platdata.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'arch/x86/cpu/apollolake')
-rw-r--r-- | arch/x86/cpu/apollolake/Makefile | 5 | ||||
-rw-r--r-- | arch/x86/cpu/apollolake/pmc.c | 216 |
2 files changed, 221 insertions, 0 deletions
diff --git a/arch/x86/cpu/apollolake/Makefile b/arch/x86/cpu/apollolake/Makefile new file mode 100644 index 0000000000..5e136b6515 --- /dev/null +++ b/arch/x86/cpu/apollolake/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2019 Google LLC + +obj-y += pmc.o diff --git a/arch/x86/cpu/apollolake/pmc.c b/arch/x86/cpu/apollolake/pmc.c new file mode 100644 index 0000000000..683c6082f2 --- /dev/null +++ b/arch/x86/cpu/apollolake/pmc.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Intel Corporation. + * Copyright 2019 Google LLC + * + * Modified from coreboot pmclib.c, pmc.c and pmutil.c + */ + +#define LOG_CATEGORY UCLASS_ACPI_PMC + +#include <common.h> +#include <acpi_s3.h> +#include <dt-structs.h> +#include <dm.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <power/acpi_pmc.h> + +#define GPIO_GPE_CFG 0x1050 + +/* Memory mapped IO registers behind PMC_BASE_ADDRESS */ +#define PRSTS 0x1000 +#define GEN_PMCON1 0x1020 +#define COLD_BOOT_STS BIT(27) +#define COLD_RESET_STS BIT(26) +#define WARM_RESET_STS BIT(25) +#define GLOBAL_RESET_STS BIT(24) +#define SRS BIT(20) +#define MS4V BIT(18) +#define RPS BIT(2) +#define GEN_PMCON1_CLR1_BITS (COLD_BOOT_STS | COLD_RESET_STS | \ + WARM_RESET_STS | GLOBAL_RESET_STS | \ + SRS | MS4V) +#define GEN_PMCON2 0x1024 +#define GEN_PMCON3 0x1028 + +/* Offset of TCO registers from ACPI base I/O address */ +#define TCO_REG_OFFSET 0x60 +#define TCO1_STS 0x64 +#define DMISCI_STS BIT(9) +#define BOOT_STS BIT(18) +#define TCO2_STS 0x66 +#define TCO1_CNT 0x68 +#define TCO_LOCK BIT(12) +#define TCO2_CNT 0x6a + +enum { + ETR = 0x1048, + CF9_LOCK = 1UL << 31, + CF9_GLB_RST = 1 << 20, +}; + +struct apl_pmc_platdata { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_intel_apl_pmc dtplat; +#endif + pci_dev_t bdf; +}; + +static int apl_pmc_fill_power_state(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + upriv->tco1_sts = inw(upriv->acpi_base + TCO1_STS); + upriv->tco2_sts = inw(upriv->acpi_base + TCO2_STS); + + upriv->prsts = readl(upriv->pmc_bar0 + PRSTS); + upriv->gen_pmcon1 = readl(upriv->pmc_bar0 + GEN_PMCON1); + upriv->gen_pmcon2 = readl(upriv->pmc_bar0 + GEN_PMCON2); + upriv->gen_pmcon3 = readl(upriv->pmc_bar0 + GEN_PMCON3); + + return 0; +} + +static int apl_prev_sleep_state(struct udevice *dev, int prev_sleep_state) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + /* WAK_STS bit will not be set when waking from G3 state */ + if (!(upriv->pm1_sts & WAK_STS) && + (upriv->gen_pmcon1 & COLD_BOOT_STS)) + prev_sleep_state = ACPI_S5; + + return prev_sleep_state; +} + +static int apl_disable_tco(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + pmc_disable_tco_base(upriv->acpi_base + TCO_REG_OFFSET); + + return 0; +} + +static int apl_global_reset_set_enable(struct udevice *dev, bool enable) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + if (enable) + setbits_le32(upriv->pmc_bar0 + ETR, CF9_GLB_RST); + else + clrbits_le32(upriv->pmc_bar0 + ETR, CF9_GLB_RST); + + return 0; +} + +int apl_pmc_ofdata_to_uc_platdata(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + struct apl_pmc_platdata *plat = dev_get_platdata(dev); + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + u32 base[6]; + int size; + int ret; + + ret = dev_read_u32_array(dev, "early-regs", base, ARRAY_SIZE(base)); + if (ret) + return log_msg_ret("Missing/short early-regs", ret); + upriv->pmc_bar0 = (void *)base[0]; + upriv->pmc_bar2 = (void *)base[2]; + upriv->acpi_base = base[4]; + + /* Since PCI is not enabled, we must get the BDF manually */ + plat->bdf = pci_get_devfn(dev); + if (plat->bdf < 0) + return log_msg_ret("Cannot get PMC PCI address", plat->bdf); + + /* Get the dwX values for pmc gpe settings */ + size = dev_read_size(dev, "gpe0-dw"); + if (size < 0) + return log_msg_ret("Cannot read gpe0-dm", size); + upriv->gpe0_count = size / sizeof(u32); + ret = dev_read_u32_array(dev, "gpe0-dw", upriv->gpe0_dw, + upriv->gpe0_count); + if (ret) + return log_msg_ret("Bad gpe0-dw", ret); + + return pmc_ofdata_to_uc_platdata(dev); +#else + struct dtd_intel_apl_pmc *dtplat = &plat->dtplat; + + plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]); + upriv->pmc_bar0 = (void *)dtplat->early_regs[0]; + upriv->pmc_bar2 = (void *)dtplat->early_regs[2]; + upriv->acpi_base = dtplat->early_regs[4]; + upriv->gpe0_dwx_mask = dtplat->gpe0_dwx_mask; + upriv->gpe0_dwx_shift_base = dtplat->gpe0_dwx_shift_base; + upriv->gpe0_sts_reg = dtplat->gpe0_sts; + upriv->gpe0_sts_reg += upriv->acpi_base; + upriv->gpe0_en_reg = dtplat->gpe0_en; + upriv->gpe0_en_reg += upriv->acpi_base; + upriv->gpe0_count = min((int)ARRAY_SIZE(dtplat->gpe0_dw), GPE0_REG_MAX); + memcpy(upriv->gpe0_dw, dtplat->gpe0_dw, sizeof(dtplat->gpe0_dw)); +#endif + upriv->gpe_cfg = (u32 *)(upriv->pmc_bar0 + GPIO_GPE_CFG); + + return 0; +} + +static int enable_pmcbar(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + struct apl_pmc_platdata *priv = dev_get_platdata(dev); + pci_dev_t pmc = priv->bdf; + + /* + * Set PMC base addresses and enable decoding. BARs 1 and 3 are 64-bit + * BARs. + */ + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_0, (ulong)upriv->pmc_bar0, + PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_2, (ulong)upriv->pmc_bar2, + PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_3, 0, PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_4, upriv->acpi_base, + PCI_SIZE_16); + pci_x86_write_config(pmc, PCI_COMMAND, PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, + PCI_SIZE_16); + + return 0; +} + +static int apl_pmc_probe(struct udevice *dev) +{ + if (spl_phase() == PHASE_TPL) + return enable_pmcbar(dev); + + return 0; +} + +static struct acpi_pmc_ops apl_pmc_ops = { + .init = apl_pmc_fill_power_state, + .prev_sleep_state = apl_prev_sleep_state, + .disable_tco = apl_disable_tco, + .global_reset_set_enable = apl_global_reset_set_enable, +}; + +static const struct udevice_id apl_pmc_ids[] = { + { .compatible = "intel,apl-pmc" }, + { } +}; + +U_BOOT_DRIVER(apl_pmc) = { + .name = "intel_apl_pmc", + .id = UCLASS_ACPI_PMC, + .of_match = apl_pmc_ids, + .ofdata_to_platdata = apl_pmc_ofdata_to_uc_platdata, + .probe = apl_pmc_probe, + .ops = &apl_pmc_ops, + .platdata_auto_alloc_size = sizeof(struct apl_pmc_platdata), +}; |