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>
This commit is contained in:
Simon Glass 2019-12-06 21:41:54 -07:00 committed by Bin Meng
parent 6c6d88e142
commit 3b65ee34b9
14 changed files with 510 additions and 0 deletions

View 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"

View File

@ -100,6 +100,17 @@
};
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>;
@ -109,6 +120,9 @@
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";
};

View File

@ -471,6 +471,17 @@
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 */
@ -491,6 +502,9 @@
swap_case_emul0_1f: emul0@1f,0 {
compatible = "sandbox,swap-case";
};
pmc_emul1e: emul@1e,0 {
compatible = "sandbox,pmc-emul";
};
};
pci1: pci-controller1 {

View 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

View 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

View 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

81
cmd/pmc.c Normal file
View File

@ -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"
);

View 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

View 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.

View 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

View File

@ -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);

View File

@ -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),
};

View 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

33
test/dm/pmc.c Normal file
View File

@ -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);