Set a temperature over QMP and read it back through the two-byte and single-byte register paths, checking the fixed-point encoding and the extended-range offset. A second case pins the saturation: a temperature the standard range cannot express reads back clamped, without harming the measurement itself.
Signed-off-by: Emmanuel Blot <[email protected]> --- tests/qtest/tmp421-test.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/tests/qtest/tmp421-test.c b/tests/qtest/tmp421-test.c index 2307e5f603..390f17b6b2 100644 --- a/tests/qtest/tmp421-test.c +++ b/tests/qtest/tmp421-test.c @@ -18,9 +18,13 @@ * shared with the model, so the test states the values it expects * independently of the implementation it is checking. */ +#define TMP421_TEMP_MSB0 0x00 #define TMP421_CONFIG_REG_1 0x09 +#define TMP421_CONFIG_RANGE (1 << 2) #define TMP421_CONFIG_REG_2 0x0A #define TMP421_CONVERSION_RATE_REG 0x0B +#define TMP421_TEMP_LSB0 0x10 +#define TMP421_RESET 0xFC #define TMP421_MANUFACTURER_ID_REG 0xFE #define TMP421_DEVICE_ID_REG 0xFF #define TMP421_MANUFACTURER_ID 0x55 @@ -44,6 +48,36 @@ static const TMP421Variant tmp421_variant = { TMP421_TEST_ID, 0x21, 0x1c, 2 }; static const TMP421Variant tmp422_variant = { TMP422_TEST_ID, 0x22, 0x3c, 3 }; static const TMP421Variant tmp423_variant = { TMP423_TEST_ID, 0x23, 0x7c, 4 }; +static QDict *qmp_get_temperature(const char *id, unsigned channel) +{ + g_autofree char *prop = g_strdup_printf("temperature%u", channel); + + return qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': %s } }", id, prop); +} + +static int get_temperature(const char *id, unsigned channel) +{ + QDict *response = qmp_get_temperature(id, channel); + int ret; + + g_assert(qdict_haskey(response, "return")); + ret = qdict_get_int(response, "return"); + qobject_unref(response); + return ret; +} + +static void set_temperature(const char *id, unsigned channel, int value) +{ + g_autofree char *prop = g_strdup_printf("temperature%u", channel); + QDict *response; + + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': %s, 'value': %d } }", id, prop, value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + static void test_ids(void *obj, void *data, QGuestAllocator *alloc) { const TMP421Variant *var = data; @@ -58,6 +92,70 @@ static void test_ids(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(i2c_get8(i2cdev, TMP421_CONVERSION_RATE_REG), ==, 0x07); } +static void test_temperature(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = obj; + + /* A whole degree lands on a whole register value, fraction cleared. */ + set_temperature(TMP421_TEST_ID, 0, 25000); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x1900); + + /* The standard range is two's complement below 0 C. */ + set_temperature(TMP421_TEST_ID, 0, -25000); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0xE700); + + /* A temperature the register form represents exactly. */ + set_temperature(TMP421_TEST_ID, 0, 25063); + g_assert_cmpint(get_temperature(TMP421_TEST_ID, 0), ==, 25063); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x1910); + + /* A lone low-byte read returns the same conversion as the pair. */ + g_assert_cmphex(i2c_get8(i2cdev, TMP421_TEMP_LSB0), ==, 0x10); + + /* Extended range encodes the same measurement with a +64 C offset. */ + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, TMP421_CONFIG_RANGE); + set_temperature(TMP421_TEST_ID, 0, 25063); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x5910); + + /* Leaving it, whether by a write or a reset, reports the same value */ + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, 0); + g_assert_cmpint(get_temperature(TMP421_TEST_ID, 0), ==, 25063); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x1910); + + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, TMP421_CONFIG_RANGE); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x5910); + i2c_set8(i2cdev, TMP421_RESET, 0); + g_assert_cmpint(get_temperature(TMP421_TEST_ID, 0), ==, 25063); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x1910); + + /* The extended range is unsigned: it reaches past +64 C (Table 1). */ + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, TMP421_CONFIG_RANGE); + set_temperature(TMP421_TEST_ID, 0, 100063); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0xA410); +} + +/* + * A temperature the standard range cannot express saturates at +127 C (7Fh) + * instead of wrapping, and the extended range still reports it in full: the + * range picks an encoding, it does not alter the measurement. + */ +static void test_range_saturation(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = obj; + + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, TMP421_CONFIG_RANGE); + set_temperature(TMP421_TEST_ID, 0, 149938); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0xD5F0); + + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, 0); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0x7FF0); + g_assert_cmpint(get_temperature(TMP421_TEST_ID, 0), ==, 149938); + + i2c_set8(i2cdev, TMP421_CONFIG_REG_1, TMP421_CONFIG_RANGE); + g_assert_cmphex(i2c_get16(i2cdev, TMP421_TEMP_MSB0), ==, 0xD5F0); +} + static void tmp421_register_node(const TMP421Variant *var, const char *type, uint8_t addr) { @@ -79,6 +177,10 @@ static void tmp421_register_nodes(void) tmp421_register_node(&tmp421_variant, "tmp421", 0x4c); tmp421_register_node(&tmp422_variant, "tmp422", 0x4d); tmp421_register_node(&tmp423_variant, "tmp423", 0x4e); + + /* Temperature encoding is variant-independent; exercise it once. */ + qos_add_test("temperature", "tmp421", test_temperature, NULL); + qos_add_test("range-saturation", "tmp421", test_range_saturation, NULL); } libqos_init(tmp421_register_nodes); -- 2.53.0-Meta
