test: dm: pmbus: add a sandbox PMBus chip emulator
Add a UCLASS_I2C_EMUL driver that emulates a PMBus 1.x compliant chip behind the sandbox I2C bus, plus the test.dts wiring and sandbox defconfig that bind it to the generic PMBus regulator (compatible = "pmbus"). This design is a stub only: it lets the follow-up dm unit test drive lib/pmbus.c, the generic regulator and the pmbus CLI command with no real hardware. The emulator models a flat per-command 16-bit register file and the three identification block strings (MFR_ID / MFR_MODEL / MFR_REVISION). READ_IIN and READ_POUT are deliberately left unimplemented (the chip NAKs them) so the telemetry printer's "(not supported)" path is exercised, mirroring a real buck that only calibrates a subset of the sensor classes. Signed-off-by: Vincent Jardin <vjardin@free.fr> Reviewed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Peng Fan <peng.fan@nxp.com>
This commit is contained in:
@@ -1541,6 +1541,7 @@ F: drivers/power/regulator/mpq8785.c
|
||||
F: drivers/power/regulator/pmbus_generic.c
|
||||
F: drivers/power/regulator/pmbus_helper.c
|
||||
F: drivers/power/regulator/pmbus_helper.h
|
||||
F: drivers/power/regulator/sandbox_pmbus.c
|
||||
F: drivers/thermal/pmbus_thermal.c
|
||||
F: include/pmbus.h
|
||||
F: lib/pmbus.c
|
||||
|
||||
@@ -1027,6 +1027,9 @@
|
||||
emul1: emull {
|
||||
compatible = "sandbox,i2c-rtc-emul";
|
||||
};
|
||||
emul_pmbus: emul-pmbus {
|
||||
compatible = "sandbox,i2c-pmbus";
|
||||
};
|
||||
};
|
||||
|
||||
sandbox_pmic: sandbox_pmic@40 {
|
||||
@@ -1038,6 +1041,13 @@
|
||||
reg = <0x41>;
|
||||
sandbox,emul = <&emul_pmic1>;
|
||||
};
|
||||
|
||||
pmbus@70 {
|
||||
reg = <0x70>;
|
||||
compatible = "pmbus";
|
||||
regulator-name = "sandbox-pmbus-vout";
|
||||
sandbox,emul = <&emul_pmbus>;
|
||||
};
|
||||
};
|
||||
|
||||
i3c0 {
|
||||
|
||||
@@ -138,6 +138,7 @@ CONFIG_CMD_PSTORE=y
|
||||
CONFIG_CMD_PSTORE_MEM_ADDR=0x3000000
|
||||
CONFIG_CMD_BOOTSTAGE=y
|
||||
CONFIG_CMD_PMIC=y
|
||||
CONFIG_CMD_PMBUS=y
|
||||
CONFIG_CMD_REGULATOR=y
|
||||
CONFIG_CMD_AES=y
|
||||
CONFIG_CMD_TPM=y
|
||||
@@ -312,6 +313,9 @@ CONFIG_REGULATOR_S5M8767=y
|
||||
CONFIG_DM_REGULATOR_SANDBOX=y
|
||||
CONFIG_REGULATOR_TPS65090=y
|
||||
CONFIG_DM_REGULATOR_SCMI=y
|
||||
CONFIG_DM_REGULATOR_PMBUS_HELPER=y
|
||||
CONFIG_DM_REGULATOR_PMBUS_GENERIC=y
|
||||
CONFIG_SANDBOX_PMBUS=y
|
||||
CONFIG_DM_PWM=y
|
||||
CONFIG_PWM_CROS_EC=y
|
||||
CONFIG_PWM_SANDBOX=y
|
||||
@@ -341,6 +345,7 @@ CONFIG_SYSINFO=y
|
||||
CONFIG_SYSINFO_SANDBOX=y
|
||||
CONFIG_SYSINFO_GPIO=y
|
||||
CONFIG_DM_THERMAL=y
|
||||
CONFIG_PMBUS_THERMAL=y
|
||||
CONFIG_TIMER_EARLY=y
|
||||
CONFIG_SANDBOX_TIMER=y
|
||||
CONFIG_USB=y
|
||||
@@ -381,6 +386,7 @@ CONFIG_FS_EXFAT=y
|
||||
CONFIG_FS_CRAMFS=y
|
||||
CONFIG_ADDR_MAP=y
|
||||
CONFIG_PANIC_HANG=y
|
||||
CONFIG_PMBUS=y
|
||||
CONFIG_CMD_DHRYSTONE=y
|
||||
CONFIG_MBEDTLS_LIB=y
|
||||
CONFIG_HKDF_MBEDTLS=y
|
||||
|
||||
@@ -590,3 +590,13 @@ config DM_REGULATOR_MPQ8785
|
||||
digital multiphase voltage regulators with PMBus. Supports
|
||||
MPM3695, MPM3695-25, MPM82504, and MPQ8785. Adapted from the
|
||||
Linux drivers/hwmon/pmbus/mpq8785.c reference.
|
||||
|
||||
config SANDBOX_PMBUS
|
||||
bool "Sandbox PMBus 1.x chip emulator"
|
||||
depends on SANDBOX && PMBUS && DM_I2C
|
||||
help
|
||||
Emulate a PMBus 1.x compliant chip behind a sandbox I2C bus so
|
||||
the PMBus framework (lib/pmbus.c), the generic regulator
|
||||
(DM_REGULATOR_PMBUS_GENERIC) and the pmbus CLI command can be
|
||||
exercised by the dm unit tests with no real hardware. Only
|
||||
useful for testing; say N on real boards.
|
||||
|
||||
@@ -52,3 +52,4 @@ obj-$(CONFIG_DM_REGULATOR_MT6359) += mt6359_regulator.o
|
||||
obj-$(CONFIG_DM_REGULATOR_PMBUS_HELPER) += pmbus_helper.o
|
||||
obj-$(CONFIG_DM_REGULATOR_PMBUS_GENERIC) += pmbus_generic.o
|
||||
obj-$(CONFIG_DM_REGULATOR_MPQ8785) += mpq8785.o
|
||||
obj-$(CONFIG_SANDBOX_PMBUS) += sandbox_pmbus.o
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2026 Free Mobile - Vincent Jardin
|
||||
*
|
||||
* Sandbox PMBus 1.x chip stub (UCLASS_I2C_EMUL).
|
||||
*
|
||||
* Stub a DT i2c node so the PMBus framework, the generic pmbus
|
||||
* regulator and the pmbus CLI command can be tested.
|
||||
* The model is a flat per-command 16 bit
|
||||
* register file combined with some fixed identification strings.
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <pmbus.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#define PMBUS_EMUL_NREG 256
|
||||
|
||||
struct sandbox_pmbus_priv {
|
||||
u16 reg[PMBUS_EMUL_NREG];
|
||||
bool supported[PMBUS_EMUL_NREG];
|
||||
};
|
||||
|
||||
/* Identification strings reported in the natural (forward) byte order. */
|
||||
static const char *pmbus_emul_string(u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case PMBUS_MFR_ID:
|
||||
return "SANDBOX";
|
||||
case PMBUS_MFR_MODEL:
|
||||
return "PMBUS-EMUL";
|
||||
case PMBUS_MFR_REVISION:
|
||||
return "1.0";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sandbox_pmbus_read(struct sandbox_pmbus_priv *priv, u8 cmd,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
const char *str = pmbus_emul_string(cmd);
|
||||
int i;
|
||||
|
||||
if (len < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (str) {
|
||||
int slen = strlen(str);
|
||||
|
||||
/*
|
||||
* Block payload: [length][bytes...]. A one-byte read is
|
||||
* the SMBus length probe and reports the true length (the
|
||||
* master just NAKs early). Any longer read must have room
|
||||
* for length + payload, otherwise the length byte would
|
||||
* lie about the data actually returned.
|
||||
*/
|
||||
if (len > 1 && len < slen + 1)
|
||||
return -EREMOTEIO;
|
||||
buf[0] = (u8)slen;
|
||||
for (i = 1; i < len; i++)
|
||||
buf[i] = (i - 1 < slen) ? (u8)str[i - 1] : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!priv->supported[cmd])
|
||||
return -EREMOTEIO; /* chip NAKs an unimplemented command */
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = (u8)(priv->reg[cmd] >> (8 * i));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_pmbus_write(struct sandbox_pmbus_priv *priv, u8 cmd,
|
||||
const u8 *buf, int len)
|
||||
{
|
||||
if (len == 0)
|
||||
return 0; /* send-byte (eg CLEAR_FAULTS): just ACK */
|
||||
if (!priv->supported[cmd])
|
||||
return -EREMOTEIO;
|
||||
if (len == 1)
|
||||
priv->reg[cmd] = buf[0];
|
||||
else
|
||||
priv->reg[cmd] = (u16)buf[0] | ((u16)buf[1] << 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_pmbus_xfer(struct udevice *emul, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct sandbox_pmbus_priv *priv = dev_get_priv(emul);
|
||||
u8 cmd;
|
||||
|
||||
if (nmsgs == 0)
|
||||
return 0;
|
||||
/* A PMBus transaction always opens with the command-code write. */
|
||||
if (msg[0].flags & I2C_M_RD)
|
||||
return -EIO;
|
||||
if (msg[0].len == 0)
|
||||
return 0; /* address-only probe */
|
||||
cmd = msg[0].buf[0];
|
||||
|
||||
if (nmsgs >= 2 && (msg[1].flags & I2C_M_RD))
|
||||
return sandbox_pmbus_read(priv, cmd, msg[1].buf, msg[1].len);
|
||||
|
||||
return sandbox_pmbus_write(priv, cmd, msg[0].buf + 1, msg[0].len - 1);
|
||||
}
|
||||
|
||||
static void sandbox_pmbus_support(struct sandbox_pmbus_priv *priv, u8 cmd,
|
||||
u16 val)
|
||||
{
|
||||
priv->supported[cmd] = true;
|
||||
priv->reg[cmd] = val;
|
||||
}
|
||||
|
||||
static int sandbox_pmbus_probe(struct udevice *emul)
|
||||
{
|
||||
struct sandbox_pmbus_priv *priv = dev_get_priv(emul);
|
||||
|
||||
/* Configuration / identification. */
|
||||
sandbox_pmbus_support(priv, PMBUS_PAGE, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_OPERATION, PB_OPERATION_ON);
|
||||
sandbox_pmbus_support(priv, PMBUS_ON_OFF_CONFIG, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_WRITE_PROTECT, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_CAPABILITY, 0x30);
|
||||
sandbox_pmbus_support(priv, PMBUS_VOUT_MODE, 0x18); /* LINEAR 2^-8 */
|
||||
sandbox_pmbus_support(priv, PMBUS_VOUT_COMMAND, 0x0200);
|
||||
sandbox_pmbus_support(priv, PMBUS_VOUT_TRIM, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_VOUT_MAX, 0x0400);
|
||||
sandbox_pmbus_support(priv, PMBUS_VOUT_SCALE_LOOP, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_REVISION, PMBUS_REV_13);
|
||||
|
||||
/* Status registers, all clean. */
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_BYTE, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_WORD, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_VOUT, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_IOUT, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_INPUT, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_TEMPERATURE, 0);
|
||||
sandbox_pmbus_support(priv, PMBUS_STATUS_CML, 0);
|
||||
|
||||
/*
|
||||
* Telemetry the emulated chip implements. READ_IIN and READ_POUT
|
||||
* are intentionally absent so callers see the unsupported path.
|
||||
*/
|
||||
sandbox_pmbus_support(priv, PMBUS_READ_VIN, 0x0abc);
|
||||
sandbox_pmbus_support(priv, PMBUS_READ_VOUT, 0x0200);
|
||||
sandbox_pmbus_support(priv, PMBUS_READ_IOUT, 0x0123);
|
||||
sandbox_pmbus_support(priv, PMBUS_READ_TEMPERATURE_1, 0x0019);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dm_i2c_ops sandbox_pmbus_emul_ops = {
|
||||
.xfer = sandbox_pmbus_xfer,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_pmbus_ids[] = {
|
||||
{ .compatible = "sandbox,i2c-pmbus" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_pmbus_emul) = {
|
||||
.name = "sandbox_pmbus_emul",
|
||||
.id = UCLASS_I2C_EMUL,
|
||||
.of_match = sandbox_pmbus_ids,
|
||||
.probe = sandbox_pmbus_probe,
|
||||
.priv_auto = sizeof(struct sandbox_pmbus_priv),
|
||||
.ops = &sandbox_pmbus_emul_ops,
|
||||
};
|
||||
Reference in New Issue
Block a user