diff options
| author | Vincent Jardin <[email protected]> | 2026-07-12 20:20:48 +0200 |
|---|---|---|
| committer | Peng Fan <[email protected]> | 2026-07-13 11:24:40 +0800 |
| commit | 995cc3383b7ee88bbe18e3ea8b2c13530b5af3b4 (patch) | |
| tree | ffc6a23010f6c84768ff5f16c0253120da3183dc | |
| parent | a5639a4d7bc53bf8e9a132f4e48eb0237fa588b3 (diff) | |
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 <[email protected]>
Reviewed-by: Simon Glass <[email protected]>
Signed-off-by: Peng Fan <[email protected]>
| -rw-r--r-- | MAINTAINERS | 1 | ||||
| -rw-r--r-- | arch/sandbox/dts/test.dts | 10 | ||||
| -rw-r--r-- | configs/sandbox_defconfig | 6 | ||||
| -rw-r--r-- | drivers/power/regulator/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/power/regulator/Makefile | 1 | ||||
| -rw-r--r-- | drivers/power/regulator/sandbox_pmbus.c | 171 |
6 files changed, 199 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index f60c58ed353..7b5e993a137 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 074e5c06ec8..fa23b179e38 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -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 { diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 512dbd6a0c6..79f46317e45 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -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 diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 602283474bb..9f80bb953e8 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -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. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index aa3faf2f34f..b8599ea7fd9 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -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 diff --git a/drivers/power/regulator/sandbox_pmbus.c b/drivers/power/regulator/sandbox_pmbus.c new file mode 100644 index 00000000000..7b52d2ca293 --- /dev/null +++ b/drivers/power/regulator/sandbox_pmbus.c @@ -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, +}; |
