Hi Simon, On Mon, Nov 25, 2019 at 12:12 PM Simon Glass <s...@chromium.org> wrote: > > 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 <s...@chromium.org> > --- > > Changes in v5: None > Changes in v4: > - Fix Makefile copyright message > - Fix incorrect mask check in pmc_gpe_init() > - Switch over to use pinctrl for pad init/config > - Tidy up header guards > - Use pci_ofplat_get_devfn() > - apollolake -> Apollo Lake > > Changes in v3: > - Use pci_get_devfn() > > Changes in v2: None > > arch/x86/cpu/apollolake/Makefile | 5 + > arch/x86/cpu/apollolake/pmc.c | 216 ++++++++++++++++++++++ > arch/x86/include/asm/arch-apollolake/pm.h | 19 ++ > drivers/power/acpi_pmc/acpi-pmc-uclass.c | 56 ++++++ > 4 files changed, 296 insertions(+) > create mode 100644 arch/x86/cpu/apollolake/Makefile > create mode 100644 arch/x86/cpu/apollolake/pmc.c > create mode 100644 arch/x86/include/asm/arch-apollolake/pm.h > > 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), > +}; > diff --git a/arch/x86/include/asm/arch-apollolake/pm.h > b/arch/x86/include/asm/arch-apollolake/pm.h > new file mode 100644 > index 0000000000..557fb9538e > --- /dev/null > +++ b/arch/x86/include/asm/arch-apollolake/pm.h > @@ -0,0 +1,19 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2015-2016 Intel Corp. > + * (Written by Lance Zhao <lijian.z...@intel.com> for Intel Corp.) > + */ > + > +#ifndef _ASM_ARCH_PM_H > +#define _ASM_ARCH_PM_H > + > +#define PMC_GPE_SW_31_0 0
nits: should only have one space after #define > +#define PMC_GPE_SW_63_32 1 > +#define PMC_GPE_NW_31_0 3 > +#define PMC_GPE_NW_63_32 4 > +#define PMC_GPE_NW_95_64 5 > +#define PMC_GPE_N_31_0 6 > +#define PMC_GPE_N_63_32 7 > +#define PMC_GPE_W_31_0 9 > + > +#endif > diff --git a/drivers/power/acpi_pmc/acpi-pmc-uclass.c > b/drivers/power/acpi_pmc/acpi-pmc-uclass.c > index 653c71b948..d43de87126 100644 > --- a/drivers/power/acpi_pmc/acpi-pmc-uclass.c > +++ b/drivers/power/acpi_pmc/acpi-pmc-uclass.c > @@ -9,6 +9,9 @@ > #include <acpi_s3.h> > #include <dm.h> > #include <log.h> > +#ifdef CONFIG_X86 > +#include <asm/intel_pinctrl.h> > +#endif > #include <asm/io.h> > #include <power/acpi_pmc.h> > > @@ -34,6 +37,59 @@ enum { > TCO1_CNT_HLT = 1 << 11, > }; > > +#ifdef CONFIG_X86 > +static int gpe0_shift(struct acpi_pmc_upriv *upriv, int regnum) > +{ > + return upriv->gpe0_dwx_shift_base + regnum * 4; > +} > + > +int pmc_gpe_init(struct udevice *dev) Where is this function called? and why should this be put in the acpi-pmc-uclass driver? > +{ > + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); > + struct udevice *itss; > + u32 *dw; > + u32 gpio_cfg_mask; > + u32 gpio_cfg; > + int ret, i; > + u32 mask; > + > + if (device_get_uclass_id(dev) != UCLASS_ACPI_PMC) > + return log_msg_ret("uclass", -EPROTONOSUPPORT); > + dw = upriv->gpe0_dw; > + mask = upriv->gpe0_dwx_mask; > + gpio_cfg_mask = 0; > + for (i = 0; i < upriv->gpe0_count; i++) { > + gpio_cfg_mask |= mask << gpe0_shift(upriv, i); > + if (dw[i] & ~mask) > + return log_msg_ret("Base GPE0 value", -EINVAL); > + } > + > + /* > + * Route the GPIOs to the GPE0 block. Determine that all values > + * are different and if they aren't, use the reset values. > + */ > + if (dw[0] == dw[1] || dw[1] == dw[2]) { > + log_info("PMC: Using default GPE route"); > + gpio_cfg = readl(upriv->gpe_cfg); > + for (i = 0; i < upriv->gpe0_count; i++) > + dw[i] = gpio_cfg >> gpe0_shift(upriv, i); > + } else { > + gpio_cfg = 0; > + for (i = 0; i < upriv->gpe0_count; i++) > + gpio_cfg |= dw[i] << gpe0_shift(upriv, i); > + clrsetbits_le32(upriv->gpe_cfg, gpio_cfg_mask, gpio_cfg); > + } > + > + /* Set the routes in the GPIO communities as well */ > + ret = uclass_first_device_err(UCLASS_IRQ, &itss); > + if (ret) > + return log_msg_ret("Cannot find itss", ret); > + pinctrl_route_gpe(itss, dw[0], dw[1], dw[2]); > + > + return 0; > +} > +#endif /* CONFIG_X86 */ > + > static void pmc_fill_pm_reg_info(struct udevice *dev) > { > struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); > -- Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot