x86: sandbox: Add a PMC emulator and test
authorSimon Glass <sjg@chromium.org>
Sat, 7 Dec 2019 04:41:54 +0000 (21:41 -0700)
committerBin Meng <bmeng.cn@gmail.com>
Sun, 15 Dec 2019 03:44:11 +0000 (11:44 +0800)
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>
14 files changed:
arch/Kconfig
arch/sandbox/dts/sandbox.dtsi
arch/sandbox/dts/test.dts
arch/sandbox/include/asm/test.h
cmd/Kconfig
cmd/Makefile
cmd/pmc.c [new file with mode: 0644]
drivers/Makefile
drivers/power/acpi_pmc/Kconfig
drivers/power/acpi_pmc/Makefile
drivers/power/acpi_pmc/pmc_emul.c [new file with mode: 0644]
drivers/power/acpi_pmc/sandbox.c [new file with mode: 0644]
test/dm/Makefile
test/dm/pmc.c [new file with mode: 0644]

index 141e48bc439f87a04d0960d2f503c3a2d22eea00..e1f1fcd275bbf414d0f773a1013b5a05ae6e1817 100644 (file)
@@ -133,6 +133,9 @@ config SANDBOX
        imply PHYLIB
        imply DM_MDIO
        imply DM_MDIO_MUX
+       imply ACPI_PMC
+       imply ACPI_PMC_SANDBOX
+       imply CMD_PMC
 
 config SH
        bool "SuperH architecture"
