* [PATCH v2 00/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors
@ 2026-07-31 10:44 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:44 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
This series adds the Facebook SanMiguel BMC, promotes the
TMP75/TMP175/LM75B sensors from generic TMP105 stand-ins to improved,
dedicated models, and introduces the test-side plumbing needed to
exercise real I2C devices from functional tests without hardcoding QOM
paths.
The work falls into four parts.
1. TMP* sensor models
The TMP105 model was the only stand-in for a whole family of pin- and
register-compatible parts, and several boards worked around the gap with
private TYPE_TMP75 / TYPE_LM75 aliases that resolved back to a plain
TMP105.
The model is first cleaned up for correctness and QEMU convention. One
of those changes touches migrated state, which gains an optional
subsection so existing streams stay compatible.
On that base, TMP75/TMP175/LM75B become variants selected by a small
per-type class descriptor. The variant is class data, so the wire format
is unchanged and all variants share the existing vmstate. qtest coverage
is added for the per-variant behaviour. The existing BMC boards then
drop their local aliases and instantiate the real models.
2. SanMiguel BMC
A new AST2600-based machine for the Facebook SanMiguel BMC, derived from
its device tree. It wires up three TMP75 sensors, IO expanders, FRU
EEPROMs, an I2C mux and an RTC across its I2C buses, and provides its
RAM, flash and networking; the FRU EEPROMs are populated with data read
from physical hardware. A functional test boots the OpenBMC image and
drives each sensor over QOM, checking the guest hwmon interface reports
the injected value back.
3. Python device locator
A QOM path names a device by its position under the object that owns it,
in enumeration order — it says nothing about where the device sits in
the bus topology. From a QOM path alone it is impossible to tell where a
device is actually connected or what purpose it serves.
DeviceLocator is a pure-Python helper that resolves a device from a
string descriptor of its position — bus, address, type, on-bus index —
by walking the bus/device hierarchy. It uses only standard QOM queries
over QMP: no new QEMU commands, no model changes. A self-check exercises
the grammar and its error paths against a paused SanMiguel BMC; it needs
no disk image and runs in about a second.
Note: the helper adopts the 3.10 type-hint syntax rather than the 3.9
equivalents; 3.9 reached end of life on 2025-10-31. The import is
guarded so the module still loads on 3.9 and dependent tests skip there.
Two small test-framework fixes ride along: the set_machine probe VM now
gets the per-test workdir instead of the hardcoded /var/tmp default, and
the Aspeed OpenBMC helper can optionally assert the booted device-tree
model.
4. Catalina follow-ups
The final three commits are the tail of the earlier "hw/arm: improve
Aspeed Catalina BMC emulation" series: functional tests for the PCA9555
and PCA9554 IO expanders, plus the PCA9554 change that drives its input
pins externally. They were held back because reaching such devices from
a test needs a stable way to reference them by bus position — the QOM
re-parenting patch tried then was rejected, and the device locator now
unblocks them.
Based on:
repo: https://github.com/legoater/qemu.git
branch: aspeed-11.1
commit: 04d27371e3 ("hw/arm: catalina: add NIC and FIO temperature sensors")
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
Changes in v2:
- Simplify TMP105 multiple variant initialization
- Improve reliability against malformed device locator strings
- Remove an obsolete reference to INA23x devices from a commit log message
- Rebase patch series on aspeed-next branch
- Fix invalid JSON syntax in SanMiguel FRU descriptions
- Link to v1: https://lore.kernel.org/qemu-devel/20260729-sanmiguel-bmc-locator-v1-0-c8f9a8d101f6@free.fr
To: qemu-devel@nongnu.org
Cc: Philippe Mathieu-Daudé <philmd@mailo.com>
Cc: Cédric Le Goater <clg@kaod.org>
Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Steven Lee <steven_lee@aspeedtech.com>
Cc: Jamin Lin <jamin_lin@aspeedtech.com>
Cc: Kane Chen <kane_chen@aspeedtech.com>
Cc: Andrew Jeffery <andrew@codeconstruct.com.au>
Cc: Joel Stanley <joel@jms.id.au>
Cc: qemu-arm@nongnu.org
Cc: Fabiano Rosas <farosas@suse.de>
Cc: Laurent Vivier <lvivier@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <th.huth+qemu@posteo.eu>
Cc: "Daniel P. Berrangé" <berrange@redhat.com>
---
Emmanuel Blot (25):
hw/sensor: tmp105: make device state private to the implementation
hw/sensor: tmp105: name the parent object field parent_obj
hw/sensor: tmp105: implement Resettable reset
hw/sensor: tmp105: enforce the configurable fault queue
hw/sensor: tmp105: describe the temperature property
hw/arm: aspeed: guard board-local temperature-sensor aliases
hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
tests/qtest: tmp105: cover the ALERT fault queue
tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
tests/qtest: tmp105: cover one-shot and fault-queue write immunity
tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
hw/arm: sanmiguel: add Facebook SanMiguel BMC machine
hw/arm: sanmiguel: populate EEPROM data
hw/arm: catalina: use the real TMP75 model
hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
tests/functional: aspeed: optionally check the device tree model on boot
tests/functional: give the set_machine probe VM the test workdir
tests/functional: add a pure-Python device-locator resolver
tests/functional: add a device-locator self-check
tests/functional: aspeed: add hwmon sensor read helpers
tests/functional/arm: sanmiguel: add BMC boot test
tests/functional/arm: catalina: test TMP75
tests/functional/arm: catalina: test PCA9555 IO expander via QOM
hw/arm: catalina: drive pca9554 io expander input pins externally
tests/functional/arm: catalina: test PCA9554 IO expander via QOM
hw/arm/aspeed_ast2600_catalina.c | 14 +-
hw/arm/aspeed_ast2600_fuji.c | 16 +-
hw/arm/aspeed_ast2600_sanmiguel.c | 405 ++++++++++++++++++
hw/arm/meson.build | 1 +
hw/sensor/tmp105.c | 304 ++++++++++---
include/hw/sensor/tmp105.h | 46 +-
tests/functional/arm/meson.build | 3 +
tests/functional/arm/test_aspeed_catalina.py | 107 ++++-
tests/functional/arm/test_aspeed_sanmiguel.py | 72 ++++
tests/functional/arm/test_device_locator.py | 120 ++++++
tests/functional/aspeed.py | 21 +-
tests/functional/qemu_test/locator.py | 586 ++++++++++++++++++++++++++
tests/functional/qemu_test/testcase.py | 2 +-
tests/qtest/tmp105-test.c | 359 ++++++++++++++++
14 files changed, 1934 insertions(+), 122 deletions(-)
---
base-commit: e5d3f5ac8633de310066f14862b17e4cac8ef8a5
change-id: 20260729-sanmiguel-bmc-locator-db13cceae66c
Best regards,
--
Emmanuel Blot <emmanuel.blot@free.fr>
^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH v2 00/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors
@ 2026-07-31 10:44 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:44 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
This series adds the Facebook SanMiguel BMC, promotes the
TMP75/TMP175/LM75B sensors from generic TMP105 stand-ins to improved,
dedicated models, and introduces the test-side plumbing needed to
exercise real I2C devices from functional tests without hardcoding QOM
paths.
The work falls into four parts.
1. TMP* sensor models
The TMP105 model was the only stand-in for a whole family of pin- and
register-compatible parts, and several boards worked around the gap with
private TYPE_TMP75 / TYPE_LM75 aliases that resolved back to a plain
TMP105.
The model is first cleaned up for correctness and QEMU convention. One
of those changes touches migrated state, which gains an optional
subsection so existing streams stay compatible.
On that base, TMP75/TMP175/LM75B become variants selected by a small
per-type class descriptor. The variant is class data, so the wire format
is unchanged and all variants share the existing vmstate. qtest coverage
is added for the per-variant behaviour. The existing BMC boards then
drop their local aliases and instantiate the real models.
2. SanMiguel BMC
A new AST2600-based machine for the Facebook SanMiguel BMC, derived from
its device tree. It wires up three TMP75 sensors, IO expanders, FRU
EEPROMs, an I2C mux and an RTC across its I2C buses, and provides its
RAM, flash and networking; the FRU EEPROMs are populated with data read
from physical hardware. A functional test boots the OpenBMC image and
drives each sensor over QOM, checking the guest hwmon interface reports
the injected value back.
3. Python device locator
A QOM path names a device by its position under the object that owns it,
in enumeration order — it says nothing about where the device sits in
the bus topology. From a QOM path alone it is impossible to tell where a
device is actually connected or what purpose it serves.
DeviceLocator is a pure-Python helper that resolves a device from a
string descriptor of its position — bus, address, type, on-bus index —
by walking the bus/device hierarchy. It uses only standard QOM queries
over QMP: no new QEMU commands, no model changes. A self-check exercises
the grammar and its error paths against a paused SanMiguel BMC; it needs
no disk image and runs in about a second.
Note: the helper adopts the 3.10 type-hint syntax rather than the 3.9
equivalents; 3.9 reached end of life on 2025-10-31. The import is
guarded so the module still loads on 3.9 and dependent tests skip there.
Two small test-framework fixes ride along: the set_machine probe VM now
gets the per-test workdir instead of the hardcoded /var/tmp default, and
the Aspeed OpenBMC helper can optionally assert the booted device-tree
model.
4. Catalina follow-ups
The final three commits are the tail of the earlier "hw/arm: improve
Aspeed Catalina BMC emulation" series: functional tests for the PCA9555
and PCA9554 IO expanders, plus the PCA9554 change that drives its input
pins externally. They were held back because reaching such devices from
a test needs a stable way to reference them by bus position — the QOM
re-parenting patch tried then was rejected, and the device locator now
unblocks them.
Based on:
repo: https://github.com/legoater/qemu.git
branch: aspeed-11.1
commit: 04d27371e3 ("hw/arm: catalina: add NIC and FIO temperature sensors")
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
Changes in v2:
- Simplify TMP105 multiple variant initialization
- Improve reliability against malformed device locator strings
- Remove an obsolete reference to INA23x devices from a commit log message
- Rebase patch series on aspeed-next branch
- Fix invalid JSON syntax in SanMiguel FRU descriptions
- Link to v1: https://lore.kernel.org/qemu-devel/20260729-sanmiguel-bmc-locator-v1-0-c8f9a8d101f6@free.fr
To: qemu-devel@nongnu.org
Cc: Philippe Mathieu-Daudé <philmd@mailo.com>
Cc: Cédric Le Goater <clg@kaod.org>
Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Steven Lee <steven_lee@aspeedtech.com>
Cc: Jamin Lin <jamin_lin@aspeedtech.com>
Cc: Kane Chen <kane_chen@aspeedtech.com>
Cc: Andrew Jeffery <andrew@codeconstruct.com.au>
Cc: Joel Stanley <joel@jms.id.au>
Cc: qemu-arm@nongnu.org
Cc: Fabiano Rosas <farosas@suse.de>
Cc: Laurent Vivier <lvivier@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <th.huth+qemu@posteo.eu>
Cc: "Daniel P. Berrangé" <berrange@redhat.com>
---
Emmanuel Blot (25):
hw/sensor: tmp105: make device state private to the implementation
hw/sensor: tmp105: name the parent object field parent_obj
hw/sensor: tmp105: implement Resettable reset
hw/sensor: tmp105: enforce the configurable fault queue
hw/sensor: tmp105: describe the temperature property
hw/arm: aspeed: guard board-local temperature-sensor aliases
hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
tests/qtest: tmp105: cover the ALERT fault queue
tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
tests/qtest: tmp105: cover one-shot and fault-queue write immunity
tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
hw/arm: sanmiguel: add Facebook SanMiguel BMC machine
hw/arm: sanmiguel: populate EEPROM data
hw/arm: catalina: use the real TMP75 model
hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
tests/functional: aspeed: optionally check the device tree model on boot
tests/functional: give the set_machine probe VM the test workdir
tests/functional: add a pure-Python device-locator resolver
tests/functional: add a device-locator self-check
tests/functional: aspeed: add hwmon sensor read helpers
tests/functional/arm: sanmiguel: add BMC boot test
tests/functional/arm: catalina: test TMP75
tests/functional/arm: catalina: test PCA9555 IO expander via QOM
hw/arm: catalina: drive pca9554 io expander input pins externally
tests/functional/arm: catalina: test PCA9554 IO expander via QOM
hw/arm/aspeed_ast2600_catalina.c | 14 +-
hw/arm/aspeed_ast2600_fuji.c | 16 +-
hw/arm/aspeed_ast2600_sanmiguel.c | 405 ++++++++++++++++++
hw/arm/meson.build | 1 +
hw/sensor/tmp105.c | 304 ++++++++++---
include/hw/sensor/tmp105.h | 46 +-
tests/functional/arm/meson.build | 3 +
tests/functional/arm/test_aspeed_catalina.py | 107 ++++-
tests/functional/arm/test_aspeed_sanmiguel.py | 72 ++++
tests/functional/arm/test_device_locator.py | 120 ++++++
tests/functional/aspeed.py | 21 +-
tests/functional/qemu_test/locator.py | 586 ++++++++++++++++++++++++++
tests/functional/qemu_test/testcase.py | 2 +-
tests/qtest/tmp105-test.c | 359 ++++++++++++++++
14 files changed, 1934 insertions(+), 122 deletions(-)
---
base-commit: e5d3f5ac8633de310066f14862b17e4cac8ef8a5
change-id: 20260729-sanmiguel-bmc-locator-db13cceae66c
Best regards,
--
Emmanuel Blot <emmanuel.blot@free.fr>
^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH v2 01/25] hw/sensor: tmp105: make device state private to the implementation
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The TMP105State struct, its cast macro and typedef are used only within
tmp105.c. Move them out of the public header into the source file and
drop the now-unused includes; the header is reduced to the TYPE_TMP105
name, with its guard renamed to the path-derived form per current QEMU
convention.
While here, normalize the source file include order to match the other
hw/sensor devices.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 45 ++++++++++++++++++++++++++++++++++++++++-----
include/hw/sensor/tmp105.h | 40 ++--------------------------------------
2 files changed, 42 insertions(+), 43 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index c5089d74f4b..6069c0905d9 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -19,16 +19,51 @@
*/
#include "qemu/osdep.h"
-#include "hw/i2c/i2c.h"
-#include "hw/core/irq.h"
-#include "migration/vmstate.h"
-#include "hw/sensor/tmp105.h"
+#include "qemu/module.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
-#include "qemu/module.h"
+#include "qom/object.h"
+#include "hw/sensor/tmp105.h"
+#include "hw/sensor/tmp105_regs.h"
+#include "hw/core/irq.h"
#include "hw/core/registerfields.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
#include "trace.h"
+OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
+
+/**
+ * TMP105State:
+ * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
+ * temperature. See Table 8 in the data sheet.
+ *
+ * @see_also: http://www.ti.com/lit/gpn/tmp105
+ */
+struct TMP105State {
+ /*< private >*/
+ I2CSlave i2c;
+ /*< public >*/
+
+ uint8_t len;
+ uint8_t buf[2];
+ qemu_irq pin;
+
+ uint8_t pointer;
+ uint8_t config;
+ int16_t temperature;
+ int16_t limit[2];
+ int faults;
+ uint8_t alarm;
+ /*
+ * The TMP105 initially looks for a temperature rising above T_high;
+ * once this is detected, the condition it looks for next is the
+ * temperature falling below T_low. This flag is false when initially
+ * looking for T_high, true when looking for T_low.
+ */
+ bool detect_falling;
+};
+
FIELD(CONFIG, SHUTDOWN_MODE, 0, 1)
FIELD(CONFIG, THERMOSTAT_MODE, 1, 1)
FIELD(CONFIG, POLARITY, 2, 1)
diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
index 244e2989feb..daece592e9b 100644
--- a/include/hw/sensor/tmp105.h
+++ b/include/hw/sensor/tmp105.h
@@ -11,45 +11,9 @@
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*/
-#ifndef QEMU_TMP105_H
-#define QEMU_TMP105_H
-
-#include "hw/i2c/i2c.h"
-#include "hw/sensor/tmp105_regs.h"
-#include "qom/object.h"
+#ifndef HW_SENSOR_TMP105_H
+#define HW_SENSOR_TMP105_H
#define TYPE_TMP105 "tmp105"
-OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
-
-/**
- * TMP105State:
- * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
- * temperature. See Table 8 in the data sheet.
- *
- * @see_also: http://www.ti.com/lit/gpn/tmp105
- */
-struct TMP105State {
- /*< private >*/
- I2CSlave i2c;
- /*< public >*/
-
- uint8_t len;
- uint8_t buf[2];
- qemu_irq pin;
-
- uint8_t pointer;
- uint8_t config;
- int16_t temperature;
- int16_t limit[2];
- int faults;
- uint8_t alarm;
- /*
- * The TMP105 initially looks for a temperature rising above T_high;
- * once this is detected, the condition it looks for next is the
- * temperature falling below T_low. This flag is false when initially
- * looking for T_high, true when looking for T_low.
- */
- bool detect_falling;
-};
#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 01/25] hw/sensor: tmp105: make device state private to the implementation
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The TMP105State struct, its cast macro and typedef are used only within
tmp105.c. Move them out of the public header into the source file and
drop the now-unused includes; the header is reduced to the TYPE_TMP105
name, with its guard renamed to the path-derived form per current QEMU
convention.
While here, normalize the source file include order to match the other
hw/sensor devices.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 45 ++++++++++++++++++++++++++++++++++++++++-----
include/hw/sensor/tmp105.h | 40 ++--------------------------------------
2 files changed, 42 insertions(+), 43 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index c5089d74f4b..6069c0905d9 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -19,16 +19,51 @@
*/
#include "qemu/osdep.h"
-#include "hw/i2c/i2c.h"
-#include "hw/core/irq.h"
-#include "migration/vmstate.h"
-#include "hw/sensor/tmp105.h"
+#include "qemu/module.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
-#include "qemu/module.h"
+#include "qom/object.h"
+#include "hw/sensor/tmp105.h"
+#include "hw/sensor/tmp105_regs.h"
+#include "hw/core/irq.h"
#include "hw/core/registerfields.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
#include "trace.h"
+OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
+
+/**
+ * TMP105State:
+ * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
+ * temperature. See Table 8 in the data sheet.
+ *
+ * @see_also: http://www.ti.com/lit/gpn/tmp105
+ */
+struct TMP105State {
+ /*< private >*/
+ I2CSlave i2c;
+ /*< public >*/
+
+ uint8_t len;
+ uint8_t buf[2];
+ qemu_irq pin;
+
+ uint8_t pointer;
+ uint8_t config;
+ int16_t temperature;
+ int16_t limit[2];
+ int faults;
+ uint8_t alarm;
+ /*
+ * The TMP105 initially looks for a temperature rising above T_high;
+ * once this is detected, the condition it looks for next is the
+ * temperature falling below T_low. This flag is false when initially
+ * looking for T_high, true when looking for T_low.
+ */
+ bool detect_falling;
+};
+
FIELD(CONFIG, SHUTDOWN_MODE, 0, 1)
FIELD(CONFIG, THERMOSTAT_MODE, 1, 1)
FIELD(CONFIG, POLARITY, 2, 1)
diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
index 244e2989feb..daece592e9b 100644
--- a/include/hw/sensor/tmp105.h
+++ b/include/hw/sensor/tmp105.h
@@ -11,45 +11,9 @@
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*/
-#ifndef QEMU_TMP105_H
-#define QEMU_TMP105_H
-
-#include "hw/i2c/i2c.h"
-#include "hw/sensor/tmp105_regs.h"
-#include "qom/object.h"
+#ifndef HW_SENSOR_TMP105_H
+#define HW_SENSOR_TMP105_H
#define TYPE_TMP105 "tmp105"
-OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
-
-/**
- * TMP105State:
- * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
- * temperature. See Table 8 in the data sheet.
- *
- * @see_also: http://www.ti.com/lit/gpn/tmp105
- */
-struct TMP105State {
- /*< private >*/
- I2CSlave i2c;
- /*< public >*/
-
- uint8_t len;
- uint8_t buf[2];
- qemu_irq pin;
-
- uint8_t pointer;
- uint8_t config;
- int16_t temperature;
- int16_t limit[2];
- int faults;
- uint8_t alarm;
- /*
- * The TMP105 initially looks for a temperature rising above T_high;
- * once this is detected, the condition it looks for next is the
- * temperature falling below T_low. This flag is false when initially
- * looking for T_high, true when looking for T_low.
- */
- bool detect_falling;
-};
#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 02/25] hw/sensor: tmp105: name the parent object field parent_obj
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
QEMU coding conventions expect the embedded parent object of a QOM type
to be named parent_obj. Rename the TMP105State I2CSlave member
accordingly and update its references; the change is purely cosmetic.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index 6069c0905d..aabeb34f08 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -42,7 +42,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
*/
struct TMP105State {
/*< private >*/
- I2CSlave i2c;
+ I2CSlave parent_obj;
/*< public >*/
uint8_t len;
@@ -187,12 +187,12 @@ static void tmp105_read(TMP105State *s)
break;
}
- trace_tmp105_read(s->i2c.address, s->pointer);
+ trace_tmp105_read(s->parent_obj.address, s->pointer);
}
static void tmp105_write(TMP105State *s)
{
- trace_tmp105_write(s->i2c.address, s->pointer);
+ trace_tmp105_write(s->parent_obj.address, s->pointer);
switch (s->pointer & 3) {
case TMP105_REG_TEMPERATURE:
@@ -200,7 +200,7 @@ static void tmp105_write(TMP105State *s)
case TMP105_REG_CONFIG:
if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
- trace_tmp105_write_shutdown(s->i2c.address);
+ trace_tmp105_write_shutdown(s->parent_obj.address);
}
s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
@@ -305,7 +305,7 @@ static const VMStateDescription vmstate_tmp105 = {
VMSTATE_INT16(temperature, TMP105State),
VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
VMSTATE_UINT8(alarm, TMP105State),
- VMSTATE_I2C_SLAVE(i2c, TMP105State),
+ VMSTATE_I2C_SLAVE(parent_obj, TMP105State),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * const []) {
@@ -338,7 +338,7 @@ static void tmp105_realize(DeviceState *dev, Error **errp)
qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
- tmp105_reset(&s->i2c);
+ tmp105_reset(&s->parent_obj);
}
static void tmp105_initfn(Object *obj)
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 02/25] hw/sensor: tmp105: name the parent object field parent_obj
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
QEMU coding conventions expect the embedded parent object of a QOM type
to be named parent_obj. Rename the TMP105State I2CSlave member
accordingly and update its references; the change is purely cosmetic.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index 6069c0905d..aabeb34f08 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -42,7 +42,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
*/
struct TMP105State {
/*< private >*/
- I2CSlave i2c;
+ I2CSlave parent_obj;
/*< public >*/
uint8_t len;
@@ -187,12 +187,12 @@ static void tmp105_read(TMP105State *s)
break;
}
- trace_tmp105_read(s->i2c.address, s->pointer);
+ trace_tmp105_read(s->parent_obj.address, s->pointer);
}
static void tmp105_write(TMP105State *s)
{
- trace_tmp105_write(s->i2c.address, s->pointer);
+ trace_tmp105_write(s->parent_obj.address, s->pointer);
switch (s->pointer & 3) {
case TMP105_REG_TEMPERATURE:
@@ -200,7 +200,7 @@ static void tmp105_write(TMP105State *s)
case TMP105_REG_CONFIG:
if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
- trace_tmp105_write_shutdown(s->i2c.address);
+ trace_tmp105_write_shutdown(s->parent_obj.address);
}
s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
@@ -305,7 +305,7 @@ static const VMStateDescription vmstate_tmp105 = {
VMSTATE_INT16(temperature, TMP105State),
VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
VMSTATE_UINT8(alarm, TMP105State),
- VMSTATE_I2C_SLAVE(i2c, TMP105State),
+ VMSTATE_I2C_SLAVE(parent_obj, TMP105State),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * const []) {
@@ -338,7 +338,7 @@ static void tmp105_realize(DeviceState *dev, Error **errp)
qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
- tmp105_reset(&s->i2c);
+ tmp105_reset(&s->parent_obj);
}
static void tmp105_initfn(Object *obj)
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 03/25] hw/sensor: tmp105: implement Resettable reset
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Previously the device reset routine was invoked once from realize() and
never registered with the reset subsystem, so a system or bus reset left
the TMP105 registers untouched. Convert tmp105_reset() into a proper
Resettable hold phase (tmp105_reset_hold) registered through
ResettableClass::phases.hold, and drop the manual call from realize() as
the reset framework now drives it. This matches the scheme used by the
other hw/sensor I2C devices.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index aabeb34f08..f7d0c738ca 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -314,9 +314,9 @@ static const VMStateDescription vmstate_tmp105 = {
}
};
-static void tmp105_reset(I2CSlave *i2c)
+static void tmp105_reset_hold(Object *obj, ResetType type)
{
- TMP105State *s = TMP105(i2c);
+ TMP105State *s = TMP105(obj);
s->temperature = 0;
s->pointer = 0;
@@ -337,8 +337,6 @@ static void tmp105_realize(DeviceState *dev, Error **errp)
TMP105State *s = TMP105(i2c);
qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
-
- tmp105_reset(&s->parent_obj);
}
static void tmp105_initfn(Object *obj)
@@ -352,11 +350,13 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->realize = tmp105_realize;
k->event = tmp105_event;
k->recv = tmp105_rx;
k->send = tmp105_tx;
+ rc->phases.hold = tmp105_reset_hold;
dc->vmsd = &vmstate_tmp105;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 03/25] hw/sensor: tmp105: implement Resettable reset
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Previously the device reset routine was invoked once from realize() and
never registered with the reset subsystem, so a system or bus reset left
the TMP105 registers untouched. Convert tmp105_reset() into a proper
Resettable hold phase (tmp105_reset_hold) registered through
ResettableClass::phases.hold, and drop the manual call from realize() as
the reset framework now drives it. This matches the scheme used by the
other hw/sensor I2C devices.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index aabeb34f08..f7d0c738ca 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -314,9 +314,9 @@ static const VMStateDescription vmstate_tmp105 = {
}
};
-static void tmp105_reset(I2CSlave *i2c)
+static void tmp105_reset_hold(Object *obj, ResetType type)
{
- TMP105State *s = TMP105(i2c);
+ TMP105State *s = TMP105(obj);
s->temperature = 0;
s->pointer = 0;
@@ -337,8 +337,6 @@ static void tmp105_realize(DeviceState *dev, Error **errp)
TMP105State *s = TMP105(i2c);
qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
-
- tmp105_reset(&s->parent_obj);
}
static void tmp105_initfn(Object *obj)
@@ -352,11 +350,13 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->realize = tmp105_realize;
k->event = tmp105_event;
k->recv = tmp105_rx;
k->send = tmp105_tx;
+ rc->phases.hold = tmp105_reset_hold;
dc->vmsd = &vmstate_tmp105;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 04/25] hw/sensor: tmp105: enforce the configurable fault queue
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The fault-queue depth selected in the configuration register was decoded
but never consulted, so the ALERT pin tripped on the very first
out-of-limit conversion regardless of the programmed depth.
Track the consecutive-fault count and only change the ALERT state once
it reaches the programmed depth; any in-range conversion clears the
count. The default configuration keeps the previous behaviour. The count
is migrated through an optional subsection so existing streams stay
compatible.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 87 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 55 insertions(+), 32 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index f7d0c738ca..5c77f991cc 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -54,6 +54,7 @@ struct TMP105State {
int16_t temperature;
int16_t limit[2];
int faults;
+ uint8_t fault_count;
uint8_t alarm;
/*
* The TMP105 initially looks for a temperature rising above T_high;
@@ -78,43 +79,40 @@ static void tmp105_interrupt_update(TMP105State *s)
static void tmp105_alarm_update(TMP105State *s, bool one_shot)
{
+ bool fault;
+
if (FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE) && !one_shot) {
return;
}
- if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
- /*
- * TM == 1 : Interrupt mode. We signal Alert when the
- * temperature rises above T_high, and expect the guest to clear
- * it (eg by reading a device register).
- */
- if (s->detect_falling) {
- if (s->temperature < s->limit[0]) {
- s->alarm = 1;
- s->detect_falling = false;
- }
- } else {
- if (s->temperature >= s->limit[1]) {
- s->alarm = 1;
- s->detect_falling = true;
- }
- }
+ /*
+ * A fault is a conversion that lies outside the limit currently being
+ * watched: above T_high while looking for the alarm to trip, below T_low
+ * afterwards.
+ */
+ if (s->detect_falling) {
+ fault = s->temperature < s->limit[0];
} else {
- /*
- * TM == 0 : Comparator mode. We signal Alert when the temperature
- * rises above T_high, and stop signalling it when the temperature
- * falls below T_low.
- */
+ fault = s->temperature >= s->limit[1];
+ }
+
+ if (!fault) {
+ s->fault_count = 0;
+ } else if (++s->fault_count >= s->faults) {
+ s->fault_count = 0;
if (s->detect_falling) {
- if (s->temperature < s->limit[0]) {
- s->alarm = 0;
- s->detect_falling = false;
- }
+ /*
+ * Temperature fell back below T_low. In comparator mode (TM == 0)
+ * the alarm is released; in interrupt mode (TM == 1) it is
+ * asserted again and the guest is expected to clear it by reading
+ * a register.
+ */
+ s->alarm = FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE);
+ s->detect_falling = false;
} else {
- if (s->temperature >= s->limit[1]) {
- s->alarm = 1;
- s->detect_falling = true;
- }
+ /* Temperature rose to or above T_high: assert the alarm. */
+ s->alarm = 1;
+ s->detect_falling = true;
}
}
@@ -204,7 +202,12 @@ static void tmp105_write(TMP105State *s)
}
s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
- tmp105_alarm_update(s, FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT));
+ if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
+ FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
+ tmp105_alarm_update(s, true);
+ } else {
+ tmp105_interrupt_update(s);
+ }
break;
case TMP105_REG_T_LOW:
@@ -213,7 +216,7 @@ static void tmp105_write(TMP105State *s)
s->limit[s->pointer & 1] = (int16_t)
((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
}
- tmp105_alarm_update(s, false);
+ tmp105_interrupt_update(s);
break;
}
}
@@ -281,6 +284,13 @@ static bool detect_falling_needed(void *opaque)
return s->detect_falling;
}
+static bool fault_count_needed(void *opaque)
+{
+ const TMP105State *s = opaque;
+
+ return s->fault_count != 0;
+}
+
static const VMStateDescription vmstate_tmp105_detect_falling = {
.name = "TMP105/detect-falling",
.version_id = 1,
@@ -292,6 +302,17 @@ static const VMStateDescription vmstate_tmp105_detect_falling = {
}
};
+static const VMStateDescription vmstate_tmp105_fault_count = {
+ .name = "TMP105/fault-count",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = fault_count_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT8(fault_count, TMP105State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_tmp105 = {
.name = "TMP105",
.version_id = 0,
@@ -310,6 +331,7 @@ static const VMStateDescription vmstate_tmp105 = {
},
.subsections = (const VMStateDescription * const []) {
&vmstate_tmp105_detect_falling,
+ &vmstate_tmp105_fault_count,
NULL
}
};
@@ -322,6 +344,7 @@ static void tmp105_reset_hold(Object *obj, ResetType type)
s->pointer = 0;
s->config = 0;
s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ s->fault_count = 0;
s->alarm = 0;
s->detect_falling = false;
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 04/25] hw/sensor: tmp105: enforce the configurable fault queue
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The fault-queue depth selected in the configuration register was decoded
but never consulted, so the ALERT pin tripped on the very first
out-of-limit conversion regardless of the programmed depth.
Track the consecutive-fault count and only change the ALERT state once
it reaches the programmed depth; any in-range conversion clears the
count. The default configuration keeps the previous behaviour. The count
is migrated through an optional subsection so existing streams stay
compatible.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 87 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 55 insertions(+), 32 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index f7d0c738ca..5c77f991cc 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -54,6 +54,7 @@ struct TMP105State {
int16_t temperature;
int16_t limit[2];
int faults;
+ uint8_t fault_count;
uint8_t alarm;
/*
* The TMP105 initially looks for a temperature rising above T_high;
@@ -78,43 +79,40 @@ static void tmp105_interrupt_update(TMP105State *s)
static void tmp105_alarm_update(TMP105State *s, bool one_shot)
{
+ bool fault;
+
if (FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE) && !one_shot) {
return;
}
- if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
- /*
- * TM == 1 : Interrupt mode. We signal Alert when the
- * temperature rises above T_high, and expect the guest to clear
- * it (eg by reading a device register).
- */
- if (s->detect_falling) {
- if (s->temperature < s->limit[0]) {
- s->alarm = 1;
- s->detect_falling = false;
- }
- } else {
- if (s->temperature >= s->limit[1]) {
- s->alarm = 1;
- s->detect_falling = true;
- }
- }
+ /*
+ * A fault is a conversion that lies outside the limit currently being
+ * watched: above T_high while looking for the alarm to trip, below T_low
+ * afterwards.
+ */
+ if (s->detect_falling) {
+ fault = s->temperature < s->limit[0];
} else {
- /*
- * TM == 0 : Comparator mode. We signal Alert when the temperature
- * rises above T_high, and stop signalling it when the temperature
- * falls below T_low.
- */
+ fault = s->temperature >= s->limit[1];
+ }
+
+ if (!fault) {
+ s->fault_count = 0;
+ } else if (++s->fault_count >= s->faults) {
+ s->fault_count = 0;
if (s->detect_falling) {
- if (s->temperature < s->limit[0]) {
- s->alarm = 0;
- s->detect_falling = false;
- }
+ /*
+ * Temperature fell back below T_low. In comparator mode (TM == 0)
+ * the alarm is released; in interrupt mode (TM == 1) it is
+ * asserted again and the guest is expected to clear it by reading
+ * a register.
+ */
+ s->alarm = FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE);
+ s->detect_falling = false;
} else {
- if (s->temperature >= s->limit[1]) {
- s->alarm = 1;
- s->detect_falling = true;
- }
+ /* Temperature rose to or above T_high: assert the alarm. */
+ s->alarm = 1;
+ s->detect_falling = true;
}
}
@@ -204,7 +202,12 @@ static void tmp105_write(TMP105State *s)
}
s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
- tmp105_alarm_update(s, FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT));
+ if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
+ FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
+ tmp105_alarm_update(s, true);
+ } else {
+ tmp105_interrupt_update(s);
+ }
break;
case TMP105_REG_T_LOW:
@@ -213,7 +216,7 @@ static void tmp105_write(TMP105State *s)
s->limit[s->pointer & 1] = (int16_t)
((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
}
- tmp105_alarm_update(s, false);
+ tmp105_interrupt_update(s);
break;
}
}
@@ -281,6 +284,13 @@ static bool detect_falling_needed(void *opaque)
return s->detect_falling;
}
+static bool fault_count_needed(void *opaque)
+{
+ const TMP105State *s = opaque;
+
+ return s->fault_count != 0;
+}
+
static const VMStateDescription vmstate_tmp105_detect_falling = {
.name = "TMP105/detect-falling",
.version_id = 1,
@@ -292,6 +302,17 @@ static const VMStateDescription vmstate_tmp105_detect_falling = {
}
};
+static const VMStateDescription vmstate_tmp105_fault_count = {
+ .name = "TMP105/fault-count",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = fault_count_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT8(fault_count, TMP105State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_tmp105 = {
.name = "TMP105",
.version_id = 0,
@@ -310,6 +331,7 @@ static const VMStateDescription vmstate_tmp105 = {
},
.subsections = (const VMStateDescription * const []) {
&vmstate_tmp105_detect_falling,
+ &vmstate_tmp105_fault_count,
NULL
}
};
@@ -322,6 +344,7 @@ static void tmp105_reset_hold(Object *obj, ResetType type)
s->pointer = 0;
s->config = 0;
s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ s->fault_count = 0;
s->alarm = 0;
s->detect_falling = false;
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 05/25] hw/sensor: tmp105: describe the temperature property
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Attach a description to the dynamic "temperature" QOM property so it is
documented in the QOM introspection output (qom-list-properties). The
value is expressed in millidegrees Celsius.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index 5c77f991cc..fefa661711 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -367,6 +367,8 @@ static void tmp105_initfn(Object *obj)
object_property_add(obj, "temperature", "int",
tmp105_get_temperature,
tmp105_set_temperature, NULL, NULL);
+ object_property_set_description(obj, "temperature",
+ "Temperature, in millidegrees Celsius");
}
static void tmp105_class_init(ObjectClass *klass, const void *data)
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 05/25] hw/sensor: tmp105: describe the temperature property
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Attach a description to the dynamic "temperature" QOM property so it is
documented in the QOM introspection output (qom-list-properties). The
value is expressed in millidegrees Celsius.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index 5c77f991cc..fefa661711 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -367,6 +367,8 @@ static void tmp105_initfn(Object *obj)
object_property_add(obj, "temperature", "int",
tmp105_get_temperature,
tmp105_set_temperature, NULL, NULL);
+ object_property_set_description(obj, "temperature",
+ "Temperature, in millidegrees Celsius");
}
static void tmp105_class_init(ObjectClass *klass, const void *data)
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 06/25] hw/arm: aspeed: guard board-local temperature-sensor aliases
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The Catalina and Fuji machines define TYPE_TMP75 (and, on Fuji,
TYPE_LM75) as local aliases of TYPE_TMP105 because no distinct model
existed. A following change adds a canonical TYPE_TMP75 to
hw/sensor/tmp105.h that would clash with these aliases.
Guard each alias with an #undef so the header definition can be
introduced without a redefinition warning, ahead of switching the boards
to the real models. No functional change.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_catalina.c | 5 +++++
hw/arm/aspeed_ast2600_fuji.c | 6 ++++++
2 files changed, 11 insertions(+)
diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
index f714c9d1b32..33e75338efc 100644
--- a/hw/arm/aspeed_ast2600_catalina.c
+++ b/hw/arm/aspeed_ast2600_catalina.c
@@ -22,6 +22,11 @@
#define CATALINA_BMC_HW_STRAP2 0x00000800
#define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
+/*
+ * Guard the alias against the TYPE_TMP75 that hw/sensor/tmp105.h now
+ * defines.
+ */
+#undef TYPE_TMP75
#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP421 "tmp421"
#define TYPE_DS1338 "ds1338"
diff --git a/hw/arm/aspeed_ast2600_fuji.c b/hw/arm/aspeed_ast2600_fuji.c
index 37db252e276..6dc3535f0cb 100644
--- a/hw/arm/aspeed_ast2600_fuji.c
+++ b/hw/arm/aspeed_ast2600_fuji.c
@@ -15,7 +15,13 @@
#include "hw/sensor/tmp105.h"
#include "hw/nvram/eeprom_at24c.h"
+/*
+ * Guard the aliases against the TYPE_TMP75 that hw/sensor/tmp105.h now defines;
+ * TYPE_LM75 is guarded too for consistency (no LM75 model exists yet).
+ */
+#undef TYPE_LM75
#define TYPE_LM75 TYPE_TMP105
+#undef TYPE_TMP75
#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP422 "tmp422"
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 06/25] hw/arm: aspeed: guard board-local temperature-sensor aliases
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The Catalina and Fuji machines define TYPE_TMP75 (and, on Fuji,
TYPE_LM75) as local aliases of TYPE_TMP105 because no distinct model
existed. A following change adds a canonical TYPE_TMP75 to
hw/sensor/tmp105.h that would clash with these aliases.
Guard each alias with an #undef so the header definition can be
introduced without a redefinition warning, ahead of switching the boards
to the real models. No functional change.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_catalina.c | 5 +++++
hw/arm/aspeed_ast2600_fuji.c | 6 ++++++
2 files changed, 11 insertions(+)
diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
index f714c9d1b32..33e75338efc 100644
--- a/hw/arm/aspeed_ast2600_catalina.c
+++ b/hw/arm/aspeed_ast2600_catalina.c
@@ -22,6 +22,11 @@
#define CATALINA_BMC_HW_STRAP2 0x00000800
#define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
+/*
+ * Guard the alias against the TYPE_TMP75 that hw/sensor/tmp105.h now
+ * defines.
+ */
+#undef TYPE_TMP75
#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP421 "tmp421"
#define TYPE_DS1338 "ds1338"
diff --git a/hw/arm/aspeed_ast2600_fuji.c b/hw/arm/aspeed_ast2600_fuji.c
index 37db252e276..6dc3535f0cb 100644
--- a/hw/arm/aspeed_ast2600_fuji.c
+++ b/hw/arm/aspeed_ast2600_fuji.c
@@ -15,7 +15,13 @@
#include "hw/sensor/tmp105.h"
#include "hw/nvram/eeprom_at24c.h"
+/*
+ * Guard the aliases against the TYPE_TMP75 that hw/sensor/tmp105.h now defines;
+ * TYPE_LM75 is guarded too for consistency (no LM75 model exists yet).
+ */
+#undef TYPE_LM75
#define TYPE_LM75 TYPE_TMP105
+#undef TYPE_TMP75
#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP422 "tmp422"
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 07/25] hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The TI TMP75/TMP175 and the NXP LM75B share the TMP105 register map and
control semantics, differing only in a few details. Model them as
variants of the TMP105, parameterised by a small per-type class
descriptor covering the fault-queue depths, the writable config bits,
the converter resolution, the set-point masks and the TMP75's
alert-clear on thermostat-mode change.
The variant is class data, not migrated state, so the wire format is
unchanged and all variants share the existing vmstate.
While here, align the shared shutdown handling with the hardware:
entering shutdown now clears the ALERT/OS output in interrupt mode,
correcting the base TMP105 as well.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 176 ++++++++++++++++++++++++++++++++++++---------
include/hw/sensor/tmp105.h | 6 +-
2 files changed, 148 insertions(+), 34 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index fefa661711..ece0650d70 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -1,5 +1,5 @@
/*
- * Texas Instruments TMP105 temperature sensor.
+ * Texas Instruments TMP105/TMP75/TMP175/LM75B temperature sensor.
*
* Copyright (C) 2008 Nokia Corporation
* Written by Andrzej Zaborowski <andrew@openedhand.com>
@@ -16,6 +16,13 @@
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Limitations:
+ * - The SMBus Alert Response Address protocol and the general-call commands
+ * are not implemented.
+ * - The over-limit comparison uses the full 8.8 fixed-point temperature and a
+ * ">=" boundary for every variant. The LM75B's 9-bit comparison quantisation
+ * and strict-exceed boundary are therefore only approximated.
*/
#include "qemu/osdep.h"
@@ -31,15 +38,8 @@
#include "migration/vmstate.h"
#include "trace.h"
-OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
+OBJECT_DECLARE_TYPE(TMP105State, TMP105Class, TMP105)
-/**
- * TMP105State:
- * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
- * temperature. See Table 8 in the data sheet.
- *
- * @see_also: http://www.ti.com/lit/gpn/tmp105
- */
struct TMP105State {
/*< private >*/
I2CSlave parent_obj;
@@ -53,7 +53,7 @@ struct TMP105State {
uint8_t config;
int16_t temperature;
int16_t limit[2];
- int faults;
+ uint8_t faults;
uint8_t fault_count;
uint8_t alarm;
/*
@@ -65,6 +65,27 @@ struct TMP105State {
bool detect_falling;
};
+/*
+ * Per-device-model parameters. The TMP105, TMP75, TMP175 and LM75B share the
+ * same register map and control semantics; they differ only in a handful of
+ * details captured here.
+ *
+ * @faultq: fault-queue length table selected by Config bits
+ * @config_wmask: writable Config bits. The LM75B has no resolution (R1:R0) or
+ * one-shot (OS) bits.
+ * @fixed_res: converter resolution field pinned by the device, or -1 when it is
+ * software-selectable.
+ * @limit_lsb_mask: low-byte mask applied to the T_LOW/T_HIGH limit registers.
+ */
+struct TMP105Class {
+ I2CSlaveClass parent_class;
+ const uint8_t *faultq;
+ uint8_t config_wmask;
+ int8_t fixed_res;
+ uint8_t limit_lsb_mask;
+ bool tm_change_clears_alert;
+};
+
FIELD(CONFIG, SHUTDOWN_MODE, 0, 1)
FIELD(CONFIG, THERMOSTAT_MODE, 1, 1)
FIELD(CONFIG, POLARITY, 2, 1)
@@ -152,10 +173,11 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
tmp105_alarm_update(s, false);
}
-static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
-
static void tmp105_read(TMP105State *s)
{
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
+ int res;
+
s->len = 0;
if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
@@ -165,9 +187,11 @@ static void tmp105_read(TMP105State *s)
switch (s->pointer & 3) {
case TMP105_REG_TEMPERATURE:
+ res = tc->fixed_res >= 0 ? tc->fixed_res :
+ FIELD_EX8(s->config, CONFIG, CONVERTER_RESOLUTION);
s->buf[s->len++] = (((uint16_t) s->temperature) >> 8);
s->buf[s->len++] = (((uint16_t) s->temperature) >> 0) &
- (0xf0 << (FIELD_EX8(~s->config, CONFIG, CONVERTER_RESOLUTION)));
+ (0xf0 << (3 - res));
break;
case TMP105_REG_CONFIG:
@@ -190,6 +214,10 @@ static void tmp105_read(TMP105State *s)
static void tmp105_write(TMP105State *s)
{
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
+ uint8_t config, one_shot;
+ bool waking;
+
trace_tmp105_write(s->parent_obj.address, s->pointer);
switch (s->pointer & 3) {
@@ -197,14 +225,27 @@ static void tmp105_write(TMP105State *s)
break;
case TMP105_REG_CONFIG:
- if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
+ config = s->buf[0] & tc->config_wmask;
+ if (FIELD_EX8(config & ~s->config, CONFIG, SHUTDOWN_MODE)) {
trace_tmp105_write_shutdown(s->parent_obj.address);
+ if (FIELD_EX8(config, CONFIG, THERMOSTAT_MODE)) {
+ s->alarm = 0;
+ }
}
- s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
- s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
- if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
- FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
+ if (tc->tm_change_clears_alert &&
+ FIELD_EX8(config ^ s->config, CONFIG, THERMOSTAT_MODE)) {
+ s->alarm = 0;
+ s->fault_count = 0;
+ s->detect_falling = false;
+ }
+ waking = FIELD_EX8(s->config & ~config, CONFIG, SHUTDOWN_MODE);
+ one_shot = FIELD_EX8(config, CONFIG, ONE_SHOT);
+ s->config = FIELD_DP8(config, CONFIG, ONE_SHOT, 0);
+ s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ if (one_shot && FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
tmp105_alarm_update(s, true);
+ } else if (waking) {
+ tmp105_alarm_update(s, false);
} else {
tmp105_interrupt_update(s);
}
@@ -214,7 +255,8 @@ static void tmp105_write(TMP105State *s)
case TMP105_REG_T_HIGH:
if (s->len >= 3) {
s->limit[s->pointer & 1] = (int16_t)
- ((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
+ ((((uint16_t) s->buf[0]) << 8) |
+ (s->buf[1] & tc->limit_lsb_mask));
}
tmp105_interrupt_update(s);
break;
@@ -265,8 +307,9 @@ static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
static int tmp105_post_load(void *opaque, int version_id)
{
TMP105State *s = opaque;
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
- s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
tmp105_interrupt_update(s);
return 0;
@@ -339,11 +382,12 @@ static const VMStateDescription vmstate_tmp105 = {
static void tmp105_reset_hold(Object *obj, ResetType type)
{
TMP105State *s = TMP105(obj);
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
s->temperature = 0;
s->pointer = 0;
s->config = 0;
- s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
s->fault_count = 0;
s->alarm = 0;
s->detect_falling = false;
@@ -371,11 +415,25 @@ static void tmp105_initfn(Object *obj)
"Temperature, in millidegrees Celsius");
}
+/*
+ * Fault-queue length selected by Config F1:F0. Row 0 (1/2/4/6) is used by the
+ * TMP105, TMP175 and LM75B; row 1 (1/2/3/4) by the TMP75.
+ */
+static const uint8_t tmp105_faultq[][4] = {
+ { 1, 2, 4, 6 },
+ { 1, 2, 3, 4 },
+};
+
+/* The F1:F0 field must never index past a fault-queue table row. */
+QEMU_BUILD_BUG_ON((1 << R_CONFIG_FAULT_QUEUE_LENGTH) - 1 >=
+ ARRAY_SIZE(tmp105_faultq[0]));
+
static void tmp105_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
+ TMP105Class *tc = TMP105_CLASS(klass);
dc->realize = tmp105_realize;
k->event = tmp105_event;
@@ -383,19 +441,71 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
k->send = tmp105_tx;
rc->phases.hold = tmp105_reset_hold;
dc->vmsd = &vmstate_tmp105;
+
+ tc->faultq = tmp105_faultq[0];
+ tc->config_wmask = 0xff;
+ tc->fixed_res = -1;
+ tc->limit_lsb_mask = 0xf0;
+ tc->tm_change_clears_alert = false;
+}
+
+static void tmp175_class_init(ObjectClass *klass, const void *data)
+{
+ TMP105Class *tc = TMP105_CLASS(klass);
+
+ tc->faultq = tmp105_faultq[0];
+ tc->config_wmask = 0xff;
+ tc->fixed_res = -1;
+ tc->limit_lsb_mask = 0xf0;
+ tc->tm_change_clears_alert = false;
+}
+
+static void tmp75_class_init(ObjectClass *klass, const void *data)
+{
+ TMP105Class *tc = TMP105_CLASS(klass);
+
+ tc->faultq = tmp105_faultq[1];
+ tc->config_wmask = 0xff;
+ tc->fixed_res = -1;
+ tc->limit_lsb_mask = 0xf0;
+ tc->tm_change_clears_alert = true;
+}
+
+static void lm75b_class_init(ObjectClass *klass, const void *data)
+{
+ TMP105Class *tc = TMP105_CLASS(klass);
+
+ tc->faultq = tmp105_faultq[0];
+ tc->config_wmask = 0x1f;
+ tc->fixed_res = 2;
+ tc->limit_lsb_mask = 0x80;
+ tc->tm_change_clears_alert = false;
}
-static const TypeInfo tmp105_info = {
- .name = TYPE_TMP105,
- .parent = TYPE_I2C_SLAVE,
- .instance_size = sizeof(TMP105State),
- .instance_init = tmp105_initfn,
- .class_init = tmp105_class_init,
+static const TypeInfo tmp105_types[] = {
+ {
+ .name = TYPE_TMP105,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(TMP105State),
+ .class_size = sizeof(TMP105Class),
+ .instance_init = tmp105_initfn,
+ .class_init = tmp105_class_init,
+ },
+ {
+ .name = TYPE_TMP175,
+ .parent = TYPE_TMP105,
+ .class_init = tmp175_class_init,
+ },
+ {
+ .name = TYPE_TMP75,
+ .parent = TYPE_TMP105,
+ .class_init = tmp75_class_init,
+ },
+ {
+ .name = TYPE_LM75B,
+ .parent = TYPE_TMP105,
+ .class_init = lm75b_class_init,
+ },
};
-static void tmp105_register_types(void)
-{
- type_register_static(&tmp105_info);
-}
-
-type_init(tmp105_register_types)
+DEFINE_TYPES(tmp105_types)
diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
index daece592e9..0698aeead7 100644
--- a/include/hw/sensor/tmp105.h
+++ b/include/hw/sensor/tmp105.h
@@ -1,5 +1,5 @@
/*
- * Texas Instruments TMP105 Temperature Sensor
+ * Texas Instruments TMP105/TMP75/TMP175/LM75B Temperature Sensor
*
* Browse the data sheet:
*
@@ -14,6 +14,10 @@
#ifndef HW_SENSOR_TMP105_H
#define HW_SENSOR_TMP105_H
+/* TMP75, TMP175 and NXP LM75B are register-compatible with TMP105. */
#define TYPE_TMP105 "tmp105"
+#define TYPE_TMP175 "tmp175"
+#define TYPE_TMP75 "tmp75"
+#define TYPE_LM75B "lm75b"
#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 07/25] hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The TI TMP75/TMP175 and the NXP LM75B share the TMP105 register map and
control semantics, differing only in a few details. Model them as
variants of the TMP105, parameterised by a small per-type class
descriptor covering the fault-queue depths, the writable config bits,
the converter resolution, the set-point masks and the TMP75's
alert-clear on thermostat-mode change.
The variant is class data, not migrated state, so the wire format is
unchanged and all variants share the existing vmstate.
While here, align the shared shutdown handling with the hardware:
entering shutdown now clears the ALERT/OS output in interrupt mode,
correcting the base TMP105 as well.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/sensor/tmp105.c | 176 ++++++++++++++++++++++++++++++++++++---------
include/hw/sensor/tmp105.h | 6 +-
2 files changed, 148 insertions(+), 34 deletions(-)
diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
index fefa661711..ece0650d70 100644
--- a/hw/sensor/tmp105.c
+++ b/hw/sensor/tmp105.c
@@ -1,5 +1,5 @@
/*
- * Texas Instruments TMP105 temperature sensor.
+ * Texas Instruments TMP105/TMP75/TMP175/LM75B temperature sensor.
*
* Copyright (C) 2008 Nokia Corporation
* Written by Andrzej Zaborowski <andrew@openedhand.com>
@@ -16,6 +16,13 @@
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Limitations:
+ * - The SMBus Alert Response Address protocol and the general-call commands
+ * are not implemented.
+ * - The over-limit comparison uses the full 8.8 fixed-point temperature and a
+ * ">=" boundary for every variant. The LM75B's 9-bit comparison quantisation
+ * and strict-exceed boundary are therefore only approximated.
*/
#include "qemu/osdep.h"
@@ -31,15 +38,8 @@
#include "migration/vmstate.h"
#include "trace.h"
-OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
+OBJECT_DECLARE_TYPE(TMP105State, TMP105Class, TMP105)
-/**
- * TMP105State:
- * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
- * temperature. See Table 8 in the data sheet.
- *
- * @see_also: http://www.ti.com/lit/gpn/tmp105
- */
struct TMP105State {
/*< private >*/
I2CSlave parent_obj;
@@ -53,7 +53,7 @@ struct TMP105State {
uint8_t config;
int16_t temperature;
int16_t limit[2];
- int faults;
+ uint8_t faults;
uint8_t fault_count;
uint8_t alarm;
/*
@@ -65,6 +65,27 @@ struct TMP105State {
bool detect_falling;
};
+/*
+ * Per-device-model parameters. The TMP105, TMP75, TMP175 and LM75B share the
+ * same register map and control semantics; they differ only in a handful of
+ * details captured here.
+ *
+ * @faultq: fault-queue length table selected by Config bits
+ * @config_wmask: writable Config bits. The LM75B has no resolution (R1:R0) or
+ * one-shot (OS) bits.
+ * @fixed_res: converter resolution field pinned by the device, or -1 when it is
+ * software-selectable.
+ * @limit_lsb_mask: low-byte mask applied to the T_LOW/T_HIGH limit registers.
+ */
+struct TMP105Class {
+ I2CSlaveClass parent_class;
+ const uint8_t *faultq;
+ uint8_t config_wmask;
+ int8_t fixed_res;
+ uint8_t limit_lsb_mask;
+ bool tm_change_clears_alert;
+};
+
FIELD(CONFIG, SHUTDOWN_MODE, 0, 1)
FIELD(CONFIG, THERMOSTAT_MODE, 1, 1)
FIELD(CONFIG, POLARITY, 2, 1)
@@ -152,10 +173,11 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
tmp105_alarm_update(s, false);
}
-static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
-
static void tmp105_read(TMP105State *s)
{
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
+ int res;
+
s->len = 0;
if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
@@ -165,9 +187,11 @@ static void tmp105_read(TMP105State *s)
switch (s->pointer & 3) {
case TMP105_REG_TEMPERATURE:
+ res = tc->fixed_res >= 0 ? tc->fixed_res :
+ FIELD_EX8(s->config, CONFIG, CONVERTER_RESOLUTION);
s->buf[s->len++] = (((uint16_t) s->temperature) >> 8);
s->buf[s->len++] = (((uint16_t) s->temperature) >> 0) &
- (0xf0 << (FIELD_EX8(~s->config, CONFIG, CONVERTER_RESOLUTION)));
+ (0xf0 << (3 - res));
break;
case TMP105_REG_CONFIG:
@@ -190,6 +214,10 @@ static void tmp105_read(TMP105State *s)
static void tmp105_write(TMP105State *s)
{
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
+ uint8_t config, one_shot;
+ bool waking;
+
trace_tmp105_write(s->parent_obj.address, s->pointer);
switch (s->pointer & 3) {
@@ -197,14 +225,27 @@ static void tmp105_write(TMP105State *s)
break;
case TMP105_REG_CONFIG:
- if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
+ config = s->buf[0] & tc->config_wmask;
+ if (FIELD_EX8(config & ~s->config, CONFIG, SHUTDOWN_MODE)) {
trace_tmp105_write_shutdown(s->parent_obj.address);
+ if (FIELD_EX8(config, CONFIG, THERMOSTAT_MODE)) {
+ s->alarm = 0;
+ }
}
- s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
- s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
- if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
- FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
+ if (tc->tm_change_clears_alert &&
+ FIELD_EX8(config ^ s->config, CONFIG, THERMOSTAT_MODE)) {
+ s->alarm = 0;
+ s->fault_count = 0;
+ s->detect_falling = false;
+ }
+ waking = FIELD_EX8(s->config & ~config, CONFIG, SHUTDOWN_MODE);
+ one_shot = FIELD_EX8(config, CONFIG, ONE_SHOT);
+ s->config = FIELD_DP8(config, CONFIG, ONE_SHOT, 0);
+ s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ if (one_shot && FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
tmp105_alarm_update(s, true);
+ } else if (waking) {
+ tmp105_alarm_update(s, false);
} else {
tmp105_interrupt_update(s);
}
@@ -214,7 +255,8 @@ static void tmp105_write(TMP105State *s)
case TMP105_REG_T_HIGH:
if (s->len >= 3) {
s->limit[s->pointer & 1] = (int16_t)
- ((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
+ ((((uint16_t) s->buf[0]) << 8) |
+ (s->buf[1] & tc->limit_lsb_mask));
}
tmp105_interrupt_update(s);
break;
@@ -265,8 +307,9 @@ static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
static int tmp105_post_load(void *opaque, int version_id)
{
TMP105State *s = opaque;
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
- s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
tmp105_interrupt_update(s);
return 0;
@@ -339,11 +382,12 @@ static const VMStateDescription vmstate_tmp105 = {
static void tmp105_reset_hold(Object *obj, ResetType type)
{
TMP105State *s = TMP105(obj);
+ const TMP105Class *tc = TMP105_GET_CLASS(s);
s->temperature = 0;
s->pointer = 0;
s->config = 0;
- s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
+ s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
s->fault_count = 0;
s->alarm = 0;
s->detect_falling = false;
@@ -371,11 +415,25 @@ static void tmp105_initfn(Object *obj)
"Temperature, in millidegrees Celsius");
}
+/*
+ * Fault-queue length selected by Config F1:F0. Row 0 (1/2/4/6) is used by the
+ * TMP105, TMP175 and LM75B; row 1 (1/2/3/4) by the TMP75.
+ */
+static const uint8_t tmp105_faultq[][4] = {
+ { 1, 2, 4, 6 },
+ { 1, 2, 3, 4 },
+};
+
+/* The F1:F0 field must never index past a fault-queue table row. */
+QEMU_BUILD_BUG_ON((1 << R_CONFIG_FAULT_QUEUE_LENGTH) - 1 >=
+ ARRAY_SIZE(tmp105_faultq[0]));
+
static void tmp105_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
+ TMP105Class *tc = TMP105_CLASS(klass);
dc->realize = tmp105_realize;
k->event = tmp105_event;
@@ -383,19 +441,71 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
k->send = tmp105_tx;
rc->phases.hold = tmp105_reset_hold;
dc->vmsd = &vmstate_tmp105;
+
+ tc->faultq = tmp105_faultq[0];
+ tc->config_wmask = 0xff;
+ tc->fixed_res = -1;
+ tc->limit_lsb_mask = 0xf0;
+ tc->tm_change_clears_alert = false;
+}
+
+static void tmp175_class_init(ObjectClass *klass, const void *data)
+{
+ TMP105Class *tc = TMP105_CLASS(klass);
+
+ tc->faultq = tmp105_faultq[0];
+ tc->config_wmask = 0xff;
+ tc->fixed_res = -1;
+ tc->limit_lsb_mask = 0xf0;
+ tc->tm_change_clears_alert = false;
+}
+
+static void tmp75_class_init(ObjectClass *klass, const void *data)
+{
+ TMP105Class *tc = TMP105_CLASS(klass);
+
+ tc->faultq = tmp105_faultq[1];
+ tc->config_wmask = 0xff;
+ tc->fixed_res = -1;
+ tc->limit_lsb_mask = 0xf0;
+ tc->tm_change_clears_alert = true;
+}
+
+static void lm75b_class_init(ObjectClass *klass, const void *data)
+{
+ TMP105Class *tc = TMP105_CLASS(klass);
+
+ tc->faultq = tmp105_faultq[0];
+ tc->config_wmask = 0x1f;
+ tc->fixed_res = 2;
+ tc->limit_lsb_mask = 0x80;
+ tc->tm_change_clears_alert = false;
}
-static const TypeInfo tmp105_info = {
- .name = TYPE_TMP105,
- .parent = TYPE_I2C_SLAVE,
- .instance_size = sizeof(TMP105State),
- .instance_init = tmp105_initfn,
- .class_init = tmp105_class_init,
+static const TypeInfo tmp105_types[] = {
+ {
+ .name = TYPE_TMP105,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(TMP105State),
+ .class_size = sizeof(TMP105Class),
+ .instance_init = tmp105_initfn,
+ .class_init = tmp105_class_init,
+ },
+ {
+ .name = TYPE_TMP175,
+ .parent = TYPE_TMP105,
+ .class_init = tmp175_class_init,
+ },
+ {
+ .name = TYPE_TMP75,
+ .parent = TYPE_TMP105,
+ .class_init = tmp75_class_init,
+ },
+ {
+ .name = TYPE_LM75B,
+ .parent = TYPE_TMP105,
+ .class_init = lm75b_class_init,
+ },
};
-static void tmp105_register_types(void)
-{
- type_register_static(&tmp105_info);
-}
-
-type_init(tmp105_register_types)
+DEFINE_TYPES(tmp105_types)
diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
index daece592e9..0698aeead7 100644
--- a/include/hw/sensor/tmp105.h
+++ b/include/hw/sensor/tmp105.h
@@ -1,5 +1,5 @@
/*
- * Texas Instruments TMP105 Temperature Sensor
+ * Texas Instruments TMP105/TMP75/TMP175/LM75B Temperature Sensor
*
* Browse the data sheet:
*
@@ -14,6 +14,10 @@
#ifndef HW_SENSOR_TMP105_H
#define HW_SENSOR_TMP105_H
+/* TMP75, TMP175 and NXP LM75B are register-compatible with TMP105. */
#define TYPE_TMP105 "tmp105"
+#define TYPE_TMP175 "tmp175"
+#define TYPE_TMP75 "tmp75"
+#define TYPE_LM75B "lm75b"
#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 08/25] tests/qtest: tmp105: cover the ALERT fault queue
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add two qgraph tests exercising the ALERT pin, the only way the TMP105
exposes its alarm state. One checks the default single-fault behaviour;
the other checks a deeper fault queue, including the running-count reset
on an in-range conversion and its effect on both asserting and releasing
the alarm.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index 3b114a50f55..29a031fb7bf 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -17,6 +17,11 @@
#define TMP105_TEST_ID "tmp105-test"
#define TMP105_TEST_ADDR 0x49
+#define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
+
+#define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
+#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
+#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -105,6 +110,62 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230);
}
+/*
+ * The TMP105 exposes its alarm state only through the ALERT pin.
+ */
+static void test_alert_single_fault(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_false(get_irq(0));
+}
+
+static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ /* Comparator mode, active-high ALERT, fault queue of four. */
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 25000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_true(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_false(get_irq(0));
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -116,5 +177,7 @@ static void tmp105_register_nodes(void)
qos_node_consumes("tmp105", "i2c-bus", &opts);
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
+ qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
+ qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
}
libqos_init(tmp105_register_nodes);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 08/25] tests/qtest: tmp105: cover the ALERT fault queue
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add two qgraph tests exercising the ALERT pin, the only way the TMP105
exposes its alarm state. One checks the default single-fault behaviour;
the other checks a deeper fault queue, including the running-count reset
on an in-range conversion and its effect on both asserting and releasing
the alarm.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index 3b114a50f55..29a031fb7bf 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -17,6 +17,11 @@
#define TMP105_TEST_ID "tmp105-test"
#define TMP105_TEST_ADDR 0x49
+#define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
+
+#define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
+#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
+#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -105,6 +110,62 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230);
}
+/*
+ * The TMP105 exposes its alarm state only through the ALERT pin.
+ */
+static void test_alert_single_fault(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_false(get_irq(0));
+}
+
+static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ /* Comparator mode, active-high ALERT, fault queue of four. */
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 25000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ for (i = 0; i < 3; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_true(get_irq(0));
+ }
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_false(get_irq(0));
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -116,5 +177,7 @@ static void tmp105_register_nodes(void)
qos_node_consumes("tmp105", "i2c-bus", &opts);
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
+ qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
+ qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
}
libqos_init(tmp105_register_nodes);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 09/25] tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Exercise the per-variant differences added to the model: the TMP75 and
TMP175 fault-queue depths, the TMP75 alert-clear on thermostat-mode
change, and the LM75B's reserved config bits and fixed set-point and
temperature resolutions.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 138 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index 29a031fb7b..ea6803715a 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -19,9 +19,20 @@
#define TMP105_TEST_ADDR 0x49
#define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
+#define TMP75_TEST_ID "tmp75-test"
+#define TMP75_TEST_PATH "/machine/peripheral/" TMP75_TEST_ID
+
+#define TMP175_TEST_ID "tmp175-test"
+#define TMP175_TEST_PATH "/machine/peripheral/" TMP175_TEST_ID
+
+#define LM75B_TEST_ID "lm75b-test"
+#define LM75B_TEST_PATH "/machine/peripheral/" LM75B_TEST_ID
+
#define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
+#define TMP105_CONFIG_TM (1 << 1) /* interrupt (thermostat) mode */
#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
+#define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -166,6 +177,98 @@ static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
g_assert_false(get_irq(0));
}
+/*
+ * Drive @need consecutive over-limit conversions and check that the ALERT pin
+ * only asserts on the last one. This exercises the fault-queue length.
+ */
+static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
+ const char *path, uint8_t fq_field, int need)
+{
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, path);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(fq_field));
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < need - 1; i++) {
+ qmp_tmp105_set_temperature(id, 85000);
+ g_assert_false(get_irq(0));
+ }
+ qmp_tmp105_set_temperature(id, 85000);
+ g_assert_true(get_irq(0));
+}
+
+/* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
+static void test_tmp75_fault_queue(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ check_fault_queue(obj, TMP75_TEST_ID, TMP75_TEST_PATH, 2, 3);
+}
+
+/* The TMP175 keeps the TMP105 mapping: F1:F0 = 10b means 4 faults. */
+static void test_tmp175_fault_queue(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ check_fault_queue(obj, TMP175_TEST_ID, TMP175_TEST_PATH, 2, 4);
+}
+
+/*
+ * Toggling the thermostat mode (TM) bit clears any active alert on the TMP75.
+ */
+static void test_tmp75_tm_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_TM);
+ g_assert_false(get_irq(0));
+}
+
+/*
+ * The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
+ * Config bits are reserved (read/write as zero) and the temperature register is
+ * always masked to 11 bits regardless of what is written to Config.
+ */
+static void test_lm75b_resolution(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ uint16_t value;
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60);
+ g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x00);
+
+ qmp_tmp105_set_temperature(LM75B_TEST_ID, 20938);
+ value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE);
+ g_assert_cmphex(value, ==, 0x14e0);
+}
+
+/* The LM75B set-point registers store only 9 bits. */
+static void test_lm75b_limits(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4200);
+
+ i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x12b4);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -179,5 +282,40 @@ static void tmp105_register_nodes(void)
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
+
+ /* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
+ QOSGraphEdgeOptions tmp75_opts = {
+ .extra_device_opts = "id=" TMP75_TEST_ID ",address=0x48"
+ };
+ add_qi2c_address(&tmp75_opts, &(QI2CAddress) { 0x48 });
+
+ qos_node_create_driver("tmp75", i2c_device_create);
+ qos_node_consumes("tmp75", "i2c-bus", &tmp75_opts);
+
+ qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
+ qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
+
+ /* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
+ QOSGraphEdgeOptions tmp175_opts = {
+ .extra_device_opts = "id=" TMP175_TEST_ID ",address=0x4a"
+ };
+ add_qi2c_address(&tmp175_opts, &(QI2CAddress) { 0x4a });
+
+ qos_node_create_driver("tmp175", i2c_device_create);
+ qos_node_consumes("tmp175", "i2c-bus", &tmp175_opts);
+
+ qos_add_test("fault-queue", "tmp175", test_tmp175_fault_queue, NULL);
+
+ /* LM75B: fixed 11-bit conversion and 9-bit set-point registers. */
+ QOSGraphEdgeOptions lm75b_opts = {
+ .extra_device_opts = "id=" LM75B_TEST_ID ",address=0x4c"
+ };
+ add_qi2c_address(&lm75b_opts, &(QI2CAddress) { 0x4c });
+
+ qos_node_create_driver("lm75b", i2c_device_create);
+ qos_node_consumes("lm75b", "i2c-bus", &lm75b_opts);
+
+ qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
+ qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
}
libqos_init(tmp105_register_nodes);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 09/25] tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Exercise the per-variant differences added to the model: the TMP75 and
TMP175 fault-queue depths, the TMP75 alert-clear on thermostat-mode
change, and the LM75B's reserved config bits and fixed set-point and
temperature resolutions.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 138 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index 29a031fb7b..ea6803715a 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -19,9 +19,20 @@
#define TMP105_TEST_ADDR 0x49
#define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
+#define TMP75_TEST_ID "tmp75-test"
+#define TMP75_TEST_PATH "/machine/peripheral/" TMP75_TEST_ID
+
+#define TMP175_TEST_ID "tmp175-test"
+#define TMP175_TEST_PATH "/machine/peripheral/" TMP175_TEST_ID
+
+#define LM75B_TEST_ID "lm75b-test"
+#define LM75B_TEST_PATH "/machine/peripheral/" LM75B_TEST_ID
+
#define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
+#define TMP105_CONFIG_TM (1 << 1) /* interrupt (thermostat) mode */
#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
+#define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -166,6 +177,98 @@ static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
g_assert_false(get_irq(0));
}
+/*
+ * Drive @need consecutive over-limit conversions and check that the ALERT pin
+ * only asserts on the last one. This exercises the fault-queue length.
+ */
+static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
+ const char *path, uint8_t fq_field, int need)
+{
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, path);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(fq_field));
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < need - 1; i++) {
+ qmp_tmp105_set_temperature(id, 85000);
+ g_assert_false(get_irq(0));
+ }
+ qmp_tmp105_set_temperature(id, 85000);
+ g_assert_true(get_irq(0));
+}
+
+/* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
+static void test_tmp75_fault_queue(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ check_fault_queue(obj, TMP75_TEST_ID, TMP75_TEST_PATH, 2, 3);
+}
+
+/* The TMP175 keeps the TMP105 mapping: F1:F0 = 10b means 4 faults. */
+static void test_tmp175_fault_queue(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ check_fault_queue(obj, TMP175_TEST_ID, TMP175_TEST_PATH, 2, 4);
+}
+
+/*
+ * Toggling the thermostat mode (TM) bit clears any active alert on the TMP75.
+ */
+static void test_tmp75_tm_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_TM);
+ g_assert_false(get_irq(0));
+}
+
+/*
+ * The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
+ * Config bits are reserved (read/write as zero) and the temperature register is
+ * always masked to 11 bits regardless of what is written to Config.
+ */
+static void test_lm75b_resolution(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ uint16_t value;
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60);
+ g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x00);
+
+ qmp_tmp105_set_temperature(LM75B_TEST_ID, 20938);
+ value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE);
+ g_assert_cmphex(value, ==, 0x14e0);
+}
+
+/* The LM75B set-point registers store only 9 bits. */
+static void test_lm75b_limits(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4200);
+
+ i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x12b4);
+ g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -179,5 +282,40 @@ static void tmp105_register_nodes(void)
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
+
+ /* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
+ QOSGraphEdgeOptions tmp75_opts = {
+ .extra_device_opts = "id=" TMP75_TEST_ID ",address=0x48"
+ };
+ add_qi2c_address(&tmp75_opts, &(QI2CAddress) { 0x48 });
+
+ qos_node_create_driver("tmp75", i2c_device_create);
+ qos_node_consumes("tmp75", "i2c-bus", &tmp75_opts);
+
+ qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
+ qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
+
+ /* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
+ QOSGraphEdgeOptions tmp175_opts = {
+ .extra_device_opts = "id=" TMP175_TEST_ID ",address=0x4a"
+ };
+ add_qi2c_address(&tmp175_opts, &(QI2CAddress) { 0x4a });
+
+ qos_node_create_driver("tmp175", i2c_device_create);
+ qos_node_consumes("tmp175", "i2c-bus", &tmp175_opts);
+
+ qos_add_test("fault-queue", "tmp175", test_tmp175_fault_queue, NULL);
+
+ /* LM75B: fixed 11-bit conversion and 9-bit set-point registers. */
+ QOSGraphEdgeOptions lm75b_opts = {
+ .extra_device_opts = "id=" LM75B_TEST_ID ",address=0x4c"
+ };
+ add_qi2c_address(&lm75b_opts, &(QI2CAddress) { 0x4c });
+
+ qos_node_create_driver("lm75b", i2c_device_create);
+ qos_node_consumes("lm75b", "i2c-bus", &lm75b_opts);
+
+ qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
+ qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
}
libqos_init(tmp105_register_nodes);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 10/25] tests/qtest: tmp105: cover one-shot and fault-queue write immunity
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Verify that the one-shot (OS) bit triggers a conversion only in shutdown
mode and is ignored in continuous mode, and that
configuration/limit-register writes never advance the fault queue — only
real conversions do.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index ea6803715a..e586a634d8 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -33,6 +33,8 @@
#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
#define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
+#define TMP105_CONFIG_SD (1 << 0) /* shutdown mode */
+#define TMP105_CONFIG_OS (1 << 7) /* one-shot conversion */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -200,6 +202,102 @@ static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
g_assert_true(get_irq(0));
}
+/*
+ * The one-shot (OS) bit starts a conversion only in shutdown mode. In
+ * continuous mode it is ignored, so writing it must not advance the fault
+ * queue; in shutdown each OS write performs one conversion that does.
+ */
+static void test_one_shot(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 8; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_OS);
+ g_assert_false(get_irq(0));
+ }
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD);
+ for (i = 0; i < 2; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
+ TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
+ g_assert_false(get_irq(0));
+ }
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
+ TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * Configuration and limit-register writes are not conversions and must not
+ * advance the fault queue.
+ */
+static void test_fault_queue_ignores_writes(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 8; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x5000);
+ i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x4b00);
+ g_assert_false(get_irq(0));
+ }
+
+ for (i = 0; i < 2; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * Leaving shutdown (SD 1->0) resumes continuous conversion, which must
+ * re-evaluate the current temperature against the limits.
+ */
+static void test_wake_from_shutdown(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1 | TMP105_CONFIG_SD);
+ g_assert_true(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+}
+
/* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
static void test_tmp75_fault_queue(void *obj, void *data,
QGuestAllocator *alloc)
@@ -282,6 +380,10 @@ static void tmp105_register_nodes(void)
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
+ qos_add_test("fault-queue-ignores-writes", "tmp105",
+ test_fault_queue_ignores_writes, NULL);
+ qos_add_test("one-shot", "tmp105", test_one_shot, NULL);
+ qos_add_test("wake-from-shutdown", "tmp105", test_wake_from_shutdown, NULL);
/* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
QOSGraphEdgeOptions tmp75_opts = {
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 10/25] tests/qtest: tmp105: cover one-shot and fault-queue write immunity
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Verify that the one-shot (OS) bit triggers a conversion only in shutdown
mode and is ignored in continuous mode, and that
configuration/limit-register writes never advance the fault queue — only
real conversions do.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index ea6803715a..e586a634d8 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -33,6 +33,8 @@
#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
#define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
+#define TMP105_CONFIG_SD (1 << 0) /* shutdown mode */
+#define TMP105_CONFIG_OS (1 << 7) /* one-shot conversion */
static int qmp_tmp105_get_temperature(const char *id)
{
@@ -200,6 +202,102 @@ static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
g_assert_true(get_irq(0));
}
+/*
+ * The one-shot (OS) bit starts a conversion only in shutdown mode. In
+ * continuous mode it is ignored, so writing it must not advance the fault
+ * queue; in shutdown each OS write performs one conversion that does.
+ */
+static void test_one_shot(void *obj, void *data, QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 8; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_OS);
+ g_assert_false(get_irq(0));
+ }
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD);
+ for (i = 0; i < 2; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
+ TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
+ g_assert_false(get_irq(0));
+ }
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
+ TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * Configuration and limit-register writes are not conversions and must not
+ * advance the fault queue.
+ */
+static void test_fault_queue_ignores_writes(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+
+ for (i = 0; i < 8; i++) {
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
+ i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x5000);
+ i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x4b00);
+ g_assert_false(get_irq(0));
+ }
+
+ for (i = 0; i < 2; i++) {
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_false(get_irq(0));
+ }
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+}
+
+/*
+ * Leaving shutdown (SD 1->0) resumes continuous conversion, which must
+ * re-evaluate the current temperature against the limits.
+ */
+static void test_wake_from_shutdown(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1 | TMP105_CONFIG_SD);
+ g_assert_true(get_irq(0));
+
+ qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
+ g_assert_false(get_irq(0));
+}
+
/* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
static void test_tmp75_fault_queue(void *obj, void *data,
QGuestAllocator *alloc)
@@ -282,6 +380,10 @@ static void tmp105_register_nodes(void)
qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
+ qos_add_test("fault-queue-ignores-writes", "tmp105",
+ test_fault_queue_ignores_writes, NULL);
+ qos_add_test("one-shot", "tmp105", test_one_shot, NULL);
+ qos_add_test("wake-from-shutdown", "tmp105", test_wake_from_shutdown, NULL);
/* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
QOSGraphEdgeOptions tmp75_opts = {
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 11/25] tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Verify that entering shutdown clears the ALERT/OS output in interrupt
mode but leaves it asserted in comparator mode, for both the TMP75 and
the LM75B.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index e586a634d8..504f6b7202 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -335,6 +335,41 @@ static void test_tmp75_tm_clears_alert(void *obj, void *data,
g_assert_false(get_irq(0));
}
+/*
+ * Entering shutdown clears the ALERT in interrupt mode but leaves it asserted
+ * in comparator mode.
+ */
+static void test_tmp75_shutdown_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3) |
+ TMP105_CONFIG_SD);
+ g_assert_false(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_SD);
+ g_assert_true(get_irq(0));
+}
+
/*
* The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
* Config bits are reserved (read/write as zero) and the temperature register is
@@ -367,6 +402,23 @@ static void test_lm75b_limits(void *obj, void *data,
g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
}
+/* The LM75B likewise resets its OS output on shutdown in interrupt mode. */
+static void test_lm75b_shutdown_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, LM75B_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_TM);
+ qmp_tmp105_set_temperature(LM75B_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_SD);
+ g_assert_false(get_irq(0));
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -396,6 +448,8 @@ static void tmp105_register_nodes(void)
qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
+ qos_add_test("shutdown-clears-alert", "tmp75",
+ test_tmp75_shutdown_clears_alert, NULL);
/* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
QOSGraphEdgeOptions tmp175_opts = {
@@ -419,5 +473,7 @@ static void tmp105_register_nodes(void)
qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
+ qos_add_test("shutdown-clears-alert", "lm75b",
+ test_lm75b_shutdown_clears_alert, NULL);
}
libqos_init(tmp105_register_nodes);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 11/25] tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Verify that entering shutdown clears the ALERT/OS output in interrupt
mode but leaves it asserted in comparator mode, for both the TMP75 and
the LM75B.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/qtest/tmp105-test.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
index e586a634d8..504f6b7202 100644
--- a/tests/qtest/tmp105-test.c
+++ b/tests/qtest/tmp105-test.c
@@ -335,6 +335,41 @@ static void test_tmp75_tm_clears_alert(void *obj, void *data,
g_assert_false(get_irq(0));
}
+/*
+ * Entering shutdown clears the ALERT in interrupt mode but leaves it asserted
+ * in comparator mode.
+ */
+static void test_tmp75_shutdown_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+ int i;
+
+ qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3) |
+ TMP105_CONFIG_SD);
+ g_assert_false(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
+ for (i = 0; i < 4; i++) {
+ qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
+ }
+ g_assert_true(get_irq(0));
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_SD);
+ g_assert_true(get_irq(0));
+}
+
/*
* The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
* Config bits are reserved (read/write as zero) and the temperature register is
@@ -367,6 +402,23 @@ static void test_lm75b_limits(void *obj, void *data,
g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
}
+/* The LM75B likewise resets its OS output on shutdown in interrupt mode. */
+static void test_lm75b_shutdown_clears_alert(void *obj, void *data,
+ QGuestAllocator *alloc)
+{
+ QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+ qtest_irq_intercept_out(global_qtest, LM75B_TEST_PATH);
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_TM);
+ qmp_tmp105_set_temperature(LM75B_TEST_ID, 85000);
+ g_assert_true(get_irq(0));
+
+ i2c_set8(i2cdev, TMP105_REG_CONFIG,
+ TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_SD);
+ g_assert_false(get_irq(0));
+}
+
static void tmp105_register_nodes(void)
{
QOSGraphEdgeOptions opts = {
@@ -396,6 +448,8 @@ static void tmp105_register_nodes(void)
qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
+ qos_add_test("shutdown-clears-alert", "tmp75",
+ test_tmp75_shutdown_clears_alert, NULL);
/* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
QOSGraphEdgeOptions tmp175_opts = {
@@ -419,5 +473,7 @@ static void tmp105_register_nodes(void)
qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
+ qos_add_test("shutdown-clears-alert", "lm75b",
+ test_lm75b_shutdown_clears_alert, NULL);
}
libqos_init(tmp105_register_nodes);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 12/25] hw/arm: sanmiguel: add Facebook SanMiguel BMC machine
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add an AST2600-based machine for the Facebook SanMiguel BMC, derived
from the aspeed-bmc-facebook-sanmiguel.dts device tree.
The board wires up three TMP75 temperature sensors across its I2C buses,
alongside IO expanders, FRU EEPROMs, an I2C mux and an RTC, with 2 GiB
RAM, dual FMC flash and RGMII networking.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_sanmiguel.c | 142 ++++++++++++++++++++++++++++++++++++++
hw/arm/meson.build | 1 +
2 files changed, 143 insertions(+)
diff --git a/hw/arm/aspeed_ast2600_sanmiguel.c b/hw/arm/aspeed_ast2600_sanmiguel.c
new file mode 100644
index 0000000000..0f766449ae
--- /dev/null
+++ b/hw/arm/aspeed_ast2600_sanmiguel.c
@@ -0,0 +1,142 @@
+/*
+ * Facebook SanMiguel BMC
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/arm/machines-qom.h"
+#include "hw/arm/aspeed.h"
+#include "hw/arm/aspeed_soc.h"
+#include "hw/i2c/i2c_mux_pca954x.h"
+#include "hw/gpio/pca9552.h"
+#include "hw/gpio/pca9554.h"
+#include "hw/nvram/eeprom_at24c.h"
+#include "hw/sensor/tmp105.h"
+
+/* SanMiguel hardware values */
+#define SANMIGUEL_BMC_HW_STRAP1 0x00002002
+#define SANMIGUEL_BMC_HW_STRAP2 0x00000000
+#define SANMIGUEL_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
+
+#define TYPE_DS1338 "ds1338"
+
+static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
+{
+ /* Reference: aspeed-bmc-facebook-sanmiguel.dts */
+
+ AspeedSoCState *soc = bmc->soc;
+ I2CBus *i2c[16] = {};
+
+ for (unsigned idx = 0; idx < ARRAY_SIZE(i2c); idx++) {
+ i2c[idx] = aspeed_i2c_get_bus(&soc->i2c, idx);
+ }
+
+ /* &i2c0 */
+ /* ssif-bmc@10 — no QEMU model */
+
+ /* &i2c1 — empty */
+
+ /* &i2c2 — HPM0 */
+ /* hpm0_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x20);
+ /* hpm0_ioexp_21: pca9555@21 */
+ i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x21);
+
+ /* &i2c3 — empty */
+ /* &i2c4 — empty */
+
+ /* &i2c5 — SMM */
+ /* smm_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[5], TYPE_PCA9555, 0x20);
+ /* smm_ioexp_21: pca9555@21 */
+ i2c_slave_create_simple(i2c[5], TYPE_PCA9555, 0x21);
+ /* smm_temp: lm75@48 */
+ i2c_slave_create_simple(i2c[5], TYPE_TMP75, 0x48);
+ /* smm_fru: eeprom@50 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[5], 0x50, 16 * KiB);
+ /* rtc@6f — nct3018y (ds1338 stand-in) */
+ i2c_slave_create_simple(i2c[5], TYPE_DS1338, 0x6f);
+
+ /* &i2c6 — HMC */
+ /* hmc_ioexp: tca6408@20 (pca9554 stand-in) */
+ i2c_slave_create_simple(i2c[6], TYPE_PCA9554, 0x20);
+ /* i2c-mux@70 (PCA9546) — 4 channels (imux16-19), all empty */
+ i2c_slave_create_simple(i2c[6], TYPE_PCA9546, 0x70);
+
+ /* &i2c7 — HPM1 */
+ /* hpm1_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[7], TYPE_PCA9555, 0x20);
+ /* hpm1_ioexp_21: pca9555@21 */
+ i2c_slave_create_simple(i2c[7], TYPE_PCA9555, 0x21);
+
+ /* &i2c8 — empty */
+
+ /* &i2c9 — PDB */
+ /* mp5926@10-16, lm5066i@11-17 — no QEMU model (power monitors) */
+ /* pdb_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[9], TYPE_PCA9555, 0x20);
+ /* pdb_temp: lm75@4e */
+ i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4e);
+ /* pdb_fru: eeprom@50 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[9], 0x50, 16 * KiB);
+
+ /* &i2c10 — SCM */
+ /* scm_temp: lm75@48 */
+ i2c_slave_create_simple(i2c[10], TYPE_TMP75, 0x48);
+ /* scm_fru: eeprom@50 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[10], 0x50, 16 * KiB);
+
+ /* &i2c11 — Switch Config */
+ /* sw_config: eeprom@50 (24c64 = 8 KiB) */
+ at24c_eeprom_init(i2c[11], 0x50, 8 * KiB);
+
+ /* &i2c12 — empty */
+
+ /* &i2c13 — SMM extension */
+ /* mctp@10 — no QEMU model */
+ /* smm_ext_ioexp: pca9554@38 */
+ i2c_slave_create_simple(i2c[13], TYPE_PCA9554, 0x38);
+ /* smm_ext_fru: eeprom@55 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[13], 0x55, 16 * KiB);
+
+ /* &i2c14 — FIO */
+ /* fio_ioexp: pca9555@20 */
+ i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x20);
+ /* fio_fru: eeprom@50 (24c64 = 8 KiB) */
+ at24c_eeprom_init(i2c[14], 0x50, 8 * KiB);
+
+ /* &i2c15 — empty */
+}
+
+static void aspeed_machine_sanmiguel_class_init(ObjectClass *oc,
+ const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc);
+
+ mc->desc = "Facebook SanMiguel BMC (Cortex-A7)";
+ amc->soc_name = "ast2600-a3";
+ amc->hw_strap1 = SANMIGUEL_BMC_HW_STRAP1;
+ amc->hw_strap2 = SANMIGUEL_BMC_HW_STRAP2;
+ amc->fmc_model = "mx66l1g45g";
+ amc->spi_model = "mx66l1g45g";
+ amc->num_cs = 2;
+ amc->macs_mask = ASPEED_MAC0_ON;
+ amc->i2c_init = sanmiguel_bmc_i2c_init;
+ mc->default_ram_size = SANMIGUEL_BMC_RAM_SIZE;
+ aspeed_machine_class_init_cpus_defaults(mc);
+}
+
+static const TypeInfo aspeed_ast2600_sanmiguel_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("sanmiguel-bmc"),
+ .parent = TYPE_ASPEED_MACHINE,
+ .class_init = aspeed_machine_sanmiguel_class_init,
+ .interfaces = arm_machine_interfaces,
+ }
+};
+
+DEFINE_TYPES(aspeed_ast2600_sanmiguel_types)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 4233a800be..2d5f4a0fe4 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -65,6 +65,7 @@ arm_common_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
'aspeed_ast2600_fuji.c',
'aspeed_ast2600_gb200nvl.c',
'aspeed_ast2600_rainier.c',
+ 'aspeed_ast2600_sanmiguel.c',
'aspeed_ast10x0.c',
'aspeed_ast10x0_evb.c',
'aspeed_ast1040.c',
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 12/25] hw/arm: sanmiguel: add Facebook SanMiguel BMC machine
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add an AST2600-based machine for the Facebook SanMiguel BMC, derived
from the aspeed-bmc-facebook-sanmiguel.dts device tree.
The board wires up three TMP75 temperature sensors across its I2C buses,
alongside IO expanders, FRU EEPROMs, an I2C mux and an RTC, with 2 GiB
RAM, dual FMC flash and RGMII networking.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_sanmiguel.c | 142 ++++++++++++++++++++++++++++++++++++++
hw/arm/meson.build | 1 +
2 files changed, 143 insertions(+)
diff --git a/hw/arm/aspeed_ast2600_sanmiguel.c b/hw/arm/aspeed_ast2600_sanmiguel.c
new file mode 100644
index 0000000000..0f766449ae
--- /dev/null
+++ b/hw/arm/aspeed_ast2600_sanmiguel.c
@@ -0,0 +1,142 @@
+/*
+ * Facebook SanMiguel BMC
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/arm/machines-qom.h"
+#include "hw/arm/aspeed.h"
+#include "hw/arm/aspeed_soc.h"
+#include "hw/i2c/i2c_mux_pca954x.h"
+#include "hw/gpio/pca9552.h"
+#include "hw/gpio/pca9554.h"
+#include "hw/nvram/eeprom_at24c.h"
+#include "hw/sensor/tmp105.h"
+
+/* SanMiguel hardware values */
+#define SANMIGUEL_BMC_HW_STRAP1 0x00002002
+#define SANMIGUEL_BMC_HW_STRAP2 0x00000000
+#define SANMIGUEL_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
+
+#define TYPE_DS1338 "ds1338"
+
+static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
+{
+ /* Reference: aspeed-bmc-facebook-sanmiguel.dts */
+
+ AspeedSoCState *soc = bmc->soc;
+ I2CBus *i2c[16] = {};
+
+ for (unsigned idx = 0; idx < ARRAY_SIZE(i2c); idx++) {
+ i2c[idx] = aspeed_i2c_get_bus(&soc->i2c, idx);
+ }
+
+ /* &i2c0 */
+ /* ssif-bmc@10 — no QEMU model */
+
+ /* &i2c1 — empty */
+
+ /* &i2c2 — HPM0 */
+ /* hpm0_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x20);
+ /* hpm0_ioexp_21: pca9555@21 */
+ i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x21);
+
+ /* &i2c3 — empty */
+ /* &i2c4 — empty */
+
+ /* &i2c5 — SMM */
+ /* smm_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[5], TYPE_PCA9555, 0x20);
+ /* smm_ioexp_21: pca9555@21 */
+ i2c_slave_create_simple(i2c[5], TYPE_PCA9555, 0x21);
+ /* smm_temp: lm75@48 */
+ i2c_slave_create_simple(i2c[5], TYPE_TMP75, 0x48);
+ /* smm_fru: eeprom@50 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[5], 0x50, 16 * KiB);
+ /* rtc@6f — nct3018y (ds1338 stand-in) */
+ i2c_slave_create_simple(i2c[5], TYPE_DS1338, 0x6f);
+
+ /* &i2c6 — HMC */
+ /* hmc_ioexp: tca6408@20 (pca9554 stand-in) */
+ i2c_slave_create_simple(i2c[6], TYPE_PCA9554, 0x20);
+ /* i2c-mux@70 (PCA9546) — 4 channels (imux16-19), all empty */
+ i2c_slave_create_simple(i2c[6], TYPE_PCA9546, 0x70);
+
+ /* &i2c7 — HPM1 */
+ /* hpm1_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[7], TYPE_PCA9555, 0x20);
+ /* hpm1_ioexp_21: pca9555@21 */
+ i2c_slave_create_simple(i2c[7], TYPE_PCA9555, 0x21);
+
+ /* &i2c8 — empty */
+
+ /* &i2c9 — PDB */
+ /* mp5926@10-16, lm5066i@11-17 — no QEMU model (power monitors) */
+ /* pdb_ioexp_20: pca9555@20 */
+ i2c_slave_create_simple(i2c[9], TYPE_PCA9555, 0x20);
+ /* pdb_temp: lm75@4e */
+ i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4e);
+ /* pdb_fru: eeprom@50 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[9], 0x50, 16 * KiB);
+
+ /* &i2c10 — SCM */
+ /* scm_temp: lm75@48 */
+ i2c_slave_create_simple(i2c[10], TYPE_TMP75, 0x48);
+ /* scm_fru: eeprom@50 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[10], 0x50, 16 * KiB);
+
+ /* &i2c11 — Switch Config */
+ /* sw_config: eeprom@50 (24c64 = 8 KiB) */
+ at24c_eeprom_init(i2c[11], 0x50, 8 * KiB);
+
+ /* &i2c12 — empty */
+
+ /* &i2c13 — SMM extension */
+ /* mctp@10 — no QEMU model */
+ /* smm_ext_ioexp: pca9554@38 */
+ i2c_slave_create_simple(i2c[13], TYPE_PCA9554, 0x38);
+ /* smm_ext_fru: eeprom@55 (24c128 = 16 KiB) */
+ at24c_eeprom_init(i2c[13], 0x55, 16 * KiB);
+
+ /* &i2c14 — FIO */
+ /* fio_ioexp: pca9555@20 */
+ i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x20);
+ /* fio_fru: eeprom@50 (24c64 = 8 KiB) */
+ at24c_eeprom_init(i2c[14], 0x50, 8 * KiB);
+
+ /* &i2c15 — empty */
+}
+
+static void aspeed_machine_sanmiguel_class_init(ObjectClass *oc,
+ const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc);
+
+ mc->desc = "Facebook SanMiguel BMC (Cortex-A7)";
+ amc->soc_name = "ast2600-a3";
+ amc->hw_strap1 = SANMIGUEL_BMC_HW_STRAP1;
+ amc->hw_strap2 = SANMIGUEL_BMC_HW_STRAP2;
+ amc->fmc_model = "mx66l1g45g";
+ amc->spi_model = "mx66l1g45g";
+ amc->num_cs = 2;
+ amc->macs_mask = ASPEED_MAC0_ON;
+ amc->i2c_init = sanmiguel_bmc_i2c_init;
+ mc->default_ram_size = SANMIGUEL_BMC_RAM_SIZE;
+ aspeed_machine_class_init_cpus_defaults(mc);
+}
+
+static const TypeInfo aspeed_ast2600_sanmiguel_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("sanmiguel-bmc"),
+ .parent = TYPE_ASPEED_MACHINE,
+ .class_init = aspeed_machine_sanmiguel_class_init,
+ .interfaces = arm_machine_interfaces,
+ }
+};
+
+DEFINE_TYPES(aspeed_ast2600_sanmiguel_types)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 4233a800be..2d5f4a0fe4 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -65,6 +65,7 @@ arm_common_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
'aspeed_ast2600_fuji.c',
'aspeed_ast2600_gb200nvl.c',
'aspeed_ast2600_rainier.c',
+ 'aspeed_ast2600_sanmiguel.c',
'aspeed_ast10x0.c',
'aspeed_ast10x0_evb.c',
'aspeed_ast1040.c',
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 13/25] hw/arm: sanmiguel: populate EEPROM data
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Populate AT24C FRU EEPROMs with real data from physical device.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_sanmiguel.c | 275 +++++++++++++++++++++++++++++++++++++-
1 file changed, 269 insertions(+), 6 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_sanmiguel.c b/hw/arm/aspeed_ast2600_sanmiguel.c
index 0f766449ae..251dab141f 100644
--- a/hw/arm/aspeed_ast2600_sanmiguel.c
+++ b/hw/arm/aspeed_ast2600_sanmiguel.c
@@ -23,6 +23,267 @@
#define TYPE_DS1338 "ds1338"
+/*
+ * "Front IO" FRU data. Generated with frugen.
+ *
+ * {
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel FIO EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101243"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t fio_eeprom[] = {
+ 0x01, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0xf4, 0x01, 0x09, 0x19, 0x90,
+ 0xd2, 0xf6, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61,
+ 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c, 0x20, 0x46, 0x49, 0x4f,
+ 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a,
+ 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6,
+ 0x44, 0x50, 0x24, 0x51, 0x13, 0xc1, 0x00, 0xf5, 0x01, 0x08, 0x19, 0xc6,
+ 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61,
+ 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41,
+ 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11,
+ 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t fio_eeprom_len = sizeof(fio_eeprom);
+
+/*
+ * "Power Distribution Board" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": ""
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel PDB EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101240"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t pdb_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0b, 0x00, 0x00, 0xf1, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x09, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x50, 0x44, 0x42, 0x20, 0x45, 0x56, 0x54,
+ 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x51,
+ 0x10, 0xc1, 0x00, 0x00, 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e,
+ 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
+ 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6,
+ 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t pdb_eeprom_len = sizeof(pdb_eeprom);
+
+/*
+ * "Secure Control Module" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": "",
+ * "custom": [""]
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel SCM EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101242"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t scm_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0b, 0x00, 0x00, 0xf1, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc0, 0xc1, 0xe6, 0x01, 0x09, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x43, 0x4d, 0x20, 0x45, 0x56, 0x54,
+ 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x51,
+ 0x12, 0xc1, 0x00, 0xf1, 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e,
+ 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
+ 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6,
+ 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t scm_eeprom_len = sizeof(scm_eeprom);
+
+/*
+ * "SMM Carrier" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": ""
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel SMM Carrier EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/11/2026 17:36",
+ * "custom": ["19-101238"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t smm_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0c, 0x00, 0x00, 0xf0, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x0a, 0x19, 0x4c, 0x85, 0xf7, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xe1, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x4d, 0x4d, 0x20, 0x43, 0x61, 0x72,
+ 0x72, 0x69, 0x65, 0x72, 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51, 0x45,
+ 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x01,
+ 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x4d, 0x18, 0xc1, 0x00, 0x84,
+ 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43,
+ 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c,
+ 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5,
+ 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41,
+ 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t smm_eeprom_len = sizeof(smm_eeprom);
+
+/*
+ * "SMM Extender" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": ""
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel SMM Extender EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101239"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t smm_ext_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0c, 0x00, 0x00, 0xf0, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x0a, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xe2, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x4d, 0x4d, 0x20, 0x45, 0x78, 0x74,
+ 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51,
+ 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
+ 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x4d, 0x19, 0xc1, 0x7b,
+ 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43,
+ 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c,
+ 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5,
+ 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41,
+ 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t smm_ext_eeprom_len = sizeof(smm_ext_eeprom);
+
static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
{
/* Reference: aspeed-bmc-facebook-sanmiguel.dts */
@@ -56,7 +317,8 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
/* smm_temp: lm75@48 */
i2c_slave_create_simple(i2c[5], TYPE_TMP75, 0x48);
/* smm_fru: eeprom@50 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[5], 0x50, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[5], 0x50, 16 * KiB, smm_eeprom, smm_eeprom_len);
+
/* rtc@6f — nct3018y (ds1338 stand-in) */
i2c_slave_create_simple(i2c[5], TYPE_DS1338, 0x6f);
@@ -81,16 +343,16 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
/* pdb_temp: lm75@4e */
i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4e);
/* pdb_fru: eeprom@50 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[9], 0x50, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[9], 0x50, 16 * KiB, pdb_eeprom, pdb_eeprom_len);
/* &i2c10 — SCM */
/* scm_temp: lm75@48 */
i2c_slave_create_simple(i2c[10], TYPE_TMP75, 0x48);
/* scm_fru: eeprom@50 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[10], 0x50, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[10], 0x50, 16 * KiB, scm_eeprom, scm_eeprom_len);
/* &i2c11 — Switch Config */
- /* sw_config: eeprom@50 (24c64 = 8 KiB) */
+ /* sw_config: eeprom@50 (24c64 = 8 KiB) — blank (content not modelled) */
at24c_eeprom_init(i2c[11], 0x50, 8 * KiB);
/* &i2c12 — empty */
@@ -100,13 +362,14 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
/* smm_ext_ioexp: pca9554@38 */
i2c_slave_create_simple(i2c[13], TYPE_PCA9554, 0x38);
/* smm_ext_fru: eeprom@55 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[13], 0x55, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[13], 0x55, 16 * KiB, smm_ext_eeprom,
+ smm_ext_eeprom_len);
/* &i2c14 — FIO */
/* fio_ioexp: pca9555@20 */
i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x20);
/* fio_fru: eeprom@50 (24c64 = 8 KiB) */
- at24c_eeprom_init(i2c[14], 0x50, 8 * KiB);
+ at24c_eeprom_init_rom(i2c[14], 0x50, 8 * KiB, fio_eeprom, fio_eeprom_len);
/* &i2c15 — empty */
}
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 13/25] hw/arm: sanmiguel: populate EEPROM data
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Populate AT24C FRU EEPROMs with real data from physical device.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_sanmiguel.c | 275 +++++++++++++++++++++++++++++++++++++-
1 file changed, 269 insertions(+), 6 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_sanmiguel.c b/hw/arm/aspeed_ast2600_sanmiguel.c
index 0f766449ae..251dab141f 100644
--- a/hw/arm/aspeed_ast2600_sanmiguel.c
+++ b/hw/arm/aspeed_ast2600_sanmiguel.c
@@ -23,6 +23,267 @@
#define TYPE_DS1338 "ds1338"
+/*
+ * "Front IO" FRU data. Generated with frugen.
+ *
+ * {
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel FIO EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101243"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t fio_eeprom[] = {
+ 0x01, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0xf4, 0x01, 0x09, 0x19, 0x90,
+ 0xd2, 0xf6, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61,
+ 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c, 0x20, 0x46, 0x49, 0x4f,
+ 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a,
+ 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6,
+ 0x44, 0x50, 0x24, 0x51, 0x13, 0xc1, 0x00, 0xf5, 0x01, 0x08, 0x19, 0xc6,
+ 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61,
+ 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41,
+ 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11,
+ 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t fio_eeprom_len = sizeof(fio_eeprom);
+
+/*
+ * "Power Distribution Board" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": ""
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel PDB EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101240"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t pdb_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0b, 0x00, 0x00, 0xf1, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x09, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x50, 0x44, 0x42, 0x20, 0x45, 0x56, 0x54,
+ 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x51,
+ 0x10, 0xc1, 0x00, 0x00, 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e,
+ 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
+ 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6,
+ 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t pdb_eeprom_len = sizeof(pdb_eeprom);
+
+/*
+ * "Secure Control Module" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": "",
+ * "custom": [""]
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel SCM EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101242"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t scm_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0b, 0x00, 0x00, 0xf1, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc0, 0xc1, 0xe6, 0x01, 0x09, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x43, 0x4d, 0x20, 0x45, 0x56, 0x54,
+ 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10,
+ 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x51,
+ 0x12, 0xc1, 0x00, 0xf1, 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e,
+ 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
+ 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6,
+ 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t scm_eeprom_len = sizeof(scm_eeprom);
+
+/*
+ * "SMM Carrier" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": ""
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel SMM Carrier EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/11/2026 17:36",
+ * "custom": ["19-101238"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t smm_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0c, 0x00, 0x00, 0xf0, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x0a, 0x19, 0x4c, 0x85, 0xf7, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xe1, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x4d, 0x4d, 0x20, 0x43, 0x61, 0x72,
+ 0x72, 0x69, 0x65, 0x72, 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51, 0x45,
+ 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x01,
+ 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x4d, 0x18, 0xc1, 0x00, 0x84,
+ 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43,
+ 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c,
+ 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5,
+ 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41,
+ 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t smm_eeprom_len = sizeof(smm_eeprom);
+
+/*
+ * "SMM Extender" FRU data. Generated with frugen.
+ *
+ * {
+ * "chassis": {
+ * "type": 23,
+ * "pn": "",
+ * "serial": ""
+ * },
+ * "board": {
+ * "mfg": "Quanta",
+ * "pname": "San Miguel SMM Extender EVT (QEMU)",
+ * "pn": "00000000000",
+ * "serial": "0000000000000",
+ * "date": "03/10/2026 00:00",
+ * "custom": ["19-101239"]
+ * },
+ * "product": {
+ * "mfg": "Quanta",
+ * "pname": "CI-San Miguel",
+ * "pn": "10000000001",
+ * "ver": "EVT",
+ * "serial": "10000000000000001",
+ * "atag": "QEMU"
+ * }
+ * }
+ */
+static const uint8_t smm_ext_eeprom[] = {
+ 0x01, 0x00, 0x01, 0x02, 0x0c, 0x00, 0x00, 0xf0, 0x01, 0x01, 0x17, 0xc0,
+ 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x0a, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
+ 0x75, 0x61, 0x6e, 0x74, 0x61, 0xe2, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
+ 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x4d, 0x4d, 0x20, 0x45, 0x78, 0x74,
+ 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51,
+ 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
+ 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
+ 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x4d, 0x19, 0xc1, 0x7b,
+ 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43,
+ 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c,
+ 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5,
+ 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41,
+ 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff
+};
+static const size_t smm_ext_eeprom_len = sizeof(smm_ext_eeprom);
+
static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
{
/* Reference: aspeed-bmc-facebook-sanmiguel.dts */
@@ -56,7 +317,8 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
/* smm_temp: lm75@48 */
i2c_slave_create_simple(i2c[5], TYPE_TMP75, 0x48);
/* smm_fru: eeprom@50 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[5], 0x50, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[5], 0x50, 16 * KiB, smm_eeprom, smm_eeprom_len);
+
/* rtc@6f — nct3018y (ds1338 stand-in) */
i2c_slave_create_simple(i2c[5], TYPE_DS1338, 0x6f);
@@ -81,16 +343,16 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
/* pdb_temp: lm75@4e */
i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4e);
/* pdb_fru: eeprom@50 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[9], 0x50, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[9], 0x50, 16 * KiB, pdb_eeprom, pdb_eeprom_len);
/* &i2c10 — SCM */
/* scm_temp: lm75@48 */
i2c_slave_create_simple(i2c[10], TYPE_TMP75, 0x48);
/* scm_fru: eeprom@50 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[10], 0x50, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[10], 0x50, 16 * KiB, scm_eeprom, scm_eeprom_len);
/* &i2c11 — Switch Config */
- /* sw_config: eeprom@50 (24c64 = 8 KiB) */
+ /* sw_config: eeprom@50 (24c64 = 8 KiB) — blank (content not modelled) */
at24c_eeprom_init(i2c[11], 0x50, 8 * KiB);
/* &i2c12 — empty */
@@ -100,13 +362,14 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
/* smm_ext_ioexp: pca9554@38 */
i2c_slave_create_simple(i2c[13], TYPE_PCA9554, 0x38);
/* smm_ext_fru: eeprom@55 (24c128 = 16 KiB) */
- at24c_eeprom_init(i2c[13], 0x55, 16 * KiB);
+ at24c_eeprom_init_rom(i2c[13], 0x55, 16 * KiB, smm_ext_eeprom,
+ smm_ext_eeprom_len);
/* &i2c14 — FIO */
/* fio_ioexp: pca9555@20 */
i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x20);
/* fio_fru: eeprom@50 (24c64 = 8 KiB) */
- at24c_eeprom_init(i2c[14], 0x50, 8 * KiB);
+ at24c_eeprom_init_rom(i2c[14], 0x50, 8 * KiB, fio_eeprom, fio_eeprom_len);
/* &i2c15 — empty */
}
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 14/25] hw/arm: catalina: use the real TMP75 model
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The Catalina BMC instantiates several ti,tmp75 temperature sensors. Now
that hw/sensor/tmp105.h provides a proper TMP75 model, drop the local
TYPE_TMP75 alias (and its guard) so the board instantiates the accurate
device rather than a plain TMP105.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_catalina.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
index 33e75338ef..928e140b21 100644
--- a/hw/arm/aspeed_ast2600_catalina.c
+++ b/hw/arm/aspeed_ast2600_catalina.c
@@ -22,12 +22,6 @@
#define CATALINA_BMC_HW_STRAP2 0x00000800
#define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
-/*
- * Guard the alias against the TYPE_TMP75 that hw/sensor/tmp105.h now
- * defines.
- */
-#undef TYPE_TMP75
-#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP421 "tmp421"
#define TYPE_DS1338 "ds1338"
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 14/25] hw/arm: catalina: use the real TMP75 model
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The Catalina BMC instantiates several ti,tmp75 temperature sensors. Now
that hw/sensor/tmp105.h provides a proper TMP75 model, drop the local
TYPE_TMP75 alias (and its guard) so the board instantiates the accurate
device rather than a plain TMP105.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_catalina.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
index 33e75338ef..928e140b21 100644
--- a/hw/arm/aspeed_ast2600_catalina.c
+++ b/hw/arm/aspeed_ast2600_catalina.c
@@ -22,12 +22,6 @@
#define CATALINA_BMC_HW_STRAP2 0x00000800
#define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
-/*
- * Guard the alias against the TYPE_TMP75 that hw/sensor/tmp105.h now
- * defines.
- */
-#undef TYPE_TMP75
-#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP421 "tmp421"
#define TYPE_DS1338 "ds1338"
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 15/25] hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The Fuji BMC instantiates many ti,tmp75 sensors and several NXP LM75B
sensors; the original board code approximated both with a plain TMP105
via local TYPE_TMP75 and TYPE_LM75 aliases (the latter mislabelling the
LM75B parts as LM75).
Now that hw/sensor/tmp105.h provides proper TMP75 and LM75B models, drop
the local aliases (and their guards) and instantiate the accurate
devices.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_fuji.c | 22 +++++++---------------
1 file changed, 7 insertions(+), 15 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_fuji.c b/hw/arm/aspeed_ast2600_fuji.c
index 6dc3535f0c..fe0c294e14 100644
--- a/hw/arm/aspeed_ast2600_fuji.c
+++ b/hw/arm/aspeed_ast2600_fuji.c
@@ -15,14 +15,6 @@
#include "hw/sensor/tmp105.h"
#include "hw/nvram/eeprom_at24c.h"
-/*
- * Guard the aliases against the TYPE_TMP75 that hw/sensor/tmp105.h now defines;
- * TYPE_LM75 is guarded too for consistency (no LM75 model exists yet).
- */
-#undef TYPE_LM75
-#define TYPE_LM75 TYPE_TMP105
-#undef TYPE_TMP75
-#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP422 "tmp422"
/* Fuji hardware value */
@@ -63,8 +55,8 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
get_pca9548_channels(i2c[40 + i], 0x76, &i2c[80 + i * 8]);
}
- i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4c);
- i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4d);
+ i2c_slave_create_simple(i2c[17], TYPE_LM75B, 0x4c);
+ i2c_slave_create_simple(i2c[17], TYPE_LM75B, 0x4d);
/*
* EEPROM 24c64 size is 64Kbits or 8 Kbytes
@@ -74,15 +66,15 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
at24c_eeprom_init(i2c[20], 0x50, 256);
at24c_eeprom_init(i2c[22], 0x52, 256);
- i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x48);
- i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x49);
- i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x4a);
+ i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x48);
+ i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x49);
+ i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x4a);
i2c_slave_create_simple(i2c[3], TYPE_TMP422, 0x4c);
at24c_eeprom_init(i2c[8], 0x51, 8 * KiB);
- i2c_slave_create_simple(i2c[8], TYPE_LM75, 0x4a);
+ i2c_slave_create_simple(i2c[8], TYPE_LM75B, 0x4a);
- i2c_slave_create_simple(i2c[50], TYPE_LM75, 0x4c);
+ i2c_slave_create_simple(i2c[50], TYPE_LM75B, 0x4c);
at24c_eeprom_init(i2c[50], 0x52, 8 * KiB);
i2c_slave_create_simple(i2c[51], TYPE_TMP75, 0x48);
i2c_slave_create_simple(i2c[52], TYPE_TMP75, 0x49);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 15/25] hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
The Fuji BMC instantiates many ti,tmp75 sensors and several NXP LM75B
sensors; the original board code approximated both with a plain TMP105
via local TYPE_TMP75 and TYPE_LM75 aliases (the latter mislabelling the
LM75B parts as LM75).
Now that hw/sensor/tmp105.h provides proper TMP75 and LM75B models, drop
the local aliases (and their guards) and instantiate the accurate
devices.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_fuji.c | 22 +++++++---------------
1 file changed, 7 insertions(+), 15 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_fuji.c b/hw/arm/aspeed_ast2600_fuji.c
index 6dc3535f0c..fe0c294e14 100644
--- a/hw/arm/aspeed_ast2600_fuji.c
+++ b/hw/arm/aspeed_ast2600_fuji.c
@@ -15,14 +15,6 @@
#include "hw/sensor/tmp105.h"
#include "hw/nvram/eeprom_at24c.h"
-/*
- * Guard the aliases against the TYPE_TMP75 that hw/sensor/tmp105.h now defines;
- * TYPE_LM75 is guarded too for consistency (no LM75 model exists yet).
- */
-#undef TYPE_LM75
-#define TYPE_LM75 TYPE_TMP105
-#undef TYPE_TMP75
-#define TYPE_TMP75 TYPE_TMP105
#define TYPE_TMP422 "tmp422"
/* Fuji hardware value */
@@ -63,8 +55,8 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
get_pca9548_channels(i2c[40 + i], 0x76, &i2c[80 + i * 8]);
}
- i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4c);
- i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4d);
+ i2c_slave_create_simple(i2c[17], TYPE_LM75B, 0x4c);
+ i2c_slave_create_simple(i2c[17], TYPE_LM75B, 0x4d);
/*
* EEPROM 24c64 size is 64Kbits or 8 Kbytes
@@ -74,15 +66,15 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
at24c_eeprom_init(i2c[20], 0x50, 256);
at24c_eeprom_init(i2c[22], 0x52, 256);
- i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x48);
- i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x49);
- i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x4a);
+ i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x48);
+ i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x49);
+ i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x4a);
i2c_slave_create_simple(i2c[3], TYPE_TMP422, 0x4c);
at24c_eeprom_init(i2c[8], 0x51, 8 * KiB);
- i2c_slave_create_simple(i2c[8], TYPE_LM75, 0x4a);
+ i2c_slave_create_simple(i2c[8], TYPE_LM75B, 0x4a);
- i2c_slave_create_simple(i2c[50], TYPE_LM75, 0x4c);
+ i2c_slave_create_simple(i2c[50], TYPE_LM75B, 0x4c);
at24c_eeprom_init(i2c[50], 0x52, 8 * KiB);
i2c_slave_create_simple(i2c[51], TYPE_TMP75, 0x48);
i2c_slave_create_simple(i2c[52], TYPE_TMP75, 0x49);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 16/25] tests/functional: aspeed: optionally check the device tree model on boot
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add an optional 'dt_model' argument to do_test_arm_aspeed_openbmc().
When
provided, the helper also waits for the kernel's "Machine model: ..."
line,
letting a test confirm the booted image actually matches the expected
board
device tree rather than only the SoC revision.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/aspeed.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py
index 076da1036c..08af6b4839 100644
--- a/tests/functional/aspeed.py
+++ b/tests/functional/aspeed.py
@@ -8,7 +8,8 @@
class AspeedTest(LinuxKernelTest):
def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
- cpu_id='0x0', soc='AST2500 rev A1'):
+ cpu_id='0x0', soc='AST2500 rev A1',
+ dt_model=None):
self.set_machine(machine)
self.vm.set_console()
self.vm.add_args('-drive', f'file={image},if=mtd,format=raw',
@@ -19,6 +20,8 @@ def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
self.wait_for_console_pattern('## Loading kernel from FIT Image')
self.wait_for_console_pattern('Starting kernel ...')
self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu_id}')
+ if dt_model:
+ self.wait_for_console_pattern(f'Machine model: {dt_model}')
self.wait_for_console_pattern(f'ASPEED {soc}')
self.wait_for_console_pattern('/init as init process')
self.wait_for_boot_complete()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 16/25] tests/functional: aspeed: optionally check the device tree model on boot
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add an optional 'dt_model' argument to do_test_arm_aspeed_openbmc().
When
provided, the helper also waits for the kernel's "Machine model: ..."
line,
letting a test confirm the booted image actually matches the expected
board
device tree rather than only the SoC revision.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/aspeed.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py
index 076da1036c..08af6b4839 100644
--- a/tests/functional/aspeed.py
+++ b/tests/functional/aspeed.py
@@ -8,7 +8,8 @@
class AspeedTest(LinuxKernelTest):
def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
- cpu_id='0x0', soc='AST2500 rev A1'):
+ cpu_id='0x0', soc='AST2500 rev A1',
+ dt_model=None):
self.set_machine(machine)
self.vm.set_console()
self.vm.add_args('-drive', f'file={image},if=mtd,format=raw',
@@ -19,6 +20,8 @@ def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
self.wait_for_console_pattern('## Loading kernel from FIT Image')
self.wait_for_console_pattern('Starting kernel ...')
self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu_id}')
+ if dt_model:
+ self.wait_for_console_pattern(f'Machine model: {dt_model}')
self.wait_for_console_pattern(f'ASPEED {soc}')
self.wait_for_console_pattern('/init as init process')
self.wait_for_boot_complete()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 17/25] tests/functional: give the set_machine probe VM the test workdir
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
set_machine() launches a throwaway QEMUMachine to query the available
machine types, but constructs it without base_temp_dir, so it falls back
to the hardcoded "/var/tmp" default instead of the writable per-test
workdir that the actual test VM already uses. Pass base_temp_dir=
self.workdir for consistency; this also fixes VM launch on hosts where
/var/tmp is not writable.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/qemu_test/testcase.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py
index e4179d165c..ed861709e1 100644
--- a/tests/functional/qemu_test/testcase.py
+++ b/tests/functional/qemu_test/testcase.py
@@ -317,7 +317,7 @@ def set_machine(self, machinename):
cls = type(self)
if not hasattr(cls, "_machines"):
- tmp_vm = QEMUMachine(self.qemu_bin)
+ tmp_vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir)
tmp_vm.set_machine('none')
try:
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 17/25] tests/functional: give the set_machine probe VM the test workdir
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
set_machine() launches a throwaway QEMUMachine to query the available
machine types, but constructs it without base_temp_dir, so it falls back
to the hardcoded "/var/tmp" default instead of the writable per-test
workdir that the actual test VM already uses. Pass base_temp_dir=
self.workdir for consistency; this also fixes VM launch on hosts where
/var/tmp is not writable.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/qemu_test/testcase.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py
index e4179d165c..ed861709e1 100644
--- a/tests/functional/qemu_test/testcase.py
+++ b/tests/functional/qemu_test/testcase.py
@@ -317,7 +317,7 @@ def set_machine(self, machinename):
cls = type(self)
if not hasattr(cls, "_machines"):
- tmp_vm = QEMUMachine(self.qemu_bin)
+ tmp_vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir)
tmp_vm.set_machine('none')
try:
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add DeviceLocator, a helper that addresses a device by its position on
the bus -- bus, address, type or index -- rather than by a QOM path. A
QOM path locates a device under the object that owns it, by enumeration
order, and so says nothing about where the device actually sits in the
bus topology. From a QOM anchor DeviceLocator instead walks the bus and
device hierarchy, alternating bus and device hops, to reach the target.
It relies only on standard QOM queries over QMP, needing no custom
commands or changes to QEMU.
QEMU still supports Python 3.9, but 3.9 reached end of life on
2025-10-31. This new, optional helper therefore adopts the clearer
type-hint syntax introduced in Python 3.10 (such as "X | Y" unions)
rather than the more verbose 3.9 equivalents. Its import is guarded so
the module still loads on 3.9; tests that use the resolver are skipped
there.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/qemu_test/locator.py | 586 ++++++++++++++++++++++++++++++++++
1 file changed, 586 insertions(+)
diff --git a/tests/functional/qemu_test/locator.py b/tests/functional/qemu_test/locator.py
new file mode 100644
index 0000000000..bc8ba111a4
--- /dev/null
+++ b/tests/functional/qemu_test/locator.py
@@ -0,0 +1,586 @@
+# Device locator: resolve a device from a QOM anchor plus a typed
+# bus/device traversal, entirely over QMP.
+#
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import re
+import sys
+from collections.abc import Callable, Iterable
+from typing import Any, NamedTuple
+
+from qemu.qmp import ExecuteError
+
+
+# The type hints below use PEP 604 unions ("X | None") and PEP 585 built-in
+# generics ("list[...]"), which require Python 3.10+.
+# Fail with a clear message rather than a cryptic TypeError from the
+# annotations when run on an older interpreter.
+if sys.version_info < (3, 10):
+ version = ".".join(map(str, sys.version_info[:3]))
+ raise RuntimeError(
+ f"qemu_test.locator requires Python 3.10+; "
+ f"interpreter is Python {version}")
+
+
+BusDomain = tuple[str, str | None]
+"""A bus domain: ``(bus_typename, address_property_or_None)``."""
+
+
+class LocatorError(Exception):
+ """Raised when a locator cannot be resolved."""
+
+
+class DeviceSelectors(NamedTuple):
+ """Parsed selectors of a single device hop ("type@addr#index=id")."""
+ typename: str | None = None
+ """QOM type name."""
+ addr: int | None = None
+ """Bus address."""
+ on_bus_index: int | None = None
+ """On-bus (BusChild) index."""
+ ident: str | None = None
+ """Device id."""
+
+
+class DeviceLocator:
+ """
+ Find a device by traversing the bus and device hierarchy, addressing
+ it by position -- bus, address, type or index -- rather than by a
+ fragile canonical QOM path.
+
+ A locator string is a QOM anchor followed by a typed traversal that
+ alternates bus and device hops. Resolution runs against a live guest
+ over QMP, using only qom-list / qom-get / qom-list-types, so it works
+ against any QMP-capable QEMU without extra commands.
+
+ Grammar::
+
+ <anchor> [ '::' <hop> ( '~' <hop> )* ]
+
+ '::' leaves the QOM anchor and enters typed traversal; '~' crosses
+ the device<->bus boundary (either direction). Because bus<->bus is
+ impossible and the only object->object hop is the leading controller
+ (which follows '::' directly, never '~'), every '~' marks a
+ device<->bus crossing.
+
+ <anchor> is a QOM path: absolute ("/machine/soc"), partial ("soc",
+ matched as a component suffix), or empty for /machine.
+
+ Hops alternate between object context and bus context. Starting from the
+ anchor a hop is one of:
+
+ object hop <qom-type> [ '[' index ']' ]
+ Descend to a direct (child<>) child whose concrete QOM type is-a
+ <qom-type> (e.g. "aspeed.i2c-ast2600"), picked by [index] when
+ several match. This names a controller by its real QOM type, with
+ no keyword table involved.
+
+ bus hop <bus> [ '[' channel ']' ]
+ Select a bus owned by the current object. <bus> is a domain keyword
+ ("i2c", "spi") or a bus QOM type ("i2c-bus", "SSI"); [channel] picks
+ among the buses of that type, ordered by their trailing number. Only
+ bus domains are tabulated, and solely to map a keyword to its bus
+ type and to the device address property used by '@' below.
+
+ device hop [type] [ '@' addr ] [ '#' index ] [ '=' id ]
+ Match a device on the current bus by QOM type, bus address (the I2C
+ address or SPI chip-select), on-bus (BusChild) index and/or id.
+
+ Example: "/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b".
+ """
+
+ DEFAULT_BUS_DOMAINS: dict[str, BusDomain] = {
+ "i2c": ("i2c-bus", "address"),
+ "spi": ("SSI", "cs"),
+ }
+ """Keyword -> ``(bus_typename, address_property_or_None)``."""
+
+ BUS_BASE = "bus"
+ """The QOM base type every bus derives from."""
+
+ def __init__(
+ self,
+ qmp: Callable[..., Any],
+ bus_domains: Iterable[tuple[str, str, str | None]] | None = None,
+ ) -> None:
+ """
+ :param qmp: a callable ``qmp(command, **args)`` returning the QMP
+ payload and raising on error -- e.g. ``QEMUMachine.cmd``.
+ :param bus_domains: optional iterable of ``(keyword, bus_typename,
+ address_property)`` tuples registered up front; the
+ "i2c" and "spi" bus domains are always registered.
+ """
+ self._qmp = qmp
+ self._bus_domains: dict[str, BusDomain] = dict(
+ self.DEFAULT_BUS_DOMAINS)
+ self._qom_subtypes_cache: dict[str, set[str]] = {}
+ for keyword, bus_typename, address_property in bus_domains or ():
+ self.register_bus_domain(keyword, bus_typename, address_property)
+
+ def register_bus_domain(self, keyword: str, bus_typename: str,
+ address_property: str | None = None) -> None:
+ """
+ Register an additional bus domain.
+
+ :param keyword: domain keyword used in a bus hop (e.g. "i2c").
+ :param bus_typename: the QOM bus type backing the domain.
+ :param address_property: device property holding the bus address, or
+ None when the domain has no address selector.
+ """
+ self._bus_domains[keyword] = (bus_typename, address_property)
+
+ # -- QOM access over QMP ---------------------------------------------
+
+ def _qom_list(self, path: str) -> list[dict[str, Any]]:
+ """
+ List the QOM child/link properties of an object.
+
+ :param path: canonical QOM path to enumerate.
+ :return: the ``qom-list`` payload, one dict per property.
+ """
+ return self._qmp("qom-list", path=path)
+
+ def _qom_get(self, path: str, prop: str) -> Any:
+ """
+ Read one QOM property.
+
+ :param path: canonical QOM path of the object.
+ :param prop: property name to read.
+ :return: the property value.
+ """
+ return self._qmp("qom-get", path=path, property=prop)
+
+ def _qom_type_of(self, path: str) -> str:
+ """
+ Return an object's concrete QOM type name.
+
+ :param path: canonical QOM path of the object.
+ :return: the QOM type name.
+ """
+ return self._qom_get(path, "type")
+
+ def _qom_subtypes(self, typename: str) -> set[str]:
+ """
+ Return the set of every type that is-a @typename (itself included).
+
+ :param typename: the base or interface type name.
+ :return: the implementing type names, cached per base.
+ """
+ cached = self._qom_subtypes_cache.get(typename)
+ if cached is None:
+ listed = self._qmp("qom-list-types", implements=typename,
+ abstract=True)
+ cached = {entry["name"] for entry in listed}
+ cached.add(typename)
+ self._qom_subtypes_cache[typename] = cached
+ return cached
+
+ def _qom_is_a(self, concrete: str, base: str) -> bool:
+ """
+ Report whether one QOM type is-a another.
+
+ :param concrete: a concrete QOM type name.
+ :param base: a base or interface type name.
+ :return: True if @concrete is or derives from @base.
+ """
+ return concrete == base or concrete in self._qom_subtypes(base)
+
+ # -- QOM tree helpers ------------------------------------------------
+
+ @staticmethod
+ def _inner_type(proptype: str) -> tuple[str | None, str | None]:
+ """
+ Split a "child<X>"/"link<X>" property type.
+
+ :param proptype: a QOM property type string.
+ :return: ``(kind, inner)`` with kind "child" or "link", or
+ ``(None, None)`` for any other property.
+ """
+ match = re.match(r"(child|link)<(.+)>$", proptype)
+ if match:
+ return match.group(1), match.group(2)
+ return None, None
+
+ @staticmethod
+ def _order_key(name: str) -> tuple[int, str]:
+ """
+ Ordering key for a QOM path component: the last run of decimal digits
+ in the name ("aspeed.i2c.10" -> 10), or -1 when it carries no number.
+
+ :param name: a canonical path component.
+ :return: ``(index, name)``, with the name as tie-breaker.
+ """
+ runs = re.findall(r"\d+", name)
+ return (int(runs[-1]) if runs else -1, name)
+
+ def _canonical_children(self, path: str) -> list[tuple[str, str, str]]:
+ """
+ Enumerate the canonical (child<...>) children of an object.
+
+ :param path: canonical QOM path to enumerate.
+ :return: ``(name, property_type, child_path)`` per canonical child.
+ """
+ children: list[tuple[str, str, str]] = []
+ for prop in self._qom_list(path):
+ kind, inner = self._inner_type(prop["type"])
+ if kind == "child" and inner is not None:
+ children.append((prop["name"], inner,
+ path + "/" + prop["name"]))
+ return children
+
+ def _collect_buses(self, root: str,
+ bus_typename: str) -> list[tuple[str, str]]:
+ """
+ Collect the domain buses reachable under @root, pruned at each found
+ bus so buses nested behind a downstream device (e.g. an I2C mux) are
+ not gathered.
+
+ :param root: QOM path to search from.
+ :param bus_typename: the domain's QOM bus type.
+ :return: ``(name, path)`` per bus, sorted by component order.
+ """
+ found: list[tuple[str, str]] = []
+ for name, concrete, child_path in self._canonical_children(root):
+ if self._qom_is_a(concrete, bus_typename):
+ found.append((name, child_path))
+ else:
+ found.extend(self._collect_buses(child_path, bus_typename))
+ found.sort(key=lambda item: self._order_key(item[0]))
+ return found
+
+ def _bus_children(self, bus: str) -> list[tuple[int, str]]:
+ """
+ Enumerate the devices attached to a bus.
+
+ :param bus: canonical QOM path of the bus.
+ :return: ``(on_bus_index, device_path)`` per attached device, sorted
+ by index.
+ """
+ kids: list[tuple[int, str]] = []
+ for prop in self._qom_list(bus):
+ match = re.match(r"child\[(\d+)\]$", prop["name"])
+ kind, _ = self._inner_type(prop["type"])
+ if match and kind == "link":
+ kids.append((int(match.group(1)),
+ self._qom_get(bus, prop["name"])))
+ kids.sort(key=lambda item: item[0])
+ return kids
+
+ # -- anchor / plain-path resolution ---------------------------------
+
+ def _exists(self, path: str) -> bool:
+ """
+ Report whether a QOM path resolves to an existing object.
+
+ :param path: an absolute QOM path.
+ :return: True if the object exists.
+ """
+ try:
+ self._qom_list(path)
+ return True
+ except ExecuteError:
+ return False
+
+ def _all_paths(self, root: str = "/machine") -> list[str]:
+ """
+ Collect every canonical object path under @root, @root included.
+
+ :param root: QOM path to search from.
+ :return: the list of canonical paths.
+ """
+ paths: list[str] = [root]
+ for _name, _concrete, child_path in self._canonical_children(root):
+ paths.extend(self._all_paths(child_path))
+ return paths
+
+ def _resolve_anchor(self, anchor: str) -> str:
+ """
+ Resolve a QOM anchor: empty -> /machine, an absolute path used
+ verbatim, otherwise a component-suffix search over the whole tree.
+
+ :param anchor: the anchor part of a locator.
+ :return: the resolved canonical QOM path.
+ :raises LocatorError: if the anchor is missing or ambiguous.
+ """
+ if anchor == "":
+ return "/machine"
+ if anchor.startswith("/"):
+ if not self._exists(anchor):
+ raise LocatorError(f"anchor '{anchor}' not found")
+ return anchor
+
+ wanted = anchor.split("/")
+ matches = [path for path in self._all_paths()
+ if path.split("/")[-len(wanted):] == wanted]
+ if not matches:
+ raise LocatorError(f"anchor '{anchor}' not found")
+ if len(matches) > 1:
+ raise LocatorError(f"anchor '{anchor}' is ambiguous "
+ f"({len(matches)} found)")
+ return matches[0]
+
+ # -- hop parsing ----------------------------------------------------
+
+ @staticmethod
+ def _parse_indexed(token: str) -> tuple[str, int | None]:
+ """
+ Parse an object or bus hop ("name" or "name[index]").
+
+ :param token: the hop token.
+ :return: ``(name, index_or_None)``.
+ :raises LocatorError: on a malformed hop.
+ """
+ match = re.match(r"([^\[]+)(?:\[(\d+)\])?$", token)
+ if not match:
+ raise LocatorError(f"malformed hop '{token}'")
+ index = int(match.group(2)) if match.group(2) is not None else None
+ return match.group(1), index
+
+ @staticmethod
+ def _parse_device(token: str) -> DeviceSelectors:
+ """
+ Parse a device hop into its selectors.
+
+ :param token: the device-hop token ("type@addr#index=id").
+ :return: the parsed selectors.
+ :raises LocatorError: on a malformed or empty hop.
+ """
+ typename: str | None = None
+ addr: int | None = None
+ index: int | None = None
+ ident: str | None = None
+ pos = 0
+ length = len(token)
+ # A leading bare run (no sigil) is the QOM type selector.
+ if pos < length and token[pos] not in "@#=":
+ start = pos
+ while pos < length and token[pos] not in "@#=":
+ pos += 1
+ typename = token[start:pos]
+ seen = typename is not None
+ seen_sigils: set[str] = set()
+ while pos < length:
+ sig = token[pos]
+ pos += 1
+ start = pos
+ while pos < length and token[pos] not in "@#=":
+ pos += 1
+ value = token[start:pos]
+ if sig in seen_sigils:
+ raise LocatorError(
+ f"duplicate '{sig}' selector in hop '{token}'")
+ seen_sigils.add(sig)
+ if value == "":
+ raise LocatorError(
+ f"empty '{sig}' selector in hop '{token}'")
+ if sig == "@":
+ try:
+ addr = int(value, 0)
+ except ValueError:
+ raise LocatorError(f"malformed address '@{value}'")
+ elif sig == "#":
+ try:
+ index = int(value, 10)
+ except ValueError:
+ raise LocatorError(f"malformed on-bus index '#{value}'")
+ elif sig == "=":
+ ident = value
+ seen = True
+ if not seen:
+ raise LocatorError("empty device hop")
+ return DeviceSelectors(typename, addr, index, ident)
+
+ def _is_bus_token(self, token: str) -> bool:
+ """
+ Report whether a hop names a bus (a domain keyword or a bus QOM type)
+ rather than an intermediate object.
+
+ :param token: an object-or-bus hop token.
+ :return: True for a bus hop.
+ """
+ name = token.split("[", 1)[0]
+ return name in self._bus_domains or self._qom_is_a(name, self.BUS_BASE)
+
+ @staticmethod
+ def _pick(items: list[str], index: int | None, what: str) -> str:
+ """
+ Select one entry from an ordered match list.
+
+ :param items: the ordered candidate paths.
+ :param index: the requested index, or None to require a single match.
+ :param what: a description used in error messages.
+ :return: the selected path.
+ :raises LocatorError: if nothing matches, the match is ambiguous, or
+ the index is out of range.
+ """
+ if index is None:
+ if not items:
+ raise LocatorError(f"no {what} found")
+ if len(items) > 1:
+ raise LocatorError(f"{what} is ambiguous "
+ f"({len(items)} found); add an index")
+ return items[0]
+ if index >= len(items):
+ raise LocatorError(f"{what} index {index} out of range "
+ f"({len(items)} found)")
+ return items[index]
+
+ # -- resolution -----------------------------------------------------
+
+ def resolve(self, locator: str, typename: str | None = None) -> str:
+ """
+ Resolve a locator to a canonical QOM path (usable with qom-get).
+
+ :param locator: the locator string (see the class grammar).
+ :param typename: if set, the leaf is verified to be-a this QOM type.
+ :return: the resolved canonical QOM path.
+ :raises LocatorError: if the locator does not resolve to exactly one
+ object (not found, ambiguous, unknown bus or
+ malformed), or the leaf fails the type check.
+ """
+ sep = locator.find("::")
+ if sep < 0:
+ return self._apply_type(self._resolve_anchor(locator), typename)
+
+ cur = self._resolve_anchor(locator[:sep])
+ bus: str | None = None
+ bus_concrete: str | None = None
+ dom: BusDomain | None = None
+
+ for token in locator[sep + 2:].split("~"):
+ if token == "":
+ raise LocatorError(f"empty hop in locator {locator!r}")
+ if bus is not None:
+ cur = self._hop_device(bus, bus_concrete, dom, token)
+ bus = None
+ elif self._is_bus_token(token):
+ bus, bus_concrete, dom = self._hop_bus(cur, token)
+ cur = bus
+ else:
+ cur = self._hop_object(cur, token)
+
+ return self._apply_type(cur, typename)
+
+ def _hop_object(self, cur: str, token: str) -> str:
+ """
+ Apply an object hop, descending to a direct child by QOM type.
+
+ :param cur: current QOM path to search under.
+ :param token: the object-hop token ("type" or "type[index]").
+ :return: the matched child's canonical QOM path.
+ :raises LocatorError: if zero, several (without an index) or an
+ out-of-range child matches.
+ """
+ name, index = self._parse_indexed(token)
+ matches: list[tuple[str, str]] = []
+ for child_name, _inner, child_path in self._canonical_children(cur):
+ if self._qom_is_a(self._qom_type_of(child_path), name):
+ matches.append((child_name, child_path))
+ matches.sort(key=lambda item: self._order_key(item[0]))
+ return self._pick([path for _name, path in matches], index,
+ f"object '{name}'")
+
+ def _hop_bus(self, cur: str,
+ token: str) -> tuple[str, str, BusDomain | None]:
+ """
+ Apply a bus hop, selecting a bus owned by the current object.
+
+ :param cur: current QOM path to search under.
+ :param token: the bus-hop token ("bus" or "bus[channel]"), where
+ "bus" is a domain keyword or a bus QOM type.
+ :return: ``(bus_path, bus_concrete, domain)``; @domain is the mapped
+ domain for a keyword, or None for a raw bus type (its address
+ property is then inferred at the device hop).
+ :raises LocatorError: on an unknown bus, or a missing, ambiguous or
+ out-of-range bus.
+ """
+ name, index = self._parse_indexed(token)
+ if name in self._bus_domains:
+ dom: BusDomain | None = self._bus_domains[name]
+ bus_typename = dom[0]
+ elif self._qom_is_a(name, self.BUS_BASE):
+ dom = None
+ bus_typename = name
+ else:
+ raise LocatorError(f"'{name}' is not a bus domain or bus type")
+
+ buses = self._collect_buses(cur, bus_typename)
+ bus_path = self._pick([path for _name, path in buses], index,
+ f"bus '{name}'")
+ return bus_path, self._qom_type_of(bus_path), dom
+
+ def _hop_device(self, bus: str, bus_concrete: str | None,
+ dom: BusDomain | None, token: str) -> str:
+ """
+ Apply a device hop, matching one device on the current bus.
+
+ :param bus: canonical QOM path of the current bus.
+ :param bus_concrete: the bus's concrete QOM type, for domain inference.
+ :param dom: the active domain, or None to infer it from the bus.
+ :param token: the device-hop token.
+ :return: the matched device's canonical QOM path.
+ :raises LocatorError: if zero or several devices match.
+ """
+ sel = self._parse_device(token)
+
+ if dom is None and bus_concrete is not None:
+ for spec in self._bus_domains.values():
+ if self._qom_is_a(bus_concrete, spec[0]):
+ dom = spec
+ break
+
+ matches: list[str] = []
+ for index, dev_path in self._bus_children(bus):
+ if self._device_matches(dev_path, index, sel, dom):
+ matches.append(dev_path)
+
+ if not matches:
+ raise LocatorError(f"no device matches '{token}'")
+ if len(matches) > 1:
+ raise LocatorError(f"device hop '{token}' is ambiguous "
+ f"({len(matches)} matches)")
+ return matches[0]
+
+ def _device_matches(self, dev_path: str, index: int,
+ sel: DeviceSelectors, dom: BusDomain | None) -> bool:
+ """
+ Test one device against a hop's selectors.
+
+ :param dev_path: canonical QOM path of the candidate device.
+ :param index: the device's on-bus (BusChild) index.
+ :param sel: the parsed selectors to match.
+ :param dom: the active domain (provides the address property), or None.
+ :return: True if the device satisfies every selector.
+ """
+ if sel.typename is not None:
+ if not self._qom_is_a(self._qom_type_of(dev_path), sel.typename):
+ return False
+ if sel.addr is not None:
+ address_property = dom[1] if dom else None
+ if address_property is None:
+ return False
+ if self._qom_get(dev_path, address_property) != sel.addr:
+ return False
+ if sel.on_bus_index is not None and index != sel.on_bus_index:
+ return False
+ # '=id' matches the object's canonical-path leaf, which for a device
+ # created with '-device ...,id=X' is X.
+ if sel.ident is not None and dev_path.rsplit("/", 1)[-1] != sel.ident:
+ return False
+ return True
+
+ def _apply_type(self, path: str, typename: str | None) -> str:
+ """
+ Verify a resolved object's type, if requested.
+
+ :param path: canonical QOM path of the resolved object.
+ :param typename: expected QOM type, or None to skip the check.
+ :return: @path unchanged.
+ :raises LocatorError: if the object is not of @typename.
+ """
+ if typename is not None and not self._qom_is_a(self._qom_type_of(path),
+ typename):
+ raise LocatorError(f"resolved object is not of type '{typename}'")
+ return path
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add DeviceLocator, a helper that addresses a device by its position on
the bus -- bus, address, type or index -- rather than by a QOM path. A
QOM path locates a device under the object that owns it, by enumeration
order, and so says nothing about where the device actually sits in the
bus topology. From a QOM anchor DeviceLocator instead walks the bus and
device hierarchy, alternating bus and device hops, to reach the target.
It relies only on standard QOM queries over QMP, needing no custom
commands or changes to QEMU.
QEMU still supports Python 3.9, but 3.9 reached end of life on
2025-10-31. This new, optional helper therefore adopts the clearer
type-hint syntax introduced in Python 3.10 (such as "X | Y" unions)
rather than the more verbose 3.9 equivalents. Its import is guarded so
the module still loads on 3.9; tests that use the resolver are skipped
there.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/qemu_test/locator.py | 586 ++++++++++++++++++++++++++++++++++
1 file changed, 586 insertions(+)
diff --git a/tests/functional/qemu_test/locator.py b/tests/functional/qemu_test/locator.py
new file mode 100644
index 0000000000..bc8ba111a4
--- /dev/null
+++ b/tests/functional/qemu_test/locator.py
@@ -0,0 +1,586 @@
+# Device locator: resolve a device from a QOM anchor plus a typed
+# bus/device traversal, entirely over QMP.
+#
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import re
+import sys
+from collections.abc import Callable, Iterable
+from typing import Any, NamedTuple
+
+from qemu.qmp import ExecuteError
+
+
+# The type hints below use PEP 604 unions ("X | None") and PEP 585 built-in
+# generics ("list[...]"), which require Python 3.10+.
+# Fail with a clear message rather than a cryptic TypeError from the
+# annotations when run on an older interpreter.
+if sys.version_info < (3, 10):
+ version = ".".join(map(str, sys.version_info[:3]))
+ raise RuntimeError(
+ f"qemu_test.locator requires Python 3.10+; "
+ f"interpreter is Python {version}")
+
+
+BusDomain = tuple[str, str | None]
+"""A bus domain: ``(bus_typename, address_property_or_None)``."""
+
+
+class LocatorError(Exception):
+ """Raised when a locator cannot be resolved."""
+
+
+class DeviceSelectors(NamedTuple):
+ """Parsed selectors of a single device hop ("type@addr#index=id")."""
+ typename: str | None = None
+ """QOM type name."""
+ addr: int | None = None
+ """Bus address."""
+ on_bus_index: int | None = None
+ """On-bus (BusChild) index."""
+ ident: str | None = None
+ """Device id."""
+
+
+class DeviceLocator:
+ """
+ Find a device by traversing the bus and device hierarchy, addressing
+ it by position -- bus, address, type or index -- rather than by a
+ fragile canonical QOM path.
+
+ A locator string is a QOM anchor followed by a typed traversal that
+ alternates bus and device hops. Resolution runs against a live guest
+ over QMP, using only qom-list / qom-get / qom-list-types, so it works
+ against any QMP-capable QEMU without extra commands.
+
+ Grammar::
+
+ <anchor> [ '::' <hop> ( '~' <hop> )* ]
+
+ '::' leaves the QOM anchor and enters typed traversal; '~' crosses
+ the device<->bus boundary (either direction). Because bus<->bus is
+ impossible and the only object->object hop is the leading controller
+ (which follows '::' directly, never '~'), every '~' marks a
+ device<->bus crossing.
+
+ <anchor> is a QOM path: absolute ("/machine/soc"), partial ("soc",
+ matched as a component suffix), or empty for /machine.
+
+ Hops alternate between object context and bus context. Starting from the
+ anchor a hop is one of:
+
+ object hop <qom-type> [ '[' index ']' ]
+ Descend to a direct (child<>) child whose concrete QOM type is-a
+ <qom-type> (e.g. "aspeed.i2c-ast2600"), picked by [index] when
+ several match. This names a controller by its real QOM type, with
+ no keyword table involved.
+
+ bus hop <bus> [ '[' channel ']' ]
+ Select a bus owned by the current object. <bus> is a domain keyword
+ ("i2c", "spi") or a bus QOM type ("i2c-bus", "SSI"); [channel] picks
+ among the buses of that type, ordered by their trailing number. Only
+ bus domains are tabulated, and solely to map a keyword to its bus
+ type and to the device address property used by '@' below.
+
+ device hop [type] [ '@' addr ] [ '#' index ] [ '=' id ]
+ Match a device on the current bus by QOM type, bus address (the I2C
+ address or SPI chip-select), on-bus (BusChild) index and/or id.
+
+ Example: "/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b".
+ """
+
+ DEFAULT_BUS_DOMAINS: dict[str, BusDomain] = {
+ "i2c": ("i2c-bus", "address"),
+ "spi": ("SSI", "cs"),
+ }
+ """Keyword -> ``(bus_typename, address_property_or_None)``."""
+
+ BUS_BASE = "bus"
+ """The QOM base type every bus derives from."""
+
+ def __init__(
+ self,
+ qmp: Callable[..., Any],
+ bus_domains: Iterable[tuple[str, str, str | None]] | None = None,
+ ) -> None:
+ """
+ :param qmp: a callable ``qmp(command, **args)`` returning the QMP
+ payload and raising on error -- e.g. ``QEMUMachine.cmd``.
+ :param bus_domains: optional iterable of ``(keyword, bus_typename,
+ address_property)`` tuples registered up front; the
+ "i2c" and "spi" bus domains are always registered.
+ """
+ self._qmp = qmp
+ self._bus_domains: dict[str, BusDomain] = dict(
+ self.DEFAULT_BUS_DOMAINS)
+ self._qom_subtypes_cache: dict[str, set[str]] = {}
+ for keyword, bus_typename, address_property in bus_domains or ():
+ self.register_bus_domain(keyword, bus_typename, address_property)
+
+ def register_bus_domain(self, keyword: str, bus_typename: str,
+ address_property: str | None = None) -> None:
+ """
+ Register an additional bus domain.
+
+ :param keyword: domain keyword used in a bus hop (e.g. "i2c").
+ :param bus_typename: the QOM bus type backing the domain.
+ :param address_property: device property holding the bus address, or
+ None when the domain has no address selector.
+ """
+ self._bus_domains[keyword] = (bus_typename, address_property)
+
+ # -- QOM access over QMP ---------------------------------------------
+
+ def _qom_list(self, path: str) -> list[dict[str, Any]]:
+ """
+ List the QOM child/link properties of an object.
+
+ :param path: canonical QOM path to enumerate.
+ :return: the ``qom-list`` payload, one dict per property.
+ """
+ return self._qmp("qom-list", path=path)
+
+ def _qom_get(self, path: str, prop: str) -> Any:
+ """
+ Read one QOM property.
+
+ :param path: canonical QOM path of the object.
+ :param prop: property name to read.
+ :return: the property value.
+ """
+ return self._qmp("qom-get", path=path, property=prop)
+
+ def _qom_type_of(self, path: str) -> str:
+ """
+ Return an object's concrete QOM type name.
+
+ :param path: canonical QOM path of the object.
+ :return: the QOM type name.
+ """
+ return self._qom_get(path, "type")
+
+ def _qom_subtypes(self, typename: str) -> set[str]:
+ """
+ Return the set of every type that is-a @typename (itself included).
+
+ :param typename: the base or interface type name.
+ :return: the implementing type names, cached per base.
+ """
+ cached = self._qom_subtypes_cache.get(typename)
+ if cached is None:
+ listed = self._qmp("qom-list-types", implements=typename,
+ abstract=True)
+ cached = {entry["name"] for entry in listed}
+ cached.add(typename)
+ self._qom_subtypes_cache[typename] = cached
+ return cached
+
+ def _qom_is_a(self, concrete: str, base: str) -> bool:
+ """
+ Report whether one QOM type is-a another.
+
+ :param concrete: a concrete QOM type name.
+ :param base: a base or interface type name.
+ :return: True if @concrete is or derives from @base.
+ """
+ return concrete == base or concrete in self._qom_subtypes(base)
+
+ # -- QOM tree helpers ------------------------------------------------
+
+ @staticmethod
+ def _inner_type(proptype: str) -> tuple[str | None, str | None]:
+ """
+ Split a "child<X>"/"link<X>" property type.
+
+ :param proptype: a QOM property type string.
+ :return: ``(kind, inner)`` with kind "child" or "link", or
+ ``(None, None)`` for any other property.
+ """
+ match = re.match(r"(child|link)<(.+)>$", proptype)
+ if match:
+ return match.group(1), match.group(2)
+ return None, None
+
+ @staticmethod
+ def _order_key(name: str) -> tuple[int, str]:
+ """
+ Ordering key for a QOM path component: the last run of decimal digits
+ in the name ("aspeed.i2c.10" -> 10), or -1 when it carries no number.
+
+ :param name: a canonical path component.
+ :return: ``(index, name)``, with the name as tie-breaker.
+ """
+ runs = re.findall(r"\d+", name)
+ return (int(runs[-1]) if runs else -1, name)
+
+ def _canonical_children(self, path: str) -> list[tuple[str, str, str]]:
+ """
+ Enumerate the canonical (child<...>) children of an object.
+
+ :param path: canonical QOM path to enumerate.
+ :return: ``(name, property_type, child_path)`` per canonical child.
+ """
+ children: list[tuple[str, str, str]] = []
+ for prop in self._qom_list(path):
+ kind, inner = self._inner_type(prop["type"])
+ if kind == "child" and inner is not None:
+ children.append((prop["name"], inner,
+ path + "/" + prop["name"]))
+ return children
+
+ def _collect_buses(self, root: str,
+ bus_typename: str) -> list[tuple[str, str]]:
+ """
+ Collect the domain buses reachable under @root, pruned at each found
+ bus so buses nested behind a downstream device (e.g. an I2C mux) are
+ not gathered.
+
+ :param root: QOM path to search from.
+ :param bus_typename: the domain's QOM bus type.
+ :return: ``(name, path)`` per bus, sorted by component order.
+ """
+ found: list[tuple[str, str]] = []
+ for name, concrete, child_path in self._canonical_children(root):
+ if self._qom_is_a(concrete, bus_typename):
+ found.append((name, child_path))
+ else:
+ found.extend(self._collect_buses(child_path, bus_typename))
+ found.sort(key=lambda item: self._order_key(item[0]))
+ return found
+
+ def _bus_children(self, bus: str) -> list[tuple[int, str]]:
+ """
+ Enumerate the devices attached to a bus.
+
+ :param bus: canonical QOM path of the bus.
+ :return: ``(on_bus_index, device_path)`` per attached device, sorted
+ by index.
+ """
+ kids: list[tuple[int, str]] = []
+ for prop in self._qom_list(bus):
+ match = re.match(r"child\[(\d+)\]$", prop["name"])
+ kind, _ = self._inner_type(prop["type"])
+ if match and kind == "link":
+ kids.append((int(match.group(1)),
+ self._qom_get(bus, prop["name"])))
+ kids.sort(key=lambda item: item[0])
+ return kids
+
+ # -- anchor / plain-path resolution ---------------------------------
+
+ def _exists(self, path: str) -> bool:
+ """
+ Report whether a QOM path resolves to an existing object.
+
+ :param path: an absolute QOM path.
+ :return: True if the object exists.
+ """
+ try:
+ self._qom_list(path)
+ return True
+ except ExecuteError:
+ return False
+
+ def _all_paths(self, root: str = "/machine") -> list[str]:
+ """
+ Collect every canonical object path under @root, @root included.
+
+ :param root: QOM path to search from.
+ :return: the list of canonical paths.
+ """
+ paths: list[str] = [root]
+ for _name, _concrete, child_path in self._canonical_children(root):
+ paths.extend(self._all_paths(child_path))
+ return paths
+
+ def _resolve_anchor(self, anchor: str) -> str:
+ """
+ Resolve a QOM anchor: empty -> /machine, an absolute path used
+ verbatim, otherwise a component-suffix search over the whole tree.
+
+ :param anchor: the anchor part of a locator.
+ :return: the resolved canonical QOM path.
+ :raises LocatorError: if the anchor is missing or ambiguous.
+ """
+ if anchor == "":
+ return "/machine"
+ if anchor.startswith("/"):
+ if not self._exists(anchor):
+ raise LocatorError(f"anchor '{anchor}' not found")
+ return anchor
+
+ wanted = anchor.split("/")
+ matches = [path for path in self._all_paths()
+ if path.split("/")[-len(wanted):] == wanted]
+ if not matches:
+ raise LocatorError(f"anchor '{anchor}' not found")
+ if len(matches) > 1:
+ raise LocatorError(f"anchor '{anchor}' is ambiguous "
+ f"({len(matches)} found)")
+ return matches[0]
+
+ # -- hop parsing ----------------------------------------------------
+
+ @staticmethod
+ def _parse_indexed(token: str) -> tuple[str, int | None]:
+ """
+ Parse an object or bus hop ("name" or "name[index]").
+
+ :param token: the hop token.
+ :return: ``(name, index_or_None)``.
+ :raises LocatorError: on a malformed hop.
+ """
+ match = re.match(r"([^\[]+)(?:\[(\d+)\])?$", token)
+ if not match:
+ raise LocatorError(f"malformed hop '{token}'")
+ index = int(match.group(2)) if match.group(2) is not None else None
+ return match.group(1), index
+
+ @staticmethod
+ def _parse_device(token: str) -> DeviceSelectors:
+ """
+ Parse a device hop into its selectors.
+
+ :param token: the device-hop token ("type@addr#index=id").
+ :return: the parsed selectors.
+ :raises LocatorError: on a malformed or empty hop.
+ """
+ typename: str | None = None
+ addr: int | None = None
+ index: int | None = None
+ ident: str | None = None
+ pos = 0
+ length = len(token)
+ # A leading bare run (no sigil) is the QOM type selector.
+ if pos < length and token[pos] not in "@#=":
+ start = pos
+ while pos < length and token[pos] not in "@#=":
+ pos += 1
+ typename = token[start:pos]
+ seen = typename is not None
+ seen_sigils: set[str] = set()
+ while pos < length:
+ sig = token[pos]
+ pos += 1
+ start = pos
+ while pos < length and token[pos] not in "@#=":
+ pos += 1
+ value = token[start:pos]
+ if sig in seen_sigils:
+ raise LocatorError(
+ f"duplicate '{sig}' selector in hop '{token}'")
+ seen_sigils.add(sig)
+ if value == "":
+ raise LocatorError(
+ f"empty '{sig}' selector in hop '{token}'")
+ if sig == "@":
+ try:
+ addr = int(value, 0)
+ except ValueError:
+ raise LocatorError(f"malformed address '@{value}'")
+ elif sig == "#":
+ try:
+ index = int(value, 10)
+ except ValueError:
+ raise LocatorError(f"malformed on-bus index '#{value}'")
+ elif sig == "=":
+ ident = value
+ seen = True
+ if not seen:
+ raise LocatorError("empty device hop")
+ return DeviceSelectors(typename, addr, index, ident)
+
+ def _is_bus_token(self, token: str) -> bool:
+ """
+ Report whether a hop names a bus (a domain keyword or a bus QOM type)
+ rather than an intermediate object.
+
+ :param token: an object-or-bus hop token.
+ :return: True for a bus hop.
+ """
+ name = token.split("[", 1)[0]
+ return name in self._bus_domains or self._qom_is_a(name, self.BUS_BASE)
+
+ @staticmethod
+ def _pick(items: list[str], index: int | None, what: str) -> str:
+ """
+ Select one entry from an ordered match list.
+
+ :param items: the ordered candidate paths.
+ :param index: the requested index, or None to require a single match.
+ :param what: a description used in error messages.
+ :return: the selected path.
+ :raises LocatorError: if nothing matches, the match is ambiguous, or
+ the index is out of range.
+ """
+ if index is None:
+ if not items:
+ raise LocatorError(f"no {what} found")
+ if len(items) > 1:
+ raise LocatorError(f"{what} is ambiguous "
+ f"({len(items)} found); add an index")
+ return items[0]
+ if index >= len(items):
+ raise LocatorError(f"{what} index {index} out of range "
+ f"({len(items)} found)")
+ return items[index]
+
+ # -- resolution -----------------------------------------------------
+
+ def resolve(self, locator: str, typename: str | None = None) -> str:
+ """
+ Resolve a locator to a canonical QOM path (usable with qom-get).
+
+ :param locator: the locator string (see the class grammar).
+ :param typename: if set, the leaf is verified to be-a this QOM type.
+ :return: the resolved canonical QOM path.
+ :raises LocatorError: if the locator does not resolve to exactly one
+ object (not found, ambiguous, unknown bus or
+ malformed), or the leaf fails the type check.
+ """
+ sep = locator.find("::")
+ if sep < 0:
+ return self._apply_type(self._resolve_anchor(locator), typename)
+
+ cur = self._resolve_anchor(locator[:sep])
+ bus: str | None = None
+ bus_concrete: str | None = None
+ dom: BusDomain | None = None
+
+ for token in locator[sep + 2:].split("~"):
+ if token == "":
+ raise LocatorError(f"empty hop in locator {locator!r}")
+ if bus is not None:
+ cur = self._hop_device(bus, bus_concrete, dom, token)
+ bus = None
+ elif self._is_bus_token(token):
+ bus, bus_concrete, dom = self._hop_bus(cur, token)
+ cur = bus
+ else:
+ cur = self._hop_object(cur, token)
+
+ return self._apply_type(cur, typename)
+
+ def _hop_object(self, cur: str, token: str) -> str:
+ """
+ Apply an object hop, descending to a direct child by QOM type.
+
+ :param cur: current QOM path to search under.
+ :param token: the object-hop token ("type" or "type[index]").
+ :return: the matched child's canonical QOM path.
+ :raises LocatorError: if zero, several (without an index) or an
+ out-of-range child matches.
+ """
+ name, index = self._parse_indexed(token)
+ matches: list[tuple[str, str]] = []
+ for child_name, _inner, child_path in self._canonical_children(cur):
+ if self._qom_is_a(self._qom_type_of(child_path), name):
+ matches.append((child_name, child_path))
+ matches.sort(key=lambda item: self._order_key(item[0]))
+ return self._pick([path for _name, path in matches], index,
+ f"object '{name}'")
+
+ def _hop_bus(self, cur: str,
+ token: str) -> tuple[str, str, BusDomain | None]:
+ """
+ Apply a bus hop, selecting a bus owned by the current object.
+
+ :param cur: current QOM path to search under.
+ :param token: the bus-hop token ("bus" or "bus[channel]"), where
+ "bus" is a domain keyword or a bus QOM type.
+ :return: ``(bus_path, bus_concrete, domain)``; @domain is the mapped
+ domain for a keyword, or None for a raw bus type (its address
+ property is then inferred at the device hop).
+ :raises LocatorError: on an unknown bus, or a missing, ambiguous or
+ out-of-range bus.
+ """
+ name, index = self._parse_indexed(token)
+ if name in self._bus_domains:
+ dom: BusDomain | None = self._bus_domains[name]
+ bus_typename = dom[0]
+ elif self._qom_is_a(name, self.BUS_BASE):
+ dom = None
+ bus_typename = name
+ else:
+ raise LocatorError(f"'{name}' is not a bus domain or bus type")
+
+ buses = self._collect_buses(cur, bus_typename)
+ bus_path = self._pick([path for _name, path in buses], index,
+ f"bus '{name}'")
+ return bus_path, self._qom_type_of(bus_path), dom
+
+ def _hop_device(self, bus: str, bus_concrete: str | None,
+ dom: BusDomain | None, token: str) -> str:
+ """
+ Apply a device hop, matching one device on the current bus.
+
+ :param bus: canonical QOM path of the current bus.
+ :param bus_concrete: the bus's concrete QOM type, for domain inference.
+ :param dom: the active domain, or None to infer it from the bus.
+ :param token: the device-hop token.
+ :return: the matched device's canonical QOM path.
+ :raises LocatorError: if zero or several devices match.
+ """
+ sel = self._parse_device(token)
+
+ if dom is None and bus_concrete is not None:
+ for spec in self._bus_domains.values():
+ if self._qom_is_a(bus_concrete, spec[0]):
+ dom = spec
+ break
+
+ matches: list[str] = []
+ for index, dev_path in self._bus_children(bus):
+ if self._device_matches(dev_path, index, sel, dom):
+ matches.append(dev_path)
+
+ if not matches:
+ raise LocatorError(f"no device matches '{token}'")
+ if len(matches) > 1:
+ raise LocatorError(f"device hop '{token}' is ambiguous "
+ f"({len(matches)} matches)")
+ return matches[0]
+
+ def _device_matches(self, dev_path: str, index: int,
+ sel: DeviceSelectors, dom: BusDomain | None) -> bool:
+ """
+ Test one device against a hop's selectors.
+
+ :param dev_path: canonical QOM path of the candidate device.
+ :param index: the device's on-bus (BusChild) index.
+ :param sel: the parsed selectors to match.
+ :param dom: the active domain (provides the address property), or None.
+ :return: True if the device satisfies every selector.
+ """
+ if sel.typename is not None:
+ if not self._qom_is_a(self._qom_type_of(dev_path), sel.typename):
+ return False
+ if sel.addr is not None:
+ address_property = dom[1] if dom else None
+ if address_property is None:
+ return False
+ if self._qom_get(dev_path, address_property) != sel.addr:
+ return False
+ if sel.on_bus_index is not None and index != sel.on_bus_index:
+ return False
+ # '=id' matches the object's canonical-path leaf, which for a device
+ # created with '-device ...,id=X' is X.
+ if sel.ident is not None and dev_path.rsplit("/", 1)[-1] != sel.ident:
+ return False
+ return True
+
+ def _apply_type(self, path: str, typename: str | None) -> str:
+ """
+ Verify a resolved object's type, if requested.
+
+ :param path: canonical QOM path of the resolved object.
+ :param typename: expected QOM type, or None to skip the check.
+ :return: @path unchanged.
+ :raises LocatorError: if the object is not of @typename.
+ """
+ if typename is not None and not self._qom_is_a(self._qom_type_of(path),
+ typename):
+ raise LocatorError(f"resolved object is not of type '{typename}'")
+ return path
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 19/25] tests/functional: add a device-locator self-check
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Launch the SanMiguel BMC paused (-S) and resolve its I2C temperature
sensors and SPI flashes by position over QMP, exercising the
DeviceLocator grammar: controller/bus hops, the
address/type/on-bus-index selectors, the leaf type assertion and the
ambiguous/unknown/out-of-range error paths.
The test needs no disk image and completes in about a second, so it
joins the quick arm test set.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/meson.build | 1 +
tests/functional/arm/test_device_locator.py | 120 ++++++++++++++++++++++++++++
2 files changed, 121 insertions(+)
diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build
index 61b00932db..bb78dcf3e4 100644
--- a/tests/functional/arm/meson.build
+++ b/tests/functional/arm/meson.build
@@ -29,6 +29,7 @@ test_arm_timeouts = {
}
tests_arm_system_quick = [
+ 'device_locator',
'migration',
]
diff --git a/tests/functional/arm/test_device_locator.py b/tests/functional/arm/test_device_locator.py
new file mode 100644
index 0000000000..3807a49361
--- /dev/null
+++ b/tests/functional/arm/test_device_locator.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# Functional self-check for the pure-Python DeviceLocator helper.
+#
+# It launches the SanMiguel BMC paused (-S) and resolves its I2C sensors and
+# SPI flashes by position over QMP, so it needs no disk image and runs in
+# about a second.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import sys
+
+from unittest import skipIf
+
+from qemu_test import QemuSystemTest
+
+# DeviceLocator's type hints require Python 3.10+; import it only when
+# available so this module still loads (and skips) on the 3.9 baseline.
+if sys.version_info >= (3, 10):
+ from qemu_test.locator import DeviceLocator, LocatorError
+
+
+@skipIf(sys.version_info < (3, 10), 'DeviceLocator requires Python 3.10+')
+class DeviceLocatorTest(QemuSystemTest):
+
+ def setUp(self):
+ super().setUp()
+ self.set_machine('sanmiguel-bmc')
+ # Launch paused with defaults enabled so the aspeed board instantiates
+ # its SPI flashes (created only under defaults_enabled()); still no
+ # disk image is needed.
+ self.vm.add_args('-S', '-display', 'none')
+ self.vm.launch()
+ self.locator = DeviceLocator(self.vm.cmd)
+
+ def _address(self, path):
+ return self.vm.cmd('qom-get', path=path, property='address')
+
+ def _cs(self, path):
+ return self.vm.cmd('qom-get', path=path, property='cs')
+
+ def test_resolve_tmp75_sensors(self):
+ # The SanMiguel board wires ti,tmp75 sensors on I2C buses 5, 9 and 10
+ # behind the aspeed.i2c-ast2600 controller.
+ ctrl = '/machine/soc::aspeed.i2c-ast2600[0]'
+ for locator, addr in ((f'{ctrl}~i2c[5]~@0x48', 0x48),
+ (f'{ctrl}~i2c[9]~@0x4e', 0x4e),
+ (f'{ctrl}~i2c[10]~@0x48', 0x48)):
+ path = self.locator.resolve(locator, 'tmp75')
+ self.assertEqual(self._address(path), addr)
+
+ def test_device_selectors(self):
+ bus = '/machine/soc::aspeed.i2c-ast2600[0]~i2c[5]'
+ by_addr = self.locator.resolve(f'{bus}~@0x48')
+ # A type selector combined with the address must reach the same device.
+ self.assertEqual(self.locator.resolve(f'{bus}~tmp75@0x48'), by_addr)
+ # The on-bus (BusChild) index selector addresses a device positionally.
+ by_index = self.locator.resolve(f'{bus}~#0')
+ self.assertTrue(by_index.startswith('/machine'))
+ # A bus hop also accepts the raw bus QOM type in place of the keyword.
+ raw = '/machine/soc::aspeed.i2c-ast2600[0]~i2c-bus[5]~@0x48'
+ self.assertEqual(self.locator.resolve(raw), by_addr)
+
+ def test_type_assertion(self):
+ # The leaf type is validated when a type argument is passed.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ '/machine/soc::aspeed.i2c-ast2600[0]~i2c[5]~@0x48',
+ 'ds1338')
+
+ def test_resolution_errors(self):
+ ctrl = '/machine/soc::aspeed.i2c-ast2600[0]'
+ # No device sits at that address on the bus.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{ctrl}~i2c[5]~@0x99')
+ # A bare bus hop under /machine/soc spans every i2c bus on both the
+ # i2c and i3c controllers, so it is ambiguous without a controller hop.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ '/machine/soc::i2c')
+ # Unknown object type in a controller hop.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ '/machine/soc::nope')
+ # Out-of-range bus index.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{ctrl}~i2c[99]~@0x48')
+ # A stray or doubled '~' yields an empty hop, which is malformed.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{ctrl}~~i2c[5]~@0x48')
+ # A repeated or empty device selector is malformed, not last-wins.
+ for bad in (f'{ctrl}~i2c[5]~@0x48@0x4e',
+ f'{ctrl}~i2c[5]~#0#1',
+ f'{ctrl}~i2c[5]~tmp75=a=b',
+ f'{ctrl}~i2c[5]~tmp75='):
+ self.assertRaises(LocatorError, self.locator.resolve, bad)
+
+ def test_resolve_spi_flashes(self):
+ # The FMC controller carries two flashes on chip-selects 0 and 1; the
+ # first SPI controller carries one on chip-select 0.
+ for locator, cs in (
+ ('/machine/soc::aspeed.fmc-ast2600[0]~spi[0]~@0', 0),
+ ('/machine/soc::aspeed.fmc-ast2600[0]~spi[0]~@1', 1),
+ ('/machine/soc::aspeed.spi1-ast2600[0]~spi[0]~@0', 0)):
+ path = self.locator.resolve(locator, 'ssi-peripheral')
+ self.assertEqual(self._cs(path), cs)
+
+ def test_spi_selectors(self):
+ bus = '/machine/soc::aspeed.fmc-ast2600[0]~spi[0]'
+ by_cs = self.locator.resolve(f'{bus}~@0')
+ # A type selector plus the chip-select reaches the same flash.
+ self.assertEqual(
+ self.locator.resolve(f'{bus}~ssi-peripheral@0'), by_cs)
+ # The on-bus index selector resolves positionally.
+ self.assertTrue(
+ self.locator.resolve(f'{bus}~#1').startswith('/machine'))
+ # A wrong-type assertion on an SPI leaf is rejected.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{bus}~@0', 'tmp75')
+
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 19/25] tests/functional: add a device-locator self-check
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Launch the SanMiguel BMC paused (-S) and resolve its I2C temperature
sensors and SPI flashes by position over QMP, exercising the
DeviceLocator grammar: controller/bus hops, the
address/type/on-bus-index selectors, the leaf type assertion and the
ambiguous/unknown/out-of-range error paths.
The test needs no disk image and completes in about a second, so it
joins the quick arm test set.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/meson.build | 1 +
tests/functional/arm/test_device_locator.py | 120 ++++++++++++++++++++++++++++
2 files changed, 121 insertions(+)
diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build
index 61b00932db..bb78dcf3e4 100644
--- a/tests/functional/arm/meson.build
+++ b/tests/functional/arm/meson.build
@@ -29,6 +29,7 @@ test_arm_timeouts = {
}
tests_arm_system_quick = [
+ 'device_locator',
'migration',
]
diff --git a/tests/functional/arm/test_device_locator.py b/tests/functional/arm/test_device_locator.py
new file mode 100644
index 0000000000..3807a49361
--- /dev/null
+++ b/tests/functional/arm/test_device_locator.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# Functional self-check for the pure-Python DeviceLocator helper.
+#
+# It launches the SanMiguel BMC paused (-S) and resolves its I2C sensors and
+# SPI flashes by position over QMP, so it needs no disk image and runs in
+# about a second.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import sys
+
+from unittest import skipIf
+
+from qemu_test import QemuSystemTest
+
+# DeviceLocator's type hints require Python 3.10+; import it only when
+# available so this module still loads (and skips) on the 3.9 baseline.
+if sys.version_info >= (3, 10):
+ from qemu_test.locator import DeviceLocator, LocatorError
+
+
+@skipIf(sys.version_info < (3, 10), 'DeviceLocator requires Python 3.10+')
+class DeviceLocatorTest(QemuSystemTest):
+
+ def setUp(self):
+ super().setUp()
+ self.set_machine('sanmiguel-bmc')
+ # Launch paused with defaults enabled so the aspeed board instantiates
+ # its SPI flashes (created only under defaults_enabled()); still no
+ # disk image is needed.
+ self.vm.add_args('-S', '-display', 'none')
+ self.vm.launch()
+ self.locator = DeviceLocator(self.vm.cmd)
+
+ def _address(self, path):
+ return self.vm.cmd('qom-get', path=path, property='address')
+
+ def _cs(self, path):
+ return self.vm.cmd('qom-get', path=path, property='cs')
+
+ def test_resolve_tmp75_sensors(self):
+ # The SanMiguel board wires ti,tmp75 sensors on I2C buses 5, 9 and 10
+ # behind the aspeed.i2c-ast2600 controller.
+ ctrl = '/machine/soc::aspeed.i2c-ast2600[0]'
+ for locator, addr in ((f'{ctrl}~i2c[5]~@0x48', 0x48),
+ (f'{ctrl}~i2c[9]~@0x4e', 0x4e),
+ (f'{ctrl}~i2c[10]~@0x48', 0x48)):
+ path = self.locator.resolve(locator, 'tmp75')
+ self.assertEqual(self._address(path), addr)
+
+ def test_device_selectors(self):
+ bus = '/machine/soc::aspeed.i2c-ast2600[0]~i2c[5]'
+ by_addr = self.locator.resolve(f'{bus}~@0x48')
+ # A type selector combined with the address must reach the same device.
+ self.assertEqual(self.locator.resolve(f'{bus}~tmp75@0x48'), by_addr)
+ # The on-bus (BusChild) index selector addresses a device positionally.
+ by_index = self.locator.resolve(f'{bus}~#0')
+ self.assertTrue(by_index.startswith('/machine'))
+ # A bus hop also accepts the raw bus QOM type in place of the keyword.
+ raw = '/machine/soc::aspeed.i2c-ast2600[0]~i2c-bus[5]~@0x48'
+ self.assertEqual(self.locator.resolve(raw), by_addr)
+
+ def test_type_assertion(self):
+ # The leaf type is validated when a type argument is passed.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ '/machine/soc::aspeed.i2c-ast2600[0]~i2c[5]~@0x48',
+ 'ds1338')
+
+ def test_resolution_errors(self):
+ ctrl = '/machine/soc::aspeed.i2c-ast2600[0]'
+ # No device sits at that address on the bus.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{ctrl}~i2c[5]~@0x99')
+ # A bare bus hop under /machine/soc spans every i2c bus on both the
+ # i2c and i3c controllers, so it is ambiguous without a controller hop.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ '/machine/soc::i2c')
+ # Unknown object type in a controller hop.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ '/machine/soc::nope')
+ # Out-of-range bus index.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{ctrl}~i2c[99]~@0x48')
+ # A stray or doubled '~' yields an empty hop, which is malformed.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{ctrl}~~i2c[5]~@0x48')
+ # A repeated or empty device selector is malformed, not last-wins.
+ for bad in (f'{ctrl}~i2c[5]~@0x48@0x4e',
+ f'{ctrl}~i2c[5]~#0#1',
+ f'{ctrl}~i2c[5]~tmp75=a=b',
+ f'{ctrl}~i2c[5]~tmp75='):
+ self.assertRaises(LocatorError, self.locator.resolve, bad)
+
+ def test_resolve_spi_flashes(self):
+ # The FMC controller carries two flashes on chip-selects 0 and 1; the
+ # first SPI controller carries one on chip-select 0.
+ for locator, cs in (
+ ('/machine/soc::aspeed.fmc-ast2600[0]~spi[0]~@0', 0),
+ ('/machine/soc::aspeed.fmc-ast2600[0]~spi[0]~@1', 1),
+ ('/machine/soc::aspeed.spi1-ast2600[0]~spi[0]~@0', 0)):
+ path = self.locator.resolve(locator, 'ssi-peripheral')
+ self.assertEqual(self._cs(path), cs)
+
+ def test_spi_selectors(self):
+ bus = '/machine/soc::aspeed.fmc-ast2600[0]~spi[0]'
+ by_cs = self.locator.resolve(f'{bus}~@0')
+ # A type selector plus the chip-select reaches the same flash.
+ self.assertEqual(
+ self.locator.resolve(f'{bus}~ssi-peripheral@0'), by_cs)
+ # The on-bus index selector resolves positionally.
+ self.assertTrue(
+ self.locator.resolve(f'{bus}~#1').startswith('/machine'))
+ # A wrong-type assertion on an SPI leaf is rejected.
+ self.assertRaises(LocatorError, self.locator.resolve,
+ f'{bus}~@0', 'tmp75')
+
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 20/25] tests/functional: aspeed: add hwmon sensor read helpers
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add read_hwmon()/wait_hwmon_value() to AspeedTest so board tests can
read a sysfs hwmon attribute over the console and poll it until it
reaches an expected value, instead of each test carrying its own copy.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/aspeed.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py
index 08af6b4839..84d40fc0e9 100644
--- a/tests/functional/aspeed.py
+++ b/tests/functional/aspeed.py
@@ -2,6 +2,9 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import re
+import time
+
from qemu_test import exec_command_and_wait_for_pattern
from qemu_test import LinuxKernelTest
@@ -29,6 +32,19 @@ def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
def wait_for_boot_complete(self):
self.wait_for_console_pattern('login:')
+ def read_hwmon(self, hwmon, attr):
+ return exec_command_and_wait_for_pattern(
+ self, f'cat {hwmon}/{attr}', self.PROMPT)
+
+ def wait_hwmon_value(self, hwmon, attr, expected):
+ pattern = re.compile(rb'(?m)^%d\r*$' % expected)
+ if pattern.search(self.read_hwmon(hwmon, attr)):
+ return
+ time.sleep(2)
+ out = self.read_hwmon(hwmon, attr)
+ if not pattern.search(out):
+ self.fail(f'{attr} did not reach {expected}: {out!r}')
+
def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'):
self.require_netdev('user')
self.vm.set_console()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 20/25] tests/functional: aspeed: add hwmon sensor read helpers
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Add read_hwmon()/wait_hwmon_value() to AspeedTest so board tests can
read a sysfs hwmon attribute over the console and poll it until it
reaches an expected value, instead of each test carrying its own copy.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/aspeed.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py
index 08af6b4839..84d40fc0e9 100644
--- a/tests/functional/aspeed.py
+++ b/tests/functional/aspeed.py
@@ -2,6 +2,9 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import re
+import time
+
from qemu_test import exec_command_and_wait_for_pattern
from qemu_test import LinuxKernelTest
@@ -29,6 +32,19 @@ def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
def wait_for_boot_complete(self):
self.wait_for_console_pattern('login:')
+ def read_hwmon(self, hwmon, attr):
+ return exec_command_and_wait_for_pattern(
+ self, f'cat {hwmon}/{attr}', self.PROMPT)
+
+ def wait_hwmon_value(self, hwmon, attr, expected):
+ pattern = re.compile(rb'(?m)^%d\r*$' % expected)
+ if pattern.search(self.read_hwmon(hwmon, attr)):
+ return
+ time.sleep(2)
+ out = self.read_hwmon(hwmon, attr)
+ if not pattern.search(out):
+ self.fail(f'{attr} did not reach {expected}: {out!r}')
+
def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'):
self.require_netdev('user')
self.vm.set_console()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 21/25] tests/functional/arm: sanmiguel: add BMC boot test
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Boot the SanMiguel BMC from its OpenBMC image and exercise its three
TMP75 sensors: inject a temperature into each over QOM and check the
guest hwmon interface reports it back.
Address the sensors through the device locator rather than hardcoded QOM
paths.
The boot image is currently hosted in a personal fork
(github.com/eblot/qemu-aspeed-boot); the asset URL will be updated to
the Aspeed maintainer repository (github.com/legoater/qemu-aspeed-boot)
once the machine image is accepted there.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/meson.build | 2 +
tests/functional/arm/test_aspeed_sanmiguel.py | 72 +++++++++++++++++++++++++++
2 files changed, 74 insertions(+)
diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build
index bb78dcf3e4..4b263dd3e0 100644
--- a/tests/functional/arm/meson.build
+++ b/tests/functional/arm/meson.build
@@ -17,6 +17,7 @@ test_arm_timeouts = {
'aspeed_catalina' : 480,
'aspeed_gb200nvl_bmc' : 480,
'aspeed_rainier' : 480,
+ 'aspeed_sanmiguel' : 480,
'bpim2u' : 500,
'collie' : 180,
'cubieboard' : 360,
@@ -53,6 +54,7 @@ tests_arm_system_thorough = [
'aspeed_catalina',
'aspeed_gb200nvl_bmc',
'aspeed_rainier',
+ 'aspeed_sanmiguel',
'bpim2u',
'canona1100',
'collie',
diff --git a/tests/functional/arm/test_aspeed_sanmiguel.py b/tests/functional/arm/test_aspeed_sanmiguel.py
new file mode 100644
index 0000000000..3d35e4aa86
--- /dev/null
+++ b/tests/functional/arm/test_aspeed_sanmiguel.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots the Facebook SanMiguel BMC machine
+#
+# Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import sys
+
+from qemu_test import Asset
+from qemu_test import exec_command_and_wait_for_pattern
+from aspeed import AspeedTest
+
+# DeviceLocator's type hints require Python 3.10+; import it only when
+# available. On the 3.9 baseline the module still loads and the boot
+# test still runs; only the locator-based sensor checks are skipped.
+if sys.version_info >= (3, 10):
+ from qemu_test.locator import DeviceLocator
+
+
+class SanMiguelMachine(AspeedTest):
+
+ ASSET_SANMIGUEL_FLASH = Asset(
+ 'https://github.com/eblot/qemu-aspeed-boot/raw/5a972e968856bdf0493e4040ccc70455d1879511/images/'
+ 'sanmiguel-bmc/openbmc-20260616025450/obmc-phosphor-image-sanmiguel-20260616025450.static.mtd.xz',
+ 'f0b79a09cd861d79919facc16af1dbdc85a3df55b06dd62ba0318a01580d447b')
+
+ TMP75_SENSORS = (
+ ('/machine/soc::aspeed.i2c-ast2600[0]~i2c[5]~tmp75@0x48',
+ '/sys/bus/i2c/devices/5-0048/hwmon/hwmon*'),
+ ('/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4e',
+ '/sys/bus/i2c/devices/9-004e/hwmon/hwmon*'),
+ ('/machine/soc::aspeed.i2c-ast2600[0]~i2c[10]~tmp75@0x48',
+ '/sys/bus/i2c/devices/10-0048/hwmon/hwmon*'),
+ )
+ PROMPT = 'root@sanmiguel:~#'
+
+ def test_arm_ast2600_sanmiguel_openbmc(self):
+ image_path = self.uncompress(self.ASSET_SANMIGUEL_FLASH)
+
+ self.do_test_arm_aspeed_openbmc('sanmiguel-bmc', image=image_path,
+ uboot='2019.04', cpu_id='0xf00',
+ soc='AST2600 rev A3',
+ dt_model='Facebook SanMiguel BMC')
+
+ exec_command_and_wait_for_pattern(self, 'root', 'Password:')
+ exec_command_and_wait_for_pattern(self, '0penBmc', self.PROMPT)
+
+ # The sensor round-trip below resolves devices with DeviceLocator, which
+ # requires Python 3.10+; skip just that part on older interpreters.
+ if sys.version_info < (3, 10):
+ return
+
+ locator = DeviceLocator(self.vm.cmd)
+
+ # Drive each sensor through QOM and check the kernel hwmon interface
+ # reports it back; a distinct 0.5 C per-sensor step (the finest the
+ # TMP75 register round-trips at any resolution) catches a mis-resolved
+ # locator hitting the wrong sensor.
+ for idx, (sensor, hwmon) in enumerate(self.TMP75_SENSORS):
+ path = locator.resolve(sensor, 'tmp75')
+ self.assertIn(b'lm75', self.read_hwmon(hwmon, 'name'))
+ offset = idx * 500
+ for milli_c in (55000 + offset, 30000 + offset):
+ self.vm.cmd('qom-set', path=path,
+ property='temperature', value=milli_c)
+ self.wait_hwmon_value(hwmon, 'temp1_input', milli_c)
+
+
+if __name__ == '__main__':
+ AspeedTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 21/25] tests/functional/arm: sanmiguel: add BMC boot test
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Boot the SanMiguel BMC from its OpenBMC image and exercise its three
TMP75 sensors: inject a temperature into each over QOM and check the
guest hwmon interface reports it back.
Address the sensors through the device locator rather than hardcoded QOM
paths.
The boot image is currently hosted in a personal fork
(github.com/eblot/qemu-aspeed-boot); the asset URL will be updated to
the Aspeed maintainer repository (github.com/legoater/qemu-aspeed-boot)
once the machine image is accepted there.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/meson.build | 2 +
tests/functional/arm/test_aspeed_sanmiguel.py | 72 +++++++++++++++++++++++++++
2 files changed, 74 insertions(+)
diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build
index bb78dcf3e4..4b263dd3e0 100644
--- a/tests/functional/arm/meson.build
+++ b/tests/functional/arm/meson.build
@@ -17,6 +17,7 @@ test_arm_timeouts = {
'aspeed_catalina' : 480,
'aspeed_gb200nvl_bmc' : 480,
'aspeed_rainier' : 480,
+ 'aspeed_sanmiguel' : 480,
'bpim2u' : 500,
'collie' : 180,
'cubieboard' : 360,
@@ -53,6 +54,7 @@ tests_arm_system_thorough = [
'aspeed_catalina',
'aspeed_gb200nvl_bmc',
'aspeed_rainier',
+ 'aspeed_sanmiguel',
'bpim2u',
'canona1100',
'collie',
diff --git a/tests/functional/arm/test_aspeed_sanmiguel.py b/tests/functional/arm/test_aspeed_sanmiguel.py
new file mode 100644
index 0000000000..3d35e4aa86
--- /dev/null
+++ b/tests/functional/arm/test_aspeed_sanmiguel.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots the Facebook SanMiguel BMC machine
+#
+# Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import sys
+
+from qemu_test import Asset
+from qemu_test import exec_command_and_wait_for_pattern
+from aspeed import AspeedTest
+
+# DeviceLocator's type hints require Python 3.10+; import it only when
+# available. On the 3.9 baseline the module still loads and the boot
+# test still runs; only the locator-based sensor checks are skipped.
+if sys.version_info >= (3, 10):
+ from qemu_test.locator import DeviceLocator
+
+
+class SanMiguelMachine(AspeedTest):
+
+ ASSET_SANMIGUEL_FLASH = Asset(
+ 'https://github.com/eblot/qemu-aspeed-boot/raw/5a972e968856bdf0493e4040ccc70455d1879511/images/'
+ 'sanmiguel-bmc/openbmc-20260616025450/obmc-phosphor-image-sanmiguel-20260616025450.static.mtd.xz',
+ 'f0b79a09cd861d79919facc16af1dbdc85a3df55b06dd62ba0318a01580d447b')
+
+ TMP75_SENSORS = (
+ ('/machine/soc::aspeed.i2c-ast2600[0]~i2c[5]~tmp75@0x48',
+ '/sys/bus/i2c/devices/5-0048/hwmon/hwmon*'),
+ ('/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4e',
+ '/sys/bus/i2c/devices/9-004e/hwmon/hwmon*'),
+ ('/machine/soc::aspeed.i2c-ast2600[0]~i2c[10]~tmp75@0x48',
+ '/sys/bus/i2c/devices/10-0048/hwmon/hwmon*'),
+ )
+ PROMPT = 'root@sanmiguel:~#'
+
+ def test_arm_ast2600_sanmiguel_openbmc(self):
+ image_path = self.uncompress(self.ASSET_SANMIGUEL_FLASH)
+
+ self.do_test_arm_aspeed_openbmc('sanmiguel-bmc', image=image_path,
+ uboot='2019.04', cpu_id='0xf00',
+ soc='AST2600 rev A3',
+ dt_model='Facebook SanMiguel BMC')
+
+ exec_command_and_wait_for_pattern(self, 'root', 'Password:')
+ exec_command_and_wait_for_pattern(self, '0penBmc', self.PROMPT)
+
+ # The sensor round-trip below resolves devices with DeviceLocator, which
+ # requires Python 3.10+; skip just that part on older interpreters.
+ if sys.version_info < (3, 10):
+ return
+
+ locator = DeviceLocator(self.vm.cmd)
+
+ # Drive each sensor through QOM and check the kernel hwmon interface
+ # reports it back; a distinct 0.5 C per-sensor step (the finest the
+ # TMP75 register round-trips at any resolution) catches a mis-resolved
+ # locator hitting the wrong sensor.
+ for idx, (sensor, hwmon) in enumerate(self.TMP75_SENSORS):
+ path = locator.resolve(sensor, 'tmp75')
+ self.assertIn(b'lm75', self.read_hwmon(hwmon, 'name'))
+ offset = idx * 500
+ for milli_c in (55000 + offset, 30000 + offset):
+ self.vm.cmd('qom-set', path=path,
+ property='temperature', value=milli_c)
+ self.wait_hwmon_value(hwmon, 'temp1_input', milli_c)
+
+
+if __name__ == '__main__':
+ AspeedTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 22/25] tests/functional/arm: catalina: test TMP75
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Extend the Catalina boot test to exercise the TMP75 sensor on i2c9:
confirm the guest exposes it through hwmon, then drive the temperature
over QOM and check hwmon reports the injected value back.
Address the sensor through the device locator rather than a hardcoded
QOM path.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/test_aspeed_catalina.py | 37 +++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/tests/functional/arm/test_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py
index dc2f24e7b43..b80b5fe92b7 100755
--- a/tests/functional/arm/test_aspeed_catalina.py
+++ b/tests/functional/arm/test_aspeed_catalina.py
@@ -4,16 +4,30 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import sys
+
from qemu_test import Asset
+from qemu_test import exec_command_and_wait_for_pattern
from aspeed import AspeedTest
+# DeviceLocator's type hints require Python 3.10+; import it only when
+# available. On the 3.9 baseline the module still loads and the boot
+# test still runs; only the locator-based device checks are skipped.
+if sys.version_info >= (3, 10):
+ from qemu_test.locator import DeviceLocator
+
class CatalinaMachine(AspeedTest):
ASSET_CATALINA_FLASH = Asset(
- 'https://github.com/legoater/qemu-aspeed-boot/raw/a866feb5ef81245b4827a214584bf6bcc72939f6/images/catalina-bmc/obmc-phosphor-image-catalina-20250619123021.static.mtd.xz',
+ 'https://github.com/legoater/qemu-aspeed-boot/raw/a866feb5ef81245b4827a214584bf6bcc72939f6/images/'
+ 'catalina-bmc/obmc-phosphor-image-catalina-20250619123021.static.mtd.xz',
'287402e1ba021991e06be1d098f509444a02a3d81a73a932f66528b159e864f9')
+ TMP75_LOCATOR = "/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b"
+ TMP75_HWMON = "/sys/bus/i2c/devices/9-004b/hwmon/hwmon*"
+ PROMPT = "root@catalina:~#"
+
def test_arm_ast2600_catalina_openbmc(self):
image_path = self.uncompress(self.ASSET_CATALINA_FLASH)
@@ -21,5 +35,26 @@ def test_arm_ast2600_catalina_openbmc(self):
uboot='2019.04', cpu_id='0xf00',
soc='AST2600 rev A3')
+ exec_command_and_wait_for_pattern(self, 'root', 'Password:')
+ exec_command_and_wait_for_pattern(self, '0penBmc', self.PROMPT)
+
+ self.assertIn(b"tmp75", self.read_hwmon(self.TMP75_HWMON, "name"))
+
+ # The QOM round-trips below resolve devices with DeviceLocator, which
+ # require Python 3.10+; skip just those on older interpreters.
+ if sys.version_info < (3, 10):
+ return
+
+ tmp75 = DeviceLocator(self.vm.cmd).resolve(self.TMP75_LOCATOR, "tmp75")
+
+ # temp1_input reports the temperature in millidegrees Celsius. Drive it
+ # through QOM (units of 0.001 C) and check the kernel hwmon interface
+ # reports the injected value back.
+ for milli_c in (55000, 30000):
+ self.vm.cmd("qom-set", path=tmp75,
+ property="temperature", value=milli_c)
+ self.wait_hwmon_value(self.TMP75_HWMON, "temp1_input", milli_c)
+
+
if __name__ == '__main__':
AspeedTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 22/25] tests/functional/arm: catalina: test TMP75
@ 2026-07-31 10:45 ` Emmanuel Blot via qemu development
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Extend the Catalina boot test to exercise the TMP75 sensor on i2c9:
confirm the guest exposes it through hwmon, then drive the temperature
over QOM and check hwmon reports the injected value back.
Address the sensor through the device locator rather than a hardcoded
QOM path.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/test_aspeed_catalina.py | 37 +++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/tests/functional/arm/test_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py
index dc2f24e7b43..b80b5fe92b7 100755
--- a/tests/functional/arm/test_aspeed_catalina.py
+++ b/tests/functional/arm/test_aspeed_catalina.py
@@ -4,16 +4,30 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import sys
+
from qemu_test import Asset
+from qemu_test import exec_command_and_wait_for_pattern
from aspeed import AspeedTest
+# DeviceLocator's type hints require Python 3.10+; import it only when
+# available. On the 3.9 baseline the module still loads and the boot
+# test still runs; only the locator-based device checks are skipped.
+if sys.version_info >= (3, 10):
+ from qemu_test.locator import DeviceLocator
+
class CatalinaMachine(AspeedTest):
ASSET_CATALINA_FLASH = Asset(
- 'https://github.com/legoater/qemu-aspeed-boot/raw/a866feb5ef81245b4827a214584bf6bcc72939f6/images/catalina-bmc/obmc-phosphor-image-catalina-20250619123021.static.mtd.xz',
+ 'https://github.com/legoater/qemu-aspeed-boot/raw/a866feb5ef81245b4827a214584bf6bcc72939f6/images/'
+ 'catalina-bmc/obmc-phosphor-image-catalina-20250619123021.static.mtd.xz',
'287402e1ba021991e06be1d098f509444a02a3d81a73a932f66528b159e864f9')
+ TMP75_LOCATOR = "/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b"
+ TMP75_HWMON = "/sys/bus/i2c/devices/9-004b/hwmon/hwmon*"
+ PROMPT = "root@catalina:~#"
+
def test_arm_ast2600_catalina_openbmc(self):
image_path = self.uncompress(self.ASSET_CATALINA_FLASH)
@@ -21,5 +35,26 @@ def test_arm_ast2600_catalina_openbmc(self):
uboot='2019.04', cpu_id='0xf00',
soc='AST2600 rev A3')
+ exec_command_and_wait_for_pattern(self, 'root', 'Password:')
+ exec_command_and_wait_for_pattern(self, '0penBmc', self.PROMPT)
+
+ self.assertIn(b"tmp75", self.read_hwmon(self.TMP75_HWMON, "name"))
+
+ # The QOM round-trips below resolve devices with DeviceLocator, which
+ # require Python 3.10+; skip just those on older interpreters.
+ if sys.version_info < (3, 10):
+ return
+
+ tmp75 = DeviceLocator(self.vm.cmd).resolve(self.TMP75_LOCATOR, "tmp75")
+
+ # temp1_input reports the temperature in millidegrees Celsius. Drive it
+ # through QOM (units of 0.001 C) and check the kernel hwmon interface
+ # reports the injected value back.
+ for milli_c in (55000, 30000):
+ self.vm.cmd("qom-set", path=tmp75,
+ property="temperature", value=milli_c)
+ self.wait_hwmon_value(self.TMP75_HWMON, "temp1_input", milli_c)
+
+
if __name__ == '__main__':
AspeedTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 23/25] tests/functional/arm: catalina: test PCA9555 IO expander via QOM
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Drive the io_expander0 (pca9555@20 on i2c2) input pins from the host
through the pin%d QOM properties and read each one back both via qom-get
and via the guest kernel gpiochip (libgpiod), spanning both 8-bit ports
and both ends, then check two pins on different ports are reported
independently.
Introduce a check_ioexp() helper doing that round-trip so further
expanders can reuse it. Address the expander through the device locator
rather than a hardcoded QOM path.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/test_aspeed_catalina.py | 64 +++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/tests/functional/arm/test_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py
index b80b5fe92b..5624774891 100755
--- a/tests/functional/arm/test_aspeed_catalina.py
+++ b/tests/functional/arm/test_aspeed_catalina.py
@@ -2,6 +2,8 @@
#
# Functional test that boots the ASPEED machines
#
+# Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+#
# SPDX-License-Identifier: GPL-2.0-or-later
import sys
@@ -28,6 +30,10 @@ class CatalinaMachine(AspeedTest):
TMP75_HWMON = "/sys/bus/i2c/devices/9-004b/hwmon/hwmon*"
PROMPT = "root@catalina:~#"
+ # io_expander0: pca9555@20 on i2c2, a 16-bit expander directly on the bus.
+ IOEXP0_LOCATOR = "/machine/soc::aspeed.i2c-ast2600[0]~i2c[2]~pca9555@0x20"
+ IOEXP0_GPIODETECT = r"\[2-0020\]"
+
def test_arm_ast2600_catalina_openbmc(self):
image_path = self.uncompress(self.ASSET_CATALINA_FLASH)
@@ -45,7 +51,9 @@ def test_arm_ast2600_catalina_openbmc(self):
if sys.version_info < (3, 10):
return
- tmp75 = DeviceLocator(self.vm.cmd).resolve(self.TMP75_LOCATOR, "tmp75")
+ locator = DeviceLocator(self.vm.cmd)
+ tmp75 = locator.resolve(self.TMP75_LOCATOR, "tmp75")
+ ioexp0 = locator.resolve(self.IOEXP0_LOCATOR, "pca9555")
# temp1_input reports the temperature in millidegrees Celsius. Drive it
# through QOM (units of 0.001 C) and check the kernel hwmon interface
@@ -55,6 +63,60 @@ def test_arm_ast2600_catalina_openbmc(self):
property="temperature", value=milli_c)
self.wait_hwmon_value(self.TMP75_HWMON, "temp1_input", milli_c)
+ # pca9555@20 spans two 8-bit ports (pins 0-7 and 8-15).
+ self.check_ioexp(ioexp0, self.IOEXP0_GPIODETECT, (0, 7, 8, 15))
+
+ def check_ioexp(self, qom, gpiodetect, pins):
+ # The guest drives these lines as inputs: set_pin injects an external
+ # level, read back over QOM and gpioget. Outputs are covered by qtests.
+ chip = self.resolve_gpiochip(gpiodetect)
+ for pin in pins:
+ self.set_pin(qom, pin, "low")
+ self.assert_pin(qom, pin, "low")
+ self.assert_gpio_line(chip, pin, 0)
+ self.set_pin(qom, pin, "high")
+ self.assert_pin(qom, pin, "high")
+ self.assert_gpio_line(chip, pin, 1)
+
+ # Drive two pins to opposite levels at once to confirm they are
+ # reported back independently.
+ lo, hi = pins[0], pins[-1]
+ self.set_pin(qom, lo, "low")
+ self.set_pin(qom, hi, "high")
+ self.assert_pin(qom, lo, "low")
+ self.assert_pin(qom, hi, "high")
+ self.assert_gpio_line(chip, lo, 0)
+ self.assert_gpio_line(chip, hi, 1)
+
+ def read_catalina_console(self, command):
+ return exec_command_and_wait_for_pattern(self, command, self.PROMPT)
+
+ def resolve_gpiochip(self, gpiodetect):
+ # awk already prints the "gpiochipN" name; just pick it out of the
+ # console output (the command echo and prompt hold no such token).
+ out = self.read_catalina_console(
+ f"gpiodetect | awk '/{gpiodetect}/{{print $1}}'")
+ for token in out.split():
+ if token.startswith(b"gpiochip"):
+ return token.decode()
+ self.fail(f"could not resolve gpiochip {gpiodetect}")
+
+ def set_pin(self, qom, pin, level):
+ self.vm.cmd("qom-set", path=qom, property=f"pin{pin}", value=level)
+
+ def get_pin(self, qom, pin):
+ return self.vm.cmd("qom-get", path=qom, property=f"pin{pin}")
+
+ def assert_pin(self, qom, pin, expected):
+ level = self.get_pin(qom, pin)
+ self.assertEqual(level, expected,
+ f"pin{pin} QOM read {level!r}, expected {expected!r}")
+
+ def assert_gpio_line(self, chip, pin, expected):
+ out = self.read_catalina_console(
+ f"echo LINE{pin}=$(gpioget {chip} {pin})")
+ self.assertIn(f"LINE{pin}={expected}".encode(), out)
+
if __name__ == '__main__':
AspeedTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 23/25] tests/functional/arm: catalina: test PCA9555 IO expander via QOM
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Drive the io_expander0 (pca9555@20 on i2c2) input pins from the host
through the pin%d QOM properties and read each one back both via qom-get
and via the guest kernel gpiochip (libgpiod), spanning both 8-bit ports
and both ends, then check two pins on different ports are reported
independently.
Introduce a check_ioexp() helper doing that round-trip so further
expanders can reuse it. Address the expander through the device locator
rather than a hardcoded QOM path.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/test_aspeed_catalina.py | 64 +++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/tests/functional/arm/test_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py
index b80b5fe92b..5624774891 100755
--- a/tests/functional/arm/test_aspeed_catalina.py
+++ b/tests/functional/arm/test_aspeed_catalina.py
@@ -2,6 +2,8 @@
#
# Functional test that boots the ASPEED machines
#
+# Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+#
# SPDX-License-Identifier: GPL-2.0-or-later
import sys
@@ -28,6 +30,10 @@ class CatalinaMachine(AspeedTest):
TMP75_HWMON = "/sys/bus/i2c/devices/9-004b/hwmon/hwmon*"
PROMPT = "root@catalina:~#"
+ # io_expander0: pca9555@20 on i2c2, a 16-bit expander directly on the bus.
+ IOEXP0_LOCATOR = "/machine/soc::aspeed.i2c-ast2600[0]~i2c[2]~pca9555@0x20"
+ IOEXP0_GPIODETECT = r"\[2-0020\]"
+
def test_arm_ast2600_catalina_openbmc(self):
image_path = self.uncompress(self.ASSET_CATALINA_FLASH)
@@ -45,7 +51,9 @@ def test_arm_ast2600_catalina_openbmc(self):
if sys.version_info < (3, 10):
return
- tmp75 = DeviceLocator(self.vm.cmd).resolve(self.TMP75_LOCATOR, "tmp75")
+ locator = DeviceLocator(self.vm.cmd)
+ tmp75 = locator.resolve(self.TMP75_LOCATOR, "tmp75")
+ ioexp0 = locator.resolve(self.IOEXP0_LOCATOR, "pca9555")
# temp1_input reports the temperature in millidegrees Celsius. Drive it
# through QOM (units of 0.001 C) and check the kernel hwmon interface
@@ -55,6 +63,60 @@ def test_arm_ast2600_catalina_openbmc(self):
property="temperature", value=milli_c)
self.wait_hwmon_value(self.TMP75_HWMON, "temp1_input", milli_c)
+ # pca9555@20 spans two 8-bit ports (pins 0-7 and 8-15).
+ self.check_ioexp(ioexp0, self.IOEXP0_GPIODETECT, (0, 7, 8, 15))
+
+ def check_ioexp(self, qom, gpiodetect, pins):
+ # The guest drives these lines as inputs: set_pin injects an external
+ # level, read back over QOM and gpioget. Outputs are covered by qtests.
+ chip = self.resolve_gpiochip(gpiodetect)
+ for pin in pins:
+ self.set_pin(qom, pin, "low")
+ self.assert_pin(qom, pin, "low")
+ self.assert_gpio_line(chip, pin, 0)
+ self.set_pin(qom, pin, "high")
+ self.assert_pin(qom, pin, "high")
+ self.assert_gpio_line(chip, pin, 1)
+
+ # Drive two pins to opposite levels at once to confirm they are
+ # reported back independently.
+ lo, hi = pins[0], pins[-1]
+ self.set_pin(qom, lo, "low")
+ self.set_pin(qom, hi, "high")
+ self.assert_pin(qom, lo, "low")
+ self.assert_pin(qom, hi, "high")
+ self.assert_gpio_line(chip, lo, 0)
+ self.assert_gpio_line(chip, hi, 1)
+
+ def read_catalina_console(self, command):
+ return exec_command_and_wait_for_pattern(self, command, self.PROMPT)
+
+ def resolve_gpiochip(self, gpiodetect):
+ # awk already prints the "gpiochipN" name; just pick it out of the
+ # console output (the command echo and prompt hold no such token).
+ out = self.read_catalina_console(
+ f"gpiodetect | awk '/{gpiodetect}/{{print $1}}'")
+ for token in out.split():
+ if token.startswith(b"gpiochip"):
+ return token.decode()
+ self.fail(f"could not resolve gpiochip {gpiodetect}")
+
+ def set_pin(self, qom, pin, level):
+ self.vm.cmd("qom-set", path=qom, property=f"pin{pin}", value=level)
+
+ def get_pin(self, qom, pin):
+ return self.vm.cmd("qom-get", path=qom, property=f"pin{pin}")
+
+ def assert_pin(self, qom, pin, expected):
+ level = self.get_pin(qom, pin)
+ self.assertEqual(level, expected,
+ f"pin{pin} QOM read {level!r}, expected {expected!r}")
+
+ def assert_gpio_line(self, chip, pin, expected):
+ out = self.read_catalina_console(
+ f"echo LINE{pin}=$(gpioget {chip} {pin})")
+ self.assertIn(f"LINE{pin}={expected}".encode(), out)
+
if __name__ == '__main__':
AspeedTest.main()
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 24/25] hw/arm: catalina: drive pca9554 io expander input pins externally
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Create io_expander5 (pca9554@27) with hw-dir=true so its pin%d QOM
properties drive the external input level of each pin, matching the
pca9555 expanders, instead of the legacy behaviour that forces the pin
to an output. This lets external agents, including tests, stimulate the
input pins and have the guest read them back.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_catalina.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
index 928e140b21..69e072c932 100644
--- a/hw/arm/aspeed_ast2600_catalina.c
+++ b/hw/arm/aspeed_ast2600_catalina.c
@@ -545,9 +545,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc)
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 5), TYPE_TMP75, 0x4f);
/* i2c1mux0ch6 */
- /* io_expander5 - pca9554@27 */
- i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6),
- TYPE_PCA9554, 0x27);
+ /* io_expander5 - pca9554@27 (external agents drive its input pins) */
+ {
+ I2CBus *bus = pca954x_i2c_get_bus(i2c_mux, 6);
+ I2CSlave *ioexp = i2c_slave_new(TYPE_PCA9554, 0x27);
+
+ object_property_add_child(OBJECT(bus), "0x27", OBJECT(ioexp));
+ object_property_set_bool(OBJECT(ioexp), "hw-dir", true,
+ &error_abort);
+ i2c_slave_realize_and_unref(ioexp, bus, &error_abort);
+ }
/* io_expander6 - pca9555@25 */
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6),
TYPE_PCA9555, 0x25);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 24/25] hw/arm: catalina: drive pca9554 io expander input pins externally
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Create io_expander5 (pca9554@27) with hw-dir=true so its pin%d QOM
properties drive the external input level of each pin, matching the
pca9555 expanders, instead of the legacy behaviour that forces the pin
to an output. This lets external agents, including tests, stimulate the
input pins and have the guest read them back.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
hw/arm/aspeed_ast2600_catalina.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
index 928e140b21..69e072c932 100644
--- a/hw/arm/aspeed_ast2600_catalina.c
+++ b/hw/arm/aspeed_ast2600_catalina.c
@@ -545,9 +545,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc)
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 5), TYPE_TMP75, 0x4f);
/* i2c1mux0ch6 */
- /* io_expander5 - pca9554@27 */
- i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6),
- TYPE_PCA9554, 0x27);
+ /* io_expander5 - pca9554@27 (external agents drive its input pins) */
+ {
+ I2CBus *bus = pca954x_i2c_get_bus(i2c_mux, 6);
+ I2CSlave *ioexp = i2c_slave_new(TYPE_PCA9554, 0x27);
+
+ object_property_add_child(OBJECT(bus), "0x27", OBJECT(ioexp));
+ object_property_set_bool(OBJECT(ioexp), "hw-dir", true,
+ &error_abort);
+ i2c_slave_realize_and_unref(ioexp, bus, &error_abort);
+ }
/* io_expander6 - pca9555@25 */
i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6),
TYPE_PCA9555, 0x25);
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 25/25] tests/functional/arm: catalina: test PCA9554 IO expander via QOM
2026-07-31 10:44 ` Emmanuel Blot via qemu development
@ 2026-07-31 10:45 ` Emmanuel Blot via
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via qemu development @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Cover io_expander5 (pca9554@27, behind pca9548@70 channel 6 on i2c1), an
8-bit expander reached through the pca9548 mux.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/test_aspeed_catalina.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/functional/arm/test_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py
index 5624774891..f9ee62260b 100755
--- a/tests/functional/arm/test_aspeed_catalina.py
+++ b/tests/functional/arm/test_aspeed_catalina.py
@@ -33,6 +33,11 @@ class CatalinaMachine(AspeedTest):
# io_expander0: pca9555@20 on i2c2, a 16-bit expander directly on the bus.
IOEXP0_LOCATOR = "/machine/soc::aspeed.i2c-ast2600[0]~i2c[2]~pca9555@0x20"
IOEXP0_GPIODETECT = r"\[2-0020\]"
+ # io_expander5: pca9554@27 behind pca9548@70 channel 6 on i2c1, an 8-bit
+ # expander reached through the mux; gpiodetect confirms its 8 lines.
+ IOEXP5_LOCATOR = ("/machine/soc::aspeed.i2c-ast2600[0]"
+ "~i2c[1]~pca9548@0x70~i2c[6]~pca9554@0x27")
+ IOEXP5_GPIODETECT = r"-0027] \(8 lines\)"
def test_arm_ast2600_catalina_openbmc(self):
image_path = self.uncompress(self.ASSET_CATALINA_FLASH)
@@ -54,6 +59,7 @@ def test_arm_ast2600_catalina_openbmc(self):
locator = DeviceLocator(self.vm.cmd)
tmp75 = locator.resolve(self.TMP75_LOCATOR, "tmp75")
ioexp0 = locator.resolve(self.IOEXP0_LOCATOR, "pca9555")
+ ioexp5 = locator.resolve(self.IOEXP5_LOCATOR, "pca9554")
# temp1_input reports the temperature in millidegrees Celsius. Drive it
# through QOM (units of 0.001 C) and check the kernel hwmon interface
@@ -65,6 +71,8 @@ def test_arm_ast2600_catalina_openbmc(self):
# pca9555@20 spans two 8-bit ports (pins 0-7 and 8-15).
self.check_ioexp(ioexp0, self.IOEXP0_GPIODETECT, (0, 7, 8, 15))
+ # pca9554@27 is a single 8-bit port.
+ self.check_ioexp(ioexp5, self.IOEXP5_GPIODETECT, (0, 3, 7))
def check_ioexp(self, qom, gpiodetect, pins):
# The guest drives these lines as inputs: set_pin injects an external
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH v2 25/25] tests/functional/arm: catalina: test PCA9554 IO expander via QOM
@ 2026-07-31 10:45 ` Emmanuel Blot via
0 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot via @ 2026-07-31 10:45 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé, Cédric Le Goater, Peter Maydell,
Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery, Joel Stanley,
qemu-arm, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
Thomas Huth, Daniel P. Berrangé, Emmanuel Blot,
Emmanuel Blot
Cover io_expander5 (pca9554@27, behind pca9548@70 channel 6 on i2c1), an
8-bit expander reached through the pca9548 mux.
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
---
tests/functional/arm/test_aspeed_catalina.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/functional/arm/test_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py
index 5624774891..f9ee62260b 100755
--- a/tests/functional/arm/test_aspeed_catalina.py
+++ b/tests/functional/arm/test_aspeed_catalina.py
@@ -33,6 +33,11 @@ class CatalinaMachine(AspeedTest):
# io_expander0: pca9555@20 on i2c2, a 16-bit expander directly on the bus.
IOEXP0_LOCATOR = "/machine/soc::aspeed.i2c-ast2600[0]~i2c[2]~pca9555@0x20"
IOEXP0_GPIODETECT = r"\[2-0020\]"
+ # io_expander5: pca9554@27 behind pca9548@70 channel 6 on i2c1, an 8-bit
+ # expander reached through the mux; gpiodetect confirms its 8 lines.
+ IOEXP5_LOCATOR = ("/machine/soc::aspeed.i2c-ast2600[0]"
+ "~i2c[1]~pca9548@0x70~i2c[6]~pca9554@0x27")
+ IOEXP5_GPIODETECT = r"-0027] \(8 lines\)"
def test_arm_ast2600_catalina_openbmc(self):
image_path = self.uncompress(self.ASSET_CATALINA_FLASH)
@@ -54,6 +59,7 @@ def test_arm_ast2600_catalina_openbmc(self):
locator = DeviceLocator(self.vm.cmd)
tmp75 = locator.resolve(self.TMP75_LOCATOR, "tmp75")
ioexp0 = locator.resolve(self.IOEXP0_LOCATOR, "pca9555")
+ ioexp5 = locator.resolve(self.IOEXP5_LOCATOR, "pca9554")
# temp1_input reports the temperature in millidegrees Celsius. Drive it
# through QOM (units of 0.001 C) and check the kernel hwmon interface
@@ -65,6 +71,8 @@ def test_arm_ast2600_catalina_openbmc(self):
# pca9555@20 spans two 8-bit ports (pins 0-7 and 8-15).
self.check_ioexp(ioexp0, self.IOEXP0_GPIODETECT, (0, 7, 8, 15))
+ # pca9554@27 is a single 8-bit port.
+ self.check_ioexp(ioexp5, self.IOEXP5_GPIODETECT, (0, 3, 7))
def check_ioexp(self, qom, gpiodetect, pins):
# The guest drives these lines as inputs: set_pin injects an external
--
2.50.1
^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PING] Re: [PATCH v2 00/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors
2026-07-31 10:44 ` Emmanuel Blot via qemu development
` (25 preceding siblings ...)
(?)
@ 2026-08-31 14:42 ` Emmanuel Blot
-1 siblings, 0 replies; 74+ messages in thread
From: Emmanuel Blot @ 2026-08-31 14:42 UTC (permalink / raw)
To: qemu-devel, Philippe Mathieu-Daudé
Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot, Emmanuel Blot
[-- Attachment #1: Type: text/plain, Size: 8222 bytes --]
Hi Philippe,
Gentle ping on the sensor half of this series.
The patches that need your eyes are the hw/sensor/tmp105 ones:
01-05 cleanups: state made private to the implementation,
parent_obj rename, Resettable conversion, fault-queue
enforcement, temperature property description
07 TMP75, TMP175 and LM75B variants
08-11 qtest coverage for the above
01-05 are behaviour-preserving apart from 04, which enforces the
fault queue that was already configurable but ignored.
Happy to respin the sensor patches as their own series if that is
easier to review separately from the Aspeed board work.
Thanks,
Emmanuel
On 31 Jul 2026, at 12:44, Emmanuel Blot wrote:
> This series adds the Facebook SanMiguel BMC, promotes the
> TMP75/TMP175/LM75B sensors from generic TMP105 stand-ins to improved,
> dedicated models, and introduces the test-side plumbing needed to
> exercise real I2C devices from functional tests without hardcoding QOM
> paths.
>
> The work falls into four parts.
>
> 1. TMP* sensor models
>
> The TMP105 model was the only stand-in for a whole family of pin- and
> register-compatible parts, and several boards worked around the gap with
> private TYPE_TMP75 / TYPE_LM75 aliases that resolved back to a plain
> TMP105.
>
> The model is first cleaned up for correctness and QEMU convention. One
> of those changes touches migrated state, which gains an optional
> subsection so existing streams stay compatible.
>
> On that base, TMP75/TMP175/LM75B become variants selected by a small
> per-type class descriptor. The variant is class data, so the wire format
> is unchanged and all variants share the existing vmstate. qtest coverage
> is added for the per-variant behaviour. The existing BMC boards then
> drop their local aliases and instantiate the real models.
>
> 2. SanMiguel BMC
>
> A new AST2600-based machine for the Facebook SanMiguel BMC, derived from
> its device tree. It wires up three TMP75 sensors, IO expanders, FRU
> EEPROMs, an I2C mux and an RTC across its I2C buses, and provides its
> RAM, flash and networking; the FRU EEPROMs are populated with data read
> from physical hardware. A functional test boots the OpenBMC image and
> drives each sensor over QOM, checking the guest hwmon interface reports
> the injected value back.
>
> 3. Python device locator
>
> A QOM path names a device by its position under the object that owns it,
> in enumeration order — it says nothing about where the device sits in
> the bus topology. From a QOM path alone it is impossible to tell where a
> device is actually connected or what purpose it serves.
>
> DeviceLocator is a pure-Python helper that resolves a device from a
> string descriptor of its position — bus, address, type, on-bus index —
> by walking the bus/device hierarchy. It uses only standard QOM queries
> over QMP: no new QEMU commands, no model changes. A self-check exercises
> the grammar and its error paths against a paused SanMiguel BMC; it needs
> no disk image and runs in about a second.
>
> Note: the helper adopts the 3.10 type-hint syntax rather than the 3.9
> equivalents; 3.9 reached end of life on 2025-10-31. The import is
> guarded so the module still loads on 3.9 and dependent tests skip there.
>
> Two small test-framework fixes ride along: the set_machine probe VM now
> gets the per-test workdir instead of the hardcoded /var/tmp default, and
> the Aspeed OpenBMC helper can optionally assert the booted device-tree
> model.
>
> 4. Catalina follow-ups
>
> The final three commits are the tail of the earlier "hw/arm: improve
> Aspeed Catalina BMC emulation" series: functional tests for the PCA9555
> and PCA9554 IO expanders, plus the PCA9554 change that drives its input
> pins externally. They were held back because reaching such devices from
> a test needs a stable way to reference them by bus position — the QOM
> re-parenting patch tried then was rejected, and the device locator now
> unblocks them.
>
> Based on:
>
> repo: https://github.com/legoater/qemu.git
> branch: aspeed-11.1
> commit: 04d27371e3 ("hw/arm: catalina: add NIC and FIO temperature sensors")
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> Changes in v2:
> - Simplify TMP105 multiple variant initialization
> - Improve reliability against malformed device locator strings
> - Remove an obsolete reference to INA23x devices from a commit log message
> - Rebase patch series on aspeed-next branch
> - Fix invalid JSON syntax in SanMiguel FRU descriptions
> - Link to v1: https://lore.kernel.org/qemu-devel/20260729-sanmiguel-bmc-locator-v1-0-c8f9a8d101f6@free.fr
>
> To: qemu-devel@nongnu.org
> Cc: Philippe Mathieu-Daudé <philmd@mailo.com>
> Cc: Cédric Le Goater <clg@kaod.org>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Steven Lee <steven_lee@aspeedtech.com>
> Cc: Jamin Lin <jamin_lin@aspeedtech.com>
> Cc: Kane Chen <kane_chen@aspeedtech.com>
> Cc: Andrew Jeffery <andrew@codeconstruct.com.au>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: qemu-arm@nongnu.org
> Cc: Fabiano Rosas <farosas@suse.de>
> Cc: Laurent Vivier <lvivier@redhat.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <th.huth+qemu@posteo.eu>
> Cc: "Daniel P. Berrangé" <berrange@redhat.com>
>
> ---
> Emmanuel Blot (25):
> hw/sensor: tmp105: make device state private to the implementation
> hw/sensor: tmp105: name the parent object field parent_obj
> hw/sensor: tmp105: implement Resettable reset
> hw/sensor: tmp105: enforce the configurable fault queue
> hw/sensor: tmp105: describe the temperature property
> hw/arm: aspeed: guard board-local temperature-sensor aliases
> hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
> tests/qtest: tmp105: cover the ALERT fault queue
> tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
> tests/qtest: tmp105: cover one-shot and fault-queue write immunity
> tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
> hw/arm: sanmiguel: add Facebook SanMiguel BMC machine
> hw/arm: sanmiguel: populate EEPROM data
> hw/arm: catalina: use the real TMP75 model
> hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
> tests/functional: aspeed: optionally check the device tree model on boot
> tests/functional: give the set_machine probe VM the test workdir
> tests/functional: add a pure-Python device-locator resolver
> tests/functional: add a device-locator self-check
> tests/functional: aspeed: add hwmon sensor read helpers
> tests/functional/arm: sanmiguel: add BMC boot test
> tests/functional/arm: catalina: test TMP75
> tests/functional/arm: catalina: test PCA9555 IO expander via QOM
> hw/arm: catalina: drive pca9554 io expander input pins externally
> tests/functional/arm: catalina: test PCA9554 IO expander via QOM
>
> hw/arm/aspeed_ast2600_catalina.c | 14 +-
> hw/arm/aspeed_ast2600_fuji.c | 16 +-
> hw/arm/aspeed_ast2600_sanmiguel.c | 405 ++++++++++++++++++
> hw/arm/meson.build | 1 +
> hw/sensor/tmp105.c | 304 ++++++++++---
> include/hw/sensor/tmp105.h | 46 +-
> tests/functional/arm/meson.build | 3 +
> tests/functional/arm/test_aspeed_catalina.py | 107 ++++-
> tests/functional/arm/test_aspeed_sanmiguel.py | 72 ++++
> tests/functional/arm/test_device_locator.py | 120 ++++++
> tests/functional/aspeed.py | 21 +-
> tests/functional/qemu_test/locator.py | 586 ++++++++++++++++++++++++++
> tests/functional/qemu_test/testcase.py | 2 +-
> tests/qtest/tmp105-test.c | 359 ++++++++++++++++
> 14 files changed, 1934 insertions(+), 122 deletions(-)
> ---
> base-commit: e5d3f5ac8633de310066f14862b17e4cac8ef8a5
> change-id: 20260729-sanmiguel-bmc-locator-db13cceae66c
>
> Best regards,
> --
> Emmanuel Blot <emmanuel.blot@free.fr>
[-- Attachment #2: Type: text/html, Size: 10544 bytes --]
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 01/25] hw/sensor: tmp105: make device state private to the implementation
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:58 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:58 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> The TMP105State struct, its cast macro and typedef are used only within
> tmp105.c. Move them out of the public header into the source file and
> drop the now-unused includes; the header is reduced to the TYPE_TMP105
> name, with its guard renamed to the path-derived form per current QEMU
> convention.
>
> While here, normalize the source file include order to match the other
> hw/sensor devices.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/sensor/tmp105.c | 45 ++++++++++++++++++++++++++++++++++++++++-----
> include/hw/sensor/tmp105.h | 40 ++--------------------------------------
> 2 files changed, 42 insertions(+), 43 deletions(-)
>
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index c5089d74f4b..6069c0905d9 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -19,16 +19,51 @@
> */
>
> #include "qemu/osdep.h"
> -#include "hw/i2c/i2c.h"
> -#include "hw/core/irq.h"
> -#include "migration/vmstate.h"
> -#include "hw/sensor/tmp105.h"
> +#include "qemu/module.h"
> #include "qapi/error.h"
> #include "qapi/visitor.h"
> -#include "qemu/module.h"
> +#include "qom/object.h"
> +#include "hw/sensor/tmp105.h"
> +#include "hw/sensor/tmp105_regs.h"
> +#include "hw/core/irq.h"
> #include "hw/core/registerfields.h"
> +#include "hw/i2c/i2c.h"
> +#include "migration/vmstate.h"
> #include "trace.h"
>
> +OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
> +
> +/**
> + * TMP105State:
> + * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
> + * temperature. See Table 8 in the data sheet.
> + *
> + * @see_also: http://www.ti.com/lit/gpn/tmp105
> + */
> +struct TMP105State {
> + /*< private >*/
> + I2CSlave i2c;
> + /*< public >*/
> +
> + uint8_t len;
> + uint8_t buf[2];
> + qemu_irq pin;
> +
> + uint8_t pointer;
> + uint8_t config;
> + int16_t temperature;
> + int16_t limit[2];
> + int faults;
> + uint8_t alarm;
> + /*
> + * The TMP105 initially looks for a temperature rising above T_high;
> + * once this is detected, the condition it looks for next is the
> + * temperature falling below T_low. This flag is false when initially
> + * looking for T_high, true when looking for T_low.
> + */
> + bool detect_falling;
> +};
> +
> FIELD(CONFIG, SHUTDOWN_MODE, 0, 1)
> FIELD(CONFIG, THERMOSTAT_MODE, 1, 1)
> FIELD(CONFIG, POLARITY, 2, 1)
> diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
> index 244e2989feb..daece592e9b 100644
> --- a/include/hw/sensor/tmp105.h
> +++ b/include/hw/sensor/tmp105.h
> @@ -11,45 +11,9 @@
> * This work is licensed under the terms of the GNU GPL, version 2 or
> * later. See the COPYING file in the top-level directory.
> */
> -#ifndef QEMU_TMP105_H
> -#define QEMU_TMP105_H
> -
> -#include "hw/i2c/i2c.h"
> -#include "hw/sensor/tmp105_regs.h"
> -#include "qom/object.h"
> +#ifndef HW_SENSOR_TMP105_H
> +#define HW_SENSOR_TMP105_H
>
> #define TYPE_TMP105 "tmp105"
> -OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
> -
> -/**
> - * TMP105State:
> - * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
> - * temperature. See Table 8 in the data sheet.
> - *
> - * @see_also: http://www.ti.com/lit/gpn/tmp105
> - */
> -struct TMP105State {
> - /*< private >*/
> - I2CSlave i2c;
> - /*< public >*/
> -
> - uint8_t len;
> - uint8_t buf[2];
> - qemu_irq pin;
> -
> - uint8_t pointer;
> - uint8_t config;
> - int16_t temperature;
> - int16_t limit[2];
> - int faults;
> - uint8_t alarm;
> - /*
> - * The TMP105 initially looks for a temperature rising above T_high;
> - * once this is detected, the condition it looks for next is the
> - * temperature falling below T_low. This flag is false when initially
> - * looking for T_high, true when looking for T_low.
> - */
> - bool detect_falling;
> -};
>
> #endif
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 02/25] hw/sensor: tmp105: name the parent object field parent_obj
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:58 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:58 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> QEMU coding conventions expect the embedded parent object of a QOM type
> to be named parent_obj. Rename the TMP105State I2CSlave member
> accordingly and update its references; the change is purely cosmetic.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/sensor/tmp105.c | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index 6069c0905d..aabeb34f08 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -42,7 +42,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
> */
> struct TMP105State {
> /*< private >*/
> - I2CSlave i2c;
> + I2CSlave parent_obj;
> /*< public >*/
>
> uint8_t len;
> @@ -187,12 +187,12 @@ static void tmp105_read(TMP105State *s)
> break;
> }
>
> - trace_tmp105_read(s->i2c.address, s->pointer);
> + trace_tmp105_read(s->parent_obj.address, s->pointer);
> }
>
> static void tmp105_write(TMP105State *s)
> {
> - trace_tmp105_write(s->i2c.address, s->pointer);
> + trace_tmp105_write(s->parent_obj.address, s->pointer);
>
> switch (s->pointer & 3) {
> case TMP105_REG_TEMPERATURE:
> @@ -200,7 +200,7 @@ static void tmp105_write(TMP105State *s)
>
> case TMP105_REG_CONFIG:
> if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
> - trace_tmp105_write_shutdown(s->i2c.address);
> + trace_tmp105_write_shutdown(s->parent_obj.address);
> }
> s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
> s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> @@ -305,7 +305,7 @@ static const VMStateDescription vmstate_tmp105 = {
> VMSTATE_INT16(temperature, TMP105State),
> VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
> VMSTATE_UINT8(alarm, TMP105State),
> - VMSTATE_I2C_SLAVE(i2c, TMP105State),
> + VMSTATE_I2C_SLAVE(parent_obj, TMP105State),
> VMSTATE_END_OF_LIST()
> },
> .subsections = (const VMStateDescription * const []) {
> @@ -338,7 +338,7 @@ static void tmp105_realize(DeviceState *dev, Error **errp)
>
> qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
>
> - tmp105_reset(&s->i2c);
> + tmp105_reset(&s->parent_obj);
> }
>
> static void tmp105_initfn(Object *obj)
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 03/25] hw/sensor: tmp105: implement Resettable reset
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:58 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:58 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Previously the device reset routine was invoked once from realize() and
> never registered with the reset subsystem, so a system or bus reset left
> the TMP105 registers untouched. Convert tmp105_reset() into a proper
> Resettable hold phase (tmp105_reset_hold) registered through
> ResettableClass::phases.hold, and drop the manual call from realize() as
> the reset framework now drives it. This matches the scheme used by the
> other hw/sensor I2C devices.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/sensor/tmp105.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index aabeb34f08..f7d0c738ca 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -314,9 +314,9 @@ static const VMStateDescription vmstate_tmp105 = {
> }
> };
>
> -static void tmp105_reset(I2CSlave *i2c)
> +static void tmp105_reset_hold(Object *obj, ResetType type)
> {
> - TMP105State *s = TMP105(i2c);
> + TMP105State *s = TMP105(obj);
>
> s->temperature = 0;
> s->pointer = 0;
> @@ -337,8 +337,6 @@ static void tmp105_realize(DeviceState *dev, Error **errp)
> TMP105State *s = TMP105(i2c);
>
> qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
> -
> - tmp105_reset(&s->parent_obj);
> }
>
> static void tmp105_initfn(Object *obj)
> @@ -352,11 +350,13 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
> {
> DeviceClass *dc = DEVICE_CLASS(klass);
> I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
> + ResettableClass *rc = RESETTABLE_CLASS(klass);
>
> dc->realize = tmp105_realize;
> k->event = tmp105_event;
> k->recv = tmp105_rx;
> k->send = tmp105_tx;
> + rc->phases.hold = tmp105_reset_hold;
> dc->vmsd = &vmstate_tmp105;
> }
>
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 04/25] hw/sensor: tmp105: enforce the configurable fault queue
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> The fault-queue depth selected in the configuration register was decoded
> but never consulted, so the ALERT pin tripped on the very first
> out-of-limit conversion regardless of the programmed depth.
>
> Track the consecutive-fault count and only change the ALERT state once
> it reaches the programmed depth; any in-range conversion clears the
> count. The default configuration keeps the previous behaviour. The count
> is migrated through an optional subsection so existing streams stay
> compatible.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/sensor/tmp105.c | 87 ++++++++++++++++++++++++++++++++++--------------------
> 1 file changed, 55 insertions(+), 32 deletions(-)
>
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index f7d0c738ca..5c77f991cc 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -54,6 +54,7 @@ struct TMP105State {
> int16_t temperature;
> int16_t limit[2];
> int faults;
> + uint8_t fault_count;
> uint8_t alarm;
> /*
> * The TMP105 initially looks for a temperature rising above T_high;
> @@ -78,43 +79,40 @@ static void tmp105_interrupt_update(TMP105State *s)
>
> static void tmp105_alarm_update(TMP105State *s, bool one_shot)
> {
> + bool fault;
> +
> if (FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE) && !one_shot) {
> return;
> }
>
> - if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
> - /*
> - * TM == 1 : Interrupt mode. We signal Alert when the
> - * temperature rises above T_high, and expect the guest to clear
> - * it (eg by reading a device register).
> - */
> - if (s->detect_falling) {
> - if (s->temperature < s->limit[0]) {
> - s->alarm = 1;
> - s->detect_falling = false;
> - }
> - } else {
> - if (s->temperature >= s->limit[1]) {
> - s->alarm = 1;
> - s->detect_falling = true;
> - }
> - }
> + /*
> + * A fault is a conversion that lies outside the limit currently being
> + * watched: above T_high while looking for the alarm to trip, below T_low
> + * afterwards.
> + */
> + if (s->detect_falling) {
> + fault = s->temperature < s->limit[0];
> } else {
> - /*
> - * TM == 0 : Comparator mode. We signal Alert when the temperature
> - * rises above T_high, and stop signalling it when the temperature
> - * falls below T_low.
> - */
> + fault = s->temperature >= s->limit[1];
> + }
> +
> + if (!fault) {
> + s->fault_count = 0;
> + } else if (++s->fault_count >= s->faults) {
> + s->fault_count = 0;
> if (s->detect_falling) {
> - if (s->temperature < s->limit[0]) {
> - s->alarm = 0;
> - s->detect_falling = false;
> - }
> + /*
> + * Temperature fell back below T_low. In comparator mode (TM == 0)
> + * the alarm is released; in interrupt mode (TM == 1) it is
> + * asserted again and the guest is expected to clear it by reading
> + * a register.
> + */
> + s->alarm = FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE);
> + s->detect_falling = false;
> } else {
> - if (s->temperature >= s->limit[1]) {
> - s->alarm = 1;
> - s->detect_falling = true;
> - }
> + /* Temperature rose to or above T_high: assert the alarm. */
> + s->alarm = 1;
> + s->detect_falling = true;
> }
> }
>
> @@ -204,7 +202,12 @@ static void tmp105_write(TMP105State *s)
> }
> s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
> s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> - tmp105_alarm_update(s, FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT));
> + if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
> + FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
> + tmp105_alarm_update(s, true);
> + } else {
> + tmp105_interrupt_update(s);
> + }
> break;
>
> case TMP105_REG_T_LOW:
> @@ -213,7 +216,7 @@ static void tmp105_write(TMP105State *s)
> s->limit[s->pointer & 1] = (int16_t)
> ((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
> }
> - tmp105_alarm_update(s, false);
> + tmp105_interrupt_update(s);
> break;
> }
> }
> @@ -281,6 +284,13 @@ static bool detect_falling_needed(void *opaque)
> return s->detect_falling;
> }
>
> +static bool fault_count_needed(void *opaque)
> +{
> + const TMP105State *s = opaque;
> +
> + return s->fault_count != 0;
> +}
> +
> static const VMStateDescription vmstate_tmp105_detect_falling = {
> .name = "TMP105/detect-falling",
> .version_id = 1,
> @@ -292,6 +302,17 @@ static const VMStateDescription vmstate_tmp105_detect_falling = {
> }
> };
>
> +static const VMStateDescription vmstate_tmp105_fault_count = {
> + .name = "TMP105/fault-count",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .needed = fault_count_needed,
> + .fields = (const VMStateField[]) {
> + VMSTATE_UINT8(fault_count, TMP105State),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> static const VMStateDescription vmstate_tmp105 = {
> .name = "TMP105",
> .version_id = 0,
> @@ -310,6 +331,7 @@ static const VMStateDescription vmstate_tmp105 = {
> },
> .subsections = (const VMStateDescription * const []) {
> &vmstate_tmp105_detect_falling,
> + &vmstate_tmp105_fault_count,
> NULL
> }
> };
> @@ -322,6 +344,7 @@ static void tmp105_reset_hold(Object *obj, ResetType type)
> s->pointer = 0;
> s->config = 0;
> s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> + s->fault_count = 0;
> s->alarm = 0;
> s->detect_falling = false;
>
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 05/25] hw/sensor: tmp105: describe the temperature property
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Attach a description to the dynamic "temperature" QOM property so it is
> documented in the QOM introspection output (qom-list-properties). The
> value is expressed in millidegrees Celsius.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/sensor/tmp105.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index 5c77f991cc..fefa661711 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -367,6 +367,8 @@ static void tmp105_initfn(Object *obj)
> object_property_add(obj, "temperature", "int",
> tmp105_get_temperature,
> tmp105_set_temperature, NULL, NULL);
> + object_property_set_description(obj, "temperature",
> + "Temperature, in millidegrees Celsius");
> }
>
> static void tmp105_class_init(ObjectClass *klass, const void *data)
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 06/25] hw/arm: aspeed: guard board-local temperature-sensor aliases
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> The Catalina and Fuji machines define TYPE_TMP75 (and, on Fuji,
> TYPE_LM75) as local aliases of TYPE_TMP105 because no distinct model
> existed. A following change adds a canonical TYPE_TMP75 to
> hw/sensor/tmp105.h that would clash with these aliases.
>
> Guard each alias with an #undef so the header definition can be
> introduced without a redefinition warning, ahead of switching the boards
> to the real models. No functional change.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/arm/aspeed_ast2600_catalina.c | 5 +++++
> hw/arm/aspeed_ast2600_fuji.c | 6 ++++++
> 2 files changed, 11 insertions(+)
>
> diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
> index f714c9d1b32..33e75338efc 100644
> --- a/hw/arm/aspeed_ast2600_catalina.c
> +++ b/hw/arm/aspeed_ast2600_catalina.c
> @@ -22,6 +22,11 @@
> #define CATALINA_BMC_HW_STRAP2 0x00000800
> #define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
>
> +/*
> + * Guard the alias against the TYPE_TMP75 that hw/sensor/tmp105.h now
> + * defines.
> + */
> +#undef TYPE_TMP75
> #define TYPE_TMP75 TYPE_TMP105
> #define TYPE_TMP421 "tmp421"
> #define TYPE_DS1338 "ds1338"
> diff --git a/hw/arm/aspeed_ast2600_fuji.c b/hw/arm/aspeed_ast2600_fuji.c
> index 37db252e276..6dc3535f0cb 100644
> --- a/hw/arm/aspeed_ast2600_fuji.c
> +++ b/hw/arm/aspeed_ast2600_fuji.c
> @@ -15,7 +15,13 @@
> #include "hw/sensor/tmp105.h"
> #include "hw/nvram/eeprom_at24c.h"
>
> +/*
> + * Guard the aliases against the TYPE_TMP75 that hw/sensor/tmp105.h now defines;
> + * TYPE_LM75 is guarded too for consistency (no LM75 model exists yet).
> + */
> +#undef TYPE_LM75
> #define TYPE_LM75 TYPE_TMP105
> +#undef TYPE_TMP75
> #define TYPE_TMP75 TYPE_TMP105
> #define TYPE_TMP422 "tmp422"
>
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 07/25] hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> The TI TMP75/TMP175 and the NXP LM75B share the TMP105 register map and
> control semantics, differing only in a few details. Model them as
> variants of the TMP105, parameterised by a small per-type class
> descriptor covering the fault-queue depths, the writable config bits,
> the converter resolution, the set-point masks and the TMP75's
> alert-clear on thermostat-mode change.
>
> The variant is class data, not migrated state, so the wire format is
> unchanged and all variants share the existing vmstate.
>
> While here, align the shared shutdown handling with the hardware:
> entering shutdown now clears the ALERT/OS output in interrupt mode,
> correcting the base TMP105 as well.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/sensor/tmp105.c | 176 ++++++++++++++++++++++++++++++++++++---------
> include/hw/sensor/tmp105.h | 6 +-
> 2 files changed, 148 insertions(+), 34 deletions(-)
>
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index fefa661711..ece0650d70 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -1,5 +1,5 @@
> /*
> - * Texas Instruments TMP105 temperature sensor.
> + * Texas Instruments TMP105/TMP75/TMP175/LM75B temperature sensor.
> *
> * Copyright (C) 2008 Nokia Corporation
> * Written by Andrzej Zaborowski <andrew@openedhand.com>
> @@ -16,6 +16,13 @@
> *
> * You should have received a copy of the GNU General Public License along
> * with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * Limitations:
> + * - The SMBus Alert Response Address protocol and the general-call commands
> + * are not implemented.
> + * - The over-limit comparison uses the full 8.8 fixed-point temperature and a
> + * ">=" boundary for every variant. The LM75B's 9-bit comparison quantisation
> + * and strict-exceed boundary are therefore only approximated.
> */
>
> #include "qemu/osdep.h"
> @@ -31,15 +38,8 @@
> #include "migration/vmstate.h"
> #include "trace.h"
>
> -OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
> +OBJECT_DECLARE_TYPE(TMP105State, TMP105Class, TMP105)
>
> -/**
> - * TMP105State:
> - * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
> - * temperature. See Table 8 in the data sheet.
> - *
> - * @see_also: http://www.ti.com/lit/gpn/tmp105
> - */
> struct TMP105State {
> /*< private >*/
> I2CSlave parent_obj;
> @@ -53,7 +53,7 @@ struct TMP105State {
> uint8_t config;
> int16_t temperature;
> int16_t limit[2];
> - int faults;
> + uint8_t faults;
> uint8_t fault_count;
> uint8_t alarm;
> /*
> @@ -65,6 +65,27 @@ struct TMP105State {
> bool detect_falling;
> };
>
> +/*
> + * Per-device-model parameters. The TMP105, TMP75, TMP175 and LM75B share the
> + * same register map and control semantics; they differ only in a handful of
> + * details captured here.
> + *
> + * @faultq: fault-queue length table selected by Config bits
> + * @config_wmask: writable Config bits. The LM75B has no resolution (R1:R0) or
> + * one-shot (OS) bits.
> + * @fixed_res: converter resolution field pinned by the device, or -1 when it is
> + * software-selectable.
> + * @limit_lsb_mask: low-byte mask applied to the T_LOW/T_HIGH limit registers.
> + */
> +struct TMP105Class {
> + I2CSlaveClass parent_class;
> + const uint8_t *faultq;
> + uint8_t config_wmask;
> + int8_t fixed_res;
> + uint8_t limit_lsb_mask;
> + bool tm_change_clears_alert;
> +};
> +
> FIELD(CONFIG, SHUTDOWN_MODE, 0, 1)
> FIELD(CONFIG, THERMOSTAT_MODE, 1, 1)
> FIELD(CONFIG, POLARITY, 2, 1)
> @@ -152,10 +173,11 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
> tmp105_alarm_update(s, false);
> }
>
> -static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
> -
> static void tmp105_read(TMP105State *s)
> {
> + const TMP105Class *tc = TMP105_GET_CLASS(s);
> + int res;
> +
> s->len = 0;
>
> if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
> @@ -165,9 +187,11 @@ static void tmp105_read(TMP105State *s)
>
> switch (s->pointer & 3) {
> case TMP105_REG_TEMPERATURE:
> + res = tc->fixed_res >= 0 ? tc->fixed_res :
> + FIELD_EX8(s->config, CONFIG, CONVERTER_RESOLUTION);
> s->buf[s->len++] = (((uint16_t) s->temperature) >> 8);
> s->buf[s->len++] = (((uint16_t) s->temperature) >> 0) &
> - (0xf0 << (FIELD_EX8(~s->config, CONFIG, CONVERTER_RESOLUTION)));
> + (0xf0 << (3 - res));
> break;
>
> case TMP105_REG_CONFIG:
> @@ -190,6 +214,10 @@ static void tmp105_read(TMP105State *s)
>
> static void tmp105_write(TMP105State *s)
> {
> + const TMP105Class *tc = TMP105_GET_CLASS(s);
> + uint8_t config, one_shot;
> + bool waking;
> +
> trace_tmp105_write(s->parent_obj.address, s->pointer);
>
> switch (s->pointer & 3) {
> @@ -197,14 +225,27 @@ static void tmp105_write(TMP105State *s)
> break;
>
> case TMP105_REG_CONFIG:
> - if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
> + config = s->buf[0] & tc->config_wmask;
> + if (FIELD_EX8(config & ~s->config, CONFIG, SHUTDOWN_MODE)) {
> trace_tmp105_write_shutdown(s->parent_obj.address);
> + if (FIELD_EX8(config, CONFIG, THERMOSTAT_MODE)) {
> + s->alarm = 0;
> + }
> }
> - s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
> - s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> - if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
> - FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
> + if (tc->tm_change_clears_alert &&
> + FIELD_EX8(config ^ s->config, CONFIG, THERMOSTAT_MODE)) {
> + s->alarm = 0;
> + s->fault_count = 0;
> + s->detect_falling = false;
> + }
> + waking = FIELD_EX8(s->config & ~config, CONFIG, SHUTDOWN_MODE);
> + one_shot = FIELD_EX8(config, CONFIG, ONE_SHOT);
> + s->config = FIELD_DP8(config, CONFIG, ONE_SHOT, 0);
> + s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> + if (one_shot && FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
> tmp105_alarm_update(s, true);
> + } else if (waking) {
> + tmp105_alarm_update(s, false);
> } else {
> tmp105_interrupt_update(s);
> }
> @@ -214,7 +255,8 @@ static void tmp105_write(TMP105State *s)
> case TMP105_REG_T_HIGH:
> if (s->len >= 3) {
> s->limit[s->pointer & 1] = (int16_t)
> - ((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
> + ((((uint16_t) s->buf[0]) << 8) |
> + (s->buf[1] & tc->limit_lsb_mask));
> }
> tmp105_interrupt_update(s);
> break;
> @@ -265,8 +307,9 @@ static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
> static int tmp105_post_load(void *opaque, int version_id)
> {
> TMP105State *s = opaque;
> + const TMP105Class *tc = TMP105_GET_CLASS(s);
>
> - s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> + s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
>
> tmp105_interrupt_update(s);
> return 0;
> @@ -339,11 +382,12 @@ static const VMStateDescription vmstate_tmp105 = {
> static void tmp105_reset_hold(Object *obj, ResetType type)
> {
> TMP105State *s = TMP105(obj);
> + const TMP105Class *tc = TMP105_GET_CLASS(s);
>
> s->temperature = 0;
> s->pointer = 0;
> s->config = 0;
> - s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> + s->faults = tc->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> s->fault_count = 0;
> s->alarm = 0;
> s->detect_falling = false;
> @@ -371,11 +415,25 @@ static void tmp105_initfn(Object *obj)
> "Temperature, in millidegrees Celsius");
> }
>
> +/*
> + * Fault-queue length selected by Config F1:F0. Row 0 (1/2/4/6) is used by the
> + * TMP105, TMP175 and LM75B; row 1 (1/2/3/4) by the TMP75.
> + */
> +static const uint8_t tmp105_faultq[][4] = {
> + { 1, 2, 4, 6 },
> + { 1, 2, 3, 4 },
> +};
> +
> +/* The F1:F0 field must never index past a fault-queue table row. */
> +QEMU_BUILD_BUG_ON((1 << R_CONFIG_FAULT_QUEUE_LENGTH) - 1 >=
> + ARRAY_SIZE(tmp105_faultq[0]));
> +
> static void tmp105_class_init(ObjectClass *klass, const void *data)
> {
> DeviceClass *dc = DEVICE_CLASS(klass);
> I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
> ResettableClass *rc = RESETTABLE_CLASS(klass);
> + TMP105Class *tc = TMP105_CLASS(klass);
>
> dc->realize = tmp105_realize;
> k->event = tmp105_event;
> @@ -383,19 +441,71 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
> k->send = tmp105_tx;
> rc->phases.hold = tmp105_reset_hold;
> dc->vmsd = &vmstate_tmp105;
> +
> + tc->faultq = tmp105_faultq[0];
> + tc->config_wmask = 0xff;
> + tc->fixed_res = -1;
> + tc->limit_lsb_mask = 0xf0;
> + tc->tm_change_clears_alert = false;
> +}
> +
> +static void tmp175_class_init(ObjectClass *klass, const void *data)
> +{
> + TMP105Class *tc = TMP105_CLASS(klass);
> +
> + tc->faultq = tmp105_faultq[0];
> + tc->config_wmask = 0xff;
> + tc->fixed_res = -1;
> + tc->limit_lsb_mask = 0xf0;
> + tc->tm_change_clears_alert = false;
> +}
> +
> +static void tmp75_class_init(ObjectClass *klass, const void *data)
> +{
> + TMP105Class *tc = TMP105_CLASS(klass);
> +
> + tc->faultq = tmp105_faultq[1];
> + tc->config_wmask = 0xff;
> + tc->fixed_res = -1;
> + tc->limit_lsb_mask = 0xf0;
> + tc->tm_change_clears_alert = true;
> +}
> +
> +static void lm75b_class_init(ObjectClass *klass, const void *data)
> +{
> + TMP105Class *tc = TMP105_CLASS(klass);
> +
> + tc->faultq = tmp105_faultq[0];
> + tc->config_wmask = 0x1f;
> + tc->fixed_res = 2;
> + tc->limit_lsb_mask = 0x80;
> + tc->tm_change_clears_alert = false;
> }
>
> -static const TypeInfo tmp105_info = {
> - .name = TYPE_TMP105,
> - .parent = TYPE_I2C_SLAVE,
> - .instance_size = sizeof(TMP105State),
> - .instance_init = tmp105_initfn,
> - .class_init = tmp105_class_init,
> +static const TypeInfo tmp105_types[] = {
> + {
> + .name = TYPE_TMP105,
> + .parent = TYPE_I2C_SLAVE,
> + .instance_size = sizeof(TMP105State),
> + .class_size = sizeof(TMP105Class),
> + .instance_init = tmp105_initfn,
> + .class_init = tmp105_class_init,
> + },
> + {
> + .name = TYPE_TMP175,
> + .parent = TYPE_TMP105,
> + .class_init = tmp175_class_init,
> + },
> + {
> + .name = TYPE_TMP75,
> + .parent = TYPE_TMP105,
> + .class_init = tmp75_class_init,
> + },
> + {
> + .name = TYPE_LM75B,
> + .parent = TYPE_TMP105,
> + .class_init = lm75b_class_init,
> + },
> };
>
> -static void tmp105_register_types(void)
> -{
> - type_register_static(&tmp105_info);
> -}
> -
> -type_init(tmp105_register_types)
> +DEFINE_TYPES(tmp105_types)
> diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
> index daece592e9..0698aeead7 100644
> --- a/include/hw/sensor/tmp105.h
> +++ b/include/hw/sensor/tmp105.h
> @@ -1,5 +1,5 @@
> /*
> - * Texas Instruments TMP105 Temperature Sensor
> + * Texas Instruments TMP105/TMP75/TMP175/LM75B Temperature Sensor
> *
> * Browse the data sheet:
> *
> @@ -14,6 +14,10 @@
> #ifndef HW_SENSOR_TMP105_H
> #define HW_SENSOR_TMP105_H
>
> +/* TMP75, TMP175 and NXP LM75B are register-compatible with TMP105. */
> #define TYPE_TMP105 "tmp105"
> +#define TYPE_TMP175 "tmp175"
> +#define TYPE_TMP75 "tmp75"
> +#define TYPE_LM75B "lm75b"
>
> #endif
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 08/25] tests/qtest: tmp105: cover the ALERT fault queue
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Add two qgraph tests exercising the ALERT pin, the only way the TMP105
> exposes its alarm state. One checks the default single-fault behaviour;
> the other checks a deeper fault queue, including the running-count reset
> on an in-range conversion and its effect on both asserting and releasing
> the alarm.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> tests/qtest/tmp105-test.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
>
> diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
> index 3b114a50f55..29a031fb7bf 100644
> --- a/tests/qtest/tmp105-test.c
> +++ b/tests/qtest/tmp105-test.c
> @@ -17,6 +17,11 @@
>
> #define TMP105_TEST_ID "tmp105-test"
> #define TMP105_TEST_ADDR 0x49
> +#define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
> +
> +#define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
> +#define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
> +#define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
>
> static int qmp_tmp105_get_temperature(const char *id)
> {
> @@ -105,6 +110,62 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
> g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230);
> }
>
> +/*
> + * The TMP105 exposes its alarm state only through the ALERT pin.
> + */
> +static void test_alert_single_fault(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +
> + qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
> + g_assert_false(get_irq(0));
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_true(get_irq(0));
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
> + g_assert_false(get_irq(0));
> +}
> +
> +static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> + int i;
> +
> + qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
> +
> + /* Comparator mode, active-high ALERT, fault queue of four. */
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
> + g_assert_false(get_irq(0));
> +
> + for (i = 0; i < 3; i++) {
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_false(get_irq(0));
> + }
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 25000);
> + g_assert_false(get_irq(0));
> +
> + for (i = 0; i < 3; i++) {
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_false(get_irq(0));
> + }
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_true(get_irq(0));
> +
> + for (i = 0; i < 3; i++) {
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
> + g_assert_true(get_irq(0));
> + }
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
> + g_assert_false(get_irq(0));
> +}
> +
> static void tmp105_register_nodes(void)
> {
> QOSGraphEdgeOptions opts = {
> @@ -116,5 +177,7 @@ static void tmp105_register_nodes(void)
> qos_node_consumes("tmp105", "i2c-bus", &opts);
>
> qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
> + qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
> + qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
> }
> libqos_init(tmp105_register_nodes);
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 09/25] tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Exercise the per-variant differences added to the model: the TMP75 and
> TMP175 fault-queue depths, the TMP75 alert-clear on thermostat-mode
> change, and the LM75B's reserved config bits and fixed set-point and
> temperature resolutions.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> tests/qtest/tmp105-test.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 138 insertions(+)
>
> diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
> index 29a031fb7b..ea6803715a 100644
> --- a/tests/qtest/tmp105-test.c
> +++ b/tests/qtest/tmp105-test.c
> @@ -19,9 +19,20 @@
> #define TMP105_TEST_ADDR 0x49
> #define TMP105_TEST_PATH "/machine/peripheral/" TMP105_TEST_ID
>
> +#define TMP75_TEST_ID "tmp75-test"
> +#define TMP75_TEST_PATH "/machine/peripheral/" TMP75_TEST_ID
> +
> +#define TMP175_TEST_ID "tmp175-test"
> +#define TMP175_TEST_PATH "/machine/peripheral/" TMP175_TEST_ID
> +
> +#define LM75B_TEST_ID "lm75b-test"
> +#define LM75B_TEST_PATH "/machine/peripheral/" LM75B_TEST_ID
> +
> #define TMP105_CONFIG_POL (1 << 2) /* ALERT active-high when set */
> +#define TMP105_CONFIG_TM (1 << 1) /* interrupt (thermostat) mode */
> #define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
> #define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
> +#define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
>
> static int qmp_tmp105_get_temperature(const char *id)
> {
> @@ -166,6 +177,98 @@ static void test_fault_queue(void *obj, void *data, QGuestAllocator *alloc)
> g_assert_false(get_irq(0));
> }
>
> +/*
> + * Drive @need consecutive over-limit conversions and check that the ALERT pin
> + * only asserts on the last one. This exercises the fault-queue length.
> + */
> +static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
> + const char *path, uint8_t fq_field, int need)
> +{
> + int i;
> +
> + qtest_irq_intercept_out(global_qtest, path);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ(fq_field));
> + g_assert_false(get_irq(0));
> +
> + for (i = 0; i < need - 1; i++) {
> + qmp_tmp105_set_temperature(id, 85000);
> + g_assert_false(get_irq(0));
> + }
> + qmp_tmp105_set_temperature(id, 85000);
> + g_assert_true(get_irq(0));
> +}
> +
> +/* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
> +static void test_tmp75_fault_queue(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + check_fault_queue(obj, TMP75_TEST_ID, TMP75_TEST_PATH, 2, 3);
> +}
> +
> +/* The TMP175 keeps the TMP105 mapping: F1:F0 = 10b means 4 faults. */
> +static void test_tmp175_fault_queue(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + check_fault_queue(obj, TMP175_TEST_ID, TMP175_TEST_PATH, 2, 4);
> +}
> +
> +/*
> + * Toggling the thermostat mode (TM) bit clears any active alert on the TMP75.
> + */
> +static void test_tmp75_tm_clears_alert(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> + int i;
> +
> + qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
> + for (i = 0; i < 4; i++) {
> + qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
> + }
> + g_assert_true(get_irq(0));
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_TM);
> + g_assert_false(get_irq(0));
> +}
> +
> +/*
> + * The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
> + * Config bits are reserved (read/write as zero) and the temperature register is
> + * always masked to 11 bits regardless of what is written to Config.
> + */
> +static void test_lm75b_resolution(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> + uint16_t value;
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60);
> + g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x00);
> +
> + qmp_tmp105_set_temperature(LM75B_TEST_ID, 20938);
> + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE);
> + g_assert_cmphex(value, ==, 0x14e0);
> +}
> +
> +/* The LM75B set-point registers store only 9 bits. */
> +static void test_lm75b_limits(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +
> + i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231);
> + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4200);
> +
> + i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x12b4);
> + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
> +}
> +
> static void tmp105_register_nodes(void)
> {
> QOSGraphEdgeOptions opts = {
> @@ -179,5 +282,40 @@ static void tmp105_register_nodes(void)
> qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
> qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
> qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
> +
> + /* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
> + QOSGraphEdgeOptions tmp75_opts = {
> + .extra_device_opts = "id=" TMP75_TEST_ID ",address=0x48"
> + };
> + add_qi2c_address(&tmp75_opts, &(QI2CAddress) { 0x48 });
> +
> + qos_node_create_driver("tmp75", i2c_device_create);
> + qos_node_consumes("tmp75", "i2c-bus", &tmp75_opts);
> +
> + qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
> + qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
> +
> + /* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
> + QOSGraphEdgeOptions tmp175_opts = {
> + .extra_device_opts = "id=" TMP175_TEST_ID ",address=0x4a"
> + };
> + add_qi2c_address(&tmp175_opts, &(QI2CAddress) { 0x4a });
> +
> + qos_node_create_driver("tmp175", i2c_device_create);
> + qos_node_consumes("tmp175", "i2c-bus", &tmp175_opts);
> +
> + qos_add_test("fault-queue", "tmp175", test_tmp175_fault_queue, NULL);
> +
> + /* LM75B: fixed 11-bit conversion and 9-bit set-point registers. */
> + QOSGraphEdgeOptions lm75b_opts = {
> + .extra_device_opts = "id=" LM75B_TEST_ID ",address=0x4c"
> + };
> + add_qi2c_address(&lm75b_opts, &(QI2CAddress) { 0x4c });
> +
> + qos_node_create_driver("lm75b", i2c_device_create);
> + qos_node_consumes("lm75b", "i2c-bus", &lm75b_opts);
> +
> + qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
> + qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
> }
> libqos_init(tmp105_register_nodes);
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 10/25] tests/qtest: tmp105: cover one-shot and fault-queue write immunity
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-01 6:59 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 6:59 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Verify that the one-shot (OS) bit triggers a conversion only in shutdown
> mode and is ignored in continuous mode, and that
> configuration/limit-register writes never advance the fault queue — only
> real conversions do.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> tests/qtest/tmp105-test.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 102 insertions(+)
>
> diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
> index ea6803715a..e586a634d8 100644
> --- a/tests/qtest/tmp105-test.c
> +++ b/tests/qtest/tmp105-test.c
> @@ -33,6 +33,8 @@
> #define TMP105_CONFIG_FQ_1 (0 << 3) /* fault queue: 1 consecutive fault */
> #define TMP105_CONFIG_FQ_4 (2 << 3) /* fault queue: 4 consecutive faults */
> #define TMP105_CONFIG_FQ(f) ((f) << 3) /* raw F1:F0 fault-queue field value */
> +#define TMP105_CONFIG_SD (1 << 0) /* shutdown mode */
> +#define TMP105_CONFIG_OS (1 << 7) /* one-shot conversion */
>
> static int qmp_tmp105_get_temperature(const char *id)
> {
> @@ -200,6 +202,102 @@ static void check_fault_queue(QI2CDevice *i2cdev, const char *id,
> g_assert_true(get_irq(0));
> }
>
> +/*
> + * The one-shot (OS) bit starts a conversion only in shutdown mode. In
> + * continuous mode it is ignored, so writing it must not advance the fault
> + * queue; in shutdown each OS write performs one conversion that does.
> + */
> +static void test_one_shot(void *obj, void *data, QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> + int i;
> +
> + qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_false(get_irq(0));
> +
> + for (i = 0; i < 8; i++) {
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_OS);
> + g_assert_false(get_irq(0));
> + }
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD);
> + for (i = 0; i < 2; i++) {
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
> + TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
> + g_assert_false(get_irq(0));
> + }
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL |
> + TMP105_CONFIG_FQ_4 | TMP105_CONFIG_SD | TMP105_CONFIG_OS);
> + g_assert_true(get_irq(0));
> +}
> +
> +/*
> + * Configuration and limit-register writes are not conversions and must not
> + * advance the fault queue.
> + */
> +static void test_fault_queue_ignores_writes(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> + int i;
> +
> + qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
> + g_assert_false(get_irq(0));
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_false(get_irq(0));
> +
> + for (i = 0; i < 8; i++) {
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ_4);
> + i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x5000);
> + i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x4b00);
> + g_assert_false(get_irq(0));
> + }
> +
> + for (i = 0; i < 2; i++) {
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_false(get_irq(0));
> + }
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_true(get_irq(0));
> +}
> +
> +/*
> + * Leaving shutdown (SD 1->0) resumes continuous conversion, which must
> + * re-evaluate the current temperature against the limits.
> + */
> +static void test_wake_from_shutdown(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +
> + qtest_irq_intercept_out(global_qtest, TMP105_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
> + g_assert_false(get_irq(0));
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 85000);
> + g_assert_true(get_irq(0));
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1 | TMP105_CONFIG_SD);
> + g_assert_true(get_irq(0));
> +
> + qmp_tmp105_set_temperature(TMP105_TEST_ID, 70000);
> + g_assert_true(get_irq(0));
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_FQ_1);
> + g_assert_false(get_irq(0));
> +}
> +
> /* The TMP75 maps F1:F0 = 10b to 3 consecutive faults. */
> static void test_tmp75_fault_queue(void *obj, void *data,
> QGuestAllocator *alloc)
> @@ -282,6 +380,10 @@ static void tmp105_register_nodes(void)
> qos_add_test("tx-rx", "tmp105", send_and_receive, NULL);
> qos_add_test("alert-single-fault", "tmp105", test_alert_single_fault, NULL);
> qos_add_test("fault-queue", "tmp105", test_fault_queue, NULL);
> + qos_add_test("fault-queue-ignores-writes", "tmp105",
> + test_fault_queue_ignores_writes, NULL);
> + qos_add_test("one-shot", "tmp105", test_one_shot, NULL);
> + qos_add_test("wake-from-shutdown", "tmp105", test_wake_from_shutdown, NULL);
>
> /* TMP75: register-compatible, but with a 1/2/3/4 fault queue. */
> QOSGraphEdgeOptions tmp75_opts = {
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 11/25] tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-01 7:00 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-01 7:00 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Verify that entering shutdown clears the ALERT/OS output in interrupt
> mode but leaves it asserted in comparator mode, for both the TMP75 and
> the LM75B.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> tests/qtest/tmp105-test.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
>
> diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c
> index e586a634d8..504f6b7202 100644
> --- a/tests/qtest/tmp105-test.c
> +++ b/tests/qtest/tmp105-test.c
> @@ -335,6 +335,41 @@ static void test_tmp75_tm_clears_alert(void *obj, void *data,
> g_assert_false(get_irq(0));
> }
>
> +/*
> + * Entering shutdown clears the ALERT in interrupt mode but leaves it asserted
> + * in comparator mode.
> + */
> +static void test_tmp75_shutdown_clears_alert(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> + int i;
> +
> + qtest_irq_intercept_out(global_qtest, TMP75_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3));
> + for (i = 0; i < 4; i++) {
> + qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
> + }
> + g_assert_true(get_irq(0));
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_FQ(3) |
> + TMP105_CONFIG_SD);
> + g_assert_false(get_irq(0));
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3));
> + for (i = 0; i < 4; i++) {
> + qmp_tmp105_set_temperature(TMP75_TEST_ID, 85000);
> + }
> + g_assert_true(get_irq(0));
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_FQ(3) | TMP105_CONFIG_SD);
> + g_assert_true(get_irq(0));
> +}
> +
> /*
> * The LM75B has a fixed 11-bit (0.125 C) converter: the resolution and one-shot
> * Config bits are reserved (read/write as zero) and the temperature register is
> @@ -367,6 +402,23 @@ static void test_lm75b_limits(void *obj, void *data,
> g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1280);
> }
>
> +/* The LM75B likewise resets its OS output on shutdown in interrupt mode. */
> +static void test_lm75b_shutdown_clears_alert(void *obj, void *data,
> + QGuestAllocator *alloc)
> +{
> + QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +
> + qtest_irq_intercept_out(global_qtest, LM75B_TEST_PATH);
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG, TMP105_CONFIG_POL | TMP105_CONFIG_TM);
> + qmp_tmp105_set_temperature(LM75B_TEST_ID, 85000);
> + g_assert_true(get_irq(0));
> +
> + i2c_set8(i2cdev, TMP105_REG_CONFIG,
> + TMP105_CONFIG_POL | TMP105_CONFIG_TM | TMP105_CONFIG_SD);
> + g_assert_false(get_irq(0));
> +}
> +
> static void tmp105_register_nodes(void)
> {
> QOSGraphEdgeOptions opts = {
> @@ -396,6 +448,8 @@ static void tmp105_register_nodes(void)
>
> qos_add_test("fault-queue", "tmp75", test_tmp75_fault_queue, NULL);
> qos_add_test("tm-clears-alert", "tmp75", test_tmp75_tm_clears_alert, NULL);
> + qos_add_test("shutdown-clears-alert", "tmp75",
> + test_tmp75_shutdown_clears_alert, NULL);
>
> /* TMP175: like the TMP105, with a 1/2/4/6 fault queue. */
> QOSGraphEdgeOptions tmp175_opts = {
> @@ -419,5 +473,7 @@ static void tmp105_register_nodes(void)
>
> qos_add_test("resolution", "lm75b", test_lm75b_resolution, NULL);
> qos_add_test("limits", "lm75b", test_lm75b_limits, NULL);
> + qos_add_test("shutdown-clears-alert", "lm75b",
> + test_lm75b_shutdown_clears_alert, NULL);
> }
> libqos_init(tmp105_register_nodes);
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 13/25] hw/arm: sanmiguel: populate EEPROM data
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-02 5:50 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-02 5:50 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Populate AT24C FRU EEPROMs with real data from physical device.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/arm/aspeed_ast2600_sanmiguel.c | 275 +++++++++++++++++++++++++++++++++++++-
> 1 file changed, 269 insertions(+), 6 deletions(-)
>
> diff --git a/hw/arm/aspeed_ast2600_sanmiguel.c b/hw/arm/aspeed_ast2600_sanmiguel.c
> index 0f766449ae..251dab141f 100644
> --- a/hw/arm/aspeed_ast2600_sanmiguel.c
> +++ b/hw/arm/aspeed_ast2600_sanmiguel.c
> @@ -23,6 +23,267 @@
>
> #define TYPE_DS1338 "ds1338"
>
> +/*
> + * "Front IO" FRU data. Generated with frugen.
> + *
> + * {
> + * "board": {
> + * "mfg": "Quanta",
> + * "pname": "San Miguel FIO EVT (QEMU)",
> + * "pn": "00000000000",
> + * "serial": "0000000000000",
> + * "date": "03/10/2026 00:00",
> + * "custom": ["19-101243"]
> + * },
> + * "product": {
> + * "mfg": "Quanta",
> + * "pname": "CI-San Miguel",
> + * "pn": "10000000001",
> + * "ver": "EVT",
> + * "serial": "10000000000000001",
> + * "atag": "QEMU"
> + * }
> + * }
> + */
> +static const uint8_t fio_eeprom[] = {
> + 0x01, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0xf4, 0x01, 0x09, 0x19, 0x90,
> + 0xd2, 0xf6, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61,
> + 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c, 0x20, 0x46, 0x49, 0x4f,
> + 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a,
> + 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10,
> + 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6,
> + 0x44, 0x50, 0x24, 0x51, 0x13, 0xc1, 0x00, 0xf5, 0x01, 0x08, 0x19, 0xc6,
> + 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61,
> + 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41,
> + 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11,
> + 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11,
> + 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff
> +};
> +static const size_t fio_eeprom_len = sizeof(fio_eeprom);
> +
> +/*
> + * "Power Distribution Board" FRU data. Generated with frugen.
> + *
> + * {
> + * "chassis": {
> + * "type": 23,
> + * "pn": "",
> + * "serial": ""
> + * },
> + * "board": {
> + * "mfg": "Quanta",
> + * "pname": "San Miguel PDB EVT (QEMU)",
> + * "pn": "00000000000",
> + * "serial": "0000000000000",
> + * "date": "03/10/2026 00:00",
> + * "custom": ["19-101240"]
> + * },
> + * "product": {
> + * "mfg": "Quanta",
> + * "pname": "CI-San Miguel",
> + * "pn": "10000000001",
> + * "ver": "EVT",
> + * "serial": "10000000000000001",
> + * "atag": "QEMU"
> + * }
> + * }
> + */
> +static const uint8_t pdb_eeprom[] = {
> + 0x01, 0x00, 0x01, 0x02, 0x0b, 0x00, 0x00, 0xf1, 0x01, 0x01, 0x17, 0xc0,
> + 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x09, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
> + 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
> + 0x67, 0x75, 0x65, 0x6c, 0x20, 0x50, 0x44, 0x42, 0x20, 0x45, 0x56, 0x54,
> + 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10,
> + 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04,
> + 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x51,
> + 0x10, 0xc1, 0x00, 0x00, 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e,
> + 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
> + 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
> + 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04,
> + 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6,
> + 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff
> +};
> +static const size_t pdb_eeprom_len = sizeof(pdb_eeprom);
> +
> +/*
> + * "Secure Control Module" FRU data. Generated with frugen.
> + *
> + * {
> + * "chassis": {
> + * "type": 23,
> + * "pn": "",
> + * "serial": "",
> + * "custom": [""]
> + * },
> + * "board": {
> + * "mfg": "Quanta",
> + * "pname": "San Miguel SCM EVT (QEMU)",
> + * "pn": "00000000000",
> + * "serial": "0000000000000",
> + * "date": "03/10/2026 00:00",
> + * "custom": ["19-101242"]
> + * },
> + * "product": {
> + * "mfg": "Quanta",
> + * "pname": "CI-San Miguel",
> + * "pn": "10000000001",
> + * "ver": "EVT",
> + * "serial": "10000000000000001",
> + * "atag": "QEMU"
> + * }
> + * }
> + */
> +static const uint8_t scm_eeprom[] = {
> + 0x01, 0x00, 0x01, 0x02, 0x0b, 0x00, 0x00, 0xf1, 0x01, 0x01, 0x17, 0xc0,
> + 0xc0, 0xc0, 0xc1, 0xe6, 0x01, 0x09, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
> + 0x75, 0x61, 0x6e, 0x74, 0x61, 0xd9, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
> + 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x43, 0x4d, 0x20, 0x45, 0x56, 0x54,
> + 0x20, 0x28, 0x51, 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10,
> + 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04,
> + 0x41, 0x10, 0x04, 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x51,
> + 0x12, 0xc1, 0x00, 0xf1, 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e,
> + 0x74, 0x61, 0xcd, 0x43, 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
> + 0x67, 0x75, 0x65, 0x6c, 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
> + 0x14, 0x01, 0x83, 0xa5, 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04,
> + 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6,
> + 0xc0, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff
> +};
> +static const size_t scm_eeprom_len = sizeof(scm_eeprom);
> +
> +/*
> + * "SMM Carrier" FRU data. Generated with frugen.
> + *
> + * {
> + * "chassis": {
> + * "type": 23,
> + * "pn": "",
> + * "serial": ""
> + * },
> + * "board": {
> + * "mfg": "Quanta",
> + * "pname": "San Miguel SMM Carrier EVT (QEMU)",
> + * "pn": "00000000000",
> + * "serial": "0000000000000",
> + * "date": "03/11/2026 17:36",
> + * "custom": ["19-101238"]
> + * },
> + * "product": {
> + * "mfg": "Quanta",
> + * "pname": "CI-San Miguel",
> + * "pn": "10000000001",
> + * "ver": "EVT",
> + * "serial": "10000000000000001",
> + * "atag": "QEMU"
> + * }
> + * }
> + */
> +static const uint8_t smm_eeprom[] = {
> + 0x01, 0x00, 0x01, 0x02, 0x0c, 0x00, 0x00, 0xf0, 0x01, 0x01, 0x17, 0xc0,
> + 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x0a, 0x19, 0x4c, 0x85, 0xf7, 0xc6, 0x51,
> + 0x75, 0x61, 0x6e, 0x74, 0x61, 0xe1, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
> + 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x4d, 0x4d, 0x20, 0x43, 0x61, 0x72,
> + 0x72, 0x69, 0x65, 0x72, 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51, 0x45,
> + 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
> + 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x01,
> + 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x4d, 0x18, 0xc1, 0x00, 0x84,
> + 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43,
> + 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c,
> + 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5,
> + 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41,
> + 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff
> +};
> +static const size_t smm_eeprom_len = sizeof(smm_eeprom);
> +
> +/*
> + * "SMM Extender" FRU data. Generated with frugen.
> + *
> + * {
> + * "chassis": {
> + * "type": 23,
> + * "pn": "",
> + * "serial": ""
> + * },
> + * "board": {
> + * "mfg": "Quanta",
> + * "pname": "San Miguel SMM Extender EVT (QEMU)",
> + * "pn": "00000000000",
> + * "serial": "0000000000000",
> + * "date": "03/10/2026 00:00",
> + * "custom": ["19-101239"]
> + * },
> + * "product": {
> + * "mfg": "Quanta",
> + * "pname": "CI-San Miguel",
> + * "pn": "10000000001",
> + * "ver": "EVT",
> + * "serial": "10000000000000001",
> + * "atag": "QEMU"
> + * }
> + * }
> + */
> +static const uint8_t smm_ext_eeprom[] = {
> + 0x01, 0x00, 0x01, 0x02, 0x0c, 0x00, 0x00, 0xf0, 0x01, 0x01, 0x17, 0xc0,
> + 0xc0, 0xc1, 0x00, 0xa6, 0x01, 0x0a, 0x19, 0x90, 0xd2, 0xf6, 0xc6, 0x51,
> + 0x75, 0x61, 0x6e, 0x74, 0x61, 0xe2, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69,
> + 0x67, 0x75, 0x65, 0x6c, 0x20, 0x53, 0x4d, 0x4d, 0x20, 0x45, 0x78, 0x74,
> + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x45, 0x56, 0x54, 0x20, 0x28, 0x51,
> + 0x45, 0x4d, 0x55, 0x29, 0x8a, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10,
> + 0x04, 0x41, 0x10, 0x89, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04,
> + 0x01, 0xc0, 0x87, 0x51, 0xd6, 0x44, 0x50, 0x24, 0x4d, 0x19, 0xc1, 0x7b,
> + 0x01, 0x08, 0x19, 0xc6, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x61, 0xcd, 0x43,
> + 0x49, 0x2d, 0x53, 0x61, 0x6e, 0x20, 0x4d, 0x69, 0x67, 0x75, 0x65, 0x6c,
> + 0x89, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x14, 0x01, 0x83, 0xa5,
> + 0x4d, 0x03, 0x8d, 0x11, 0x04, 0x41, 0x10, 0x04, 0x41, 0x10, 0x04, 0x41,
> + 0x10, 0x04, 0x41, 0x11, 0x83, 0x71, 0xd9, 0xd6, 0xc0, 0xc1, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xff, 0xff
> +};
> +static const size_t smm_ext_eeprom_len = sizeof(smm_ext_eeprom);
> +
> static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
> {
> /* Reference: aspeed-bmc-facebook-sanmiguel.dts */
> @@ -56,7 +317,8 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
> /* smm_temp: lm75@48 */
> i2c_slave_create_simple(i2c[5], TYPE_TMP75, 0x48);
> /* smm_fru: eeprom@50 (24c128 = 16 KiB) */
> - at24c_eeprom_init(i2c[5], 0x50, 16 * KiB);
> + at24c_eeprom_init_rom(i2c[5], 0x50, 16 * KiB, smm_eeprom, smm_eeprom_len);
> +
> /* rtc@6f — nct3018y (ds1338 stand-in) */
> i2c_slave_create_simple(i2c[5], TYPE_DS1338, 0x6f);
>
> @@ -81,16 +343,16 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
> /* pdb_temp: lm75@4e */
> i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4e);
> /* pdb_fru: eeprom@50 (24c128 = 16 KiB) */
> - at24c_eeprom_init(i2c[9], 0x50, 16 * KiB);
> + at24c_eeprom_init_rom(i2c[9], 0x50, 16 * KiB, pdb_eeprom, pdb_eeprom_len);
>
> /* &i2c10 — SCM */
> /* scm_temp: lm75@48 */
> i2c_slave_create_simple(i2c[10], TYPE_TMP75, 0x48);
> /* scm_fru: eeprom@50 (24c128 = 16 KiB) */
> - at24c_eeprom_init(i2c[10], 0x50, 16 * KiB);
> + at24c_eeprom_init_rom(i2c[10], 0x50, 16 * KiB, scm_eeprom, scm_eeprom_len);
>
> /* &i2c11 — Switch Config */
> - /* sw_config: eeprom@50 (24c64 = 8 KiB) */
> + /* sw_config: eeprom@50 (24c64 = 8 KiB) — blank (content not modelled) */
> at24c_eeprom_init(i2c[11], 0x50, 8 * KiB);
>
> /* &i2c12 — empty */
> @@ -100,13 +362,14 @@ static void sanmiguel_bmc_i2c_init(AspeedMachineState *bmc)
> /* smm_ext_ioexp: pca9554@38 */
> i2c_slave_create_simple(i2c[13], TYPE_PCA9554, 0x38);
> /* smm_ext_fru: eeprom@55 (24c128 = 16 KiB) */
> - at24c_eeprom_init(i2c[13], 0x55, 16 * KiB);
> + at24c_eeprom_init_rom(i2c[13], 0x55, 16 * KiB, smm_ext_eeprom,
> + smm_ext_eeprom_len);
>
> /* &i2c14 — FIO */
> /* fio_ioexp: pca9555@20 */
> i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x20);
> /* fio_fru: eeprom@50 (24c64 = 8 KiB) */
> - at24c_eeprom_init(i2c[14], 0x50, 8 * KiB);
> + at24c_eeprom_init_rom(i2c[14], 0x50, 8 * KiB, fio_eeprom, fio_eeprom_len);
>
> /* &i2c15 — empty */
> }
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 14/25] hw/arm: catalina: use the real TMP75 model
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-02 5:50 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-02 5:50 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> The Catalina BMC instantiates several ti,tmp75 temperature sensors. Now
> that hw/sensor/tmp105.h provides a proper TMP75 model, drop the local
> TYPE_TMP75 alias (and its guard) so the board instantiates the accurate
> device rather than a plain TMP105.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/arm/aspeed_ast2600_catalina.c | 6 ------
> 1 file changed, 6 deletions(-)
>
> diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c
> index 33e75338ef..928e140b21 100644
> --- a/hw/arm/aspeed_ast2600_catalina.c
> +++ b/hw/arm/aspeed_ast2600_catalina.c
> @@ -22,12 +22,6 @@
> #define CATALINA_BMC_HW_STRAP2 0x00000800
> #define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
>
> -/*
> - * Guard the alias against the TYPE_TMP75 that hw/sensor/tmp105.h now
> - * defines.
> - */
> -#undef TYPE_TMP75
> -#define TYPE_TMP75 TYPE_TMP105
> #define TYPE_TMP421 "tmp421"
> #define TYPE_DS1338 "ds1338"
>
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 15/25] hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
2026-07-31 10:45 ` Emmanuel Blot via
(?)
@ 2026-09-02 5:50 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-02 5:50 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> The Fuji BMC instantiates many ti,tmp75 sensors and several NXP LM75B
> sensors; the original board code approximated both with a plain TMP105
> via local TYPE_TMP75 and TYPE_LM75 aliases (the latter mislabelling the
> LM75B parts as LM75).
>
> Now that hw/sensor/tmp105.h provides proper TMP75 and LM75B models, drop
> the local aliases (and their guards) and instantiate the accurate
> devices.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> hw/arm/aspeed_ast2600_fuji.c | 22 +++++++---------------
> 1 file changed, 7 insertions(+), 15 deletions(-)
>
> diff --git a/hw/arm/aspeed_ast2600_fuji.c b/hw/arm/aspeed_ast2600_fuji.c
> index 6dc3535f0c..fe0c294e14 100644
> --- a/hw/arm/aspeed_ast2600_fuji.c
> +++ b/hw/arm/aspeed_ast2600_fuji.c
> @@ -15,14 +15,6 @@
> #include "hw/sensor/tmp105.h"
> #include "hw/nvram/eeprom_at24c.h"
>
> -/*
> - * Guard the aliases against the TYPE_TMP75 that hw/sensor/tmp105.h now defines;
> - * TYPE_LM75 is guarded too for consistency (no LM75 model exists yet).
> - */
> -#undef TYPE_LM75
> -#define TYPE_LM75 TYPE_TMP105
> -#undef TYPE_TMP75
> -#define TYPE_TMP75 TYPE_TMP105
> #define TYPE_TMP422 "tmp422"
>
> /* Fuji hardware value */
> @@ -63,8 +55,8 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
> get_pca9548_channels(i2c[40 + i], 0x76, &i2c[80 + i * 8]);
> }
>
> - i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4c);
> - i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4d);
> + i2c_slave_create_simple(i2c[17], TYPE_LM75B, 0x4c);
> + i2c_slave_create_simple(i2c[17], TYPE_LM75B, 0x4d);
>
> /*
> * EEPROM 24c64 size is 64Kbits or 8 Kbytes
> @@ -74,15 +66,15 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc)
> at24c_eeprom_init(i2c[20], 0x50, 256);
> at24c_eeprom_init(i2c[22], 0x52, 256);
>
> - i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x48);
> - i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x49);
> - i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x4a);
> + i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x48);
> + i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x49);
> + i2c_slave_create_simple(i2c[3], TYPE_LM75B, 0x4a);
> i2c_slave_create_simple(i2c[3], TYPE_TMP422, 0x4c);
>
> at24c_eeprom_init(i2c[8], 0x51, 8 * KiB);
> - i2c_slave_create_simple(i2c[8], TYPE_LM75, 0x4a);
> + i2c_slave_create_simple(i2c[8], TYPE_LM75B, 0x4a);
>
> - i2c_slave_create_simple(i2c[50], TYPE_LM75, 0x4c);
> + i2c_slave_create_simple(i2c[50], TYPE_LM75B, 0x4c);
> at24c_eeprom_init(i2c[50], 0x52, 8 * KiB);
> i2c_slave_create_simple(i2c[51], TYPE_TMP75, 0x48);
> i2c_slave_create_simple(i2c[52], TYPE_TMP75, 0x49);
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 16/25] tests/functional: aspeed: optionally check the device tree model on boot
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-02 5:50 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-02 5:50 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> Add an optional 'dt_model' argument to do_test_arm_aspeed_openbmc().
> When
> provided, the helper also waits for the kernel's "Machine model: ..."
> line,
> letting a test confirm the booted image actually matches the expected
> board
> device tree rather than only the SoC revision.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> tests/functional/aspeed.py | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py
> index 076da1036c..08af6b4839 100644
> --- a/tests/functional/aspeed.py
> +++ b/tests/functional/aspeed.py
> @@ -8,7 +8,8 @@
> class AspeedTest(LinuxKernelTest):
>
> def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
> - cpu_id='0x0', soc='AST2500 rev A1'):
> + cpu_id='0x0', soc='AST2500 rev A1',
> + dt_model=None):
> self.set_machine(machine)
> self.vm.set_console()
> self.vm.add_args('-drive', f'file={image},if=mtd,format=raw',
> @@ -19,6 +20,8 @@ def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04',
> self.wait_for_console_pattern('## Loading kernel from FIT Image')
> self.wait_for_console_pattern('Starting kernel ...')
> self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu_id}')
> + if dt_model:
> + self.wait_for_console_pattern(f'Machine model: {dt_model}')
> self.wait_for_console_pattern(f'ASPEED {soc}')
> self.wait_for_console_pattern('/init as init process')
> self.wait_for_boot_complete()
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 17/25] tests/functional: give the set_machine probe VM the test workdir
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-02 5:51 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-02 5:51 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot
On 7/31/26 12:45, Emmanuel Blot wrote:
> set_machine() launches a throwaway QEMUMachine to query the available
> machine types, but constructs it without base_temp_dir, so it falls back
> to the hardcoded "/var/tmp" default instead of the writable per-test
> workdir that the actual test VM already uses. Pass base_temp_dir=
> self.workdir for consistency; this also fixes VM launch on hosts where
> /var/tmp is not writable.
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> tests/functional/qemu_test/testcase.py | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py
> index e4179d165c..ed861709e1 100644
> --- a/tests/functional/qemu_test/testcase.py
> +++ b/tests/functional/qemu_test/testcase.py
> @@ -317,7 +317,7 @@ def set_machine(self, machinename):
> cls = type(self)
>
> if not hasattr(cls, "_machines"):
> - tmp_vm = QEMUMachine(self.qemu_bin)
> + tmp_vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir)
> tmp_vm.set_machine('none')
>
> try:
>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v2 00/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors
2026-07-31 10:44 ` Emmanuel Blot via qemu development
` (26 preceding siblings ...)
(?)
@ 2026-09-02 6:19 ` Cédric Le Goater
-1 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-02 6:19 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot, Markus Armbruster
On 7/31/26 12:44, Emmanuel Blot wrote:
> This series adds the Facebook SanMiguel BMC, promotes the
> TMP75/TMP175/LM75B sensors from generic TMP105 stand-ins to improved,
> dedicated models, and introduces the test-side plumbing needed to
> exercise real I2C devices from functional tests without hardcoding QOM
> paths.
>
> The work falls into four parts.
>
> 1. TMP* sensor models
>
> The TMP105 model was the only stand-in for a whole family of pin- and
> register-compatible parts, and several boards worked around the gap with
> private TYPE_TMP75 / TYPE_LM75 aliases that resolved back to a plain
> TMP105.
>
> The model is first cleaned up for correctness and QEMU convention. One
> of those changes touches migrated state, which gains an optional
> subsection so existing streams stay compatible.
>
> On that base, TMP75/TMP175/LM75B become variants selected by a small
> per-type class descriptor. The variant is class data, so the wire format
> is unchanged and all variants share the existing vmstate. qtest coverage
> is added for the per-variant behaviour. The existing BMC boards then
> drop their local aliases and instantiate the real models.
>
> 2. SanMiguel BMC
>
> A new AST2600-based machine for the Facebook SanMiguel BMC, derived from
> its device tree. It wires up three TMP75 sensors, IO expanders, FRU
> EEPROMs, an I2C mux and an RTC across its I2C buses, and provides its
> RAM, flash and networking; the FRU EEPROMs are populated with data read
> from physical hardware. A functional test boots the OpenBMC image and
> drives each sensor over QOM, checking the guest hwmon interface reports
> the injected value back.
>
> 3. Python device locator
>
> A QOM path names a device by its position under the object that owns it,
> in enumeration order — it says nothing about where the device sits in
> the bus topology. From a QOM path alone it is impossible to tell where a
> device is actually connected or what purpose it serves.
>
> DeviceLocator is a pure-Python helper that resolves a device from a
> string descriptor of its position — bus, address, type, on-bus index —
> by walking the bus/device hierarchy. It uses only standard QOM queries
> over QMP: no new QEMU commands, no model changes. A self-check exercises
> the grammar and its error paths against a paused SanMiguel BMC; it needs
> no disk image and runs in about a second.
>
> Note: the helper adopts the 3.10 type-hint syntax rather than the 3.9
> equivalents; 3.9 reached end of life on 2025-10-31. The import is
> guarded so the module still loads on 3.9 and dependent tests skip there.
>
> Two small test-framework fixes ride along: the set_machine probe VM now
> gets the per-test workdir instead of the hardcoded /var/tmp default, and
> the Aspeed OpenBMC helper can optionally assert the booted device-tree
> model.
>
> 4. Catalina follow-ups
>
> The final three commits are the tail of the earlier "hw/arm: improve
> Aspeed Catalina BMC emulation" series: functional tests for the PCA9555
> and PCA9554 IO expanders, plus the PCA9554 change that drives its input
> pins externally. They were held back because reaching such devices from
> a test needs a stable way to reference them by bus position — the QOM
> re-parenting patch tried then was rejected, and the device locator now
> unblocks them.
>
> Based on:
>
> repo: https://github.com/legoater/qemu.git
> branch: aspeed-11.1
> commit: 04d27371e3 ("hw/arm: catalina: add NIC and FIO temperature sensors")
>
> Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
> ---
> Changes in v2:
> - Simplify TMP105 multiple variant initialization
> - Improve reliability against malformed device locator strings
> - Remove an obsolete reference to INA23x devices from a commit log message
> - Rebase patch series on aspeed-next branch
> - Fix invalid JSON syntax in SanMiguel FRU descriptions
> - Link to v1: https://lore.kernel.org/qemu-devel/20260729-sanmiguel-bmc-locator-v1-0-c8f9a8d101f6@free.fr
>
> To: qemu-devel@nongnu.org
> Cc: Philippe Mathieu-Daudé <philmd@mailo.com>
> Cc: Cédric Le Goater <clg@kaod.org>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Steven Lee <steven_lee@aspeedtech.com>
> Cc: Jamin Lin <jamin_lin@aspeedtech.com>
> Cc: Kane Chen <kane_chen@aspeedtech.com>
> Cc: Andrew Jeffery <andrew@codeconstruct.com.au>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: qemu-arm@nongnu.org
> Cc: Fabiano Rosas <farosas@suse.de>
> Cc: Laurent Vivier <lvivier@redhat.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <th.huth+qemu@posteo.eu>
> Cc: "Daniel P. Berrangé" <berrange@redhat.com>
>
> ---
> Emmanuel Blot (25):
> hw/sensor: tmp105: make device state private to the implementation
> hw/sensor: tmp105: name the parent object field parent_obj
> hw/sensor: tmp105: implement Resettable reset
> hw/sensor: tmp105: enforce the configurable fault queue
> hw/sensor: tmp105: describe the temperature property
> hw/arm: aspeed: guard board-local temperature-sensor aliases
> hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants
> tests/qtest: tmp105: cover the ALERT fault queue
> tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants
> tests/qtest: tmp105: cover one-shot and fault-queue write immunity
> tests/qtest: tmp105: cover shutdown clearing the ALERT across variants
> hw/arm: sanmiguel: add Facebook SanMiguel BMC machine
> hw/arm: sanmiguel: populate EEPROM data
> hw/arm: catalina: use the real TMP75 model
> hw/arm: fuji: use the real TMP75 and LM75B temperature sensors
> tests/functional: aspeed: optionally check the device tree model on boot
> tests/functional: give the set_machine probe VM the test workdir
> tests/functional: add a pure-Python device-locator resolver
> tests/functional: add a device-locator self-check
> tests/functional: aspeed: add hwmon sensor read helpers
> tests/functional/arm: sanmiguel: add BMC boot test
> tests/functional/arm: catalina: test TMP75
> tests/functional/arm: catalina: test PCA9555 IO expander via QOM
> hw/arm: catalina: drive pca9554 io expander input pins externally
> tests/functional/arm: catalina: test PCA9554 IO expander via QOM
>
> hw/arm/aspeed_ast2600_catalina.c | 14 +-
> hw/arm/aspeed_ast2600_fuji.c | 16 +-
> hw/arm/aspeed_ast2600_sanmiguel.c | 405 ++++++++++++++++++
> hw/arm/meson.build | 1 +
> hw/sensor/tmp105.c | 304 ++++++++++---
> include/hw/sensor/tmp105.h | 46 +-
> tests/functional/arm/meson.build | 3 +
> tests/functional/arm/test_aspeed_catalina.py | 107 ++++-
> tests/functional/arm/test_aspeed_sanmiguel.py | 72 ++++
> tests/functional/arm/test_device_locator.py | 120 ++++++
> tests/functional/aspeed.py | 21 +-
> tests/functional/qemu_test/locator.py | 586 ++++++++++++++++++++++++++
> tests/functional/qemu_test/testcase.py | 2 +-
> tests/qtest/tmp105-test.c | 359 ++++++++++++++++
> 14 files changed, 1934 insertions(+), 122 deletions(-)
> ---
> base-commit: e5d3f5ac8633de310066f14862b17e4cac8ef8a5
> change-id: 20260729-sanmiguel-bmc-locator-db13cceae66c
>
> Best regards,
> --
> Emmanuel Blot <emmanuel.blot@free.fr>
>
Applied 1-16 to
https://github.com/legoater/qemu aspeed-next
Functional tests need to identify devices in the bus topology and
we lack support for that. Several recent threads have touched this
topic:
- "[PATCH v5 0/8] hw/sensor: Add new device emulation for TI ADC128D818" [1]
- "Call to clean up QOM onboard devices lacking a parent" [2]
- "[PATCH RFC 001/134] qom: Introduce object_new_child()" [3]
- "[PATCH RFC v2 0/137] qom: Make composition-tree parenting mandatory" [4]
- "[PATCH v2 0/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors" [5]
Not addressed yet. It's still maturing, like all the good stuff.
Thanks,
C.
[1] https://lore.kernel.org/qemu-devel/20260701-i2c-adc128d818-anacapa-v5-6-fe8292d86b38@free.fr
[2] https://lore.kernel.org/qemu-devel/87se5scipx.fsf@pond.sub.org
[3] https://lore.kernel.org/qemu-devel/20260711223707.42139-1-graf@amazon.com
[4] https://lore.kernel.org/qemu-devel/20260718213652.37673-1-graf@amazon.com
[5] https://lore.kernel.org/qemu-devel/20260731-sanmiguel-bmc-locator-v2-0-1266926ba769@free.fr
^ permalink raw reply [flat|nested] 74+ messages in thread
* Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver)
2026-07-31 10:45 ` Emmanuel Blot via qemu development
(?)
@ 2026-09-03 14:21 ` Cédric Le Goater
2026-09-04 8:22 ` Markus Armbruster
2026-09-05 12:45 ` Mark Cave-Ayland
-1 siblings, 2 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-03 14:21 UTC (permalink / raw)
To: Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot, Markus Armbruster
On 7/31/26 12:45, Emmanuel Blot wrote:
> Add DeviceLocator, a helper that addresses a device by its position on
> the bus -- bus, address, type or index -- rather than by a QOM path. A
> QOM path locates a device under the object that owns it, by enumeration
> order, and so says nothing about where the device actually sits in the
> bus topology. From a QOM anchor DeviceLocator instead walks the bus and
> device hierarchy, alternating bus and device hops, to reach the target.
>
> It relies only on standard QOM queries over QMP, needing no custom
> commands or changes to QEMU.
Let's recap first :
Several recent threads have touched the same topic: QOM
composition-tree paths encode ownership, but developers and tests
regularly need to identify devices by their position in the bus
topology. The threads in question:
- "[PATCH v5 0/8] hw/sensor: Add new device emulation for TI ADC128D818" [1]
- "Call to clean up QOM onboard devices lacking a parent" [2]
- "[PATCH RFC 001/134] qom: Introduce object_new_child()" [3]
- "[PATCH RFC v2 0/137] qom: Make composition-tree parenting mandatory" [4]
- "[PATCH v2 0/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors" [5]
* The problem
QEMU has two device hierarchies serving different purposes:
- The QOM composition tree describes parent/child ownership. Every
object has exactly one parent. Paths look like:
/machine/soc/i2c-controller/child-device.
- The bus topology describes electrical connectivity (visible
through "info qtree"). A device sits on a bus at an address.
Devices created without an explicit QOM parent fall into
/machine/unattached/device[N], the "orphanage", with names that depend
on enumeration order and are therefore unstable. Markus identified
380+ such devices across all QEMU machines [2]. Even with a stable QOM
path, the path says nothing about where the device sits in the bus
topology or what purpose it serves.
* Three approaches on the table
1. Mandatory QOM parenting (Graf [3][4] / Armbruster [2])
object_new_child() and per-bus creators enforce parenting at
creation time. Deletes the orphanage. Scope: 440 files, 137
patches.
2. Bus-child parenting (Blot [1], rejected)
i2c_slave_create_simple() would parent slaves to their bus,
named by hex address (e.g. /machine/i2c[0]/i2c-bus/0x6f).
3. DeviceLocator (Blot [5])
Pure-Python helper that resolves devices by bus-position
descriptors like:
/machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b
It walks the bus/device hierarchy using only qom-list/qom-get
over QMP, no QEMU code changes needed. 600 lines of python.
* /machine/labels: DT-style alias container for QOM
The idea: add a flat container /machine/labels under the machine, where
board code registers link<> properties pointing to devices by
purpose name. Like device tree /aliases (serial0 > /soc/uart@9000000).
Container creation is one line in qemu_create_machine_containers().
QMP introspection works today: qom-get /machine/labels/rtc returns the
target's canonical path.
A small helper is all that is needed:
void machine_add_label(const char *label, Object *target)
{
object_property_add_const_link(
machine_get_container("labels"), label, target);
}
Board code registers one label per well-known device:
machine_add_label("rtc", OBJECT(dev));
machine_add_label("tmp75-bus9-0x4b", OBJECT(sensor));
Tests resolve devices with a single QMP call instead of walking
the bus hierarchy. For the common case, functional tests targeting
specific board devices (SanMiguel TMP75s, Catalina PCA9555s, Anacapa
ADC128D818s), the board author already knows which devices matter.
Thoughts?
Thanks,
C.
[1] https://lore.kernel.org/qemu-devel/20260701-i2c-adc128d818-anacapa-v5-6-fe8292d86b38@free.fr
[2] https://lore.kernel.org/qemu-devel/87se5scipx.fsf@pond.sub.org
[3] https://lore.kernel.org/qemu-devel/20260711223707.42139-1-graf@amazon.com
[4] https://lore.kernel.org/qemu-devel/20260718213652.37673-1-graf@amazon.com
[5] https://lore.kernel.org/qemu-devel/20260731-sanmiguel-bmc-locator-v2-0-1266926ba769@free.fr
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver)
2026-09-03 14:21 ` Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver) Cédric Le Goater
@ 2026-09-04 8:22 ` Markus Armbruster
2026-09-05 12:45 ` Mark Cave-Ayland
1 sibling, 0 replies; 74+ messages in thread
From: Markus Armbruster @ 2026-09-04 8:22 UTC (permalink / raw)
To: Cédric Le Goater
Cc: Emmanuel Blot, qemu-devel, Philippe Mathieu-Daudé,
Peter Maydell, Steven Lee, Jamin Lin, Kane Chen, Andrew Jeffery,
Joel Stanley, qemu-arm, Fabiano Rosas, Laurent Vivier,
Paolo Bonzini, Thomas Huth, Daniel P. Berrangé,
Emmanuel Blot
Cédric Le Goater <clg@kaod.org> writes:
> On 7/31/26 12:45, Emmanuel Blot wrote:
>> Add DeviceLocator, a helper that addresses a device by its position on
>> the bus -- bus, address, type or index -- rather than by a QOM path. A
>> QOM path locates a device under the object that owns it, by enumeration
>> order, and so says nothing about where the device actually sits in the
>> bus topology. From a QOM anchor DeviceLocator instead walks the bus and
>> device hierarchy, alternating bus and device hops, to reach the target.
>> It relies only on standard QOM queries over QMP, needing no custom
>> commands or changes to QEMU.
>
> Let's recap first :
>
> Several recent threads have touched the same topic: QOM
> composition-tree paths encode ownership, but developers and tests
> regularly need to identify devices by their position in the bus
> topology. The threads in question:
>
> - "[PATCH v5 0/8] hw/sensor: Add new device emulation for TI ADC128D818" [1]
> - "Call to clean up QOM onboard devices lacking a parent" [2]
> - "[PATCH RFC 001/134] qom: Introduce object_new_child()" [3]
> - "[PATCH RFC v2 0/137] qom: Make composition-tree parenting mandatory" [4]
> - "[PATCH v2 0/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors" [5]
>
>
> * The problem
>
> QEMU has two device hierarchies serving different purposes:
>
> - The QOM composition tree describes parent/child ownership. Every
> object has exactly one parent. Paths look like:
> /machine/soc/i2c-controller/child-device.
> - The bus topology describes electrical connectivity (visible
> through "info qtree"). A device sits on a bus at an address.
Yes.
*Composition* tree means the children are *components* of their parent.
At least that's the intended use.
The qtree predates QOM and is a bit of a relic. Its design is too
simplistic to match electric reality: it's a *tree*, whereas real wires
form a *graph*.
Tree is fine for ordinary devices plugging into something a hardware
dude would recognize as a bus, say PCI or USB.
Caveat: as long as they plug into exactly one such thing; "multi-master
I2C" was mentiond as a counter-example.
Many (most?) of our devices are sysbus devices. Sysbus is not a bus,
it's a cop out: it doesn't actually describe electrical connections
beyond "there are some to other parts of 'the system'".
And then there are funny devices like certain PCI VGA devices that plug
into a PCI but also bypass PCI for mapping their frame buffer.
I guess (the saner parts of) qtree may still provide some value. I'm
curious: do people use it in programs? Management applications in
particular.
QOM provides a mechanism that is suitable for modeling electrical
connections other than the ones to the parent: links. These do form a
graph.
> Devices created without an explicit QOM parent fall into
> /machine/unattached/device[N], the "orphanage", with names that depend
> on enumeration order and are therefore unstable. Markus identified
> 380+ such devices across all QEMU machines [2]. Even with a stable QOM
> path, the path says nothing about where the device sits in the bus
> topology or what purpose it serves.
Yes, and that can be a problem.
> * Three approaches on the table
>
> 1. Mandatory QOM parenting (Graf [3][4] / Armbruster [2])
> object_new_child() and per-bus creators enforce parenting at
> creation time. Deletes the orphanage. Scope: 440 files, 137
> patches.
>
> 2. Bus-child parenting (Blot [1], rejected)
> i2c_slave_create_simple() would parent slaves to their bus,
> named by hex address (e.g. /machine/i2c[0]/i2c-bus/0x6f).
>
> 3. DeviceLocator (Blot [5])
> Pure-Python helper that resolves devices by bus-position
> descriptors like:
>
> /machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b
>
> It walks the bus/device hierarchy using only qom-list/qom-get
> over QMP, no QEMU code changes needed. 600 lines of python.
>
>
> * /machine/labels: DT-style alias container for QOM
>
> The idea: add a flat container /machine/labels under the machine, where
> board code registers link<> properties pointing to devices by
> purpose name. Like device tree /aliases (serial0 > /soc/uart@9000000).
This general idea has been sloshing around in my head for a long time, I
just haven't had a compelling reason to flesh it out.
> Container creation is one line in qemu_create_machine_containers().
> QMP introspection works today: qom-get /machine/labels/rtc returns the
> target's canonical path.
>
> A small helper is all that is needed:
>
> void machine_add_label(const char *label, Object *target)
> {
> object_property_add_const_link(
> machine_get_container("labels"), label, target);
> }
>
> Board code registers one label per well-known device:
>
> machine_add_label("rtc", OBJECT(dev));
> machine_add_label("tmp75-bus9-0x4b", OBJECT(sensor));
>
> Tests resolve devices with a single QMP call instead of walking
> the bus hierarchy. For the common case, functional tests targeting
> specific board devices (SanMiguel TMP75s, Catalina PCA9555s, Anacapa
> ADC128D818s), the board author already knows which devices matter.
>
> Thoughts?
To get the most value out of /machine/labels/, we'd want
* Documented naming conventions such as "/machine/label/serial0 always
refers to the machine's first serial device"
* Reliability, i.e. if /machine/label/ exists, then
/machine/label/serial0 exists unless the machine has no first serial
device
* /machine/label/ to exist for the machines we actually care about :)
> Thanks,
>
> C.
>
> [1] https://lore.kernel.org/qemu-devel/20260701-i2c-adc128d818-anacapa-v5-6-fe8292d86b38@free.fr
> [2] https://lore.kernel.org/qemu-devel/87se5scipx.fsf@pond.sub.org
> [3] https://lore.kernel.org/qemu-devel/20260711223707.42139-1-graf@amazon.com
> [4] https://lore.kernel.org/qemu-devel/20260718213652.37673-1-graf@amazon.com
> [5] https://lore.kernel.org/qemu-devel/20260731-sanmiguel-bmc-locator-v2-0-1266926ba769@free.fr
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver)
2026-09-03 14:21 ` Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver) Cédric Le Goater
2026-09-04 8:22 ` Markus Armbruster
@ 2026-09-05 12:45 ` Mark Cave-Ayland
2026-09-05 15:20 ` Cédric Le Goater
1 sibling, 1 reply; 74+ messages in thread
From: Mark Cave-Ayland @ 2026-09-05 12:45 UTC (permalink / raw)
To: Cédric Le Goater, Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot, Markus Armbruster
On 03/09/2026 15:21, Cédric Le Goater wrote:
> On 7/31/26 12:45, Emmanuel Blot wrote:
>> Add DeviceLocator, a helper that addresses a device by its position on
>> the bus -- bus, address, type or index -- rather than by a QOM path. A
>> QOM path locates a device under the object that owns it, by enumeration
>> order, and so says nothing about where the device actually sits in the
>> bus topology. From a QOM anchor DeviceLocator instead walks the bus and
>> device hierarchy, alternating bus and device hops, to reach the target.
>>
>> It relies only on standard QOM queries over QMP, needing no custom
>> commands or changes to QEMU.
>
> Let's recap first :
>
> Several recent threads have touched the same topic: QOM
> composition-tree paths encode ownership, but developers and tests
> regularly need to identify devices by their position in the bus
> topology. The threads in question:
>
> - "[PATCH v5 0/8] hw/sensor: Add new device emulation for TI ADC128D818" [1]
> - "Call to clean up QOM onboard devices lacking a parent" [2]
> - "[PATCH RFC 001/134] qom: Introduce object_new_child()" [3]
> - "[PATCH RFC v2 0/137] qom: Make composition-tree parenting mandatory" [4]
> - "[PATCH v2 0/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors" [5]
>
>
> * The problem
>
> QEMU has two device hierarchies serving different purposes:
>
> - The QOM composition tree describes parent/child ownership. Every
> object has exactly one parent. Paths look like:
> /machine/soc/i2c-controller/child-device.
> - The bus topology describes electrical connectivity (visible
> through "info qtree"). A device sits on a bus at an address.
>
> Devices created without an explicit QOM parent fall into
> /machine/unattached/device[N], the "orphanage", with names that depend
> on enumeration order and are therefore unstable. Markus identified
> 380+ such devices across all QEMU machines [2]. Even with a stable QOM
> path, the path says nothing about where the device sits in the bus
> topology or what purpose it serves.
>
>
> * Three approaches on the table
>
> 1. Mandatory QOM parenting (Graf [3][4] / Armbruster [2])
> object_new_child() and per-bus creators enforce parenting at
> creation time. Deletes the orphanage. Scope: 440 files, 137
> patches.
>
> 2. Bus-child parenting (Blot [1], rejected)
> i2c_slave_create_simple() would parent slaves to their bus,
> named by hex address (e.g. /machine/i2c[0]/i2c-bus/0x6f).
>
> 3. DeviceLocator (Blot [5])
> Pure-Python helper that resolves devices by bus-position
> descriptors like:
>
> /machine/soc::aspeed.i2c-ast2600[0]~i2c[9]~tmp75@0x4b
>
> It walks the bus/device hierarchy using only qom-list/qom-get
> over QMP, no QEMU code changes needed. 600 lines of python.
>
>
> * /machine/labels: DT-style alias container for QOM
>
> The idea: add a flat container /machine/labels under the machine, where
> board code registers link<> properties pointing to devices by
> purpose name. Like device tree /aliases (serial0 > /soc/uart@9000000).
>
> Container creation is one line in qemu_create_machine_containers().
> QMP introspection works today: qom-get /machine/labels/rtc returns the
> target's canonical path.
>
> A small helper is all that is needed:
>
> void machine_add_label(const char *label, Object *target)
> {
> object_property_add_const_link(
> machine_get_container("labels"), label, target);
> }
>
> Board code registers one label per well-known device:
>
> machine_add_label("rtc", OBJECT(dev));
> machine_add_label("tmp75-bus9-0x4b", OBJECT(sensor));
>
> Tests resolve devices with a single QMP call instead of walking
> the bus hierarchy. For the common case, functional tests targeting
> specific board devices (SanMiguel TMP75s, Catalina PCA9555s, Anacapa
> ADC128D818s), the board author already knows which devices matter.
>
> Thoughts?
>
> Thanks,
>
> C.
>
> [1] https://lore.kernel.org/qemu-devel/20260701-i2c-adc128d818-anacapa-v5-6-
> fe8292d86b38@free.fr
> [2] https://lore.kernel.org/qemu-devel/87se5scipx.fsf@pond.sub.org
> [3] https://lore.kernel.org/qemu-devel/20260711223707.42139-1-graf@amazon.com
> [4] https://lore.kernel.org/qemu-devel/20260718213652.37673-1-graf@amazon.com
> [5] https://lore.kernel.org/qemu-devel/20260731-sanmiguel-bmc-locator-
> v2-0-1266926ba769@free.fr
I remember expressing a similar idea a while back in a private thread but as
/machine/aliases, possibly with Pierrick and Phil?
The problem I was trying to solve was how to access in-built PCI buses and NIC
devices from the command line consistently without resorting to various hacks such as
having to use -nic to connect an in-built NIC to a backend: instead expose the
frontend as an alias that can be referenced by the backend, so the exact QOM path is
fixed and doesn't matter.
I'd certainly be interested to see the design fleshed out in more detail to explore
more potential use cases.
ATB,
Mark.
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver)
2026-09-05 12:45 ` Mark Cave-Ayland
@ 2026-09-05 15:20 ` Cédric Le Goater
0 siblings, 0 replies; 74+ messages in thread
From: Cédric Le Goater @ 2026-09-05 15:20 UTC (permalink / raw)
To: Mark Cave-Ayland, Emmanuel Blot, qemu-devel
Cc: Philippe Mathieu-Daudé, Peter Maydell, Steven Lee, Jamin Lin,
Kane Chen, Andrew Jeffery, Joel Stanley, qemu-arm, Fabiano Rosas,
Laurent Vivier, Paolo Bonzini, Thomas Huth,
Daniel P. Berrangé, Emmanuel Blot, Markus Armbruster
Hell Mark,
>> [1] https://lore.kernel.org/qemu-devel/20260701-i2c-adc128d818-anacapa-v5-6- fe8292d86b38@free.fr
>> [2] https://lore.kernel.org/qemu-devel/87se5scipx.fsf@pond.sub.org
>> [3] https://lore.kernel.org/qemu-devel/20260711223707.42139-1-graf@amazon.com
>> [4] https://lore.kernel.org/qemu-devel/20260718213652.37673-1-graf@amazon.com
>> [5] https://lore.kernel.org/qemu-devel/20260731-sanmiguel-bmc-locator- v2-0-1266926ba769@free.fr
>
> I remember expressing a similar idea a while back in a private thread but as /machine/aliases, possibly with Pierrick and Phil?
>
> The problem I was trying to solve was how to access in-built PCI buses and NIC devices from the command line consistently without resorting to various hacks such as having to use -nic to connect an in-built NIC to a backend: instead expose the frontend as an alias that can be referenced by the backend, so the exact QOM path is fixed and doesn't matter.
>
> I'd certainly be interested to see the design fleshed out in more detail to explore more potential use cases.
Here is a simple proposal for the aspeed machines :
https://lore.kernel.org/qemu-devel/20260904050424.4049984-1-clg@redhat.com/
Thanks,
C.
^ permalink raw reply [flat|nested] 74+ messages in thread
end of thread, other threads:[~2026-09-05 15:21 UTC | newest]
Thread overview: 74+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 10:44 [PATCH v2 00/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors Emmanuel Blot via
2026-07-31 10:44 ` Emmanuel Blot via qemu development
2026-07-31 10:45 ` [PATCH v2 01/25] hw/sensor: tmp105: make device state private to the implementation Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:58 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 02/25] hw/sensor: tmp105: name the parent object field parent_obj Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:58 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 03/25] hw/sensor: tmp105: implement Resettable reset Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:58 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 04/25] hw/sensor: tmp105: enforce the configurable fault queue Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 05/25] hw/sensor: tmp105: describe the temperature property Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 06/25] hw/arm: aspeed: guard board-local temperature-sensor aliases Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 07/25] hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 08/25] tests/qtest: tmp105: cover the ALERT fault queue Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 09/25] tests/qtest: tmp105: cover the TMP75, TMP175 and LM75B variants Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 10/25] tests/qtest: tmp105: cover one-shot and fault-queue write immunity Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-01 6:59 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 11/25] tests/qtest: tmp105: cover shutdown clearing the ALERT across variants Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-01 7:00 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 12/25] hw/arm: sanmiguel: add Facebook SanMiguel BMC machine Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-07-31 10:45 ` [PATCH v2 13/25] hw/arm: sanmiguel: populate EEPROM data Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-02 5:50 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 14/25] hw/arm: catalina: use the real TMP75 model Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-02 5:50 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 15/25] hw/arm: fuji: use the real TMP75 and LM75B temperature sensors Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-09-02 5:50 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 16/25] tests/functional: aspeed: optionally check the device tree model on boot Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-02 5:50 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 17/25] tests/functional: give the set_machine probe VM the test workdir Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-02 5:51 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-09-03 14:21 ` Adding /machines/labels (was Re: [PATCH v2 18/25] tests/functional: add a pure-Python device-locator resolver) Cédric Le Goater
2026-09-04 8:22 ` Markus Armbruster
2026-09-05 12:45 ` Mark Cave-Ayland
2026-09-05 15:20 ` Cédric Le Goater
2026-07-31 10:45 ` [PATCH v2 19/25] tests/functional: add a device-locator self-check Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-07-31 10:45 ` [PATCH v2 20/25] tests/functional: aspeed: add hwmon sensor read helpers Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-07-31 10:45 ` [PATCH v2 21/25] tests/functional/arm: sanmiguel: add BMC boot test Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-07-31 10:45 ` [PATCH v2 22/25] tests/functional/arm: catalina: test TMP75 Emmanuel Blot via
2026-07-31 10:45 ` Emmanuel Blot via qemu development
2026-07-31 10:45 ` [PATCH v2 23/25] tests/functional/arm: catalina: test PCA9555 IO expander via QOM Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-07-31 10:45 ` [PATCH v2 24/25] hw/arm: catalina: drive pca9554 io expander input pins externally Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-07-31 10:45 ` [PATCH v2 25/25] tests/functional/arm: catalina: test PCA9554 IO expander via QOM Emmanuel Blot via qemu development
2026-07-31 10:45 ` Emmanuel Blot via
2026-08-31 14:42 ` [PING] Re: [PATCH v2 00/25] hw/arm: Facebook SanMiguel BMC and TMP75-family sensors Emmanuel Blot
2026-09-02 6:19 ` Cédric Le Goater
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.