aboutsummaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorSimon Glass2019-12-06 21:41:54 -0700
committerBin Meng2019-12-15 11:44:11 +0800
commit3b65ee34b908ce0c495c25987f5feb37ac163eab (patch)
treeba7a13fb9a4254844237c545e40a4068dba0ff84 /drivers/power
parent6c6d88e142e8714ed88d051c524036ff17278390 (diff)
x86: sandbox: Add a PMC emulator and test
Add a simple PMC for sandbox to permit tests to run. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/acpi_pmc/Kconfig9
-rw-r--r--drivers/power/acpi_pmc/Makefile1
-rw-r--r--drivers/power/acpi_pmc/pmc_emul.c246
-rw-r--r--drivers/power/acpi_pmc/sandbox.c97
4 files changed, 353 insertions, 0 deletions
diff --git a/drivers/power/acpi_pmc/Kconfig b/drivers/power/acpi_pmc/Kconfig
index 472a61a9fd1..fcd50e36cad 100644
--- a/drivers/power/acpi_pmc/Kconfig
+++ b/drivers/power/acpi_pmc/Kconfig
@@ -23,3 +23,12 @@ config TPL_ACPI_PMC
provides features including checking whether the system started from
resume, powering off the system and enabling/disabling the reset
mechanism.
+
+config ACPI_PMC_SANDBOX
+ bool "Test power manager (PMC) for sandbox"
+ depends on ACPI_PMC && SANDBOX
+ help
+ This driver emulates a PMC (Power-Management Controller) so that
+ the uclass logic can be tested. You can use the 'pmc' command to
+ access information from the driver. It uses I/O access to read
+ from the PMC.
diff --git a/drivers/power/acpi_pmc/Makefile b/drivers/power/acpi_pmc/Makefile
index 7c1ba05c9f3..115788f109a 100644
--- a/drivers/power/acpi_pmc/Makefile
+++ b/drivers/power/acpi_pmc/Makefile
@@ -3,3 +3,4 @@
# Copyright 2019 Google LLC
obj-$(CONFIG_$(SPL_TPL_)ACPI_PMC) += acpi-pmc-uclass.o
+obj-$(CONFIG_$(SPL_TPL_)ACPI_PMC_SANDBOX) += sandbox.o pmc_emul.o
diff --git a/drivers/power/acpi_pmc/pmc_emul.c b/drivers/power/acpi_pmc/pmc_emul.c
new file mode 100644
index 00000000000..15cc7acaf33
--- /dev/null
+++ b/drivers/power/acpi_pmc/pmc_emul.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PCI emulation device for an x86 Power-Management Controller (PMC)
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+#include <asm/test.h>
+#include <power/acpi_pmc.h>
+
+/**
+ * struct pmc_emul_platdata - platform data for this device
+ *
+ * @command: Current PCI command value
+ * @bar: Current base address values
+ */
+struct pmc_emul_platdata {
+ u16 command;
+ u32 bar[6];
+};
+
+enum {
+ MEMMAP_SIZE = 0x80,
+};
+
+static struct pci_bar {
+ int type;
+ u32 size;
+} barinfo[] = {
+ { PCI_BASE_ADDRESS_MEM_TYPE_32, MEMMAP_SIZE },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { PCI_BASE_ADDRESS_SPACE_IO, 256 },
+};
+
+struct pmc_emul_priv {
+ u8 regs[MEMMAP_SIZE];
+};
+
+static int sandbox_pmc_emul_read_config(struct udevice *emul, uint offset,
+ ulong *valuep, enum pci_size_t size)
+{
+ struct pmc_emul_platdata *plat = dev_get_platdata(emul);
+
+ switch (offset) {
+ case PCI_COMMAND:
+ *valuep = plat->command;
+ break;
+ case PCI_HEADER_TYPE:
+ *valuep = 0;
+ break;
+ case PCI_VENDOR_ID:
+ *valuep = SANDBOX_PCI_VENDOR_ID;
+ break;
+ case PCI_DEVICE_ID:
+ *valuep = SANDBOX_PCI_PMC_EMUL_ID;
+ break;
+ case PCI_CLASS_DEVICE:
+ if (size == PCI_SIZE_8) {
+ *valuep = SANDBOX_PCI_CLASS_SUB_CODE;
+ } else {
+ *valuep = (SANDBOX_PCI_CLASS_CODE << 8) |
+ SANDBOX_PCI_CLASS_SUB_CODE;
+ }
+ break;
+ case PCI_CLASS_CODE:
+ *valuep = SANDBOX_PCI_CLASS_CODE;
+ break;
+ case PCI_BASE_ADDRESS_0:
+ case PCI_BASE_ADDRESS_1:
+ case PCI_BASE_ADDRESS_2:
+ case PCI_BASE_ADDRESS_3:
+ case PCI_BASE_ADDRESS_4:
+ case PCI_BASE_ADDRESS_5: {
+ int barnum;
+ u32 *bar;
+
+ barnum = pci_offset_to_barnum(offset);
+ bar = &plat->bar[barnum];
+
+ *valuep = sandbox_pci_read_bar(*bar, barinfo[barnum].type,
+ barinfo[barnum].size);
+ break;
+ }
+ case PCI_CAPABILITY_LIST:
+ *valuep = PCI_CAP_ID_PM_OFFSET;
+ break;
+ }
+
+ return 0;
+}
+
+static int sandbox_pmc_emul_write_config(struct udevice *emul, uint offset,
+ ulong value, enum pci_size_t size)
+{
+ struct pmc_emul_platdata *plat = dev_get_platdata(emul);
+
+ switch (offset) {
+ case PCI_COMMAND:
+ plat->command = value;
+ break;
+ case PCI_BASE_ADDRESS_0:
+ case PCI_BASE_ADDRESS_1: {
+ int barnum;
+ u32 *bar;
+
+ barnum = pci_offset_to_barnum(offset);
+ bar = &plat->bar[barnum];
+
+ debug("w bar %d=%lx\n", barnum, value);
+ *bar = value;
+ /* space indicator (bit#0) is read-only */
+ *bar |= barinfo[barnum].type;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int sandbox_pmc_emul_find_bar(struct udevice *emul, unsigned int addr,
+ int *barnump, unsigned int *offsetp)
+{
+ struct pmc_emul_platdata *plat = dev_get_platdata(emul);
+ int barnum;
+
+ for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) {
+ unsigned int size = barinfo[barnum].size;
+ u32 base = plat->bar[barnum] & ~PCI_BASE_ADDRESS_SPACE;
+
+ if (addr >= base && addr < base + size) {
+ *barnump = barnum;
+ *offsetp = addr - base;
+ return 0;
+ }
+ }
+ *barnump = -1;
+
+ return -ENOENT;
+}
+
+static int sandbox_pmc_emul_read_io(struct udevice *dev, unsigned int addr,
+ ulong *valuep, enum pci_size_t size)
+{
+ unsigned int offset;
+ int barnum;
+ int ret;
+
+ ret = sandbox_pmc_emul_find_bar(dev, addr, &barnum, &offset);
+ if (ret)
+ return ret;
+
+ if (barnum == 4)
+ *valuep = offset;
+ else if (barnum == 0)
+ *valuep = offset;
+
+ return 0;
+}
+
+static int sandbox_pmc_emul_write_io(struct udevice *dev, unsigned int addr,
+ ulong value, enum pci_size_t size)
+{
+ unsigned int offset;
+ int barnum;
+ int ret;
+
+ ret = sandbox_pmc_emul_find_bar(dev, addr, &barnum, &offset);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sandbox_pmc_emul_map_physmem(struct udevice *dev,
+ phys_addr_t addr, unsigned long *lenp,
+ void **ptrp)
+{
+ struct pmc_emul_priv *priv = dev_get_priv(dev);
+ unsigned int offset, avail;
+ int barnum;
+ int ret;
+
+ ret = sandbox_pmc_emul_find_bar(dev, addr, &barnum, &offset);
+ if (ret)
+ return ret;
+
+ if (barnum == 0) {
+ *ptrp = priv->regs + offset;
+ avail = barinfo[0].size - offset;
+ if (avail > barinfo[0].size)
+ *lenp = 0;
+ else
+ *lenp = min(*lenp, (ulong)avail);
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int sandbox_pmc_probe(struct udevice *dev)
+{
+ struct pmc_emul_priv *priv = dev_get_priv(dev);
+ int i;
+
+ for (i = 0; i < MEMMAP_SIZE; i++)
+ priv->regs[i] = i;
+
+ return 0;
+}
+
+static struct dm_pci_emul_ops sandbox_pmc_emul_emul_ops = {
+ .read_config = sandbox_pmc_emul_read_config,
+ .write_config = sandbox_pmc_emul_write_config,
+ .read_io = sandbox_pmc_emul_read_io,
+ .write_io = sandbox_pmc_emul_write_io,
+ .map_physmem = sandbox_pmc_emul_map_physmem,
+};
+
+static const struct udevice_id sandbox_pmc_emul_ids[] = {
+ { .compatible = "sandbox,pmc-emul" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_pmc_emul_emul) = {
+ .name = "sandbox_pmc_emul_emul",
+ .id = UCLASS_PCI_EMUL,
+ .of_match = sandbox_pmc_emul_ids,
+ .ops = &sandbox_pmc_emul_emul_ops,
+ .probe = sandbox_pmc_probe,
+ .priv_auto_alloc_size = sizeof(struct pmc_emul_priv),
+ .platdata_auto_alloc_size = sizeof(struct pmc_emul_platdata),
+};
+
+static struct pci_device_id sandbox_pmc_emul_supported[] = {
+ { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_PMC_EMUL_ID) },
+ {},
+};
+
+U_BOOT_PCI_DEVICE(sandbox_pmc_emul_emul, sandbox_pmc_emul_supported);
diff --git a/drivers/power/acpi_pmc/sandbox.c b/drivers/power/acpi_pmc/sandbox.c
new file mode 100644
index 00000000000..7fbbf97b454
--- /dev/null
+++ b/drivers/power/acpi_pmc/sandbox.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sandbox PMC for testing
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#define LOG_CATEGORY UCLASS_ACPI_PMC
+
+#include <common.h>
+#include <dm.h>
+#include <asm/io.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 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 TCO2_STS 0x66
+#define TCO1_CNT 0x68
+#define TCO2_CNT 0x6a
+
+struct sandbox_pmc_priv {
+ ulong base;
+};
+
+static int sandbox_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 sandbox_prev_sleep_state(struct udevice *dev, int prev_sleep_state)
+{
+ return prev_sleep_state;
+}
+
+static int sandbox_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 sandbox_pmc_probe(struct udevice *dev)
+{
+ struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);
+ struct udevice *bus;
+ ulong base;
+
+ uclass_first_device(UCLASS_PCI, &bus);
+ base = dm_pci_read_bar32(dev, 0);
+ if (base == FDT_ADDR_T_NONE)
+ return log_msg_ret("No base address", -EINVAL);
+ upriv->pmc_bar0 = map_sysmem(base, 0x2000);
+ upriv->gpe_cfg = (u32 *)(upriv->pmc_bar0 + GPIO_GPE_CFG);
+
+ return pmc_ofdata_to_uc_platdata(dev);
+}
+
+static struct acpi_pmc_ops sandbox_pmc_ops = {
+ .init = sandbox_pmc_fill_power_state,
+ .prev_sleep_state = sandbox_prev_sleep_state,
+ .disable_tco = sandbox_disable_tco,
+};
+
+static const struct udevice_id sandbox_pmc_ids[] = {
+ { .compatible = "sandbox,pmc" },
+ { }
+};
+
+U_BOOT_DRIVER(pmc_sandbox) = {
+ .name = "pmc_sandbox",
+ .id = UCLASS_ACPI_PMC,
+ .of_match = sandbox_pmc_ids,
+ .probe = sandbox_pmc_probe,
+ .ops = &sandbox_pmc_ops,
+ .priv_auto_alloc_size = sizeof(struct sandbox_pmc_priv),
+};