index f09bc70b0da71dc2b73f0989b367d7ecbb791356..7bf144f53265a0be85932f0dfe820355ddc3e128 100644 (file)
        };
 
        pci-controller {
+               pci@1e,0 {
+                       compatible = "sandbox,pmc";
+                       reg = <0xf000 0 0 0 0>;
+                       sandbox,emul = <&pmc_emul>;
+                       gpe0-dwx-mask = <0xf>;
+                       gpe0-dwx-shift-base = <4>;
+                       gpe0-dw = <6 7 9>;
+                       gpe0-sts = <0x20>;
+                       gpe0-en = <0x30>;
+               };
+
                pci@1f,0 {
                        compatible = "pci-generic";
                        reg = <0xf800 0 0 0 0>;
 
        emul {
                compatible = "sandbox,pci-emul-parent";
+               pmc_emul: emul@1e,0 {
+                       compatible = "sandbox,pmc-emul";
+               };
                swap_case_emul: emul@1f,0 {
                        compatible = "sandbox,swap-case";
                };
index fdb08f21115985924466463e8919edcef732109c..99905677abd5ef350f1764d3c3003e787b82105d 100644 (file)
                               0x01000810 0 0 0 0>;
                        sandbox,emul = <&swap_case_emul0_1>;
                };
+               pci@1e,0 {
+                       compatible = "sandbox,pmc";
+                       reg = <0xf000 0 0 0 0>;
+                       sandbox,emul = <&pmc_emul1e>;
+                       acpi-base = <0x400>;
+                       gpe0-dwx-mask = <0xf>;
+                       gpe0-dwx-shift-base = <4>;
+                       gpe0-dw = <6 7 9>;
+                       gpe0-sts = <0x20>;
+                       gpe0-en = <0x30>;
+               };
                pci@1f,0 {
                        compatible = "pci-generic";
                        /* reg 0 is at 0x10, using FDT_PCI_SPACE_IO */
                swap_case_emul0_1f: emul0@1f,0 {
                        compatible = "sandbox,swap-case";
                };
+               pmc_emul1e: emul@1e,0 {
+                       compatible = "sandbox,pmc-emul";
+               };
        };
 
        pci1: pci-controller1 {
index b885e1a14f19f803e18b6344a025d25d0609001f..fa40d21f3f5c18a20bcf3952410c403f81127848 100644 (file)
@@ -13,6 +13,7 @@
 
 #define SANDBOX_PCI_VENDOR_ID          0x1234
 #define SANDBOX_PCI_SWAP_CASE_EMUL_ID  0x5678
+#define SANDBOX_PCI_PMC_EMUL_ID                0x5677
 #define SANDBOX_PCI_CLASS_CODE         PCI_CLASS_CODE_COMM
 #define SANDBOX_PCI_CLASS_SUB_CODE     PCI_CLASS_SUB_CODE_COMM_SERIAL
 
index 1e4cf146c5091744b7eab810e4a746a4c51bd42e..4e29e7b3c5467dd66436e5d1191d1ce142193507 100644 (file)
@@ -228,6 +228,14 @@ config CMD_LICENSE
        help
          Print GPL license text
 
+config CMD_PMC
+       bool "pmc"
+       help
+         Provides access to the Intel Power-Management Controller (PMC) so
+         that its state can be examined. This does not currently support
+         changing the state but it is still useful for debugging and seeing
+         what is going on.
+
 config CMD_REGINFO
        bool "reginfo"
        depends on PPC
index 3ac710454652019078b3f64557ee6d81d37a7b12..12e898d96205d74523753e04147629f4697559ac 100644 (file)
@@ -109,6 +109,7 @@ ifdef CONFIG_PCI
 obj-$(CONFIG_CMD_PCI) += pci.o
 endif
 obj-$(CONFIG_CMD_PINMUX) += pinmux.o
+obj-$(CONFIG_CMD_PMC) += pmc.o
 obj-$(CONFIG_CMD_PXE) += pxe.o pxe_utils.o
 obj-$(CONFIG_CMD_WOL) += wol.o
 obj-$(CONFIG_CMD_QFW) += qfw.o
diff --git a/cmd/pmc.c b/cmd/pmc.c
new file mode 100644 (file)
index 0000000..cafeba9
--- /dev/null
+++ b/cmd/pmc.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel PMC command
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <power/acpi_pmc.h>
+
+static int get_pmc_dev(struct udevice **devp)
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = uclass_first_device_err(UCLASS_ACPI_PMC, &dev);
+       if (ret) {
+               printf("Could not find device (err=%d)\n", ret);
+               return ret;
+       }
+       ret = pmc_init(dev);
+       if (ret) {
+               printf("Could not init device (err=%d)\n", ret);
+               return ret;
+       }
+       *devp = dev;
+
+       return 0;
+}
+
+static int do_pmc_init(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = get_pmc_dev(&dev);
+       if (ret)
+               return CMD_RET_FAILURE;
+
+       return 0;
+}
+
+static int do_pmc_info(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = get_pmc_dev(&dev);
+       if (ret)
+               return CMD_RET_FAILURE;
+       pmc_dump_info(dev);
+
+       return 0;
+}
+
+static cmd_tbl_t cmd_pmc_sub[] = {
+       U_BOOT_CMD_MKENT(init, 0, 1, do_pmc_init, "", ""),
+       U_BOOT_CMD_MKENT(info, 0, 1, do_pmc_info, "", ""),
+};
+
+static int do_pmc(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       const cmd_tbl_t *cp;
+
+       if (argc < 2) /* no subcommand */
+               return cmd_usage(cmdtp);
+
+       cp = find_cmd_tbl(argv[1], &cmd_pmc_sub[0], ARRAY_SIZE(cmd_pmc_sub));
+       if (!cp)
+               return CMD_RET_USAGE;
+
+       return cp->cmd(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(
+       pmc, 2, 1, do_pmc, "Power-management controller info",
+       "info - read state and show info about the PMC\n"
+       "pmc init - read state from the PMC\n"
+       );
index e977f19af6822d527e74cb638c4189414527a800..cb8c215e76700b873ea331cb2ab47ce1bec48f2f 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/
 obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/
 obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
 obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/
+obj-$(CONFIG_$(SPL_TPL_)ACPI_PMC) += power/acpi_pmc/
 
 ifndef CONFIG_TPL_BUILD
 ifdef CONFIG_SPL_BUILD
index 472a61a9fd163656d1cc3d929cc0316bcaf153cf..fcd50e36cad424cdd1cc73863fc9f0f7324f96e0 100644 (file)
@@ -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.
index 7c1ba05c9f3e0575734a128c2f345a18d744407d..115788f109aab21e38dcb12344685c680df430c7 100644 (file)
@@ -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 (file)
index 0000000..15cc7ac
--- /dev/null
@@ -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 (file)
index 0000000..7fbbf97
--- /dev/null
@@ -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),
+};
index 0c2fd5cb5e2ec7c071e6cc5064bf39a8a841c739..10a19a00c908047f8e79889ef67826bff4e8d04d 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_PCI_ENDPOINT) += pci_ep.o
 obj-$(CONFIG_PCH) += pch.o
 obj-$(CONFIG_PHY) += phy.o
 obj-$(CONFIG_POWER_DOMAIN) += power-domain.o
+obj-$(CONFIG_ACPI_PMC) += pmc.o
 obj-$(CONFIG_DM_PWM) += pwm.o
 obj-$(CONFIG_RAM) += ram.o
 obj-y += regmap.o
diff --git a/test/dm/pmc.c b/test/dm/pmc.c
new file mode 100644 (file)
index 0000000..1a22283
--- /dev/null
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for power-management controller uclass (PMC)
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <power/acpi_pmc.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Base test of the PMC uclass */
+static int dm_test_pmc_base(struct unit_test_state *uts)
+{
+       struct acpi_pmc_upriv *upriv;
+       struct udevice *dev;
+
+       ut_assertok(uclass_first_device_err(UCLASS_ACPI_PMC, &dev));
+
+       ut_assertok(pmc_disable_tco(dev));
+       ut_assertok(pmc_init(dev));
+       ut_assertok(pmc_prev_sleep_state(dev));
+
+       /* Check some values to see that I/O works */
+       upriv = dev_get_uclass_priv(dev);
+       ut_asserteq(0x24, upriv->gpe0_sts[1]);
+       ut_asserteq(0x64, upriv->tco1_sts);
+
+       return 0;
+}
+DM_TEST(dm_test_pmc_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);