* [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes
@ 2026-10-27 19:50 Timur Maksimov
2026-10-27 19:50 ` [PATCH v2 1/6] hw/i2c: add dummy device Timur Maksimov
` (6 more replies)
0 siblings, 7 replies; 33+ messages in thread
From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin,
Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier,
Timur Maksimov
In non-packet mode, buffer and DMA modes are broken
in the Aspeed I2C controller,
probably because no available driver currently uses them.
This patch fixes them, so byte, buffer, and DMA transfer modes
now work correctly for both register modes, old and new ones.
Tests are provided to prevent possible regressions.
Signed-off-by: Timur Maksimov <gessingate@gmail.com>
---
Changes in v2:
- Fix commits description format. Some of them are reordered and clarified.
- Link to v1: https://lore.kernel.org/qemu-devel/20261027-aspeed-i2c-nonpkt-fix-v1-0-076cdde514f8@gmail.com
---
Timur Maksimov (6):
hw/i2c: add dummy device
hw/i2c/aspeed: fix BUSY status reading in new register mode
hw/i2c/aspeed: fix I2CD_CMD register reading
hw/i2c/aspeed: fix buffer transfer in non-packet mode
hw/i2c/aspeed: fix DMA transfer in non-packet mode
tests/qtest: Add cases for 32-bit Aspeed I2C controllers
hw/i2c/Kconfig | 5 +
hw/i2c/aspeed_i2c.c | 23 +-
hw/i2c/i2c-dummy-device.c | 168 +++++++++
hw/i2c/meson.build | 3 +-
tests/qtest/aspeed_i2c-test.c | 836 ++++++++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +
6 files changed, 1029 insertions(+), 8 deletions(-)
---
base-commit: 9b77fa7a4fe86c80d1ff526769c61ada7d28666a
change-id: 20261025-aspeed-i2c-nonpkt-fix-89b852b5c84a
--
Timur Maksimov <gessingate@gmail.com>
^ permalink raw reply [flat|nested] 33+ messages in thread* [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov @ 2026-10-27 19:50 ` Timur Maksimov 2026-10-28 8:54 ` Cédric Le Goater 2026-10-27 19:50 ` [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode Timur Maksimov ` (5 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw) To: qemu-devel Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Timur Maksimov Simple I2C device with QOM properties to access transmit and receive buffers. Can be used for testing or prototyping. Signed-off-by: Timur Maksimov <gessingate@gmail.com> --- hw/i2c/Kconfig | 5 ++ hw/i2c/i2c-dummy-device.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++ hw/i2c/meson.build | 3 +- 3 files changed, 175 insertions(+), 1 deletion(-) diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 0766130b59..04912671db 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -6,6 +6,11 @@ config I2C_DEVICES # to any board's i2c bus bool +config I2C_DUMMY_DEVICE + bool + default y if TEST_DEVICES + depends on I2C + config SMBUS bool select I2C diff --git a/hw/i2c/i2c-dummy-device.c b/hw/i2c/i2c-dummy-device.c new file mode 100644 index 0000000000..645525a572 --- /dev/null +++ b/hw/i2c/i2c-dummy-device.c @@ -0,0 +1,168 @@ +/* + * QEMU I2C Dummy Device + * + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/core/qdev-properties.h" +#include "migration/vmstate.h" + +#define TYPE_I2C_DUMMY_DEVICE "i2c-dummy-device" +OBJECT_DECLARE_SIMPLE_TYPE(I2CDummyDeviceState, I2C_DUMMY_DEVICE) + +#define I2C_DUMMY_ARRAY_SIZE 64 + +struct I2CDummyDeviceState { + I2CSlave parent_obj; + + uint8_t tx[I2C_DUMMY_ARRAY_SIZE]; + uint8_t rx[I2C_DUMMY_ARRAY_SIZE]; + + uint8_t tx_pos; + uint8_t rx_pos; + uint8_t event; +}; + +static void i2c_dummy_device_reset(DeviceState *dev) +{ + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(dev); + + s->tx_pos = 0; + s->rx_pos = 0; + s->event = I2C_FINISH; + memset(s->tx, 0, sizeof s->tx); + /* Don't reset rx as it is a readonly parameter for the model */ +} + +static int i2c_dummy_device_event(I2CSlave *i2c, enum i2c_event event) +{ + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(i2c); + + switch (event) { + case I2C_START_SEND: + s->tx_pos = 0; + break; + + case I2C_START_RECV: + s->rx_pos = 0; + break; + + default: + break; + } + + s->event = event; + return 0; +} + +static int i2c_dummy_device_send(I2CSlave *i2c, uint8_t data) +{ + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(i2c); + + if (s->tx_pos >= I2C_DUMMY_ARRAY_SIZE) { + /* + * The TX buffer is full. NACK the byte. + */ + return 1; + } + + if (s->event == I2C_START_SEND) { + s->tx[s->tx_pos++] = data; + } + + return 0; +} + +static uint8_t i2c_dummy_device_recv(I2CSlave *i2c) +{ + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(i2c); + + if (s->rx_pos >= I2C_DUMMY_ARRAY_SIZE || s->event != I2C_START_RECV) { + /* + * There is no more data available. + */ + return 0xff; + } + + return s->rx[s->rx_pos++]; +} + +static void i2c_dummy_device_init(Object *obj) +{ + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(obj); + + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { + g_autofree char *name = g_strdup_printf("tx[%d]", i); + + object_property_add_uint8_ptr( + obj, name, &s->tx[i], + OBJ_PROP_FLAG_READWRITE); + } + + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { + g_autofree char *name = g_strdup_printf("rx[%d]", i); + + object_property_add_uint8_ptr( + obj, name, &s->rx[i], + OBJ_PROP_FLAG_READWRITE); + } + + object_property_add_uint8_ptr( + obj, "tx-pos", &s->tx_pos, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint8_ptr( + obj, "rx-pos", &s->rx_pos, + OBJ_PROP_FLAG_READWRITE); +} + +static const VMStateDescription vmstate_i2c_dummy_device = { + .name = TYPE_I2C_DUMMY_DEVICE, + .version_id = 1, + .minimum_version_id = 1, + + .fields = (const VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, I2CDummyDeviceState), + + VMSTATE_UINT8_ARRAY(tx, I2CDummyDeviceState, + I2C_DUMMY_ARRAY_SIZE), + + VMSTATE_UINT8_ARRAY(rx, I2CDummyDeviceState, + I2C_DUMMY_ARRAY_SIZE), + + VMSTATE_UINT8(tx_pos, I2CDummyDeviceState), + VMSTATE_UINT8(rx_pos, I2CDummyDeviceState), + VMSTATE_UINT8(event, I2CDummyDeviceState), + + VMSTATE_END_OF_LIST() + }, +}; + +static void i2c_dummy_device_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *isc = I2C_SLAVE_CLASS(klass); + + device_class_set_legacy_reset(dc, i2c_dummy_device_reset); + + dc->vmsd = &vmstate_i2c_dummy_device; + + isc->event = i2c_dummy_device_event; + isc->send = i2c_dummy_device_send; + isc->recv = i2c_dummy_device_recv; +} + +static const TypeInfo i2c_dummy_device_info[] = { + { + .name = TYPE_I2C_DUMMY_DEVICE, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(I2CDummyDeviceState), + .instance_init = i2c_dummy_device_init, + .class_init = i2c_dummy_device_class_init, + } +}; + +DEFINE_TYPES(i2c_dummy_device_info) diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index 88aea35662..4695cbcd28 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -1,5 +1,5 @@ i2c_ss = ss.source_set() -i2c_ss.add(when: 'CONFIG_I2C', if_true: files('core.c')) +i2c_ss.add(when: 'CONFIG_I2C', if_true: files('core.c', 'i2c-dummy-device.c')) i2c_ss.add(when: 'CONFIG_SMBUS', if_true: files('smbus_slave.c', 'smbus_master.c')) i2c_ss.add(when: 'CONFIG_ACPI_SMBUS', if_true: files('pm_smbus.c')) i2c_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('smbus_ich9.c')) @@ -19,4 +19,5 @@ i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c')) i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c')) +i2c_ss.add(when: 'CONFIG_I2C_DUMMY_DEVICE', if_true: files('i2c-dummy-device.c')) system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) -- 2.53.0 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-27 19:50 ` [PATCH v2 1/6] hw/i2c: add dummy device Timur Maksimov @ 2026-10-28 8:54 ` Cédric Le Goater 2026-10-28 10:17 ` Markus Armbruster 0 siblings, 1 reply; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 8:54 UTC (permalink / raw) To: Timur Maksimov, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard, Markus Armbruster + Corey for I2C feed back. + Markus for QOM The brute-force solution to expose the contents of the device array with 128 properties has a slight AI smell. I find it ugly but I am human :) That said I don't know if there are better alternatives. My approach would have been to use a string property : object_property_add_str(obj, "tx", i2c_dummy_device_get_tx, NULL); object_property_add_str(obj, "rx", i2c_dummy_device_get_rx, i2c_dummy_device_set_rx); Any better ideas ? On 10/27/26 20:50, Timur Maksimov wrote: > Simple I2C device with QOM properties to access > transmit and receive buffers. > Can be used for testing or prototyping. > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > hw/i2c/Kconfig | 5 ++ > hw/i2c/i2c-dummy-device.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++ MAINTAINERS need updating. > hw/i2c/meson.build | 3 +- > 3 files changed, 175 insertions(+), 1 deletion(-) > > diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig > index 0766130b59..04912671db 100644 > --- a/hw/i2c/Kconfig > +++ b/hw/i2c/Kconfig > @@ -6,6 +6,11 @@ config I2C_DEVICES > # to any board's i2c bus > bool > > +config I2C_DUMMY_DEVICE > + bool > + default y if TEST_DEVICES > + depends on I2C > + > config SMBUS > bool > select I2C > diff --git a/hw/i2c/i2c-dummy-device.c b/hw/i2c/i2c-dummy-device.c > new file mode 100644 > index 0000000000..645525a572 > --- /dev/null > +++ b/hw/i2c/i2c-dummy-device.c > @@ -0,0 +1,168 @@ > +/* > + * QEMU I2C Dummy Device > + * > + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "hw/i2c/i2c.h" > +#include "hw/core/qdev-properties.h" > +#include "migration/vmstate.h" > + > +#define TYPE_I2C_DUMMY_DEVICE "i2c-dummy-device" > +OBJECT_DECLARE_SIMPLE_TYPE(I2CDummyDeviceState, I2C_DUMMY_DEVICE) > + > +#define I2C_DUMMY_ARRAY_SIZE 64 > + > +struct I2CDummyDeviceState { > + I2CSlave parent_obj; > + > + uint8_t tx[I2C_DUMMY_ARRAY_SIZE]; > + uint8_t rx[I2C_DUMMY_ARRAY_SIZE]; > + > + uint8_t tx_pos; > + uint8_t rx_pos; > + uint8_t event; > +}; > + > +static void i2c_dummy_device_reset(DeviceState *dev) > +{ > + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(dev); > + > + s->tx_pos = 0; > + s->rx_pos = 0; > + s->event = I2C_FINISH; > + memset(s->tx, 0, sizeof s->tx); > + /* Don't reset rx as it is a readonly parameter for the model */ > +} > + > +static int i2c_dummy_device_event(I2CSlave *i2c, enum i2c_event event) > +{ > + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(i2c); > + > + switch (event) { > + case I2C_START_SEND: > + s->tx_pos = 0; > + break; > + > + case I2C_START_RECV: > + s->rx_pos = 0; > + break; > + > + default: > + break; > + } > + > + s->event = event; > + return 0; > +} > + > +static int i2c_dummy_device_send(I2CSlave *i2c, uint8_t data) > +{ > + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(i2c); > + > + if (s->tx_pos >= I2C_DUMMY_ARRAY_SIZE) { > + /* > + * The TX buffer is full. NACK the byte. > + */ > + return 1; > + } > + > + if (s->event == I2C_START_SEND) { > + s->tx[s->tx_pos++] = data; > + } > + > + return 0; > +} > + > +static uint8_t i2c_dummy_device_recv(I2CSlave *i2c) > +{ > + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(i2c); > + > + if (s->rx_pos >= I2C_DUMMY_ARRAY_SIZE || s->event != I2C_START_RECV) { > + /* > + * There is no more data available. > + */ > + return 0xff; > + } > + > + return s->rx[s->rx_pos++]; > +} > + > +static void i2c_dummy_device_init(Object *obj) > +{ > + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(obj); > > + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { > + g_autofree char *name = g_strdup_printf("tx[%d]", i); > + > + object_property_add_uint8_ptr( > + obj, name, &s->tx[i], > + OBJ_PROP_FLAG_READWRITE); > + } > + > + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { > + g_autofree char *name = g_strdup_printf("rx[%d]", i); > + > + object_property_add_uint8_ptr( > + obj, name, &s->rx[i], > + OBJ_PROP_FLAG_READWRITE); > + } > + > + object_property_add_uint8_ptr( > + obj, "tx-pos", &s->tx_pos, > + OBJ_PROP_FLAG_READWRITE); > + object_property_add_uint8_ptr( > + obj, "rx-pos", &s->rx_pos, > + OBJ_PROP_FLAG_READWRITE); alignment could be improved. > +} > + > +static const VMStateDescription vmstate_i2c_dummy_device = { > + .name = TYPE_I2C_DUMMY_DEVICE, > + .version_id = 1, > + .minimum_version_id = 1, > + > + .fields = (const VMStateField[]) { > + VMSTATE_I2C_SLAVE(parent_obj, I2CDummyDeviceState), > + > + VMSTATE_UINT8_ARRAY(tx, I2CDummyDeviceState, > + I2C_DUMMY_ARRAY_SIZE), > + > + VMSTATE_UINT8_ARRAY(rx, I2CDummyDeviceState, > + I2C_DUMMY_ARRAY_SIZE), > + > + VMSTATE_UINT8(tx_pos, I2CDummyDeviceState), > + VMSTATE_UINT8(rx_pos, I2CDummyDeviceState), > + VMSTATE_UINT8(event, I2CDummyDeviceState), > + > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +static void i2c_dummy_device_class_init(ObjectClass *klass, const void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + I2CSlaveClass *isc = I2C_SLAVE_CLASS(klass); > + > + device_class_set_legacy_reset(dc, i2c_dummy_device_reset); Please use Resettable phases for new devices. > + > + dc->vmsd = &vmstate_i2c_dummy_device; > + > + isc->event = i2c_dummy_device_event; > + isc->send = i2c_dummy_device_send; > + isc->recv = i2c_dummy_device_recv; > +} > + > +static const TypeInfo i2c_dummy_device_info[] = { > + { > + .name = TYPE_I2C_DUMMY_DEVICE, > + .parent = TYPE_I2C_SLAVE, > + .instance_size = sizeof(I2CDummyDeviceState), > + .instance_init = i2c_dummy_device_init, > + .class_init = i2c_dummy_device_class_init, > + } > +}; > + > +DEFINE_TYPES(i2c_dummy_device_info) > diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build > index 88aea35662..4695cbcd28 100644 > --- a/hw/i2c/meson.build > +++ b/hw/i2c/meson.build > @@ -1,5 +1,5 @@ > i2c_ss = ss.source_set() > -i2c_ss.add(when: 'CONFIG_I2C', if_true: files('core.c')) > +i2c_ss.add(when: 'CONFIG_I2C', if_true: files('core.c', 'i2c-dummy-device.c')) Please drop this change. Config I2C_DUMMY_DEVICE which defaults to y when TEST_DEVICES is set should be enough. > i2c_ss.add(when: 'CONFIG_SMBUS', if_true: files('smbus_slave.c', 'smbus_master.c')) > i2c_ss.add(when: 'CONFIG_ACPI_SMBUS', if_true: files('pm_smbus.c')) > i2c_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('smbus_ich9.c')) > @@ -19,4 +19,5 @@ i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) > i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) > i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c')) > i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c')) > +i2c_ss.add(when: 'CONFIG_I2C_DUMMY_DEVICE', if_true: files('i2c-dummy-device.c')) > system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) > Thanks, C. ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-28 8:54 ` Cédric Le Goater @ 2026-10-28 10:17 ` Markus Armbruster 2026-10-28 11:04 ` Cédric Le Goater 2026-10-28 16:20 ` Timur Maksimov 0 siblings, 2 replies; 33+ messages in thread From: Markus Armbruster @ 2026-10-28 10:17 UTC (permalink / raw) To: Cédric Le Goater Cc: Timur Maksimov, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard Cédric Le Goater <clg@kaod.org> writes: > + Corey for I2C feed back. > > + Markus for QOM > > The brute-force solution to expose the contents of the device array > with 128 properties has a slight AI smell. I find it ugly but I am > human :) Timur, have you read docs/devel/code-provenance.rst section "Use of AI-generated content"? > That said I don't know if there are better alternatives. > My approach would have been to use a string property : > > object_property_add_str(obj, "tx", > i2c_dummy_device_get_tx, NULL); > object_property_add_str(obj, "rx", > i2c_dummy_device_get_rx, > i2c_dummy_device_set_rx); > Any better ideas ? I believe you're talking about this function: [...] >> +static void i2c_dummy_device_init(Object *obj) >> +{ >> + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(obj); >> >> + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { >> + g_autofree char *name = g_strdup_printf("tx[%d]", i); >> + >> + object_property_add_uint8_ptr( >> + obj, name, &s->tx[i], >> + OBJ_PROP_FLAG_READWRITE); >> + } >> + >> + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { >> + g_autofree char *name = g_strdup_printf("rx[%d]", i); >> + >> + object_property_add_uint8_ptr( >> + obj, name, &s->rx[i], >> + OBJ_PROP_FLAG_READWRITE); >> + } >> + >> + object_property_add_uint8_ptr( >> + obj, "tx-pos", &s->tx_pos, >> + OBJ_PROP_FLAG_READWRITE); >> + object_property_add_uint8_ptr( >> + obj, "rx-pos", &s->rx_pos, >> + OBJ_PROP_FLAG_READWRITE); > > alignment could be improved. Yes, this style is unusual in QEMU, and should therefore be avoided. >> +} The function adds properties "tx-pos", "tx[0]", ... "tx[63]", and same for rx. The commit message tells us this is a "Simple I2C device with QOM properties to access transmit and receive buffers [...] for testing or prototyping." It doesn't tell us how it could be used, and I can't see any documentation, either. So let's start right there: please show us how this device could be used. This should help me understand what the properties are for. Only then can I advise on proper QOM usage. [...] ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-28 10:17 ` Markus Armbruster @ 2026-10-28 11:04 ` Cédric Le Goater 2026-10-28 11:26 ` Markus Armbruster 2026-10-28 16:20 ` Timur Maksimov 1 sibling, 1 reply; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 11:04 UTC (permalink / raw) To: Markus Armbruster Cc: Timur Maksimov, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard > The commit message tells us this is a "Simple I2C device with QOM > properties to access transmit and receive buffers [...] for testing or > prototyping." It doesn't tell us how it could be used, and I can't see > any documentation, either. So let's start right there: please show us > how this device could be used. This should help me understand what the > properties are for. Only then can I advise on proper QOM usage. > > [...] sorry, I should have added the link to the test case where these properties are used : https://lore.kernel.org/qemu-devel/20261027-aspeed-i2c-nonpkt-fix-v2-6-07deed1dff60@gmail.com/ Thanks, C. ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-28 11:04 ` Cédric Le Goater @ 2026-10-28 11:26 ` Markus Armbruster 0 siblings, 0 replies; 33+ messages in thread From: Markus Armbruster @ 2026-10-28 11:26 UTC (permalink / raw) To: Cédric Le Goater Cc: Timur Maksimov, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard Cédric Le Goater <clg@kaod.org> writes: >> The commit message tells us this is a "Simple I2C device with QOM >> properties to access transmit and receive buffers [...] for testing or >> prototyping." It doesn't tell us how it could be used, and I can't see >> any documentation, either. So let's start right there: please show us >> how this device could be used. This should help me understand what the >> properties are for. Only then can I advise on proper QOM usage. >> [...] > > sorry, I should have added the link to the test case where these > properties are used : > > https://lore.kernel.org/qemu-devel/20261027-aspeed-i2c-nonpkt-fix-v2-6-07deed1dff60@gmail.com/ Uh, that's a bit much for me to read. Timur, please answer my question. ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-28 10:17 ` Markus Armbruster 2026-10-28 11:04 ` Cédric Le Goater @ 2026-10-28 16:20 ` Timur Maksimov 2026-10-29 7:22 ` Markus Armbruster 1 sibling, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-28 16:20 UTC (permalink / raw) To: Markus Armbruster, Cédric Le Goater Cc: qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard Hi, thanks for the review. It was my decision to use such API, I thought it would be okay for a test device. I don't think there is a way to expose integer array as a single QOM property. Actually, we can go without this model. I suppose I will rewrite tests so they don't use it. On Wed, 2026-10-28 at 11:17 +0100, Markus Armbruster wrote: > Cédric Le Goater <clg@kaod.org> writes: > > > + Corey for I2C feed back. > > > > + Markus for QOM > > > > The brute-force solution to expose the contents of the device array > > with 128 properties has a slight AI smell. I find it ugly but I am > > human :) > > Timur, have you read docs/devel/code-provenance.rst section "Use of > AI-generated content"? > > > That said I don't know if there are better alternatives. > > My approach would have been to use a string property : > > > > object_property_add_str(obj, "tx", > > i2c_dummy_device_get_tx, NULL); > > object_property_add_str(obj, "rx", > > i2c_dummy_device_get_rx, > > i2c_dummy_device_set_rx); > > Any better ideas ? > > I believe you're talking about this function: > > [...] > > > > +static void i2c_dummy_device_init(Object *obj) > > > +{ > > > + I2CDummyDeviceState *s = I2C_DUMMY_DEVICE(obj); > > > > > > + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { > > > + g_autofree char *name = g_strdup_printf("tx[%d]", i); > > > + > > > + object_property_add_uint8_ptr( > > > + obj, name, &s->tx[i], > > > + OBJ_PROP_FLAG_READWRITE); > > > + } > > > + > > > + for (int i = 0; i < I2C_DUMMY_ARRAY_SIZE; i++) { > > > + g_autofree char *name = g_strdup_printf("rx[%d]", i); > > > + > > > + object_property_add_uint8_ptr( > > > + obj, name, &s->rx[i], > > > + OBJ_PROP_FLAG_READWRITE); > > > + } > > > + > > > + object_property_add_uint8_ptr( > > > + obj, "tx-pos", &s->tx_pos, > > > + OBJ_PROP_FLAG_READWRITE); > > > + object_property_add_uint8_ptr( > > > + obj, "rx-pos", &s->rx_pos, > > > + OBJ_PROP_FLAG_READWRITE); > > > > alignment could be improved. > > Yes, this style is unusual in QEMU, and should therefore be avoided. > > > > +} > > The function adds properties "tx-pos", "tx[0]", ... "tx[63]", and > same > for rx. > > The commit message tells us this is a "Simple I2C device with QOM > properties to access transmit and receive buffers [...] for testing > or > prototyping." It doesn't tell us how it could be used, and I can't > see > any documentation, either. So let's start right there: please show > us > how this device could be used. This should help me understand what > the > properties are for. Only then can I advise on proper QOM usage. > > [...] ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-28 16:20 ` Timur Maksimov @ 2026-10-29 7:22 ` Markus Armbruster 2026-10-29 8:20 ` Timur Maksimov 0 siblings, 1 reply; 33+ messages in thread From: Markus Armbruster @ 2026-10-29 7:22 UTC (permalink / raw) To: Timur Maksimov Cc: Cédric Le Goater, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard Timur Maksimov <gessingate@gmail.com> writes: > Hi, thanks for the review. > > It was my decision to use such API, I thought it would be okay for a > test device. I don't think there is a way to expose integer array as a > single QOM property. There are ways, but I need to know what you're trying to accomplish to be able to provide guidance. > Actually, we can go without this model. I suppose I will rewrite tests > so they don't use it. You haven't answered my question on code-provenance.rst. > On Wed, 2026-10-28 at 11:17 +0100, Markus Armbruster wrote: >> Cédric Le Goater <clg@kaod.org> writes: >> >> > + Corey for I2C feed back. >> > >> > + Markus for QOM >> > >> > The brute-force solution to expose the contents of the device array >> > with 128 properties has a slight AI smell. I find it ugly but I am >> > human :) >> >> Timur, have you read docs/devel/code-provenance.rst section "Use of >> AI-generated content"? >> >> > That said I don't know if there are better alternatives. [...] ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-29 7:22 ` Markus Armbruster @ 2026-10-29 8:20 ` Timur Maksimov 2026-10-29 8:37 ` Cédric Le Goater 0 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-29 8:20 UTC (permalink / raw) To: Markus Armbruster Cc: Cédric Le Goater, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard Hi, Markus On Thu, 2026-10-29 at 08:22 +0100, Markus Armbruster wrote: > Timur Maksimov <gessingate@gmail.com> writes: > > > Hi, thanks for the review. > > > > It was my decision to use such API, I thought it would be okay for > > a > > test device. I don't think there is a way to expose integer array > > as a > > single QOM property. > > There are ways, but I need to know what you're trying to accomplish > to > be able to provide guidance. We need to access the transfer buffer array with QOM. Actually, I have a few ideas on how to do this without cluttering the QOM with properties, such as using a FIFO interface. > > > Actually, we can go without this model. I suppose I will rewrite > > tests > > so they don't use it. > > You haven't answered my question on code-provenance.rst. > > > On Wed, 2026-10-28 at 11:17 +0100, Markus Armbruster wrote: > > > Cédric Le Goater <clg@kaod.org> writes: > > > > > > > + Corey for I2C feed back. > > > > > > > > + Markus for QOM > > > > > > > > The brute-force solution to expose the contents of the device > > > > array > > > > with 128 properties has a slight AI smell. I find it ugly but I > > > > am > > > > human :) > > > > > > Timur, have you read docs/devel/code-provenance.rst section "Use > > > of > > > AI-generated content"? > > > > > > > That said I don't know if there are better > > > > alternatives. > > [...] Sorry. Yes, I have read it. This is solely my work, no AI was used to write it. I also have a question about MAINTAINERS file. If we keep this device, where should I put this file? ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-29 8:20 ` Timur Maksimov @ 2026-10-29 8:37 ` Cédric Le Goater 2026-10-29 12:45 ` Corey Minyard 0 siblings, 1 reply; 33+ messages in thread From: Cédric Le Goater @ 2026-10-29 8:37 UTC (permalink / raw) To: Timur Maksimov, Markus Armbruster Cc: qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Corey Minyard On 10/29/26 09:20, Timur Maksimov wrote: > Hi, Markus > > On Thu, 2026-10-29 at 08:22 +0100, Markus Armbruster wrote: >> Timur Maksimov <gessingate@gmail.com> writes: >> >>> Hi, thanks for the review. >>> >>> It was my decision to use such API, I thought it would be okay for >>> a >>> test device. I don't think there is a way to expose integer array >>> as a >>> single QOM property. >> >> There are ways, but I need to know what you're trying to accomplish >> to >> be able to provide guidance. > > We need to access the transfer buffer array with QOM. Actually, I have > a few ideas on how to do this without cluttering the QOM with > properties, such as using a FIFO interface. >> >>> Actually, we can go without this model. I suppose I will rewrite >>> tests >>> so they don't use it. >> >> You haven't answered my question on code-provenance.rst. >> >>> On Wed, 2026-10-28 at 11:17 +0100, Markus Armbruster wrote: >>>> Cédric Le Goater <clg@kaod.org> writes: >>>> >>>>> + Corey for I2C feed back. >>>>> >>>>> + Markus for QOM >>>>> >>>>> The brute-force solution to expose the contents of the device >>>>> array >>>>> with 128 properties has a slight AI smell. I find it ugly but I >>>>> am >>>>> human :) >>>> >>>> Timur, have you read docs/devel/code-provenance.rst section "Use >>>> of >>>> AI-generated content"? >>>> >>>>> That said I don't know if there are better >>>>> alternatives. >> >> [...] > > Sorry. Yes, I have read it. This is solely my work, no AI was used to > write it. > > I also have a question about MAINTAINERS file. If we keep this device, > where should I put this file? You can put it under Aspeed for now, since it will be only used by Aspeed machines for tests. If it grows, I guess we should move it under "I2C and SMBus" if Corey agrees. Thanks, C. ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-29 8:37 ` Cédric Le Goater @ 2026-10-29 12:45 ` Corey Minyard 2026-10-29 13:04 ` Cédric Le Goater 0 siblings, 1 reply; 33+ messages in thread From: Corey Minyard @ 2026-10-29 12:45 UTC (permalink / raw) To: Cédric Le Goater Cc: Timur Maksimov, Markus Armbruster, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier [-- Attachment #1: Type: text/plain, Size: 2305 bytes --] On Thu, Oct 29, 2026 at 09:37:14AM +0100, Cédric Le Goater wrote: > On 10/29/26 09:20, Timur Maksimov wrote: > > Hi, Markus > > > > On Thu, 2026-10-29 at 08:22 +0100, Markus Armbruster wrote: > > > Timur Maksimov <gessingate@gmail.com> writes: > > > > > > > Hi, thanks for the review. > > > > > > > > It was my decision to use such API, I thought it would be okay for > > > > a > > > > test device. I don't think there is a way to expose integer array > > > > as a > > > > single QOM property. > > > > > > There are ways, but I need to know what you're trying to accomplish > > > to > > > be able to provide guidance. > > > > We need to access the transfer buffer array with QOM. Actually, I have > > a few ideas on how to do this without cluttering the QOM with > > properties, such as using a FIFO interface. > > > > > > > Actually, we can go without this model. I suppose I will rewrite > > > > tests > > > > so they don't use it. > > > > > > You haven't answered my question on code-provenance.rst. > > > > > > > On Wed, 2026-10-28 at 11:17 +0100, Markus Armbruster wrote: > > > > > Cédric Le Goater <clg@kaod.org> writes: > > > > > > > > > > > + Corey for I2C feed back. > > > > > > > > > > > > + Markus for QOM > > > > > > > > > > > > The brute-force solution to expose the contents of the device > > > > > > array > > > > > > with 128 properties has a slight AI smell. I find it ugly but I > > > > > > am > > > > > > human :) > > > > > > > > > > Timur, have you read docs/devel/code-provenance.rst section "Use > > > > > of > > > > > AI-generated content"? > > > > > > > > > > > That said I don't know if there are better > > > > > > alternatives. > > > > > > [...] > > > > Sorry. Yes, I have read it. This is solely my work, no AI was used to > > write it. > > > > I also have a question about MAINTAINERS file. If we keep this device, > > where should I put this file? > > You can put it under Aspeed for now, since it will be only used by > Aspeed machines for tests. If it grows, I guess we should move it > under "I2C and SMBus" if Corey agrees. This seems pretty generic, so I think it makes more sense under the I2C directory. But I'm fine either way. -corey > > Thanks, > > C. [-- Attachment #2: smime.p7s --] [-- Type: application/x-pkcs7-signature, Size: 3365 bytes --] ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-29 12:45 ` Corey Minyard @ 2026-10-29 13:04 ` Cédric Le Goater 2026-10-29 15:56 ` Corey Minyard 0 siblings, 1 reply; 33+ messages in thread From: Cédric Le Goater @ 2026-10-29 13:04 UTC (permalink / raw) To: cminyard Cc: Timur Maksimov, Markus Armbruster, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier >>> I also have a question about MAINTAINERS file. If we keep this device, >>> where should I put this file? >> >> You can put it under Aspeed for now, since it will be only used by >> Aspeed machines for tests. If it grows, I guess we should move it >> under "I2C and SMBus" if Corey agrees. > > This seems pretty generic, so I think it makes more sense under the > I2C directory. But I'm fine either way. Under the I2C directory then. Fine with me. There is nothing specific to aspeed about it. Thanks, C. ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 1/6] hw/i2c: add dummy device 2026-10-29 13:04 ` Cédric Le Goater @ 2026-10-29 15:56 ` Corey Minyard 0 siblings, 0 replies; 33+ messages in thread From: Corey Minyard @ 2026-10-29 15:56 UTC (permalink / raw) To: Cédric Le Goater Cc: Timur Maksimov, Markus Armbruster, qemu-devel, Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier [-- Attachment #1: Type: text/plain, Size: 743 bytes --] On Thu, Oct 29, 2026 at 02:04:16PM +0100, Cédric Le Goater wrote: > > > > > I also have a question about MAINTAINERS file. If we keep this device, > > > > where should I put this file? > > > > > > You can put it under Aspeed for now, since it will be only used by > > > Aspeed machines for tests. If it grows, I guess we should move it > > > under "I2C and SMBus" if Corey agrees. > > > > This seems pretty generic, so I think it makes more sense under the > > I2C directory. But I'm fine either way. > > Under the I2C directory then. Fine with me. There is nothing specific > to aspeed about it. Sounds good. The change seems ok. I'll wait for the changes based on comments. -corey > > Thanks, > > C. > [-- Attachment #2: smime.p7s --] [-- Type: application/x-pkcs7-signature, Size: 3365 bytes --] ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov 2026-10-27 19:50 ` [PATCH v2 1/6] hw/i2c: add dummy device Timur Maksimov @ 2026-10-27 19:50 ` Timur Maksimov 2026-10-28 8:17 ` Cédric Le Goater ` (2 more replies) 2026-10-27 19:50 ` [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading Timur Maksimov ` (4 subsequent siblings) 6 siblings, 3 replies; 33+ messages in thread From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw) To: qemu-devel Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Timur Maksimov In new register mode, BUS_BUSY_STS is bit 16 of I2CC_MS_TXRX_BYTE_BUF; bit 16 of I2CM_CMD is PKT_OP_EN. Report the bus busy state in the correct register. Signed-off-by: Timur Maksimov <gessingate@gmail.com> --- hw/i2c/aspeed_i2c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 0bc4bb6fbe..fb6d19c37b 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -146,7 +146,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, case A_I2CC_POOL_CTRL: case A_I2CM_INTR_CTRL: case A_I2CM_INTR_STS: - case A_I2CC_MS_TXRX_BYTE_BUF: + case A_I2CM_CMD: case A_I2CM_DMA_LEN: case A_I2CM_DMA_TX_ADDR: case A_I2CM_DMA_RX_ADDR: @@ -166,7 +166,8 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, case A_I2CC_DMA_ADDR: value = extract64(bus->dma_dram_offset, 0, 32); break; - case A_I2CM_CMD: + case A_I2CC_MS_TXRX_BYTE_BUF: + value = bus->regs[offset / sizeof(*bus->regs)]; value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); break; case A_I2CM_DMA_TX_ADDR_HI: -- 2.53.0 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode 2026-10-27 19:50 ` [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode Timur Maksimov @ 2026-10-28 8:17 ` Cédric Le Goater 2026-10-28 21:24 ` Cédric Le Goater 2026-10-29 5:21 ` Jamin Lin 2 siblings, 0 replies; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 8:17 UTC (permalink / raw) To: Timur Maksimov, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier On 10/27/26 20:50, Timur Maksimov wrote: > In new register mode, BUS_BUSY_STS is bit 16 of I2CC_MS_TXRX_BYTE_BUF; > bit 16 of I2CM_CMD is PKT_OP_EN. Report the bus busy state in the > correct register. > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> Could you provide a Fixes tag please ? Thanks, C. > --- > hw/i2c/aspeed_i2c.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c > index 0bc4bb6fbe..fb6d19c37b 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -146,7 +146,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CC_POOL_CTRL: > case A_I2CM_INTR_CTRL: > case A_I2CM_INTR_STS: > - case A_I2CC_MS_TXRX_BYTE_BUF: > + case A_I2CM_CMD: > case A_I2CM_DMA_LEN: > case A_I2CM_DMA_TX_ADDR: > case A_I2CM_DMA_RX_ADDR: > @@ -166,7 +166,8 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CC_DMA_ADDR: > value = extract64(bus->dma_dram_offset, 0, 32); > break; > - case A_I2CM_CMD: > + case A_I2CC_MS_TXRX_BYTE_BUF: > + value = bus->regs[offset / sizeof(*bus->regs)]; > value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); > break; > case A_I2CM_DMA_TX_ADDR_HI: > ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode 2026-10-27 19:50 ` [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode Timur Maksimov 2026-10-28 8:17 ` Cédric Le Goater @ 2026-10-28 21:24 ` Cédric Le Goater 2026-10-29 5:21 ` Jamin Lin 2 siblings, 0 replies; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 21:24 UTC (permalink / raw) To: Timur Maksimov, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier On 10/27/26 20:50, Timur Maksimov wrote: > In new register mode, BUS_BUSY_STS is bit 16 of I2CC_MS_TXRX_BYTE_BUF; > bit 16 of I2CM_CMD is PKT_OP_EN. Report the bus busy state in the > correct register. Fixes: ba2cccd64e90 ("aspeed: i2c: Add new mode support") > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > hw/i2c/aspeed_i2c.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c > index 0bc4bb6fbe..fb6d19c37b 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -146,7 +146,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CC_POOL_CTRL: > case A_I2CM_INTR_CTRL: > case A_I2CM_INTR_STS: > - case A_I2CC_MS_TXRX_BYTE_BUF: > + case A_I2CM_CMD: > case A_I2CM_DMA_LEN: > case A_I2CM_DMA_TX_ADDR: > case A_I2CM_DMA_RX_ADDR: > @@ -166,7 +166,8 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CC_DMA_ADDR: > value = extract64(bus->dma_dram_offset, 0, 32); > break; > - case A_I2CM_CMD: > + case A_I2CC_MS_TXRX_BYTE_BUF: > + value = bus->regs[offset / sizeof(*bus->regs)]; > value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); > break; > case A_I2CM_DMA_TX_ADDR_HI: > ^ permalink raw reply [flat|nested] 33+ messages in thread
* RE: [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode 2026-10-27 19:50 ` [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode Timur Maksimov 2026-10-28 8:17 ` Cédric Le Goater 2026-10-28 21:24 ` Cédric Le Goater @ 2026-10-29 5:21 ` Jamin Lin 2 siblings, 0 replies; 33+ messages in thread From: Jamin Lin @ 2026-10-29 5:21 UTC (permalink / raw) To: Timur Maksimov, qemu-devel@nongnu.org Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier > Subject: [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register > mode > > In new register mode, BUS_BUSY_STS is bit 16 of I2CC_MS_TXRX_BYTE_BUF; > bit 16 of I2CM_CMD is PKT_OP_EN. Report the bus busy state in the correct > register. > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > hw/i2c/aspeed_i2c.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > 0bc4bb6fbe..fb6d19c37b 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -146,7 +146,7 @@ static uint64_t > aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CC_POOL_CTRL: > case A_I2CM_INTR_CTRL: > case A_I2CM_INTR_STS: > - case A_I2CC_MS_TXRX_BYTE_BUF: > + case A_I2CM_CMD: > case A_I2CM_DMA_LEN: > case A_I2CM_DMA_TX_ADDR: > case A_I2CM_DMA_RX_ADDR: > @@ -166,7 +166,8 @@ static uint64_t > aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CC_DMA_ADDR: > value = extract64(bus->dma_dram_offset, 0, 32); > break; > - case A_I2CM_CMD: > + case A_I2CC_MS_TXRX_BYTE_BUF: > + value = bus->regs[offset / sizeof(*bus->regs)]; > value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, > i2c_bus_busy(bus->bus)); > break; > case A_I2CM_DMA_TX_ADDR_HI: > > -- > 2.53.0 Reviewed-by: Jamin Lin <jamin_lin@aspeedtech.com> Thanks, Jamin ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov 2026-10-27 19:50 ` [PATCH v2 1/6] hw/i2c: add dummy device Timur Maksimov 2026-10-27 19:50 ` [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode Timur Maksimov @ 2026-10-27 19:50 ` Timur Maksimov 2026-10-29 5:23 ` Jamin Lin 2026-10-27 19:50 ` [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode Timur Maksimov ` (3 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw) To: qemu-devel Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Timur Maksimov The I2CD_CMD register now returns a valid value in old register mode. Fixes: c2c5beec42bf ("hw/i2c/aspeed_i2c: Fix out-of-bounds read in I2C MMIO handlers") Signed-off-by: Timur Maksimov <gessingate@gmail.com> --- hw/i2c/aspeed_i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index fb6d19c37b..ff6ef6d23a 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -108,6 +108,7 @@ static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, value = bus->regs[offset / sizeof(*bus->regs)]; break; case A_I2CD_CMD: + value = bus->regs[offset / sizeof(*bus->regs)]; value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); break; case A_I2CD_DMA_ADDR: -- 2.53.0 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* RE: [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading 2026-10-27 19:50 ` [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading Timur Maksimov @ 2026-10-29 5:23 ` Jamin Lin 0 siblings, 0 replies; 33+ messages in thread From: Jamin Lin @ 2026-10-29 5:23 UTC (permalink / raw) To: Timur Maksimov, qemu-devel@nongnu.org Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier > Subject: [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading > > The I2CD_CMD register now returns a valid value in old register mode. > > Fixes: c2c5beec42bf ("hw/i2c/aspeed_i2c: Fix out-of-bounds read in I2C MMIO > handlers") > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > hw/i2c/aspeed_i2c.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > fb6d19c37b..ff6ef6d23a 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -108,6 +108,7 @@ static uint64_t > aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, > value = bus->regs[offset / sizeof(*bus->regs)]; > break; > case A_I2CD_CMD: > + value = bus->regs[offset / sizeof(*bus->regs)]; > value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, > i2c_bus_busy(bus->bus)); > break; > case A_I2CD_DMA_ADDR: > > -- > 2.53.0 Reviewed-by: Jamin Lin <jamin_lin@aspeedtech.com> Thanks, Jamin ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov ` (2 preceding siblings ...) 2026-10-27 19:50 ` [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading Timur Maksimov @ 2026-10-27 19:50 ` Timur Maksimov 2026-10-29 6:33 ` Jamin Lin 2026-10-27 19:50 ` [PATCH v2 5/6] hw/i2c/aspeed: fix DMA " Timur Maksimov ` (2 subsequent siblings) 6 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw) To: qemu-devel Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Timur Maksimov In non-packet mode, the first byte of the tx buffer pool contains a device address, so avoid sending it as a data byte. Fixes: 961faf3ddbd8 ("hw/i2c/aspeed: Fix TXBUF transmission start position error") Signed-off-by: Timur Maksimov <gessingate@gmail.com> --- hw/i2c/aspeed_i2c.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index ff6ef6d23a..510758fb7a 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -382,7 +382,7 @@ static void aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); } -static int aspeed_i2c_bus_send(AspeedI2CBus *bus) +static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool send_addr) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); int ret = -1; @@ -391,11 +391,12 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus) uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint8_t pool_offset = send_addr ? 1 : 0; int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) + 1; if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { - for (i = 0; i < pool_tx_count; i++) { + for (i = pool_offset; i < pool_tx_count; i++) { uint8_t *pool_base = aic->bus_pool_base(bus); trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, @@ -633,6 +634,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + bool start_cmd = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD); if (!aspeed_i2c_check_sram(bus)) { return; @@ -642,7 +644,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) aspeed_i2c_bus_cmd_dump(bus); } - if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) { + if (start_cmd) { uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? I2CD_MSTARTR : I2CD_MSTART; uint8_t addr; @@ -686,7 +688,8 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { aspeed_i2c_set_state(bus, I2CD_MTXD); - if (aspeed_i2c_bus_send(bus)) { + if (aspeed_i2c_bus_send(bus, start_cmd && + !aspeed_i2c_bus_pkt_mode_en(bus))) { SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); i2c_end_transfer(bus->bus); } else { -- 2.53.0 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* RE: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode 2026-10-27 19:50 ` [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode Timur Maksimov @ 2026-10-29 6:33 ` Jamin Lin 2026-10-29 7:23 ` Timur Maksimov 0 siblings, 1 reply; 33+ messages in thread From: Jamin Lin @ 2026-10-29 6:33 UTC (permalink / raw) To: Timur Maksimov, qemu-devel@nongnu.org Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier Hi Timur, As far as I can see, no ASPEED Linux driver uses non-packet buffer or DMA mode for master transfers: - i2c-aspeed.c (old register mode) only uses byte mode; the address is always written to the byte buffer register before START. - i2c-ast2600.c in the ASPEED SDK kernel (new register mode) sets PKT_EN for every master transfer, and the address comes from PKT_ADDR. Have you verified this behavior on real hardware? Which firmware are you running that uses non-packet buffer/DMA mode? Thanks, Jamin > Subject: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode > > In non-packet mode, the first byte of the tx buffer pool contains a device > address, so avoid sending it as a data byte. > > Fixes: 961faf3ddbd8 ("hw/i2c/aspeed: Fix TXBUF transmission start position > error") > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > hw/i2c/aspeed_i2c.c | 11 +++++++---- > 1 file changed, 7 insertions(+), 4 deletions(-) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > ff6ef6d23a..510758fb7a 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -382,7 +382,7 @@ static void > aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); } > > -static int aspeed_i2c_bus_send(AspeedI2CBus *bus) > +static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool send_addr) > { > AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); > int ret = -1; > @@ -391,11 +391,12 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus) > uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); > uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > + uint8_t pool_offset = send_addr ? 1 : 0; > int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, > reg_pool_ctrl, > TX_COUNT) + 1; > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { > - for (i = 0; i < pool_tx_count; i++) { > + for (i = pool_offset; i < pool_tx_count; i++) { > uint8_t *pool_base = aic->bus_pool_base(bus); > > trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, @@ > -633,6 +634,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus > *bus, uint64_t value) > uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); > uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > + bool start_cmd = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, > + M_START_CMD); > > if (!aspeed_i2c_check_sram(bus)) { > return; > @@ -642,7 +644,7 @@ static void > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > aspeed_i2c_bus_cmd_dump(bus); > } > > - if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) { > + if (start_cmd) { > uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? > I2CD_MSTARTR : I2CD_MSTART; > uint8_t addr; > @@ -686,7 +688,8 @@ static void > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { > aspeed_i2c_set_state(bus, I2CD_MTXD); > - if (aspeed_i2c_bus_send(bus)) { > + if (aspeed_i2c_bus_send(bus, start_cmd && > + !aspeed_i2c_bus_pkt_mode_en > (bus))) > + { > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, > 1); > i2c_end_transfer(bus->bus); > } else { > > -- > 2.53.0 ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode 2026-10-29 6:33 ` Jamin Lin @ 2026-10-29 7:23 ` Timur Maksimov 2026-10-29 7:44 ` Jamin Lin 0 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-29 7:23 UTC (permalink / raw) To: Jamin Lin, qemu-devel@nongnu.org Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier Hi Jamin, you are right; no driver uses those modes, and I can't verify their work at the moment. Do you suppose, that they don't work? Are there issues with them? We can exclude them from tests, but buffer mode is important for old chips like AST2500. On Thu, 2026-10-29 at 06:33 +0000, Jamin Lin wrote: > Hi Timur, > > As far as I can see, no ASPEED Linux driver uses non-packet buffer or > DMA mode for master transfers: > - i2c-aspeed.c (old register mode) only uses byte mode; the address > is always written to the byte buffer register before START. > - i2c-ast2600.c in the ASPEED SDK kernel (new register mode) sets > PKT_EN for every master transfer, and the address comes from > PKT_ADDR. > > Have you verified this behavior on real hardware? Which firmware are > you running that uses non-packet buffer/DMA mode? > > Thanks, > Jamin > > > Subject: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non- > > packet mode > > > > In non-packet mode, the first byte of the tx buffer pool contains a > > device > > address, so avoid sending it as a data byte. > > > > Fixes: 961faf3ddbd8 ("hw/i2c/aspeed: Fix TXBUF transmission start > > position > > error") > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > --- > > hw/i2c/aspeed_i2c.c | 11 +++++++---- > > 1 file changed, 7 insertions(+), 4 deletions(-) > > > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > > ff6ef6d23a..510758fb7a 100644 > > --- a/hw/i2c/aspeed_i2c.c > > +++ b/hw/i2c/aspeed_i2c.c > > @@ -382,7 +382,7 @@ static void > > aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) > > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); } > > > > -static int aspeed_i2c_bus_send(AspeedI2CBus *bus) > > +static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool send_addr) > > { > > AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); > > int ret = -1; > > @@ -391,11 +391,12 @@ static int aspeed_i2c_bus_send(AspeedI2CBus > > *bus) > > uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); > > uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); > > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > > + uint8_t pool_offset = send_addr ? 1 : 0; > > int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, > > reg_pool_ctrl, > > TX_COUNT) + 1; > > > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { > > - for (i = 0; i < pool_tx_count; i++) { > > + for (i = pool_offset; i < pool_tx_count; i++) { > > uint8_t *pool_base = aic->bus_pool_base(bus); > > > > trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, > > @@ > > -633,6 +634,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus > > *bus, uint64_t value) > > uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); > > uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); > > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > > + bool start_cmd = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, > > + M_START_CMD); > > > > if (!aspeed_i2c_check_sram(bus)) { > > return; > > @@ -642,7 +644,7 @@ static void > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > aspeed_i2c_bus_cmd_dump(bus); > > } > > > > - if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) > > { > > + if (start_cmd) { > > uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? > > I2CD_MSTARTR : I2CD_MSTART; > > uint8_t addr; > > @@ -686,7 +688,8 @@ static void > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { > > aspeed_i2c_set_state(bus, I2CD_MTXD); > > - if (aspeed_i2c_bus_send(bus)) { > > + if (aspeed_i2c_bus_send(bus, start_cmd && > > + !aspeed_i2c_bus_pkt_mode_en > > (bus))) > > + { > > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, > > TX_NAK, > > 1); > > i2c_end_transfer(bus->bus); > > } else { > > > > -- > > 2.53.0 ^ permalink raw reply [flat|nested] 33+ messages in thread
* RE: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode 2026-10-29 7:23 ` Timur Maksimov @ 2026-10-29 7:44 ` Jamin Lin 2026-10-29 8:45 ` Timur Maksimov 0 siblings, 1 reply; 33+ messages in thread From: Jamin Lin @ 2026-10-29 7:44 UTC (permalink / raw) To: Timur Maksimov, qemu-devel@nongnu.org Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier Hi Timur, I'm not saying they don't work. My main concern is that we currently don't have firmware to test this mode on real hardware. Although we can test it in QEMU using QTest, this does not guarantee that QEMU behaves the same way as real hardware. Without testing on real hardware, we cannot confirm that the behavior implemented by this patch matches the actual hardware behavior. That said, I think the QTest in this series is very useful, as the functional tests don't cover all the I2C modes. Thanks, Jamin > Subject: Re: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet > mode > > Hi Jamin, you are right; no driver uses those modes, and I can't verify their > work at the moment. > Do you suppose, that they don't work? Are there issues with them? > We can exclude them from tests, but buffer mode is important for old chips > like AST2500. > > On Thu, 2026-10-29 at 06:33 +0000, Jamin Lin wrote: > > Hi Timur, > > > > As far as I can see, no ASPEED Linux driver uses non-packet buffer or > > DMA mode for master transfers: > > - i2c-aspeed.c (old register mode) only uses byte mode; the address > > is always written to the byte buffer register before START. > > - i2c-ast2600.c in the ASPEED SDK kernel (new register mode) sets > > PKT_EN for every master transfer, and the address comes from > > PKT_ADDR. > > > > Have you verified this behavior on real hardware? Which firmware are > > you running that uses non-packet buffer/DMA mode? > > > > Thanks, > > Jamin > > > > > Subject: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non- > > > packet mode > > > > > > In non-packet mode, the first byte of the tx buffer pool contains a > > > device address, so avoid sending it as a data byte. > > > > > > Fixes: 961faf3ddbd8 ("hw/i2c/aspeed: Fix TXBUF transmission start > > > position > > > error") > > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > > --- > > > hw/i2c/aspeed_i2c.c | 11 +++++++---- > > > 1 file changed, 7 insertions(+), 4 deletions(-) > > > > > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > > > ff6ef6d23a..510758fb7a 100644 > > > --- a/hw/i2c/aspeed_i2c.c > > > +++ b/hw/i2c/aspeed_i2c.c > > > @@ -382,7 +382,7 @@ static void > > > aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) > > > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, > 0); } > > > > > > -static int aspeed_i2c_bus_send(AspeedI2CBus *bus) > > > +static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool send_addr) > > > { > > > AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); > > > int ret = -1; > > > @@ -391,11 +391,12 @@ static int aspeed_i2c_bus_send(AspeedI2CBus > > > *bus) > > > uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); > > > uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); > > > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > > > + uint8_t pool_offset = send_addr ? 1 : 0; > > > int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, > > > reg_pool_ctrl, > > > > T > X_COUNT) + 1; > > > > > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) > { > > > - for (i = 0; i < pool_tx_count; i++) { > > > + for (i = pool_offset; i < pool_tx_count; i++) { > > > uint8_t *pool_base = aic->bus_pool_base(bus); > > > > > > trace_aspeed_i2c_bus_send("BUF", i + 1, > pool_tx_count, > > > @@ > > > -633,6 +634,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus > > > *bus, uint64_t value) > > > uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); > > > uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); > > > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > > > + bool start_cmd = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, > > > +M_START_CMD); > > > > > > if (!aspeed_i2c_check_sram(bus)) { > > > return; > > > @@ -642,7 +644,7 @@ static void > > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > > aspeed_i2c_bus_cmd_dump(bus); > > > } > > > > > > - if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, > M_START_CMD)) { > > > + if (start_cmd) { > > > uint8_t state = aspeed_i2c_get_state(bus) & > I2CD_MACTIVE ? > > > I2CD_MSTARTR : I2CD_MSTART; > > > uint8_t addr; > > > @@ -686,7 +688,8 @@ static void > > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > > > > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) > { > > > aspeed_i2c_set_state(bus, I2CD_MTXD); > > > - if (aspeed_i2c_bus_send(bus)) { > > > + if (aspeed_i2c_bus_send(bus, start_cmd && > > > > + !aspeed_i2c_bus_pkt > _mode_en > > > (bus))) > > > + { > > > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, > > > TX_NAK, 1); > > > i2c_end_transfer(bus->bus); > > > } else { > > > > > > -- > > > 2.53.0 ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode 2026-10-29 7:44 ` Jamin Lin @ 2026-10-29 8:45 ` Timur Maksimov 0 siblings, 0 replies; 33+ messages in thread From: Timur Maksimov @ 2026-10-29 8:45 UTC (permalink / raw) To: Jamin Lin, qemu-devel@nongnu.org Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier Hi, Jamin. Understood. I don’t planning to write a driver, but I might verify them if I have some time. Otherwise, let’s just skip them, as I am not sure that the current implementation is incorrect. On Thu, 2026-10-29 at 07:44 +0000, Jamin Lin wrote: > Hi Timur, > > I'm not saying they don't work. > > My main concern is that we currently don't have firmware to test this > mode on real hardware. > Although we can test it in QEMU using QTest, this does not guarantee > that QEMU behaves the same way as real hardware. > Without testing on real hardware, we cannot confirm that the behavior > implemented by this patch matches the actual hardware behavior. > > That said, I think the QTest in this series is very useful, as the > functional tests don't cover all the I2C modes. > > Thanks, > Jamin > > > Subject: Re: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in > > non-packet > > mode > > > > Hi Jamin, you are right; no driver uses those modes, and I can't > > verify their > > work at the moment. > > Do you suppose, that they don't work? Are there issues with them? > > We can exclude them from tests, but buffer mode is important for > > old chips > > like AST2500. > > > > On Thu, 2026-10-29 at 06:33 +0000, Jamin Lin wrote: > > > Hi Timur, > > > > > > As far as I can see, no ASPEED Linux driver uses non-packet > > > buffer or > > > DMA mode for master transfers: > > > - i2c-aspeed.c (old register mode) only uses byte mode; the > > > address > > > is always written to the byte buffer register before START. > > > - i2c-ast2600.c in the ASPEED SDK kernel (new register mode) sets > > > PKT_EN for every master transfer, and the address comes from > > > PKT_ADDR. > > > > > > Have you verified this behavior on real hardware? Which firmware > > > are > > > you running that uses non-packet buffer/DMA mode? > > > > > > Thanks, > > > Jamin > > > > > > > Subject: [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in > > > > non- > > > > packet mode > > > > > > > > In non-packet mode, the first byte of the tx buffer pool > > > > contains a > > > > device address, so avoid sending it as a data byte. > > > > > > > > Fixes: 961faf3ddbd8 ("hw/i2c/aspeed: Fix TXBUF transmission > > > > start > > > > position > > > > error") > > > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > > > --- > > > > hw/i2c/aspeed_i2c.c | 11 +++++++---- > > > > 1 file changed, 7 insertions(+), 4 deletions(-) > > > > > > > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > > > > ff6ef6d23a..510758fb7a 100644 > > > > --- a/hw/i2c/aspeed_i2c.c > > > > +++ b/hw/i2c/aspeed_i2c.c > > > > @@ -382,7 +382,7 @@ static void > > > > aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) > > > > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, > > 0); } > > > > > > > > -static int aspeed_i2c_bus_send(AspeedI2CBus *bus) > > > > +static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool > > > > send_addr) > > > > { > > > > AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus- > > > > >controller); > > > > int ret = -1; > > > > @@ -391,11 +391,12 @@ static int > > > > aspeed_i2c_bus_send(AspeedI2CBus > > > > *bus) > > > > uint32_t reg_pool_ctrl = > > > > aspeed_i2c_bus_pool_ctrl_offset(bus); > > > > uint32_t reg_byte_buf = > > > > aspeed_i2c_bus_byte_buf_offset(bus); > > > > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > > > > + uint8_t pool_offset = send_addr ? 1 : 0; > > > > int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, > > > > reg_pool_ctrl, > > > > > > T > > X_COUNT) + 1; > > > > > > > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, > > > > TX_BUFF_EN)) > > { > > > > - for (i = 0; i < pool_tx_count; i++) { > > > > + for (i = pool_offset; i < pool_tx_count; i++) { > > > > uint8_t *pool_base = aic->bus_pool_base(bus); > > > > > > > > trace_aspeed_i2c_bus_send("BUF", i + 1, > > pool_tx_count, > > > > @@ > > > > -633,6 +634,7 @@ static void > > > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus > > > > *bus, uint64_t value) > > > > uint32_t reg_intr_sts = > > > > aspeed_i2c_bus_intr_sts_offset(bus); > > > > uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); > > > > uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); > > > > + bool start_cmd = SHARED_ARRAY_FIELD_EX32(bus->regs, > > > > reg_cmd, > > > > +M_START_CMD); > > > > > > > > if (!aspeed_i2c_check_sram(bus)) { > > > > return; > > > > @@ -642,7 +644,7 @@ static void > > > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > > > aspeed_i2c_bus_cmd_dump(bus); > > > > } > > > > > > > > - if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, > > M_START_CMD)) { > > > > + if (start_cmd) { > > > > uint8_t state = aspeed_i2c_get_state(bus) & > > I2CD_MACTIVE ? > > > > I2CD_MSTARTR : I2CD_MSTART; > > > > uint8_t addr; > > > > @@ -686,7 +688,8 @@ static void > > > > aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) > > > > > > > > if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) > > { > > > > aspeed_i2c_set_state(bus, I2CD_MTXD); > > > > - if (aspeed_i2c_bus_send(bus)) { > > > > + if (aspeed_i2c_bus_send(bus, start_cmd && > > > > > > + !aspeed_i2c_bus_pkt > > _mode_en > > > > (bus))) > > > > + { > > > > SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, > > > > TX_NAK, 1); > > > > i2c_end_transfer(bus->bus); > > > > } else { > > > > > > > > -- > > > > 2.53.0 ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 5/6] hw/i2c/aspeed: fix DMA transfer in non-packet mode 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov ` (3 preceding siblings ...) 2026-10-27 19:50 ` [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode Timur Maksimov @ 2026-10-27 19:50 ` Timur Maksimov 2026-10-28 21:32 ` Cédric Le Goater 2026-10-27 19:50 ` [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers Timur Maksimov 2026-10-28 11:07 ` [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Cédric Le Goater 6 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw) To: qemu-devel Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Timur Maksimov Avoid calling aspeed_i2c_set_tx_dma_dram_offset twice in non-packet mode where this function is called first in aspeed_i2c_get_addr. Also fix writing to I2CD_DMA_ADDR in old register mode. Signed-off-by: Timur Maksimov <gessingate@gmail.com> --- hw/i2c/aspeed_i2c.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 510758fb7a..a2b4c8b576 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -416,7 +416,9 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool send_addr) if (aspeed_i2c_is_new_mode(bus->controller)) { ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); } - aspeed_i2c_set_tx_dma_dram_offset(bus); + if (!send_addr) { + aspeed_i2c_set_tx_dma_dram_offset(bus); + } while (bus->regs[reg_dma_len]) { uint8_t data; ret = aspeed_i2c_dma_read(bus, &data); @@ -1070,11 +1072,13 @@ static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, bus->pending_intr_sts |= old_intr & cmd_intr; aspeed_i2c_bus_raise_interrupt(bus); break; + case A_I2CD_DMA_ADDR: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); break; } + bus->regs[R_I2CD_DMA_ADDR] = value; break; case A_I2CD_DMA_LEN: -- 2.53.0 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v2 5/6] hw/i2c/aspeed: fix DMA transfer in non-packet mode 2026-10-27 19:50 ` [PATCH v2 5/6] hw/i2c/aspeed: fix DMA " Timur Maksimov @ 2026-10-28 21:32 ` Cédric Le Goater 2026-10-29 6:23 ` Jamin Lin 0 siblings, 1 reply; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 21:32 UTC (permalink / raw) To: Timur Maksimov, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier On 10/27/26 20:50, Timur Maksimov wrote: > Avoid calling aspeed_i2c_set_tx_dma_dram_offset twice in non-packet mode > where this function is called first in aspeed_i2c_get_addr. > Also fix writing to I2CD_DMA_ADDR in old register mode. This should be 2 changes. > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > hw/i2c/aspeed_i2c.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c > index 510758fb7a..a2b4c8b576 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -416,7 +416,9 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus, bool send_addr) > if (aspeed_i2c_is_new_mode(bus->controller)) { > ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); > } > - aspeed_i2c_set_tx_dma_dram_offset(bus); > + if (!send_addr) { > + aspeed_i2c_set_tx_dma_dram_offset(bus); > + } > while (bus->regs[reg_dma_len]) { > uint8_t data; > ret = aspeed_i2c_dma_read(bus, &data); Fixes: efea7ddb4689 ("hw/i2c/aspeed_i2c: Fix DMA moving data into incorrect address") > @@ -1070,11 +1072,13 @@ static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, > bus->pending_intr_sts |= old_intr & cmd_intr; > aspeed_i2c_bus_raise_interrupt(bus); > break; > + > case A_I2CD_DMA_ADDR: > if (!aic->has_dma) { > qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); > break; > } > + bus->regs[R_I2CD_DMA_ADDR] = value; > break; > > case A_I2CD_DMA_LEN: > and Fixes: 2260fc6ff3ff ("aspeed: i2c: Use reg array instead of individual vars") Thanks, C. ^ permalink raw reply [flat|nested] 33+ messages in thread
* RE: [PATCH v2 5/6] hw/i2c/aspeed: fix DMA transfer in non-packet mode 2026-10-28 21:32 ` Cédric Le Goater @ 2026-10-29 6:23 ` Jamin Lin 0 siblings, 0 replies; 33+ messages in thread From: Jamin Lin @ 2026-10-29 6:23 UTC (permalink / raw) To: Cédric Le Goater, Timur Maksimov, qemu-devel@nongnu.org Cc: Paolo Bonzini, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier > Subject: Re: [PATCH v2 5/6] hw/i2c/aspeed: fix DMA transfer in non-packet > mode > > On 10/27/26 20:50, Timur Maksimov wrote: > > Avoid calling aspeed_i2c_set_tx_dma_dram_offset twice in non-packet > > mode where this function is called first in aspeed_i2c_get_addr. > > Also fix writing to I2CD_DMA_ADDR in old register mode. > > This should be 2 changes. > > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > --- > > hw/i2c/aspeed_i2c.c | 6 +++++- > > 1 file changed, 5 insertions(+), 1 deletion(-) > > > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > > 510758fb7a..a2b4c8b576 100644 > > --- a/hw/i2c/aspeed_i2c.c > > +++ b/hw/i2c/aspeed_i2c.c > > @@ -416,7 +416,9 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus, > bool send_addr) > > if (aspeed_i2c_is_new_mode(bus->controller)) { > > ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, > TX_LEN, 0); > > } > > - aspeed_i2c_set_tx_dma_dram_offset(bus); > > + if (!send_addr) { > > + aspeed_i2c_set_tx_dma_dram_offset(bus); > > + } > > while (bus->regs[reg_dma_len]) { > > uint8_t data; > > ret = aspeed_i2c_dma_read(bus, &data); > > > Fixes: efea7ddb4689 ("hw/i2c/aspeed_i2c: Fix DMA moving data into incorrect > address") > This please see my comments in patch 4. > > @@ -1070,11 +1072,13 @@ static void > aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, > > bus->pending_intr_sts |= old_intr & cmd_intr; > > aspeed_i2c_bus_raise_interrupt(bus); > > break; > > + > > case A_I2CD_DMA_ADDR: > > if (!aic->has_dma) { > > qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA > support\n", __func__); > > break; > > } > > + bus->regs[R_I2CD_DMA_ADDR] = value; > > break; > > > > case A_I2CD_DMA_LEN: > > > > and > > Fixes: 2260fc6ff3ff ("aspeed: i2c: Use reg array instead of individual vars") > This looks good to me. > Thanks, > > C. ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov ` (4 preceding siblings ...) 2026-10-27 19:50 ` [PATCH v2 5/6] hw/i2c/aspeed: fix DMA " Timur Maksimov @ 2026-10-27 19:50 ` Timur Maksimov 2026-10-28 8:56 ` Cédric Le Goater 2026-10-28 11:07 ` [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Cédric Le Goater 6 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-27 19:50 UTC (permalink / raw) To: qemu-devel Cc: Paolo Bonzini, Cédric Le Goater, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier, Timur Maksimov Test all supported register and transfer modes. Signed-off-by: Timur Maksimov <gessingate@gmail.com> --- tests/qtest/aspeed_i2c-test.c | 836 ++++++++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 + 2 files changed, 838 insertions(+) diff --git a/tests/qtest/aspeed_i2c-test.c b/tests/qtest/aspeed_i2c-test.c new file mode 100644 index 0000000000..7fb1debb56 --- /dev/null +++ b/tests/qtest/aspeed_i2c-test.c @@ -0,0 +1,836 @@ +/* + * QTest testcase for Aspeed I2C controller + * + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/i2c/aspeed_i2c.h" + +typedef enum TestAspeedI2CMode { + TEST_I2C_MODE_OLD, + TEST_I2C_MODE_NEW, + TEST_I2C_MODE_PKT, +} TestAspeedI2CMode; + +typedef enum TestAspeedMachine { + TEST_MACHINE_AST2400, + TEST_MACHINE_AST2500, + TEST_MACHINE_AST2600, +} TestAspeedMachine; + +typedef struct TestData { + const char *name; + TestAspeedMachine machine; + TestAspeedI2CMode mode; +} TestData; + +typedef struct TestFixture { + hwaddr base_addr; + hwaddr bus_offset; + int irq_no; + uint32_t pool_base; +} TestFixture; + +static const uint8_t tx_data[] = {0x10, 0x11, 0x12}; +static const uint8_t rx_data[] = {0x30, 0x31, 0x32}; + +#define DRAM_BASE 0x80000000 +#define DRAM_TX_OFFSET 0x1000 +#define DRAM_RX_OFFSET 0x2000 + +static void aspeed_i2c_write(TestFixture *f, uint32_t offset, uint32_t val) +{ + qtest_writel(global_qtest, f->base_addr + f->bus_offset + offset, val); +} + +static void aspeed_i2c_write_pool(TestFixture *f, uint32_t offset, uint8_t val) +{ + qtest_writeb(global_qtest, f->base_addr + f->pool_base + offset, val); +} + +static void aspeed_i2c_write_dma(TestFixture *f, uint32_t offset, uint8_t val) +{ + qtest_writeb(global_qtest, DRAM_BASE + offset, val); +} + +static uint32_t aspeed_i2c_read(TestFixture *f, uint32_t offset) +{ + return qtest_readl(global_qtest, f->base_addr + f->bus_offset + offset); +} + +static uint8_t aspeed_i2c_read_pool(TestFixture *f, uint32_t offset) +{ + return qtest_readb(global_qtest, f->base_addr + f->pool_base + offset); +} + +static uint8_t aspeed_i2c_read_dma(TestFixture *f, uint32_t offset) +{ + return qtest_readb(global_qtest, DRAM_BASE + offset); +} + +static bool aspeed_i2c_irq(TestFixture *f) +{ + return qtest_get_irq(global_qtest, f->irq_no); +} + +static uint32_t aspeed_i2c_ctrl_offset(const TestData *td) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + return A_I2CD_FUN_CTRL; + } else { + return A_I2CC_FUN_CTRL; + } +} + +static uint32_t aspeed_i2c_cmd_offset(const TestData *td) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + return A_I2CD_CMD; + } else { + return A_I2CM_CMD; + } +} + + +static uint32_t aspeed_i2c_intr_ctrl_offset(const TestData *td) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + return A_I2CD_INTR_CTRL; + } else { + return A_I2CM_INTR_CTRL; + } +} + +static uint32_t aspeed_i2c_intr_sts_offset(const TestData *td) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + return A_I2CD_INTR_STS; + } else { + return A_I2CM_INTR_STS; + } +} + +static uint32_t aspeed_i2c_pool_ctrl_offset(const TestData *td) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + return A_I2CD_POOL_CTRL; + } else { + return A_I2CC_POOL_CTRL; + } +} + +static uint32_t aspeed_i2c_byte_buf_offset(const TestData *td) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + return A_I2CD_BYTE_BUF; + } else { + return A_I2CC_MS_TXRX_BYTE_BUF; + } +} + +static void aspeed_i2c_write_slave_addr(TestFixture *f, const TestData *td, + uint8_t addr) +{ + if (td->mode == TEST_I2C_MODE_OLD) { + aspeed_i2c_write(f, A_I2CD_DEV_ADDR, addr); + } else { + aspeed_i2c_write(f, A_I2CS_DEV_ADDR, addr); + } +} + +static void aspeed_i2c_write_dma_addr(TestFixture *f, const TestData *td, + uint32_t val, bool rx) +{ + uint32_t offset; + + if (td->mode == TEST_I2C_MODE_OLD) { + offset = A_I2CD_DMA_ADDR; + } else { + offset = rx ? A_I2CM_DMA_RX_ADDR : A_I2CM_DMA_TX_ADDR; + } + aspeed_i2c_write(f, offset, val); +} + +static void aspeed_i2c_write_dma_len(TestFixture *f, const TestData *td, + uint16_t val, bool rx) +{ + uint32_t reg; + + if (td->mode == TEST_I2C_MODE_OLD) { + aspeed_i2c_write(f, A_I2CD_DMA_LEN, val + 1); + return; + } + + if (rx) { + reg = FIELD_DP32(0, I2CM_DMA_LEN, RX_BUF_LEN_W1T, 1); + reg = FIELD_DP32(reg, I2CM_DMA_LEN, RX_BUF_LEN, val); + } else { + reg = FIELD_DP32(0, I2CM_DMA_LEN, TX_BUF_LEN_W1T, 1); + reg = FIELD_DP32(reg, I2CM_DMA_LEN, TX_BUF_LEN, val); + } + aspeed_i2c_write(f, A_I2CM_DMA_LEN, reg); +} + +static uint8_t aspeed_i2c_get_txstate(TestFixture *f, const TestData *td) +{ + uint32_t offset; + + if (td->mode == TEST_I2C_MODE_OLD) { + offset = A_I2CD_CMD; + } else { + offset = A_I2CC_MS_TXRX_BYTE_BUF; + } + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), TX_STATE); +} + +static bool aspeed_i2c_get_busy_sts(TestFixture *f, const TestData *td) +{ + uint32_t offset; + + if (td->mode == TEST_I2C_MODE_OLD) { + offset = A_I2CD_CMD; + } else { + offset = A_I2CC_MS_TXRX_BYTE_BUF; + } + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), BUS_BUSY_STS); +} + +/* Write a data byte in a byte mode transfer */ +static +void aspeed_i2c_write_byte(TestFixture *f, const TestData *td, uint8_t val) +{ + aspeed_i2c_write(f, aspeed_i2c_byte_buf_offset(td), + SHARED_FIELD_DP32(0, TX_BUF, val)); +} + +/* Read a data byte in a byte mode transfer */ +static uint8_t aspeed_i2c_read_byte(TestFixture *f, const TestData *td) +{ + uint32_t reg; + + reg = aspeed_i2c_read(f, aspeed_i2c_byte_buf_offset(td)); + return SHARED_FIELD_EX32(reg, RX_BUF); +} + +static void aspeed_i2c_send_start(TestFixture *f, const TestData *td, + uint32_t cmd) +{ + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); +} + +static uint8_t qom_i2c_dummy_get_uint8(const char *prop) +{ + QDict *r; + uint8_t u; + + r = qtest_qmp(global_qtest, + "{ 'execute': 'qom-get', 'arguments': " + "{ 'path': 'i2c-dummy', 'property': %s } }", + prop); + u = qdict_get_uint(r, "return"); + qobject_unref(r); + return u; +} + +static void qom_i2c_dummy_set_uint8(const char *prop, uint8_t data) +{ + QDict *r; + + r = qtest_qmp(global_qtest, + "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': 'i2c-dummy', 'property': %s, 'value': %d } }", + prop, data); + qobject_unref(r); +} + +static void qom_i2c_dummy_set_rx(uint8_t idx, uint8_t data) +{ + g_autofree char *prop = g_strdup_printf("rx[%d]", idx); + + qom_i2c_dummy_set_uint8(prop, data); +} + +static uint8_t qom_i2c_dummy_get_tx(int8_t idx) +{ + g_autofree char *prop = g_strdup_printf("tx[%d]", idx); + + return qom_i2c_dummy_get_uint8(prop); +} + +#define I2C_ADDR_NACK 0x10 +#define I2C_ADDR_DUMMY 0x20 +#define I2C_ADDR_ECHO 0x30 +#define I2C_ADDR_SLAVE 0x40 + +#define CHECK_I2C_IDLE(f, td) do { \ + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_IDLE); \ + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); \ + g_assert_cmphex(aspeed_i2c_read(f, aspeed_i2c_intr_sts_offset(td)), ==, \ + 0); \ + g_assert_false(aspeed_i2c_irq(f)); \ +} while (0) + +#define CHECK_I2C_INTR(f, td, reg) do { \ + g_assert_true(aspeed_i2c_irq(f)); \ + g_assert_cmphex(aspeed_i2c_read(f, aspeed_i2c_intr_sts_offset(td)), ==, \ + reg); \ +} while (0) + +#define CHECK_I2C_BUS_ACTIVE(f, td) do { \ + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_MACTIVE); \ + g_assert_true(aspeed_i2c_get_busy_sts(f, td)); \ +} while (0) + +#define CHECK_I2C_ACK(f, td) do { \ + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_MACTIVE); \ + g_assert_true(aspeed_i2c_get_busy_sts(f, td)); \ + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, TX_ACK, 1)); \ +} while (0) + +#define CLEAR_I2C_INTR(f, td, reg) do { \ + CHECK_I2C_INTR(f, td, reg); \ + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), reg); \ + g_assert_cmphex(aspeed_i2c_read(f, aspeed_i2c_intr_sts_offset(td)), ==, \ + 0); \ +} while (0) + +#define SEND_I2C_STOP(f, td) do { \ + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), \ + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); \ + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, 1)); \ + CHECK_I2C_IDLE(f, td); \ +} while (0) + +#define CHECK_I2C_DUMMY_DEVICE() do { \ + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { \ + g_assert_cmphex(qom_i2c_dummy_get_tx(i), ==, tx_data[i]); \ + } \ + g_assert_cmphex(qom_i2c_dummy_get_uint8("tx-pos"), ==, \ + ARRAY_SIZE(tx_data)); \ + g_assert_cmphex(qom_i2c_dummy_get_uint8("rx-pos"), ==, \ + ARRAY_SIZE(rx_data)); \ +} while (0) + +static void aspeed_i2c_init(TestFixture *f, const TestData *td) +{ + uint32_t reg; + + qtest_qmp(global_qtest, "{ 'execute': 'system_reset' }"); + + aspeed_i2c_write_slave_addr(f, td, I2C_ADDR_SLAVE); + + reg = FIELD_DP32(0, I2C_CTRL_GLOBAL, SRAM_EN, 1); + if (td->mode != TEST_I2C_MODE_OLD) { + reg = FIELD_DP32(reg, I2C_CTRL_GLOBAL, REG_MODE, 1); + } + qtest_writel(global_qtest, f->base_addr + A_I2C_CTRL_GLOBAL, reg); + + reg = SHARED_FIELD_DP32(0, MASTER_EN, 1); + reg = SHARED_FIELD_DP32(reg, SLAVE_EN, 1); + aspeed_i2c_write(f, aspeed_i2c_ctrl_offset(td), reg); + + /* Enable all interrupts */ + reg = SHARED_FIELD_DP32(0, TX_ACK, 1); + reg = SHARED_FIELD_DP32(reg, TX_NAK, 1); + reg = SHARED_FIELD_DP32(reg, RX_DONE, 1); + reg = SHARED_FIELD_DP32(reg, ARBIT_LOSS, 1); + reg = SHARED_FIELD_DP32(reg, NORMAL_STOP, 1); + reg = SHARED_FIELD_DP32(reg, ABNORMAL, 1); + if (td->mode != TEST_I2C_MODE_OLD) { + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_DONE, 1); + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + } + aspeed_i2c_write(f, aspeed_i2c_intr_ctrl_offset(td), reg); + + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { + qom_i2c_dummy_set_rx(i, rx_data[i]); + } +} + +static uint32_t aspeed_i2c_pkt_set_addr_cmd(const TestData *td, uint8_t addr) +{ + uint32_t cmd; + + cmd = FIELD_DP32(0, I2CM_CMD, PKT_OP_EN, 1); + cmd = FIELD_DP32(cmd, I2CM_CMD, PKT_DEV_ADDR, addr); + return cmd; +} + +static +uint32_t aspeed_i2c_byte_set_addr_cmd(TestFixture *f, const TestData *td, + uint8_t addr, bool is_recv) +{ + if (td->mode == TEST_I2C_MODE_PKT) { + return aspeed_i2c_pkt_set_addr_cmd(td, addr); + } else { + aspeed_i2c_write_byte(f, td, addr << 1 | (is_recv ? 1 : 0)); + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); + } +} + +static +uint32_t aspeed_i2c_buf_set_addr_cmd(TestFixture *f, const TestData *td, + uint8_t addr, bool is_recv) +{ + if (td->mode == TEST_I2C_MODE_PKT) { + return aspeed_i2c_pkt_set_addr_cmd(td, addr); + } else { + aspeed_i2c_write_pool(f, 0, addr << 1 | (is_recv ? 1 : 0)); + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); + } +} + +static +uint32_t aspeed_i2c_dma_set_addr_cmd(TestFixture *f, const TestData *td, + uint8_t addr, bool is_recv) +{ + if (td->mode == TEST_I2C_MODE_PKT) { + return aspeed_i2c_pkt_set_addr_cmd(td, addr); + } else { + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET, addr << 1 | (is_recv ? 1 : 0)); + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); + } +} + + +static uint32_t aspeed_i2c_send_addr_status(TestFixture *f, const TestData *td) +{ + uint32_t sts; + + if (td->mode == TEST_I2C_MODE_PKT) { + sts = FIELD_DP32(0, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } else { + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); + } + return sts; +} + +/* + * Send an address byte to a non existing device. + * The TX_NAK interrupt should be raised. + */ +static void test_nack(TestFixture *f, gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + uint32_t sts, cmd; + + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_NACK, false); + aspeed_i2c_send_start(f, td, cmd); + + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_MSTART); + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); + sts = SHARED_FIELD_DP32(0, TX_NAK, 1); + if (td->mode == TEST_I2C_MODE_PKT) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } + CLEAR_I2C_INTR(f, td, sts); + + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, 1)); + CHECK_I2C_IDLE(f, td); + qtest_hmp(global_qtest, "system_reset"); +} + + +static void test_dummy_byte(TestFixture *f, gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + uint32_t cmd, sts; + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; + + /* Write device address (Write) */ + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, false); + aspeed_i2c_send_start(f, td, cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); + + /* Write data */ + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); + } else { + cmd = 0; + } + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { + aspeed_i2c_write_byte(f, td, tx_data[i]); + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, sts); + } + + /* Read data */ + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, true); + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + sts = aspeed_i2c_send_addr_status(f, td); + + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); + if (i == ARRAY_SIZE(rx_data) - 1) { + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); + } + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); + } else { + cmd = 0; + } + CLEAR_I2C_INTR(f, td, sts); + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, rx_data[i]); + sts = 0; + } + + SEND_I2C_STOP(f, td); + CHECK_I2C_DUMMY_DEVICE(); + qtest_hmp(global_qtest, "system_reset"); +} + +static void test_dummy_buf(TestFixture *f, gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + uint32_t cmd, sts, pool_ctrl; + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; + + /* Write data with device address */ + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { + aspeed_i2c_write_pool(f, i + (pkt_mode ? 0 : 1), tx_data[i]); + } + pool_ctrl = SHARED_FIELD_DP32(0, TX_COUNT, + ARRAY_SIZE(tx_data) - (pkt_mode ? 1 : 0)); + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), pool_ctrl); + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_DUMMY, false); + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, sts); + + /* Write device address (Read) in non packet mde */ + if (pkt_mode) { + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + } else { + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, M_TX_CMD, 1)); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); + cmd = 0; + } + + /* Read data */ + pool_ctrl = SHARED_FIELD_DP32(0, RX_SIZE, + ARRAY_SIZE(rx_data) - 1); + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), pool_ctrl); + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); + cmd = SHARED_FIELD_DP32(cmd, RX_BUFF_EN, 1); + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, sts); + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { + g_assert_cmphex(aspeed_i2c_read_pool(f, i), ==, rx_data[i]); + } + + SEND_I2C_STOP(f, td); + CHECK_I2C_DUMMY_DEVICE(); + qtest_hmp(global_qtest, "system_reset"); +} + +static void test_dummy_dma(TestFixture *f, gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + uint32_t cmd, sts; + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; + + /* Write data with device address*/ + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET + (pkt_mode ? 0 : 1) + i, + tx_data[i]); + } + aspeed_i2c_write_dma_addr(f, td, DRAM_TX_OFFSET, false); + aspeed_i2c_write_dma_len(f, td, + ARRAY_SIZE(tx_data) - (pkt_mode ? 1 : 0), false); + cmd = aspeed_i2c_dma_set_addr_cmd(f, td, I2C_ADDR_DUMMY, false); + + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, TX_DMA_EN, 1); + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, sts); + + /* Write device address (Read) in non packet mode */ + if (pkt_mode) { + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + } else { + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, M_TX_CMD, 1)); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); + cmd = 0; + } + + /* Read data */ + aspeed_i2c_write_dma_addr(f, td, DRAM_RX_OFFSET, true); + aspeed_i2c_write_dma_len(f, td, ARRAY_SIZE(rx_data) - 1, true); + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); + cmd = SHARED_FIELD_DP32(cmd, RX_DMA_EN, 1); + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, sts); + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { + g_assert_cmphex(aspeed_i2c_read_dma(f, DRAM_RX_OFFSET + i), ==, + rx_data[i]); + } + + SEND_I2C_STOP(f, td); + CHECK_I2C_DUMMY_DEVICE(); + qtest_hmp(global_qtest, "system_reset"); +} + +static void test_slave(TestFixture *f, gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + uint32_t cmd, sts, tmp; + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; + uint8_t offset = pkt_mode ? 0 : 1; + + aspeed_i2c_write_pool(f, offset + 0, I2C_ADDR_SLAVE); + aspeed_i2c_write_pool(f, offset + 1, tx_data[0]); + aspeed_i2c_write_pool(f, offset + 2, tx_data[1]); + + tmp = SHARED_FIELD_DP32(0, TX_COUNT, pkt_mode ? 2 : 3); + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), tmp); + + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_ECHO, false); + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); + if (pkt_mode) { + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); + CHECK_I2C_BUS_ACTIVE(f, td); + CLEAR_I2C_INTR(f, td, sts); + + /* Send stop and handle i2c-echo response as a slave */ + if (td->mode == TEST_I2C_MODE_OLD) { + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); + clock_step(1); + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, I2C_ADDR_SLAVE << 1); + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); + sts = FIELD_DP32(sts, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 1); + CHECK_I2C_INTR(f, td, sts); + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); + + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, tx_data[0]); + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); + CHECK_I2C_INTR(f, td, sts); + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); + + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, tx_data[1]); + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); + CHECK_I2C_INTR(f, td, sts); + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); + } else { + aspeed_i2c_write(f, A_I2CS_DMA_RX_ADDR, DRAM_RX_OFFSET); + tmp = SHARED_FIELD_DP32(0, RX_DMA_EN, 1); + aspeed_i2c_write(f, A_I2CS_CMD, tmp); + + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); + clock_step(1); + + tmp = FIELD_DP32(0, I2CS_DMA_LEN_STS, RX_LEN, 2); + g_assert_cmphex(aspeed_i2c_read(f, A_I2CS_DMA_LEN_STS), ==, tmp); + for (uint8_t i = 0; i < 2; i++) { + g_assert_cmphex(aspeed_i2c_read_dma(f, DRAM_RX_OFFSET + i), ==, + tx_data[i]); + } + } + + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); + CLEAR_I2C_INTR(f, td, sts); + CHECK_I2C_IDLE(f, td); + + qtest_hmp(global_qtest, "system_reset"); +} + +static void aspeed_i2c_init_fixture(TestFixture *fixture, const TestData *td) +{ + switch (td->machine) { + case TEST_MACHINE_AST2400: + fixture->base_addr = 0x1E78A000; + fixture->bus_offset = 0x40; + fixture->irq_no = 12; + fixture->pool_base = 0x800; + break; + case TEST_MACHINE_AST2500: + fixture->base_addr = 0x1E78A000; + fixture->bus_offset = 0x40; + fixture->irq_no = 12; + fixture->pool_base = 0x200; + break; + case TEST_MACHINE_AST2600: + fixture->base_addr = 0x1E78A000; + fixture->bus_offset = 0x80; + fixture->irq_no = 110; + fixture->pool_base = 0xC00; + break; + default: + return; + } +} + +static void aspeed_i2c_init_test(TestFixture *fixture, const TestData *td) +{ + aspeed_i2c_init(fixture, td); + CHECK_I2C_IDLE(fixture, td); +} + +static void aspeed_i2c_init_new_machine(TestFixture *fixture, + gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + const char *machine; + const char *irq_dev; + + aspeed_i2c_init_fixture(fixture, td); + + if (global_qtest) { + qtest_quit(global_qtest); + } + + switch (td->machine) { + case TEST_MACHINE_AST2400: + machine = "palmetto-bmc"; + irq_dev = "/machine/soc/vic"; + break; + case TEST_MACHINE_AST2500: + machine = "ast2500-evb"; + irq_dev = "/machine/soc/vic"; + break; + case TEST_MACHINE_AST2600: + machine = "ast2600-evb"; + irq_dev = "/machine/soc/a7mpcore/gic"; + break; + default: + g_test_fail(); + return; + } + + global_qtest = qtest_initf( + "-machine %s -nographic " + "-device i2c-echo,id=i2c-echo,bus=aspeed.i2c.bus.0,address=%d " + "-device i2c-dummy-device,id=i2c-dummy,bus=aspeed.i2c.bus.0,address=%d", + machine, I2C_ADDR_ECHO, I2C_ADDR_DUMMY); + qtest_irq_intercept_in(global_qtest, irq_dev); + + aspeed_i2c_init_test(fixture, td); +} + +static void aspeed_i2c_init_machine(TestFixture *fixture, + gconstpointer p_data) +{ + const TestData *td = (const TestData *)p_data; + + aspeed_i2c_init_fixture(fixture, td); + aspeed_i2c_init_test(fixture, td); +} + +static void add_test(const TestData *td, const char *name, + void (*func)(TestFixture *, const void *), + bool setup) +{ + g_autofree char *test_name; + + test_name = g_strdup_printf("%s/%s", td->name, name); + + qtest_add(test_name, TestFixture, td, + setup ? aspeed_i2c_init_new_machine : aspeed_i2c_init_machine, + func, NULL); +} + +#define ADD_TEST(td, test, new) add_test(td, #test, test_##test, new) + +static const TestData test_data[] = { + { + .name = "ast2400", + .machine = TEST_MACHINE_AST2400, + }, + { + .name = "ast2500", + .machine = TEST_MACHINE_AST2500, + }, + { + .name = "ast2600", + .machine = TEST_MACHINE_AST2600, + }, + { + .name = "ast2600-new", + .machine = TEST_MACHINE_AST2600, + .mode = TEST_I2C_MODE_NEW, + }, + { + .name = "ast2600-packet", + .machine = TEST_MACHINE_AST2600, + .mode = TEST_I2C_MODE_PKT, + }, +}; + +int main(int argc, char **argv) +{ + int ret; + bool setup; + + g_test_init(&argc, &argv, NULL); + + for (int i = 0; i < ARRAY_SIZE(test_data); i++) { + setup = i == 0 || test_data[i].machine != test_data[i - 1].machine; + ADD_TEST(&test_data[i], nack, setup); + ADD_TEST(&test_data[i], dummy_byte, false); + ADD_TEST(&test_data[i], dummy_buf, false); + if (test_data[i].machine != TEST_MACHINE_AST2400) { + ADD_TEST(&test_data[i], dummy_dma, false); + } + ADD_TEST(&test_data[i], slave, false); + } + + ret = g_test_run(); + qtest_quit(global_qtest); + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 46d4fc1d2b..c496b7c3a7 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -233,12 +233,14 @@ qtests_aspeed = \ ['aspeed-acry-test', 'aspeed_gpio-test', 'aspeed_hace-test', + 'aspeed_i2c-test', 'aspeed-sbc-test', 'aspeed_scu-test', 'aspeed_smc-test'] qtests_aspeed64 = \ ['ast2700-gpio-test', 'ast2700-hace-test', + 'aspeed_i2c-test', 'ast2700-sgpio-test', 'ast2700-smc-test'] -- 2.53.0 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers 2026-10-27 19:50 ` [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers Timur Maksimov @ 2026-10-28 8:56 ` Cédric Le Goater 2026-10-28 14:47 ` Timur Maksimov 0 siblings, 1 reply; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 8:56 UTC (permalink / raw) To: Timur Maksimov, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier On 10/27/26 20:50, Timur Maksimov wrote: > Test all supported register and transfer modes. > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > tests/qtest/aspeed_i2c-test.c | 836 ++++++++++++++++++++++++++++++++++++++++++ > tests/qtest/meson.build | 2 + > 2 files changed, 838 insertions(+) > > diff --git a/tests/qtest/aspeed_i2c-test.c b/tests/qtest/aspeed_i2c-test.c > new file mode 100644 > index 0000000000..7fb1debb56 > --- /dev/null > +++ b/tests/qtest/aspeed_i2c-test.c > @@ -0,0 +1,836 @@ > +/* > + * QTest testcase for Aspeed I2C controller > + * > + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "libqtest-single.h" > +#include "hw/i2c/aspeed_i2c.h" > + > +typedef enum TestAspeedI2CMode { > + TEST_I2C_MODE_OLD, > + TEST_I2C_MODE_NEW, > + TEST_I2C_MODE_PKT, > +} TestAspeedI2CMode; > + > +typedef enum TestAspeedMachine { > + TEST_MACHINE_AST2400, > + TEST_MACHINE_AST2500, > + TEST_MACHINE_AST2600, Do you have plans to add AST2700 support ? This is the current focus now days. Thanks, C. > +} TestAspeedMachine; > + > +typedef struct TestData { > + const char *name; > + TestAspeedMachine machine; > + TestAspeedI2CMode mode; > +} TestData; > + > +typedef struct TestFixture { > + hwaddr base_addr; > + hwaddr bus_offset; > + int irq_no; > + uint32_t pool_base; > +} TestFixture; > + > +static const uint8_t tx_data[] = {0x10, 0x11, 0x12}; > +static const uint8_t rx_data[] = {0x30, 0x31, 0x32}; > + > +#define DRAM_BASE 0x80000000 > +#define DRAM_TX_OFFSET 0x1000 > +#define DRAM_RX_OFFSET 0x2000 > + > +static void aspeed_i2c_write(TestFixture *f, uint32_t offset, uint32_t val) > +{ > + qtest_writel(global_qtest, f->base_addr + f->bus_offset + offset, val); > +} > + > +static void aspeed_i2c_write_pool(TestFixture *f, uint32_t offset, uint8_t val) > +{ > + qtest_writeb(global_qtest, f->base_addr + f->pool_base + offset, val); > +} > + > +static void aspeed_i2c_write_dma(TestFixture *f, uint32_t offset, uint8_t val) > +{ > + qtest_writeb(global_qtest, DRAM_BASE + offset, val); > +} > + > +static uint32_t aspeed_i2c_read(TestFixture *f, uint32_t offset) > +{ > + return qtest_readl(global_qtest, f->base_addr + f->bus_offset + offset); > +} > + > +static uint8_t aspeed_i2c_read_pool(TestFixture *f, uint32_t offset) > +{ > + return qtest_readb(global_qtest, f->base_addr + f->pool_base + offset); > +} > + > +static uint8_t aspeed_i2c_read_dma(TestFixture *f, uint32_t offset) > +{ > + return qtest_readb(global_qtest, DRAM_BASE + offset); > +} > + > +static bool aspeed_i2c_irq(TestFixture *f) > +{ > + return qtest_get_irq(global_qtest, f->irq_no); > +} > + > +static uint32_t aspeed_i2c_ctrl_offset(const TestData *td) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + return A_I2CD_FUN_CTRL; > + } else { > + return A_I2CC_FUN_CTRL; > + } > +} > + > +static uint32_t aspeed_i2c_cmd_offset(const TestData *td) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + return A_I2CD_CMD; > + } else { > + return A_I2CM_CMD; > + } > +} > + > + > +static uint32_t aspeed_i2c_intr_ctrl_offset(const TestData *td) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + return A_I2CD_INTR_CTRL; > + } else { > + return A_I2CM_INTR_CTRL; > + } > +} > + > +static uint32_t aspeed_i2c_intr_sts_offset(const TestData *td) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + return A_I2CD_INTR_STS; > + } else { > + return A_I2CM_INTR_STS; > + } > +} > + > +static uint32_t aspeed_i2c_pool_ctrl_offset(const TestData *td) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + return A_I2CD_POOL_CTRL; > + } else { > + return A_I2CC_POOL_CTRL; > + } > +} > + > +static uint32_t aspeed_i2c_byte_buf_offset(const TestData *td) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + return A_I2CD_BYTE_BUF; > + } else { > + return A_I2CC_MS_TXRX_BYTE_BUF; > + } > +} > + > +static void aspeed_i2c_write_slave_addr(TestFixture *f, const TestData *td, > + uint8_t addr) > +{ > + if (td->mode == TEST_I2C_MODE_OLD) { > + aspeed_i2c_write(f, A_I2CD_DEV_ADDR, addr); > + } else { > + aspeed_i2c_write(f, A_I2CS_DEV_ADDR, addr); > + } > +} > + > +static void aspeed_i2c_write_dma_addr(TestFixture *f, const TestData *td, > + uint32_t val, bool rx) > +{ > + uint32_t offset; > + > + if (td->mode == TEST_I2C_MODE_OLD) { > + offset = A_I2CD_DMA_ADDR; > + } else { > + offset = rx ? A_I2CM_DMA_RX_ADDR : A_I2CM_DMA_TX_ADDR; > + } > + aspeed_i2c_write(f, offset, val); > +} > + > +static void aspeed_i2c_write_dma_len(TestFixture *f, const TestData *td, > + uint16_t val, bool rx) > +{ > + uint32_t reg; > + > + if (td->mode == TEST_I2C_MODE_OLD) { > + aspeed_i2c_write(f, A_I2CD_DMA_LEN, val + 1); > + return; > + } > + > + if (rx) { > + reg = FIELD_DP32(0, I2CM_DMA_LEN, RX_BUF_LEN_W1T, 1); > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, RX_BUF_LEN, val); > + } else { > + reg = FIELD_DP32(0, I2CM_DMA_LEN, TX_BUF_LEN_W1T, 1); > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, TX_BUF_LEN, val); > + } > + aspeed_i2c_write(f, A_I2CM_DMA_LEN, reg); > +} > + > +static uint8_t aspeed_i2c_get_txstate(TestFixture *f, const TestData *td) > +{ > + uint32_t offset; > + > + if (td->mode == TEST_I2C_MODE_OLD) { > + offset = A_I2CD_CMD; > + } else { > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > + } > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), TX_STATE); > +} > + > +static bool aspeed_i2c_get_busy_sts(TestFixture *f, const TestData *td) > +{ > + uint32_t offset; > + > + if (td->mode == TEST_I2C_MODE_OLD) { > + offset = A_I2CD_CMD; > + } else { > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > + } > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), BUS_BUSY_STS); > +} > + > +/* Write a data byte in a byte mode transfer */ > +static > +void aspeed_i2c_write_byte(TestFixture *f, const TestData *td, uint8_t val) > +{ > + aspeed_i2c_write(f, aspeed_i2c_byte_buf_offset(td), > + SHARED_FIELD_DP32(0, TX_BUF, val)); > +} > + > +/* Read a data byte in a byte mode transfer */ > +static uint8_t aspeed_i2c_read_byte(TestFixture *f, const TestData *td) > +{ > + uint32_t reg; > + > + reg = aspeed_i2c_read(f, aspeed_i2c_byte_buf_offset(td)); > + return SHARED_FIELD_EX32(reg, RX_BUF); > +} > + > +static void aspeed_i2c_send_start(TestFixture *f, const TestData *td, > + uint32_t cmd) > +{ > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > +} > + > +static uint8_t qom_i2c_dummy_get_uint8(const char *prop) > +{ > + QDict *r; > + uint8_t u; > + > + r = qtest_qmp(global_qtest, > + "{ 'execute': 'qom-get', 'arguments': " > + "{ 'path': 'i2c-dummy', 'property': %s } }", > + prop); > + u = qdict_get_uint(r, "return"); > + qobject_unref(r); > + return u; > +} > + > +static void qom_i2c_dummy_set_uint8(const char *prop, uint8_t data) > +{ > + QDict *r; > + > + r = qtest_qmp(global_qtest, > + "{ 'execute': 'qom-set', 'arguments': " > + "{ 'path': 'i2c-dummy', 'property': %s, 'value': %d } }", > + prop, data); > + qobject_unref(r); > +} > + > +static void qom_i2c_dummy_set_rx(uint8_t idx, uint8_t data) > +{ > + g_autofree char *prop = g_strdup_printf("rx[%d]", idx); > + > + qom_i2c_dummy_set_uint8(prop, data); > +} > + > +static uint8_t qom_i2c_dummy_get_tx(int8_t idx) > +{ > + g_autofree char *prop = g_strdup_printf("tx[%d]", idx); > + > + return qom_i2c_dummy_get_uint8(prop); > +} > + > +#define I2C_ADDR_NACK 0x10 > +#define I2C_ADDR_DUMMY 0x20 > +#define I2C_ADDR_ECHO 0x30 > +#define I2C_ADDR_SLAVE 0x40 > + > +#define CHECK_I2C_IDLE(f, td) do { \ > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_IDLE); \ > + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); \ > + g_assert_cmphex(aspeed_i2c_read(f, aspeed_i2c_intr_sts_offset(td)), ==, \ > + 0); \ > + g_assert_false(aspeed_i2c_irq(f)); \ > +} while (0) > + > +#define CHECK_I2C_INTR(f, td, reg) do { \ > + g_assert_true(aspeed_i2c_irq(f)); \ > + g_assert_cmphex(aspeed_i2c_read(f, aspeed_i2c_intr_sts_offset(td)), ==, \ > + reg); \ > +} while (0) > + > +#define CHECK_I2C_BUS_ACTIVE(f, td) do { \ > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_MACTIVE); \ > + g_assert_true(aspeed_i2c_get_busy_sts(f, td)); \ > +} while (0) > + > +#define CHECK_I2C_ACK(f, td) do { \ > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_MACTIVE); \ > + g_assert_true(aspeed_i2c_get_busy_sts(f, td)); \ > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, TX_ACK, 1)); \ > +} while (0) > + > +#define CLEAR_I2C_INTR(f, td, reg) do { \ > + CHECK_I2C_INTR(f, td, reg); \ > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), reg); \ > + g_assert_cmphex(aspeed_i2c_read(f, aspeed_i2c_intr_sts_offset(td)), ==, \ > + 0); \ > +} while (0) > + > +#define SEND_I2C_STOP(f, td) do { \ > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), \ > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); \ > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, 1)); \ > + CHECK_I2C_IDLE(f, td); \ > +} while (0) > + > +#define CHECK_I2C_DUMMY_DEVICE() do { \ > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { \ > + g_assert_cmphex(qom_i2c_dummy_get_tx(i), ==, tx_data[i]); \ > + } \ > + g_assert_cmphex(qom_i2c_dummy_get_uint8("tx-pos"), ==, \ > + ARRAY_SIZE(tx_data)); \ > + g_assert_cmphex(qom_i2c_dummy_get_uint8("rx-pos"), ==, \ > + ARRAY_SIZE(rx_data)); \ > +} while (0) > + > +static void aspeed_i2c_init(TestFixture *f, const TestData *td) > +{ > + uint32_t reg; > + > + qtest_qmp(global_qtest, "{ 'execute': 'system_reset' }"); > + > + aspeed_i2c_write_slave_addr(f, td, I2C_ADDR_SLAVE); > + > + reg = FIELD_DP32(0, I2C_CTRL_GLOBAL, SRAM_EN, 1); > + if (td->mode != TEST_I2C_MODE_OLD) { > + reg = FIELD_DP32(reg, I2C_CTRL_GLOBAL, REG_MODE, 1); > + } > + qtest_writel(global_qtest, f->base_addr + A_I2C_CTRL_GLOBAL, reg); > + > + reg = SHARED_FIELD_DP32(0, MASTER_EN, 1); > + reg = SHARED_FIELD_DP32(reg, SLAVE_EN, 1); > + aspeed_i2c_write(f, aspeed_i2c_ctrl_offset(td), reg); > + > + /* Enable all interrupts */ > + reg = SHARED_FIELD_DP32(0, TX_ACK, 1); > + reg = SHARED_FIELD_DP32(reg, TX_NAK, 1); > + reg = SHARED_FIELD_DP32(reg, RX_DONE, 1); > + reg = SHARED_FIELD_DP32(reg, ARBIT_LOSS, 1); > + reg = SHARED_FIELD_DP32(reg, NORMAL_STOP, 1); > + reg = SHARED_FIELD_DP32(reg, ABNORMAL, 1); > + if (td->mode != TEST_I2C_MODE_OLD) { > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_intr_ctrl_offset(td), reg); > + > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > + qom_i2c_dummy_set_rx(i, rx_data[i]); > + } > +} > + > +static uint32_t aspeed_i2c_pkt_set_addr_cmd(const TestData *td, uint8_t addr) > +{ > + uint32_t cmd; > + > + cmd = FIELD_DP32(0, I2CM_CMD, PKT_OP_EN, 1); > + cmd = FIELD_DP32(cmd, I2CM_CMD, PKT_DEV_ADDR, addr); > + return cmd; > +} > + > +static > +uint32_t aspeed_i2c_byte_set_addr_cmd(TestFixture *f, const TestData *td, > + uint8_t addr, bool is_recv) > +{ > + if (td->mode == TEST_I2C_MODE_PKT) { > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > + } else { > + aspeed_i2c_write_byte(f, td, addr << 1 | (is_recv ? 1 : 0)); > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > + } > +} > + > +static > +uint32_t aspeed_i2c_buf_set_addr_cmd(TestFixture *f, const TestData *td, > + uint8_t addr, bool is_recv) > +{ > + if (td->mode == TEST_I2C_MODE_PKT) { > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > + } else { > + aspeed_i2c_write_pool(f, 0, addr << 1 | (is_recv ? 1 : 0)); > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > + } > +} > + > +static > +uint32_t aspeed_i2c_dma_set_addr_cmd(TestFixture *f, const TestData *td, > + uint8_t addr, bool is_recv) > +{ > + if (td->mode == TEST_I2C_MODE_PKT) { > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > + } else { > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET, addr << 1 | (is_recv ? 1 : 0)); > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > + } > +} > + > + > +static uint32_t aspeed_i2c_send_addr_status(TestFixture *f, const TestData *td) > +{ > + uint32_t sts; > + > + if (td->mode == TEST_I2C_MODE_PKT) { > + sts = FIELD_DP32(0, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } else { > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > + } > + return sts; > +} > + > +/* > + * Send an address byte to a non existing device. > + * The TX_NAK interrupt should be raised. > + */ > +static void test_nack(TestFixture *f, gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + uint32_t sts, cmd; > + > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_NACK, false); > + aspeed_i2c_send_start(f, td, cmd); > + > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, I2CD_MSTART); > + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); > + sts = SHARED_FIELD_DP32(0, TX_NAK, 1); > + if (td->mode == TEST_I2C_MODE_PKT) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } > + CLEAR_I2C_INTR(f, td, sts); > + > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, 1)); > + CHECK_I2C_IDLE(f, td); > + qtest_hmp(global_qtest, "system_reset"); > +} > + > + > +static void test_dummy_byte(TestFixture *f, gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + uint32_t cmd, sts; > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > + > + /* Write device address (Write) */ > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, false); > + aspeed_i2c_send_start(f, td, cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > + > + /* Write data */ > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > + } else { > + cmd = 0; > + } > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > + aspeed_i2c_write_byte(f, td, tx_data[i]); > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, sts); > + } > + > + /* Read data */ > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, true); > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + sts = aspeed_i2c_send_addr_status(f, td); > + > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > + if (i == ARRAY_SIZE(rx_data) - 1) { > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > + } else { > + cmd = 0; > + } > + CLEAR_I2C_INTR(f, td, sts); > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, rx_data[i]); > + sts = 0; > + } > + > + SEND_I2C_STOP(f, td); > + CHECK_I2C_DUMMY_DEVICE(); > + qtest_hmp(global_qtest, "system_reset"); > +} > + > +static void test_dummy_buf(TestFixture *f, gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + uint32_t cmd, sts, pool_ctrl; > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > + > + /* Write data with device address */ > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > + aspeed_i2c_write_pool(f, i + (pkt_mode ? 0 : 1), tx_data[i]); > + } > + pool_ctrl = SHARED_FIELD_DP32(0, TX_COUNT, > + ARRAY_SIZE(tx_data) - (pkt_mode ? 1 : 0)); > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), pool_ctrl); > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_DUMMY, false); > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, sts); > + > + /* Write device address (Read) in non packet mde */ > + if (pkt_mode) { > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + } else { > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, M_TX_CMD, 1)); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > + cmd = 0; > + } > + > + /* Read data */ > + pool_ctrl = SHARED_FIELD_DP32(0, RX_SIZE, > + ARRAY_SIZE(rx_data) - 1); > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), pool_ctrl); > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > + cmd = SHARED_FIELD_DP32(cmd, RX_BUFF_EN, 1); > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, sts); > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > + g_assert_cmphex(aspeed_i2c_read_pool(f, i), ==, rx_data[i]); > + } > + > + SEND_I2C_STOP(f, td); > + CHECK_I2C_DUMMY_DEVICE(); > + qtest_hmp(global_qtest, "system_reset"); > +} > + > +static void test_dummy_dma(TestFixture *f, gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + uint32_t cmd, sts; > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > + > + /* Write data with device address*/ > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET + (pkt_mode ? 0 : 1) + i, > + tx_data[i]); > + } > + aspeed_i2c_write_dma_addr(f, td, DRAM_TX_OFFSET, false); > + aspeed_i2c_write_dma_len(f, td, > + ARRAY_SIZE(tx_data) - (pkt_mode ? 1 : 0), false); > + cmd = aspeed_i2c_dma_set_addr_cmd(f, td, I2C_ADDR_DUMMY, false); > + > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, TX_DMA_EN, 1); > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, sts); > + > + /* Write device address (Read) in non packet mode */ > + if (pkt_mode) { > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + } else { > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, M_TX_CMD, 1)); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > + cmd = 0; > + } > + > + /* Read data */ > + aspeed_i2c_write_dma_addr(f, td, DRAM_RX_OFFSET, true); > + aspeed_i2c_write_dma_len(f, td, ARRAY_SIZE(rx_data) - 1, true); > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > + cmd = SHARED_FIELD_DP32(cmd, RX_DMA_EN, 1); > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, sts); > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > + g_assert_cmphex(aspeed_i2c_read_dma(f, DRAM_RX_OFFSET + i), ==, > + rx_data[i]); > + } > + > + SEND_I2C_STOP(f, td); > + CHECK_I2C_DUMMY_DEVICE(); > + qtest_hmp(global_qtest, "system_reset"); > +} > + > +static void test_slave(TestFixture *f, gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + uint32_t cmd, sts, tmp; > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > + uint8_t offset = pkt_mode ? 0 : 1; > + > + aspeed_i2c_write_pool(f, offset + 0, I2C_ADDR_SLAVE); > + aspeed_i2c_write_pool(f, offset + 1, tx_data[0]); > + aspeed_i2c_write_pool(f, offset + 2, tx_data[1]); > + > + tmp = SHARED_FIELD_DP32(0, TX_COUNT, pkt_mode ? 2 : 3); > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), tmp); > + > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_ECHO, false); > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > + if (pkt_mode) { > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > + } > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > + CHECK_I2C_BUS_ACTIVE(f, td); > + CLEAR_I2C_INTR(f, td, sts); > + > + /* Send stop and handle i2c-echo response as a slave */ > + if (td->mode == TEST_I2C_MODE_OLD) { > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); > + clock_step(1); > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, I2C_ADDR_SLAVE << 1); > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > + sts = FIELD_DP32(sts, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 1); > + CHECK_I2C_INTR(f, td, sts); > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > + > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, tx_data[0]); > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > + CHECK_I2C_INTR(f, td, sts); > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > + > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, tx_data[1]); > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > + CHECK_I2C_INTR(f, td, sts); > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > + } else { > + aspeed_i2c_write(f, A_I2CS_DMA_RX_ADDR, DRAM_RX_OFFSET); > + tmp = SHARED_FIELD_DP32(0, RX_DMA_EN, 1); > + aspeed_i2c_write(f, A_I2CS_CMD, tmp); > + > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); > + clock_step(1); > + > + tmp = FIELD_DP32(0, I2CS_DMA_LEN_STS, RX_LEN, 2); > + g_assert_cmphex(aspeed_i2c_read(f, A_I2CS_DMA_LEN_STS), ==, tmp); > + for (uint8_t i = 0; i < 2; i++) { > + g_assert_cmphex(aspeed_i2c_read_dma(f, DRAM_RX_OFFSET + i), ==, > + tx_data[i]); > + } > + } > + > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > + CLEAR_I2C_INTR(f, td, sts); > + CHECK_I2C_IDLE(f, td); > + > + qtest_hmp(global_qtest, "system_reset"); > +} > + > +static void aspeed_i2c_init_fixture(TestFixture *fixture, const TestData *td) > +{ > + switch (td->machine) { > + case TEST_MACHINE_AST2400: > + fixture->base_addr = 0x1E78A000; > + fixture->bus_offset = 0x40; > + fixture->irq_no = 12; > + fixture->pool_base = 0x800; > + break; > + case TEST_MACHINE_AST2500: > + fixture->base_addr = 0x1E78A000; > + fixture->bus_offset = 0x40; > + fixture->irq_no = 12; > + fixture->pool_base = 0x200; > + break; > + case TEST_MACHINE_AST2600: > + fixture->base_addr = 0x1E78A000; > + fixture->bus_offset = 0x80; > + fixture->irq_no = 110; > + fixture->pool_base = 0xC00; > + break; > + default: > + return; > + } > +} > + > +static void aspeed_i2c_init_test(TestFixture *fixture, const TestData *td) > +{ > + aspeed_i2c_init(fixture, td); > + CHECK_I2C_IDLE(fixture, td); > +} > + > +static void aspeed_i2c_init_new_machine(TestFixture *fixture, > + gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + const char *machine; > + const char *irq_dev; > + > + aspeed_i2c_init_fixture(fixture, td); > + > + if (global_qtest) { > + qtest_quit(global_qtest); > + } > + > + switch (td->machine) { > + case TEST_MACHINE_AST2400: > + machine = "palmetto-bmc"; > + irq_dev = "/machine/soc/vic"; > + break; > + case TEST_MACHINE_AST2500: > + machine = "ast2500-evb"; > + irq_dev = "/machine/soc/vic"; > + break; > + case TEST_MACHINE_AST2600: > + machine = "ast2600-evb"; > + irq_dev = "/machine/soc/a7mpcore/gic"; > + break; > + default: > + g_test_fail(); > + return; > + } > + > + global_qtest = qtest_initf( > + "-machine %s -nographic " > + "-device i2c-echo,id=i2c-echo,bus=aspeed.i2c.bus.0,address=%d " > + "-device i2c-dummy-device,id=i2c-dummy,bus=aspeed.i2c.bus.0,address=%d", > + machine, I2C_ADDR_ECHO, I2C_ADDR_DUMMY); > + qtest_irq_intercept_in(global_qtest, irq_dev); > + > + aspeed_i2c_init_test(fixture, td); > +} > + > +static void aspeed_i2c_init_machine(TestFixture *fixture, > + gconstpointer p_data) > +{ > + const TestData *td = (const TestData *)p_data; > + > + aspeed_i2c_init_fixture(fixture, td); > + aspeed_i2c_init_test(fixture, td); > +} > + > +static void add_test(const TestData *td, const char *name, > + void (*func)(TestFixture *, const void *), > + bool setup) > +{ > + g_autofree char *test_name; > + > + test_name = g_strdup_printf("%s/%s", td->name, name); > + > + qtest_add(test_name, TestFixture, td, > + setup ? aspeed_i2c_init_new_machine : aspeed_i2c_init_machine, > + func, NULL); > +} > + > +#define ADD_TEST(td, test, new) add_test(td, #test, test_##test, new) > + > +static const TestData test_data[] = { > + { > + .name = "ast2400", > + .machine = TEST_MACHINE_AST2400, > + }, > + { > + .name = "ast2500", > + .machine = TEST_MACHINE_AST2500, > + }, > + { > + .name = "ast2600", > + .machine = TEST_MACHINE_AST2600, > + }, > + { > + .name = "ast2600-new", > + .machine = TEST_MACHINE_AST2600, > + .mode = TEST_I2C_MODE_NEW, > + }, > + { > + .name = "ast2600-packet", > + .machine = TEST_MACHINE_AST2600, > + .mode = TEST_I2C_MODE_PKT, > + }, > +}; > + > +int main(int argc, char **argv) > +{ > + int ret; > + bool setup; > + > + g_test_init(&argc, &argv, NULL); > + > + for (int i = 0; i < ARRAY_SIZE(test_data); i++) { > + setup = i == 0 || test_data[i].machine != test_data[i - 1].machine; > + ADD_TEST(&test_data[i], nack, setup); > + ADD_TEST(&test_data[i], dummy_byte, false); > + ADD_TEST(&test_data[i], dummy_buf, false); > + if (test_data[i].machine != TEST_MACHINE_AST2400) { > + ADD_TEST(&test_data[i], dummy_dma, false); > + } > + ADD_TEST(&test_data[i], slave, false); > + } > + > + ret = g_test_run(); > + qtest_quit(global_qtest); > + return ret; > +} > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > index 46d4fc1d2b..c496b7c3a7 100644 > --- a/tests/qtest/meson.build > +++ b/tests/qtest/meson.build > @@ -233,12 +233,14 @@ qtests_aspeed = \ > ['aspeed-acry-test', > 'aspeed_gpio-test', > 'aspeed_hace-test', > + 'aspeed_i2c-test', > 'aspeed-sbc-test', > 'aspeed_scu-test', > 'aspeed_smc-test'] > qtests_aspeed64 = \ > ['ast2700-gpio-test', > 'ast2700-hace-test', > + 'aspeed_i2c-test', > 'ast2700-sgpio-test', > 'ast2700-smc-test'] > > ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers 2026-10-28 8:56 ` Cédric Le Goater @ 2026-10-28 14:47 ` Timur Maksimov 2026-10-29 7:25 ` Jamin Lin 0 siblings, 1 reply; 33+ messages in thread From: Timur Maksimov @ 2026-10-28 14:47 UTC (permalink / raw) To: Cédric Le Goater, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier Hi, I thought about that, but I'm not sure how to do it because I haven't found previous examples in QEMU. I guess there should be two test suites for ARM and AArch64 targets. But AArch64 also supports 32-bit ARM SoCs, so I'm not sure whether we should test them with the AArch64 target as well or if testing with the ARM target is enough. On Wed, 2026-10-28 at 09:56 +0100, Cédric Le Goater wrote: > On 10/27/26 20:50, Timur Maksimov wrote: > > Test all supported register and transfer modes. > > > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > --- > > tests/qtest/aspeed_i2c-test.c | 836 > > ++++++++++++++++++++++++++++++++++++++++++ > > tests/qtest/meson.build | 2 + > > 2 files changed, 838 insertions(+) > > > > diff --git a/tests/qtest/aspeed_i2c-test.c > > b/tests/qtest/aspeed_i2c-test.c > > new file mode 100644 > > index 0000000000..7fb1debb56 > > --- /dev/null > > +++ b/tests/qtest/aspeed_i2c-test.c > > @@ -0,0 +1,836 @@ > > +/* > > + * QTest testcase for Aspeed I2C controller > > + * > > + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> > > + * > > + * SPDX-License-Identifier: GPL-2.0-or-later > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "libqtest-single.h" > > +#include "hw/i2c/aspeed_i2c.h" > > + > > +typedef enum TestAspeedI2CMode { > > + TEST_I2C_MODE_OLD, > > + TEST_I2C_MODE_NEW, > > + TEST_I2C_MODE_PKT, > > +} TestAspeedI2CMode; > > + > > +typedef enum TestAspeedMachine { > > + TEST_MACHINE_AST2400, > > + TEST_MACHINE_AST2500, > > + TEST_MACHINE_AST2600, > > > Do you have plans to add AST2700 support ? This is the current focus > now days. > > Thanks, > > C. > > > > > +} TestAspeedMachine; > > + > > +typedef struct TestData { > > + const char *name; > > + TestAspeedMachine machine; > > + TestAspeedI2CMode mode; > > +} TestData; > > + > > +typedef struct TestFixture { > > + hwaddr base_addr; > > + hwaddr bus_offset; > > + int irq_no; > > + uint32_t pool_base; > > +} TestFixture; > > + > > +static const uint8_t tx_data[] = {0x10, 0x11, 0x12}; > > +static const uint8_t rx_data[] = {0x30, 0x31, 0x32}; > > + > > +#define DRAM_BASE 0x80000000 > > +#define DRAM_TX_OFFSET 0x1000 > > +#define DRAM_RX_OFFSET 0x2000 > > + > > +static void aspeed_i2c_write(TestFixture *f, uint32_t offset, > > uint32_t val) > > +{ > > + qtest_writel(global_qtest, f->base_addr + f->bus_offset + > > offset, val); > > +} > > + > > +static void aspeed_i2c_write_pool(TestFixture *f, uint32_t offset, > > uint8_t val) > > +{ > > + qtest_writeb(global_qtest, f->base_addr + f->pool_base + > > offset, val); > > +} > > + > > +static void aspeed_i2c_write_dma(TestFixture *f, uint32_t offset, > > uint8_t val) > > +{ > > + qtest_writeb(global_qtest, DRAM_BASE + offset, val); > > +} > > + > > +static uint32_t aspeed_i2c_read(TestFixture *f, uint32_t offset) > > +{ > > + return qtest_readl(global_qtest, f->base_addr + f->bus_offset > > + offset); > > +} > > + > > +static uint8_t aspeed_i2c_read_pool(TestFixture *f, uint32_t > > offset) > > +{ > > + return qtest_readb(global_qtest, f->base_addr + f->pool_base + > > offset); > > +} > > + > > +static uint8_t aspeed_i2c_read_dma(TestFixture *f, uint32_t > > offset) > > +{ > > + return qtest_readb(global_qtest, DRAM_BASE + offset); > > +} > > + > > +static bool aspeed_i2c_irq(TestFixture *f) > > +{ > > + return qtest_get_irq(global_qtest, f->irq_no); > > +} > > + > > +static uint32_t aspeed_i2c_ctrl_offset(const TestData *td) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + return A_I2CD_FUN_CTRL; > > + } else { > > + return A_I2CC_FUN_CTRL; > > + } > > +} > > + > > +static uint32_t aspeed_i2c_cmd_offset(const TestData *td) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + return A_I2CD_CMD; > > + } else { > > + return A_I2CM_CMD; > > + } > > +} > > + > > + > > +static uint32_t aspeed_i2c_intr_ctrl_offset(const TestData *td) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + return A_I2CD_INTR_CTRL; > > + } else { > > + return A_I2CM_INTR_CTRL; > > + } > > +} > > + > > +static uint32_t aspeed_i2c_intr_sts_offset(const TestData *td) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + return A_I2CD_INTR_STS; > > + } else { > > + return A_I2CM_INTR_STS; > > + } > > +} > > + > > +static uint32_t aspeed_i2c_pool_ctrl_offset(const TestData *td) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + return A_I2CD_POOL_CTRL; > > + } else { > > + return A_I2CC_POOL_CTRL; > > + } > > +} > > + > > +static uint32_t aspeed_i2c_byte_buf_offset(const TestData *td) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + return A_I2CD_BYTE_BUF; > > + } else { > > + return A_I2CC_MS_TXRX_BYTE_BUF; > > + } > > +} > > + > > +static void aspeed_i2c_write_slave_addr(TestFixture *f, const > > TestData *td, > > + uint8_t addr) > > +{ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + aspeed_i2c_write(f, A_I2CD_DEV_ADDR, addr); > > + } else { > > + aspeed_i2c_write(f, A_I2CS_DEV_ADDR, addr); > > + } > > +} > > + > > +static void aspeed_i2c_write_dma_addr(TestFixture *f, const > > TestData *td, > > + uint32_t val, bool rx) > > +{ > > + uint32_t offset; > > + > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + offset = A_I2CD_DMA_ADDR; > > + } else { > > + offset = rx ? A_I2CM_DMA_RX_ADDR : A_I2CM_DMA_TX_ADDR; > > + } > > + aspeed_i2c_write(f, offset, val); > > +} > > + > > +static void aspeed_i2c_write_dma_len(TestFixture *f, const > > TestData *td, > > + uint16_t val, bool rx) > > +{ > > + uint32_t reg; > > + > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + aspeed_i2c_write(f, A_I2CD_DMA_LEN, val + 1); > > + return; > > + } > > + > > + if (rx) { > > + reg = FIELD_DP32(0, I2CM_DMA_LEN, RX_BUF_LEN_W1T, 1); > > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, RX_BUF_LEN, val); > > + } else { > > + reg = FIELD_DP32(0, I2CM_DMA_LEN, TX_BUF_LEN_W1T, 1); > > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, TX_BUF_LEN, val); > > + } > > + aspeed_i2c_write(f, A_I2CM_DMA_LEN, reg); > > +} > > + > > +static uint8_t aspeed_i2c_get_txstate(TestFixture *f, const > > TestData *td) > > +{ > > + uint32_t offset; > > + > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + offset = A_I2CD_CMD; > > + } else { > > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > > + } > > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), > > TX_STATE); > > +} > > + > > +static bool aspeed_i2c_get_busy_sts(TestFixture *f, const TestData > > *td) > > +{ > > + uint32_t offset; > > + > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + offset = A_I2CD_CMD; > > + } else { > > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > > + } > > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), > > BUS_BUSY_STS); > > +} > > + > > +/* Write a data byte in a byte mode transfer */ > > +static > > +void aspeed_i2c_write_byte(TestFixture *f, const TestData *td, > > uint8_t val) > > +{ > > + aspeed_i2c_write(f, aspeed_i2c_byte_buf_offset(td), > > + SHARED_FIELD_DP32(0, TX_BUF, val)); > > +} > > + > > +/* Read a data byte in a byte mode transfer */ > > +static uint8_t aspeed_i2c_read_byte(TestFixture *f, const TestData > > *td) > > +{ > > + uint32_t reg; > > + > > + reg = aspeed_i2c_read(f, aspeed_i2c_byte_buf_offset(td)); > > + return SHARED_FIELD_EX32(reg, RX_BUF); > > +} > > + > > +static void aspeed_i2c_send_start(TestFixture *f, const TestData > > *td, > > + uint32_t cmd) > > +{ > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > +} > > + > > +static uint8_t qom_i2c_dummy_get_uint8(const char *prop) > > +{ > > + QDict *r; > > + uint8_t u; > > + > > + r = qtest_qmp(global_qtest, > > + "{ 'execute': 'qom-get', 'arguments': " > > + "{ 'path': 'i2c-dummy', 'property': %s } }", > > + prop); > > + u = qdict_get_uint(r, "return"); > > + qobject_unref(r); > > + return u; > > +} > > + > > +static void qom_i2c_dummy_set_uint8(const char *prop, uint8_t > > data) > > +{ > > + QDict *r; > > + > > + r = qtest_qmp(global_qtest, > > + "{ 'execute': 'qom-set', 'arguments': " > > + "{ 'path': 'i2c-dummy', 'property': %s, 'value': > > %d } }", > > + prop, data); > > + qobject_unref(r); > > +} > > + > > +static void qom_i2c_dummy_set_rx(uint8_t idx, uint8_t data) > > +{ > > + g_autofree char *prop = g_strdup_printf("rx[%d]", idx); > > + > > + qom_i2c_dummy_set_uint8(prop, data); > > +} > > + > > +static uint8_t qom_i2c_dummy_get_tx(int8_t idx) > > +{ > > + g_autofree char *prop = g_strdup_printf("tx[%d]", idx); > > + > > + return qom_i2c_dummy_get_uint8(prop); > > +} > > + > > +#define I2C_ADDR_NACK 0x10 > > +#define I2C_ADDR_DUMMY 0x20 > > +#define I2C_ADDR_ECHO 0x30 > > +#define I2C_ADDR_SLAVE 0x40 > > + > > +#define CHECK_I2C_IDLE(f, td) do > > { \ > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > I2CD_IDLE); \ > > + g_assert_false(aspeed_i2c_get_busy_sts(f, > > td)); \ > > + g_assert_cmphex(aspeed_i2c_read(f, > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > + > > 0); \ > > + > > g_assert_false(aspeed_i2c_irq(f)); > > \ > > +} while (0) > > + > > +#define CHECK_I2C_INTR(f, td, reg) do > > { \ > > + > > g_assert_true(aspeed_i2c_irq(f)); > > \ > > + g_assert_cmphex(aspeed_i2c_read(f, > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > + > > reg); \ > > +} while (0) > > + > > +#define CHECK_I2C_BUS_ACTIVE(f, td) do > > { \ > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > I2CD_MACTIVE); \ > > + g_assert_true(aspeed_i2c_get_busy_sts(f, > > td)); \ > > +} while (0) > > + > > +#define CHECK_I2C_ACK(f, td) do > > { \ > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > I2CD_MACTIVE); \ > > + g_assert_true(aspeed_i2c_get_busy_sts(f, > > td)); \ > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, TX_ACK, > > 1)); \ > > +} while (0) > > + > > +#define CLEAR_I2C_INTR(f, td, reg) do > > { \ > > + CHECK_I2C_INTR(f, td, > > reg); \ > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), > > reg); \ > > + g_assert_cmphex(aspeed_i2c_read(f, > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > + > > 0); \ > > +} while (0) > > + > > +#define SEND_I2C_STOP(f, td) do > > { \ > > + aspeed_i2c_write(f, > > aspeed_i2c_cmd_offset(td), \ > > + SHARED_FIELD_DP32(0, M_STOP_CMD, > > 1)); \ > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, > > 1)); \ > > + CHECK_I2C_IDLE(f, > > td); \ > > +} while (0) > > + > > +#define CHECK_I2C_DUMMY_DEVICE() do > > { \ > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) > > { \ > > + g_assert_cmphex(qom_i2c_dummy_get_tx(i), ==, > > tx_data[i]); \ > > + > > } > > \ > > + g_assert_cmphex(qom_i2c_dummy_get_uint8("tx-pos"), > > ==, \ > > + > > ARRAY_SIZE(tx_data)); \ > > + g_assert_cmphex(qom_i2c_dummy_get_uint8("rx-pos"), > > ==, \ > > + > > ARRAY_SIZE(rx_data)); \ > > +} while (0) > > + > > +static void aspeed_i2c_init(TestFixture *f, const TestData *td) > > +{ > > + uint32_t reg; > > + > > + qtest_qmp(global_qtest, "{ 'execute': 'system_reset' }"); > > + > > + aspeed_i2c_write_slave_addr(f, td, I2C_ADDR_SLAVE); > > + > > + reg = FIELD_DP32(0, I2C_CTRL_GLOBAL, SRAM_EN, 1); > > + if (td->mode != TEST_I2C_MODE_OLD) { > > + reg = FIELD_DP32(reg, I2C_CTRL_GLOBAL, REG_MODE, 1); > > + } > > + qtest_writel(global_qtest, f->base_addr + A_I2C_CTRL_GLOBAL, > > reg); > > + > > + reg = SHARED_FIELD_DP32(0, MASTER_EN, 1); > > + reg = SHARED_FIELD_DP32(reg, SLAVE_EN, 1); > > + aspeed_i2c_write(f, aspeed_i2c_ctrl_offset(td), reg); > > + > > + /* Enable all interrupts */ > > + reg = SHARED_FIELD_DP32(0, TX_ACK, 1); > > + reg = SHARED_FIELD_DP32(reg, TX_NAK, 1); > > + reg = SHARED_FIELD_DP32(reg, RX_DONE, 1); > > + reg = SHARED_FIELD_DP32(reg, ARBIT_LOSS, 1); > > + reg = SHARED_FIELD_DP32(reg, NORMAL_STOP, 1); > > + reg = SHARED_FIELD_DP32(reg, ABNORMAL, 1); > > + if (td->mode != TEST_I2C_MODE_OLD) { > > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_intr_ctrl_offset(td), reg); > > + > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > + qom_i2c_dummy_set_rx(i, rx_data[i]); > > + } > > +} > > + > > +static uint32_t aspeed_i2c_pkt_set_addr_cmd(const TestData *td, > > uint8_t addr) > > +{ > > + uint32_t cmd; > > + > > + cmd = FIELD_DP32(0, I2CM_CMD, PKT_OP_EN, 1); > > + cmd = FIELD_DP32(cmd, I2CM_CMD, PKT_DEV_ADDR, addr); > > + return cmd; > > +} > > + > > +static > > +uint32_t aspeed_i2c_byte_set_addr_cmd(TestFixture *f, const > > TestData *td, > > + uint8_t addr, bool is_recv) > > +{ > > + if (td->mode == TEST_I2C_MODE_PKT) { > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > + } else { > > + aspeed_i2c_write_byte(f, td, addr << 1 | (is_recv ? 1 : > > 0)); > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > + } > > +} > > + > > +static > > +uint32_t aspeed_i2c_buf_set_addr_cmd(TestFixture *f, const > > TestData *td, > > + uint8_t addr, bool is_recv) > > +{ > > + if (td->mode == TEST_I2C_MODE_PKT) { > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > + } else { > > + aspeed_i2c_write_pool(f, 0, addr << 1 | (is_recv ? 1 : > > 0)); > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > + } > > +} > > + > > +static > > +uint32_t aspeed_i2c_dma_set_addr_cmd(TestFixture *f, const > > TestData *td, > > + uint8_t addr, bool is_recv) > > +{ > > + if (td->mode == TEST_I2C_MODE_PKT) { > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > + } else { > > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET, addr << 1 | > > (is_recv ? 1 : 0)); > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > + } > > +} > > + > > + > > +static uint32_t aspeed_i2c_send_addr_status(TestFixture *f, const > > TestData *td) > > +{ > > + uint32_t sts; > > + > > + if (td->mode == TEST_I2C_MODE_PKT) { > > + sts = FIELD_DP32(0, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } else { > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > + } > > + return sts; > > +} > > + > > +/* > > + * Send an address byte to a non existing device. > > + * The TX_NAK interrupt should be raised. > > + */ > > +static void test_nack(TestFixture *f, gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + uint32_t sts, cmd; > > + > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_NACK, > > false); > > + aspeed_i2c_send_start(f, td, cmd); > > + > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > I2CD_MSTART); > > + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); > > + sts = SHARED_FIELD_DP32(0, TX_NAK, 1); > > + if (td->mode == TEST_I2C_MODE_PKT) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } > > + CLEAR_I2C_INTR(f, td, sts); > > + > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, 1)); > > + CHECK_I2C_IDLE(f, td); > > + qtest_hmp(global_qtest, "system_reset"); > > +} > > + > > + > > +static void test_dummy_byte(TestFixture *f, gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + uint32_t cmd, sts; > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > + > > + /* Write device address (Write) */ > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > false); > > + aspeed_i2c_send_start(f, td, cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > + > > + /* Write data */ > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > > + } else { > > + cmd = 0; > > + } > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > + aspeed_i2c_write_byte(f, td, tx_data[i]); > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, sts); > > + } > > + > > + /* Read data */ > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > true); > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + sts = aspeed_i2c_send_addr_status(f, td); > > + > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > + if (i == ARRAY_SIZE(rx_data) - 1) { > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > > + } else { > > + cmd = 0; > > + } > > + CLEAR_I2C_INTR(f, td, sts); > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > rx_data[i]); > > + sts = 0; > > + } > > + > > + SEND_I2C_STOP(f, td); > > + CHECK_I2C_DUMMY_DEVICE(); > > + qtest_hmp(global_qtest, "system_reset"); > > +} > > + > > +static void test_dummy_buf(TestFixture *f, gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + uint32_t cmd, sts, pool_ctrl; > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > + > > + /* Write data with device address */ > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > + aspeed_i2c_write_pool(f, i + (pkt_mode ? 0 : 1), > > tx_data[i]); > > + } > > + pool_ctrl = SHARED_FIELD_DP32(0, TX_COUNT, > > + ARRAY_SIZE(tx_data) - (pkt_mode > > ? 1 : 0)); > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), > > pool_ctrl); > > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > false); > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, sts); > > + > > + /* Write device address (Read) in non packet mde */ > > + if (pkt_mode) { > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + } else { > > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); > > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, > > M_TX_CMD, 1)); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > + cmd = 0; > > + } > > + > > + /* Read data */ > > + pool_ctrl = SHARED_FIELD_DP32(0, RX_SIZE, > > + ARRAY_SIZE(rx_data) - 1); > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), > > pool_ctrl); > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > + cmd = SHARED_FIELD_DP32(cmd, RX_BUFF_EN, 1); > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, sts); > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > + g_assert_cmphex(aspeed_i2c_read_pool(f, i), ==, > > rx_data[i]); > > + } > > + > > + SEND_I2C_STOP(f, td); > > + CHECK_I2C_DUMMY_DEVICE(); > > + qtest_hmp(global_qtest, "system_reset"); > > +} > > + > > +static void test_dummy_dma(TestFixture *f, gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + uint32_t cmd, sts; > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > + > > + /* Write data with device address*/ > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET + (pkt_mode ? 0 : > > 1) + i, > > + tx_data[i]); > > + } > > + aspeed_i2c_write_dma_addr(f, td, DRAM_TX_OFFSET, false); > > + aspeed_i2c_write_dma_len(f, td, > > + ARRAY_SIZE(tx_data) - (pkt_mode ? 1 : > > 0), false); > > + cmd = aspeed_i2c_dma_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > false); > > + > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, TX_DMA_EN, 1); > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, sts); > > + > > + /* Write device address (Read) in non packet mode */ > > + if (pkt_mode) { > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, I2C_ADDR_DUMMY); > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + } else { > > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); > > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, > > M_TX_CMD, 1)); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > + cmd = 0; > > + } > > + > > + /* Read data */ > > + aspeed_i2c_write_dma_addr(f, td, DRAM_RX_OFFSET, true); > > + aspeed_i2c_write_dma_len(f, td, ARRAY_SIZE(rx_data) - 1, > > true); > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > + cmd = SHARED_FIELD_DP32(cmd, RX_DMA_EN, 1); > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, sts); > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > + g_assert_cmphex(aspeed_i2c_read_dma(f, DRAM_RX_OFFSET + > > i), ==, > > + rx_data[i]); > > + } > > + > > + SEND_I2C_STOP(f, td); > > + CHECK_I2C_DUMMY_DEVICE(); > > + qtest_hmp(global_qtest, "system_reset"); > > +} > > + > > +static void test_slave(TestFixture *f, gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + uint32_t cmd, sts, tmp; > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > + uint8_t offset = pkt_mode ? 0 : 1; > > + > > + aspeed_i2c_write_pool(f, offset + 0, I2C_ADDR_SLAVE); > > + aspeed_i2c_write_pool(f, offset + 1, tx_data[0]); > > + aspeed_i2c_write_pool(f, offset + 2, tx_data[1]); > > + > > + tmp = SHARED_FIELD_DP32(0, TX_COUNT, pkt_mode ? 2 : 3); > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), tmp); > > + > > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_ECHO, > > false); > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > + if (pkt_mode) { > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > + } > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > + CHECK_I2C_BUS_ACTIVE(f, td); > > + CLEAR_I2C_INTR(f, td, sts); > > + > > + /* Send stop and handle i2c-echo response as a slave */ > > + if (td->mode == TEST_I2C_MODE_OLD) { > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); > > + clock_step(1); > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > I2C_ADDR_SLAVE << 1); > > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > > + sts = FIELD_DP32(sts, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, > > 1); > > + CHECK_I2C_INTR(f, td, sts); > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > > + > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > tx_data[0]); > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > + CHECK_I2C_INTR(f, td, sts); > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > > + > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > tx_data[1]); > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > + CHECK_I2C_INTR(f, td, sts); > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > > + } else { > > + aspeed_i2c_write(f, A_I2CS_DMA_RX_ADDR, DRAM_RX_OFFSET); > > + tmp = SHARED_FIELD_DP32(0, RX_DMA_EN, 1); > > + aspeed_i2c_write(f, A_I2CS_CMD, tmp); > > + > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > + SHARED_FIELD_DP32(0, M_STOP_CMD, 1)); > > + clock_step(1); > > + > > + tmp = FIELD_DP32(0, I2CS_DMA_LEN_STS, RX_LEN, 2); > > + g_assert_cmphex(aspeed_i2c_read(f, A_I2CS_DMA_LEN_STS), > > ==, tmp); > > + for (uint8_t i = 0; i < 2; i++) { > > + g_assert_cmphex(aspeed_i2c_read_dma(f, DRAM_RX_OFFSET > > + i), ==, > > + tx_data[i]); > > + } > > + } > > + > > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > > + CLEAR_I2C_INTR(f, td, sts); > > + CHECK_I2C_IDLE(f, td); > > + > > + qtest_hmp(global_qtest, "system_reset"); > > +} > > + > > +static void aspeed_i2c_init_fixture(TestFixture *fixture, const > > TestData *td) > > +{ > > + switch (td->machine) { > > + case TEST_MACHINE_AST2400: > > + fixture->base_addr = 0x1E78A000; > > + fixture->bus_offset = 0x40; > > + fixture->irq_no = 12; > > + fixture->pool_base = 0x800; > > + break; > > + case TEST_MACHINE_AST2500: > > + fixture->base_addr = 0x1E78A000; > > + fixture->bus_offset = 0x40; > > + fixture->irq_no = 12; > > + fixture->pool_base = 0x200; > > + break; > > + case TEST_MACHINE_AST2600: > > + fixture->base_addr = 0x1E78A000; > > + fixture->bus_offset = 0x80; > > + fixture->irq_no = 110; > > + fixture->pool_base = 0xC00; > > + break; > > + default: > > + return; > > + } > > +} > > + > > +static void aspeed_i2c_init_test(TestFixture *fixture, const > > TestData *td) > > +{ > > + aspeed_i2c_init(fixture, td); > > + CHECK_I2C_IDLE(fixture, td); > > +} > > + > > +static void aspeed_i2c_init_new_machine(TestFixture *fixture, > > + gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + const char *machine; > > + const char *irq_dev; > > + > > + aspeed_i2c_init_fixture(fixture, td); > > + > > + if (global_qtest) { > > + qtest_quit(global_qtest); > > + } > > + > > + switch (td->machine) { > > + case TEST_MACHINE_AST2400: > > + machine = "palmetto-bmc"; > > + irq_dev = "/machine/soc/vic"; > > + break; > > + case TEST_MACHINE_AST2500: > > + machine = "ast2500-evb"; > > + irq_dev = "/machine/soc/vic"; > > + break; > > + case TEST_MACHINE_AST2600: > > + machine = "ast2600-evb"; > > + irq_dev = "/machine/soc/a7mpcore/gic"; > > + break; > > + default: > > + g_test_fail(); > > + return; > > + } > > + > > + global_qtest = qtest_initf( > > + "-machine %s -nographic " > > + "-device i2c-echo,id=i2c- > > echo,bus=aspeed.i2c.bus.0,address=%d " > > + "-device i2c-dummy-device,id=i2c- > > dummy,bus=aspeed.i2c.bus.0,address=%d", > > + machine, I2C_ADDR_ECHO, I2C_ADDR_DUMMY); > > + qtest_irq_intercept_in(global_qtest, irq_dev); > > + > > + aspeed_i2c_init_test(fixture, td); > > +} > > + > > +static void aspeed_i2c_init_machine(TestFixture *fixture, > > + gconstpointer p_data) > > +{ > > + const TestData *td = (const TestData *)p_data; > > + > > + aspeed_i2c_init_fixture(fixture, td); > > + aspeed_i2c_init_test(fixture, td); > > +} > > + > > +static void add_test(const TestData *td, const char *name, > > + void (*func)(TestFixture *, const void *), > > + bool setup) > > +{ > > + g_autofree char *test_name; > > + > > + test_name = g_strdup_printf("%s/%s", td->name, name); > > + > > + qtest_add(test_name, TestFixture, td, > > + setup ? aspeed_i2c_init_new_machine : > > aspeed_i2c_init_machine, > > + func, NULL); > > +} > > + > > +#define ADD_TEST(td, test, new) add_test(td, #test, test_##test, > > new) > > + > > +static const TestData test_data[] = { > > + { > > + .name = "ast2400", > > + .machine = TEST_MACHINE_AST2400, > > + }, > > + { > > + .name = "ast2500", > > + .machine = TEST_MACHINE_AST2500, > > + }, > > + { > > + .name = "ast2600", > > + .machine = TEST_MACHINE_AST2600, > > + }, > > + { > > + .name = "ast2600-new", > > + .machine = TEST_MACHINE_AST2600, > > + .mode = TEST_I2C_MODE_NEW, > > + }, > > + { > > + .name = "ast2600-packet", > > + .machine = TEST_MACHINE_AST2600, > > + .mode = TEST_I2C_MODE_PKT, > > + }, > > +}; > > + > > +int main(int argc, char **argv) > > +{ > > + int ret; > > + bool setup; > > + > > + g_test_init(&argc, &argv, NULL); > > + > > + for (int i = 0; i < ARRAY_SIZE(test_data); i++) { > > + setup = i == 0 || test_data[i].machine != test_data[i - > > 1].machine; > > + ADD_TEST(&test_data[i], nack, setup); > > + ADD_TEST(&test_data[i], dummy_byte, false); > > + ADD_TEST(&test_data[i], dummy_buf, false); > > + if (test_data[i].machine != TEST_MACHINE_AST2400) { > > + ADD_TEST(&test_data[i], dummy_dma, false); > > + } > > + ADD_TEST(&test_data[i], slave, false); > > + } > > + > > + ret = g_test_run(); > > + qtest_quit(global_qtest); > > + return ret; > > +} > > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > > index 46d4fc1d2b..c496b7c3a7 100644 > > --- a/tests/qtest/meson.build > > +++ b/tests/qtest/meson.build > > @@ -233,12 +233,14 @@ qtests_aspeed = \ > > ['aspeed-acry-test', > > 'aspeed_gpio-test', > > 'aspeed_hace-test', > > + 'aspeed_i2c-test', > > 'aspeed-sbc-test', > > 'aspeed_scu-test', > > 'aspeed_smc-test'] > > qtests_aspeed64 = \ > > ['ast2700-gpio-test', > > 'ast2700-hace-test', > > + 'aspeed_i2c-test', > > 'ast2700-sgpio-test', > > 'ast2700-smc-test'] > > > > ^ permalink raw reply [flat|nested] 33+ messages in thread
* RE: [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers 2026-10-28 14:47 ` Timur Maksimov @ 2026-10-29 7:25 ` Jamin Lin 2026-10-29 8:25 ` Timur Maksimov 0 siblings, 1 reply; 33+ messages in thread From: Jamin Lin @ 2026-10-29 7:25 UTC (permalink / raw) To: Timur Maksimov, Cédric Le Goater, qemu-devel@nongnu.org Cc: Paolo Bonzini, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier > Hi, I thought about that, but I'm not sure how to do it because I haven't found > previous examples in QEMU. > I guess there should be two test suites for ARM and AArch64 targets. > But AArch64 also supports 32-bit ARM SoCs, so I'm not sure whether we should > test them with the AArch64 target as well or if testing with the ARM target is > enough. > Please refer to ast2700-hace-test.c, aspeed-hace-utils.c, and aspeed_hace-test.c as examples of HACE tests for AArch64 (AST2700) and ARM32 (AST2500, AST2600, and AST1030). > On Wed, 2026-10-28 at 09:56 +0100, Cédric Le Goater wrote: > > On 10/27/26 20:50, Timur Maksimov wrote: > > > Test all supported register and transfer modes. > > > > > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > > --- > > > tests/qtest/aspeed_i2c-test.c | 836 > > > ++++++++++++++++++++++++++++++++++++++++++ > > > tests/qtest/meson.build | 2 + > > > 2 files changed, 838 insertions(+) > > > > > > diff --git a/tests/qtest/aspeed_i2c-test.c > > > b/tests/qtest/aspeed_i2c-test.c new file mode 100644 index > > > 0000000000..7fb1debb56 > > > --- /dev/null > > > +++ b/tests/qtest/aspeed_i2c-test.c > > > @@ -0,0 +1,836 @@ > > > +/* > > > + * QTest testcase for Aspeed I2C controller > > > + * > > > + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> > > > + * > > > + * SPDX-License-Identifier: GPL-2.0-or-later */ > > > + > > > +#include "qemu/osdep.h" > > > +#include "libqtest-single.h" > > > +#include "hw/i2c/aspeed_i2c.h" > > > + > > > +typedef enum TestAspeedI2CMode { > > > + TEST_I2C_MODE_OLD, > > > + TEST_I2C_MODE_NEW, > > > + TEST_I2C_MODE_PKT, > > > +} TestAspeedI2CMode; > > > + > > > +typedef enum TestAspeedMachine { > > > + TEST_MACHINE_AST2400, > > > + TEST_MACHINE_AST2500, > > > + TEST_MACHINE_AST2600, > > > > > > Do you have plans to add AST2700 support ? This is the current focus > > now days. > > > > Thanks, > > > > C. > > > > > > > > > +} TestAspeedMachine; > > > + > > > +typedef struct TestData { > > > + const char *name; > > > + TestAspeedMachine machine; > > > + TestAspeedI2CMode mode; > > > +} TestData; > > > + > > > +typedef struct TestFixture { > > > + hwaddr base_addr; > > > + hwaddr bus_offset; > > > + int irq_no; > > > + uint32_t pool_base; > > > +} TestFixture; > > > + > > > +static const uint8_t tx_data[] = {0x10, 0x11, 0x12}; static const > > > +uint8_t rx_data[] = {0x30, 0x31, 0x32}; > > > + > > > +#define DRAM_BASE 0x80000000 > > > +#define DRAM_TX_OFFSET 0x1000 > > > +#define DRAM_RX_OFFSET 0x2000 > > > + > > > +static void aspeed_i2c_write(TestFixture *f, uint32_t offset, > > > uint32_t val) > > > +{ > > > + qtest_writel(global_qtest, f->base_addr + f->bus_offset + > > > offset, val); > > > +} > > > + > > > +static void aspeed_i2c_write_pool(TestFixture *f, uint32_t offset, > > > uint8_t val) > > > +{ > > > + qtest_writeb(global_qtest, f->base_addr + f->pool_base + > > > offset, val); > > > +} > > > + > > > +static void aspeed_i2c_write_dma(TestFixture *f, uint32_t offset, > > > uint8_t val) > > > +{ > > > + qtest_writeb(global_qtest, DRAM_BASE + offset, val); } > > > + > > > +static uint32_t aspeed_i2c_read(TestFixture *f, uint32_t offset) { > > > + return qtest_readl(global_qtest, f->base_addr + f->bus_offset > > > +offset); } > > > + > > > +static uint8_t aspeed_i2c_read_pool(TestFixture *f, uint32_t > > > offset) > > > +{ > > > + return qtest_readb(global_qtest, f->base_addr + f->pool_base + > > > offset); > > > +} > > > + > > > +static uint8_t aspeed_i2c_read_dma(TestFixture *f, uint32_t > > > offset) > > > +{ > > > + return qtest_readb(global_qtest, DRAM_BASE + offset); } > > > + > > > +static bool aspeed_i2c_irq(TestFixture *f) { > > > + return qtest_get_irq(global_qtest, f->irq_no); } > > > + > > > +static uint32_t aspeed_i2c_ctrl_offset(const TestData *td) { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + return A_I2CD_FUN_CTRL; > > > + } else { > > > + return A_I2CC_FUN_CTRL; > > > + } > > > +} > > > + > > > +static uint32_t aspeed_i2c_cmd_offset(const TestData *td) { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + return A_I2CD_CMD; > > > + } else { > > > + return A_I2CM_CMD; > > > + } > > > +} > > > + > > > + > > > +static uint32_t aspeed_i2c_intr_ctrl_offset(const TestData *td) { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + return A_I2CD_INTR_CTRL; > > > + } else { > > > + return A_I2CM_INTR_CTRL; > > > + } > > > +} > > > + > > > +static uint32_t aspeed_i2c_intr_sts_offset(const TestData *td) { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + return A_I2CD_INTR_STS; > > > + } else { > > > + return A_I2CM_INTR_STS; > > > + } > > > +} > > > + > > > +static uint32_t aspeed_i2c_pool_ctrl_offset(const TestData *td) { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + return A_I2CD_POOL_CTRL; > > > + } else { > > > + return A_I2CC_POOL_CTRL; > > > + } > > > +} > > > + > > > +static uint32_t aspeed_i2c_byte_buf_offset(const TestData *td) { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + return A_I2CD_BYTE_BUF; > > > + } else { > > > + return A_I2CC_MS_TXRX_BYTE_BUF; > > > + } > > > +} > > > + > > > +static void aspeed_i2c_write_slave_addr(TestFixture *f, const > > > TestData *td, > > > + uint8_t addr) > { > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + aspeed_i2c_write(f, A_I2CD_DEV_ADDR, addr); > > > + } else { > > > + aspeed_i2c_write(f, A_I2CS_DEV_ADDR, addr); > > > + } > > > +} > > > + > > > +static void aspeed_i2c_write_dma_addr(TestFixture *f, const > > > TestData *td, > > > + uint32_t val, bool > rx) { > > > + uint32_t offset; > > > + > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + offset = A_I2CD_DMA_ADDR; > > > + } else { > > > + offset = rx ? A_I2CM_DMA_RX_ADDR : > A_I2CM_DMA_TX_ADDR; > > > + } > > > + aspeed_i2c_write(f, offset, val); } > > > + > > > +static void aspeed_i2c_write_dma_len(TestFixture *f, const > > > TestData *td, > > > + uint16_t val, bool > rx) { > > > + uint32_t reg; > > > + > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + aspeed_i2c_write(f, A_I2CD_DMA_LEN, val + 1); > > > + return; > > > + } > > > + > > > + if (rx) { > > > + reg = FIELD_DP32(0, I2CM_DMA_LEN, RX_BUF_LEN_W1T, 1); > > > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, RX_BUF_LEN, val); > > > + } else { > > > + reg = FIELD_DP32(0, I2CM_DMA_LEN, TX_BUF_LEN_W1T, 1); > > > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, TX_BUF_LEN, val); > > > + } > > > + aspeed_i2c_write(f, A_I2CM_DMA_LEN, reg); } > > > + > > > +static uint8_t aspeed_i2c_get_txstate(TestFixture *f, const > > > TestData *td) > > > +{ > > > + uint32_t offset; > > > + > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + offset = A_I2CD_CMD; > > > + } else { > > > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > > > + } > > > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), > > > TX_STATE); > > > +} > > > + > > > +static bool aspeed_i2c_get_busy_sts(TestFixture *f, const TestData > > > *td) > > > +{ > > > + uint32_t offset; > > > + > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + offset = A_I2CD_CMD; > > > + } else { > > > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > > > + } > > > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), > > > BUS_BUSY_STS); > > > +} > > > + > > > +/* Write a data byte in a byte mode transfer */ static void > > > +aspeed_i2c_write_byte(TestFixture *f, const TestData *td, > > > uint8_t val) > > > +{ > > > + aspeed_i2c_write(f, aspeed_i2c_byte_buf_offset(td), > > > + SHARED_FIELD_DP32(0, TX_BUF, val)); } > > > + > > > +/* Read a data byte in a byte mode transfer */ static uint8_t > > > +aspeed_i2c_read_byte(TestFixture *f, const TestData > > > *td) > > > +{ > > > + uint32_t reg; > > > + > > > + reg = aspeed_i2c_read(f, aspeed_i2c_byte_buf_offset(td)); > > > + return SHARED_FIELD_EX32(reg, RX_BUF); } > > > + > > > +static void aspeed_i2c_send_start(TestFixture *f, const TestData > > > *td, > > > + uint32_t cmd) { > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); } > > > + > > > +static uint8_t qom_i2c_dummy_get_uint8(const char *prop) { > > > + QDict *r; > > > + uint8_t u; > > > + > > > + r = qtest_qmp(global_qtest, > > > + "{ 'execute': 'qom-get', 'arguments': " > > > + "{ 'path': 'i2c-dummy', 'property': %s } }", > > > + prop); > > > + u = qdict_get_uint(r, "return"); > > > + qobject_unref(r); > > > + return u; > > > +} > > > + > > > +static void qom_i2c_dummy_set_uint8(const char *prop, uint8_t > > > data) > > > +{ > > > + QDict *r; > > > + > > > + r = qtest_qmp(global_qtest, > > > + "{ 'execute': 'qom-set', 'arguments': " > > > + "{ 'path': 'i2c-dummy', 'property': %s, 'value': > > > %d } }", > > > + prop, data); > > > + qobject_unref(r); > > > +} > > > + > > > +static void qom_i2c_dummy_set_rx(uint8_t idx, uint8_t data) { > > > + g_autofree char *prop = g_strdup_printf("rx[%d]", idx); > > > + > > > + qom_i2c_dummy_set_uint8(prop, data); } > > > + > > > +static uint8_t qom_i2c_dummy_get_tx(int8_t idx) { > > > + g_autofree char *prop = g_strdup_printf("tx[%d]", idx); > > > + > > > + return qom_i2c_dummy_get_uint8(prop); } > > > + > > > +#define I2C_ADDR_NACK 0x10 > > > +#define I2C_ADDR_DUMMY 0x20 > > > +#define I2C_ADDR_ECHO 0x30 > > > +#define I2C_ADDR_SLAVE 0x40 > > > + Could these be static helper functions instead of multi-line macros? They do not need anything a function cannot do, and functions would be easier to read and type-checked. I understand the macros make g_assert report the caller's line, but the qtest path and the tap output already tell us which test failed. Also, CHECK_I2C_ACK() is not used anywhere, so it can be dropped. > > > +#define CHECK_I2C_IDLE(f, td) do > > > { \ > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > I2CD_IDLE); \ > > > + g_assert_false(aspeed_i2c_get_busy_sts(f, > > > td)); \ > > > + g_assert_cmphex(aspeed_i2c_read(f, > > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > > + > > > > 0); > \ > > > + > > > g_assert_false(aspeed_i2c_irq(f)); > > > \ > > > +} while (0) > > > + > > > +#define CHECK_I2C_INTR(f, td, reg) do > > > { \ > > > + > > > g_assert_true(aspeed_i2c_irq(f)); > > > \ > > > + g_assert_cmphex(aspeed_i2c_read(f, > > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > > + > > > > reg); > \ > > > +} while (0) > > > + > > > +#define CHECK_I2C_BUS_ACTIVE(f, td) do > > > { \ > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > I2CD_MACTIVE); \ > > > + g_assert_true(aspeed_i2c_get_busy_sts(f, > > > td)); \ > > > +} while (0) > > > + > > > +#define CHECK_I2C_ACK(f, td) do > > > { \ > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > I2CD_MACTIVE); \ > > > + g_assert_true(aspeed_i2c_get_busy_sts(f, > > > td)); \ > > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, TX_ACK, > > > 1)); \ > > > +} while (0) > > > + > > > +#define CLEAR_I2C_INTR(f, td, reg) do > > > { \ > > > + CHECK_I2C_INTR(f, td, > > > reg); \ > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), > > > reg); \ > > > + g_assert_cmphex(aspeed_i2c_read(f, > > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > > + > > > > 0); > \ > > > +} while (0) > > > + > > > +#define SEND_I2C_STOP(f, td) do > > > { \ > > > + aspeed_i2c_write(f, > > > aspeed_i2c_cmd_offset(td), \ > > > + SHARED_FIELD_DP32(0, M_STOP_CMD, > > > 1)); \ > > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, > > > 1)); \ > > > + CHECK_I2C_IDLE(f, > > > > td); > \ > > > +} while (0) > > > + > > > +#define CHECK_I2C_DUMMY_DEVICE() do > > > { \ > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) > > > { \ > > > + g_assert_cmphex(qom_i2c_dummy_get_tx(i), ==, > > > tx_data[i]); \ > > > + > > > } > > > \ > > > + g_assert_cmphex(qom_i2c_dummy_get_uint8("tx-pos"), > > > ==, \ > > > + > > > > ARRAY_SIZE(tx_data)); \ > > > + g_assert_cmphex(qom_i2c_dummy_get_uint8("rx-pos"), > > > ==, \ > > > + > > > > ARRAY_SIZE(rx_data)); \ > > > +} while (0) > > > + > > > +static void aspeed_i2c_init(TestFixture *f, const TestData *td) { > > > + uint32_t reg; > > > + > > > + qtest_qmp(global_qtest, "{ 'execute': 'system_reset' }"); > > > + > > > + aspeed_i2c_write_slave_addr(f, td, I2C_ADDR_SLAVE); > > > + The slave address is written before the register mode is switched: For the new register mode tests, aspeed_i2c_write_slave_addr() writes I2CS_DEV_ADDR (offset 0x40), but the controller is still in old register mode at that point, so the write is dropped as a bad offset. The slave address is never programmed, and the slave test only passes because an earlier test left the controller in new register mode. Running the slave test on its own fails every time: QTEST_QEMU_BINARY=./qemu-system-arm ./tests/qtest/aspeed_i2c-test \ -p /arm/ast2600/nack -p /arm/ast2600-new/slave ERROR: test_slave: assertion failed (aspeed_i2c_read(f, A_I2CS_DMA_LEN_STS) == tmp): (0x00000000 == 0x00020000) (/arm/ast2600/nack is only there to start the machine.) Thanks, Jamin > > > + reg = FIELD_DP32(0, I2C_CTRL_GLOBAL, SRAM_EN, 1); > > > + if (td->mode != TEST_I2C_MODE_OLD) { > > > + reg = FIELD_DP32(reg, I2C_CTRL_GLOBAL, REG_MODE, 1); > > > + } > > > + qtest_writel(global_qtest, f->base_addr + A_I2C_CTRL_GLOBAL, > > > reg); > > > + > > > + reg = SHARED_FIELD_DP32(0, MASTER_EN, 1); > > > + reg = SHARED_FIELD_DP32(reg, SLAVE_EN, 1); > > > + aspeed_i2c_write(f, aspeed_i2c_ctrl_offset(td), reg); > > > + > > > + /* Enable all interrupts */ > > > + reg = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > + reg = SHARED_FIELD_DP32(reg, TX_NAK, 1); > > > + reg = SHARED_FIELD_DP32(reg, RX_DONE, 1); > > > + reg = SHARED_FIELD_DP32(reg, ARBIT_LOSS, 1); > > > + reg = SHARED_FIELD_DP32(reg, NORMAL_STOP, 1); > > > + reg = SHARED_FIELD_DP32(reg, ABNORMAL, 1); > > > + if (td->mode != TEST_I2C_MODE_OLD) { > > > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_intr_ctrl_offset(td), reg); > > > + > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > + qom_i2c_dummy_set_rx(i, rx_data[i]); > > > + } > > > +} > > > + > > > +static uint32_t aspeed_i2c_pkt_set_addr_cmd(const TestData *td, > > > uint8_t addr) > > > +{ > > > + uint32_t cmd; > > > + > > > + cmd = FIELD_DP32(0, I2CM_CMD, PKT_OP_EN, 1); > > > + cmd = FIELD_DP32(cmd, I2CM_CMD, PKT_DEV_ADDR, addr); > > > + return cmd; > > > +} > > > + > > > +static > > > +uint32_t aspeed_i2c_byte_set_addr_cmd(TestFixture *f, const > > > TestData *td, > > > + uint8_t addr, > bool is_recv) { > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > > + } else { > > > + aspeed_i2c_write_byte(f, td, addr << 1 | (is_recv ? 1 : > > > 0)); > > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > > + } > > > +} > > > + > > > +static > > > +uint32_t aspeed_i2c_buf_set_addr_cmd(TestFixture *f, const > > > TestData *td, > > > + uint8_t addr, > bool is_recv) { > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > > + } else { > > > + aspeed_i2c_write_pool(f, 0, addr << 1 | (is_recv ? 1 : > > > 0)); > > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > > + } > > > +} > > > + > > > +static > > > +uint32_t aspeed_i2c_dma_set_addr_cmd(TestFixture *f, const > > > TestData *td, > > > + uint8_t addr, > bool is_recv) { > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > > + } else { > > > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET, addr << 1 | > > > (is_recv ? 1 : 0)); > > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > > + } > > > +} > > > + > > > + > > > +static uint32_t aspeed_i2c_send_addr_status(TestFixture *f, const > > > TestData *td) > > > +{ > > > + uint32_t sts; > > > + > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > + sts = FIELD_DP32(0, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } else { > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > + } > > > + return sts; > > > +} > > > + > > > +/* > > > + * Send an address byte to a non existing device. > > > + * The TX_NAK interrupt should be raised. > > > + */ > > > +static void test_nack(TestFixture *f, gconstpointer p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + uint32_t sts, cmd; > > > + > > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_NACK, > > > false); > > > + aspeed_i2c_send_start(f, td, cmd); > > > + > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > I2CD_MSTART); > > > + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); > > > + sts = SHARED_FIELD_DP32(0, TX_NAK, 1); > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } > > > + CLEAR_I2C_INTR(f, td, sts); > > > + > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > > + SHARED_FIELD_DP32(0, M_STOP_CMD, > 1)); > > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, 1)); > > > + CHECK_I2C_IDLE(f, td); > > > + qtest_hmp(global_qtest, "system_reset"); } > > > + > > > + > > > +static void test_dummy_byte(TestFixture *f, gconstpointer p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + uint32_t cmd, sts; > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > + > > > + /* Write device address (Write) */ > > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > false); > > > + aspeed_i2c_send_start(f, td, cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > > + > > > + /* Write data */ > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > I2C_ADDR_DUMMY); > > > + } else { > > > + cmd = 0; > > > + } > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > > + aspeed_i2c_write_byte(f, td, tx_data[i]); > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + } > > > + > > > + /* Read data */ > > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > true); > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + sts = aspeed_i2c_send_addr_status(f, td); > > > + > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > + if (i == ARRAY_SIZE(rx_data) - 1) { > > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, > 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, > PKT_CMD_DONE, 1); > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > I2C_ADDR_DUMMY); > > > + } else { > > > + cmd = 0; > > > + } > > > + CLEAR_I2C_INTR(f, td, sts); > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > rx_data[i]); > > > + sts = 0; > > > + } > > > + > > > + SEND_I2C_STOP(f, td); > > > + CHECK_I2C_DUMMY_DEVICE(); > > > + qtest_hmp(global_qtest, "system_reset"); } > > > + > > > +static void test_dummy_buf(TestFixture *f, gconstpointer p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + uint32_t cmd, sts, pool_ctrl; > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > + > > > + /* Write data with device address */ > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > > + aspeed_i2c_write_pool(f, i + (pkt_mode ? 0 : 1), > > > tx_data[i]); > > > + } > > > + pool_ctrl = SHARED_FIELD_DP32(0, TX_COUNT, > > > + ARRAY_SIZE(tx_data) > - (pkt_mode > > > ? 1 : 0)); > > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), > > > pool_ctrl); > > > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > false); > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + > > > + /* Write device address (Read) in non packet mde */ > > > + if (pkt_mode) { > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > I2C_ADDR_DUMMY); > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + } else { > > > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); > > > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, > > > M_TX_CMD, 1)); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > > + cmd = 0; > > > + } > > > + > > > + /* Read data */ > > > + pool_ctrl = SHARED_FIELD_DP32(0, RX_SIZE, > > > + ARRAY_SIZE(rx_data) > - 1); > > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), > > > pool_ctrl); > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, RX_BUFF_EN, 1); > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > + g_assert_cmphex(aspeed_i2c_read_pool(f, i), ==, > > > rx_data[i]); > > > + } > > > + > > > + SEND_I2C_STOP(f, td); > > > + CHECK_I2C_DUMMY_DEVICE(); > > > + qtest_hmp(global_qtest, "system_reset"); } > > > + > > > +static void test_dummy_dma(TestFixture *f, gconstpointer p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + uint32_t cmd, sts; > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > + > > > + /* Write data with device address*/ > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET + (pkt_mode ? > 0 : > > > 1) + i, > > > + tx_data[i]); > > > + } > > > + aspeed_i2c_write_dma_addr(f, td, DRAM_TX_OFFSET, false); > > > + aspeed_i2c_write_dma_len(f, td, > > > + ARRAY_SIZE(tx_data) - > (pkt_mode ? 1 : > > > 0), false); > > > + cmd = aspeed_i2c_dma_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > false); > > > + > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, TX_DMA_EN, 1); > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + > > > + /* Write device address (Read) in non packet mode */ > > > + if (pkt_mode) { > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > I2C_ADDR_DUMMY); > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + } else { > > > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | 1); > > > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, > > > M_TX_CMD, 1)); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > > + cmd = 0; > > > + } > > > + > > > + /* Read data */ > > > + aspeed_i2c_write_dma_addr(f, td, DRAM_RX_OFFSET, true); > > > + aspeed_i2c_write_dma_len(f, td, ARRAY_SIZE(rx_data) - 1, > > > true); > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, RX_DMA_EN, 1); > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > + g_assert_cmphex(aspeed_i2c_read_dma(f, > DRAM_RX_OFFSET + > > > i), ==, > > > + rx_data[i]); > > > + } > > > + > > > + SEND_I2C_STOP(f, td); > > > + CHECK_I2C_DUMMY_DEVICE(); > > > + qtest_hmp(global_qtest, "system_reset"); } > > > + > > > +static void test_slave(TestFixture *f, gconstpointer p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + uint32_t cmd, sts, tmp; > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > + uint8_t offset = pkt_mode ? 0 : 1; > > > + > > > + aspeed_i2c_write_pool(f, offset + 0, I2C_ADDR_SLAVE); > > > + aspeed_i2c_write_pool(f, offset + 1, tx_data[0]); > > > + aspeed_i2c_write_pool(f, offset + 2, tx_data[1]); > > > + > > > + tmp = SHARED_FIELD_DP32(0, TX_COUNT, pkt_mode ? 2 : 3); > > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), tmp); > > > + > > > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_ECHO, > > > false); > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > + if (pkt_mode) { > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > + } > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + > > > + /* Send stop and handle i2c-echo response as a slave */ > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > > + SHARED_FIELD_DP32(0, > M_STOP_CMD, 1)); > > > + clock_step(1); > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > I2C_ADDR_SLAVE << 1); > > > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > > > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > > > + sts = FIELD_DP32(sts, I2CD_INTR_STS, > SLAVE_ADDR_RX_MATCH, > > > 1); > > > + CHECK_I2C_INTR(f, td, sts); > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > > > + > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > tx_data[0]); > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > + CHECK_I2C_INTR(f, td, sts); > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > > > + > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > tx_data[1]); > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > + CHECK_I2C_INTR(f, td, sts); > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), sts); > > > + } else { > > > + aspeed_i2c_write(f, A_I2CS_DMA_RX_ADDR, > DRAM_RX_OFFSET); > > > + tmp = SHARED_FIELD_DP32(0, RX_DMA_EN, 1); > > > + aspeed_i2c_write(f, A_I2CS_CMD, tmp); > > > + > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > > + SHARED_FIELD_DP32(0, > M_STOP_CMD, 1)); > > > + clock_step(1); > > > + > > > + tmp = FIELD_DP32(0, I2CS_DMA_LEN_STS, RX_LEN, 2); > > > + g_assert_cmphex(aspeed_i2c_read(f, > A_I2CS_DMA_LEN_STS), > > > ==, tmp); > > > + for (uint8_t i = 0; i < 2; i++) { > > > + g_assert_cmphex(aspeed_i2c_read_dma(f, > DRAM_RX_OFFSET > > > +i), ==, > > > + tx_data[i]); > > > + } > > > + } > > > + > > > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > > > + CLEAR_I2C_INTR(f, td, sts); > > > + CHECK_I2C_IDLE(f, td); > > > + > > > + qtest_hmp(global_qtest, "system_reset"); } > > > + > > > +static void aspeed_i2c_init_fixture(TestFixture *fixture, const > > > TestData *td) > > > +{ > > > + switch (td->machine) { > > > + case TEST_MACHINE_AST2400: > > > + fixture->base_addr = 0x1E78A000; > > > + fixture->bus_offset = 0x40; > > > + fixture->irq_no = 12; > > > + fixture->pool_base = 0x800; > > > + break; > > > + case TEST_MACHINE_AST2500: > > > + fixture->base_addr = 0x1E78A000; > > > + fixture->bus_offset = 0x40; > > > + fixture->irq_no = 12; > > > + fixture->pool_base = 0x200; > > > + break; > > > + case TEST_MACHINE_AST2600: > > > + fixture->base_addr = 0x1E78A000; > > > + fixture->bus_offset = 0x80; > > > + fixture->irq_no = 110; > > > + fixture->pool_base = 0xC00; > > > + break; > > > + default: > > > + return; > > > + } > > > +} > > > + > > > +static void aspeed_i2c_init_test(TestFixture *fixture, const > > > TestData *td) > > > +{ > > > + aspeed_i2c_init(fixture, td); > > > + CHECK_I2C_IDLE(fixture, td); > > > +} > > > + > > > +static void aspeed_i2c_init_new_machine(TestFixture *fixture, > > > > + gconstpointer > p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + const char *machine; > > > + const char *irq_dev; > > > + > > > + aspeed_i2c_init_fixture(fixture, td); > > > + > > > + if (global_qtest) { > > > + qtest_quit(global_qtest); > > > + } > > > + > > > + switch (td->machine) { > > > + case TEST_MACHINE_AST2400: > > > + machine = "palmetto-bmc"; > > > + irq_dev = "/machine/soc/vic"; > > > + break; > > > + case TEST_MACHINE_AST2500: > > > + machine = "ast2500-evb"; > > > + irq_dev = "/machine/soc/vic"; > > > + break; > > > + case TEST_MACHINE_AST2600: > > > + machine = "ast2600-evb"; > > > + irq_dev = "/machine/soc/a7mpcore/gic"; > > > + break; > > > + default: > > > + g_test_fail(); > > > + return; > > > + } > > > + > > > + global_qtest = qtest_initf( > > > + "-machine %s -nographic " > > > + "-device i2c-echo,id=i2c- > > > echo,bus=aspeed.i2c.bus.0,address=%d " > > > + "-device i2c-dummy-device,id=i2c- > > > dummy,bus=aspeed.i2c.bus.0,address=%d", > > > + machine, I2C_ADDR_ECHO, I2C_ADDR_DUMMY); > > > + qtest_irq_intercept_in(global_qtest, irq_dev); > > > + > > > + aspeed_i2c_init_test(fixture, td); } > > > + > > > +static void aspeed_i2c_init_machine(TestFixture *fixture, > > > + gconstpointer > p_data) { > > > + const TestData *td = (const TestData *)p_data; > > > + > > > + aspeed_i2c_init_fixture(fixture, td); > > > + aspeed_i2c_init_test(fixture, td); } > > > + > > > +static void add_test(const TestData *td, const char *name, > > > + void (*func)(TestFixture *, const void *), > > > + bool setup) > > > +{ > > > + g_autofree char *test_name; > > > + > > > + test_name = g_strdup_printf("%s/%s", td->name, name); > > > + > > > + qtest_add(test_name, TestFixture, td, > > > + setup ? aspeed_i2c_init_new_machine : > > > aspeed_i2c_init_machine, > > > + func, NULL); > > > +} > > > + > > > +#define ADD_TEST(td, test, new) add_test(td, #test, test_##test, > > > new) > > > + > > > +static const TestData test_data[] = { > > > + { > > > + .name = "ast2400", > > > + .machine = TEST_MACHINE_AST2400, > > > + }, > > > + { > > > + .name = "ast2500", > > > + .machine = TEST_MACHINE_AST2500, > > > + }, > > > + { > > > + .name = "ast2600", > > > + .machine = TEST_MACHINE_AST2600, > > > + }, > > > + { > > > + .name = "ast2600-new", > > > + .machine = TEST_MACHINE_AST2600, > > > + .mode = TEST_I2C_MODE_NEW, > > > + }, > > > + { > > > + .name = "ast2600-packet", > > > + .machine = TEST_MACHINE_AST2600, > > > + .mode = TEST_I2C_MODE_PKT, > > > + }, > > > +}; > > > + > > > +int main(int argc, char **argv) > > > +{ > > > + int ret; > > > + bool setup; > > > + > > > + g_test_init(&argc, &argv, NULL); > > > + > > > + for (int i = 0; i < ARRAY_SIZE(test_data); i++) { > > > + setup = i == 0 || test_data[i].machine != test_data[i - > > > 1].machine; > > > + ADD_TEST(&test_data[i], nack, setup); > > > + ADD_TEST(&test_data[i], dummy_byte, false); > > > + ADD_TEST(&test_data[i], dummy_buf, false); > > > + if (test_data[i].machine != TEST_MACHINE_AST2400) { > > > + ADD_TEST(&test_data[i], dummy_dma, false); > > > + } > > > + ADD_TEST(&test_data[i], slave, false); > > > + } > > > + > > > + ret = g_test_run(); > > > + qtest_quit(global_qtest); > > > + return ret; > > > +} > > > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index > > > 46d4fc1d2b..c496b7c3a7 100644 > > > --- a/tests/qtest/meson.build > > > +++ b/tests/qtest/meson.build > > > @@ -233,12 +233,14 @@ qtests_aspeed = \ > > > ['aspeed-acry-test', > > > 'aspeed_gpio-test', > > > 'aspeed_hace-test', > > > + 'aspeed_i2c-test', > > > 'aspeed-sbc-test', > > > 'aspeed_scu-test', > > > 'aspeed_smc-test'] > > > qtests_aspeed64 = \ > > > ['ast2700-gpio-test', > > > 'ast2700-hace-test', > > > + 'aspeed_i2c-test', > > > 'ast2700-sgpio-test', > > > 'ast2700-smc-test'] > > > > > > ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers 2026-10-29 7:25 ` Jamin Lin @ 2026-10-29 8:25 ` Timur Maksimov 0 siblings, 0 replies; 33+ messages in thread From: Timur Maksimov @ 2026-10-29 8:25 UTC (permalink / raw) To: Jamin Lin, Cédric Le Goater, qemu-devel@nongnu.org Cc: Paolo Bonzini, Peter Maydell, Kane Chen, qemu-arm@nongnu.org, Fabiano Rosas, Laurent Vivier Thanks, I see it. I will add support for the ast2700. On Thu, 2026-10-29 at 07:25 +0000, Jamin Lin wrote: > > Hi, I thought about that, but I'm not sure how to do it because I > > haven't found > > previous examples in QEMU. > > I guess there should be two test suites for ARM and AArch64 > > targets. > > But AArch64 also supports 32-bit ARM SoCs, so I'm not sure whether > > we should > > test them with the AArch64 target as well or if testing with the > > ARM target is > > enough. > > > > Please refer to ast2700-hace-test.c, aspeed-hace-utils.c, and > aspeed_hace-test.c as examples of HACE tests for AArch64 (AST2700) > and ARM32 (AST2500, AST2600, and AST1030). > > > On Wed, 2026-10-28 at 09:56 +0100, Cédric Le Goater wrote: > > > On 10/27/26 20:50, Timur Maksimov wrote: > > > > Test all supported register and transfer modes. > > > > > > > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > > > > --- > > > > tests/qtest/aspeed_i2c-test.c | 836 > > > > ++++++++++++++++++++++++++++++++++++++++++ > > > > tests/qtest/meson.build | 2 + > > > > 2 files changed, 838 insertions(+) > > > > > > > > diff --git a/tests/qtest/aspeed_i2c-test.c > > > > b/tests/qtest/aspeed_i2c-test.c new file mode 100644 index > > > > 0000000000..7fb1debb56 > > > > --- /dev/null > > > > +++ b/tests/qtest/aspeed_i2c-test.c > > > > @@ -0,0 +1,836 @@ > > > > +/* > > > > + * QTest testcase for Aspeed I2C controller > > > > + * > > > > + * Copyright (c) 2026 Timur Maksimov <gessingate@gmail.com> > > > > + * > > > > + * SPDX-License-Identifier: GPL-2.0-or-later */ > > > > + > > > > +#include "qemu/osdep.h" > > > > +#include "libqtest-single.h" > > > > +#include "hw/i2c/aspeed_i2c.h" > > > > + > > > > +typedef enum TestAspeedI2CMode { > > > > + TEST_I2C_MODE_OLD, > > > > + TEST_I2C_MODE_NEW, > > > > + TEST_I2C_MODE_PKT, > > > > +} TestAspeedI2CMode; > > > > + > > > > +typedef enum TestAspeedMachine { > > > > + TEST_MACHINE_AST2400, > > > > + TEST_MACHINE_AST2500, > > > > + TEST_MACHINE_AST2600, > > > > > > > > > Do you have plans to add AST2700 support ? This is the current > > > focus > > > now days. > > > > > > Thanks, > > > > > > C. > > > > > > > > > > > > > +} TestAspeedMachine; > > > > + > > > > +typedef struct TestData { > > > > + const char *name; > > > > + TestAspeedMachine machine; > > > > + TestAspeedI2CMode mode; > > > > +} TestData; > > > > + > > > > +typedef struct TestFixture { > > > > + hwaddr base_addr; > > > > + hwaddr bus_offset; > > > > + int irq_no; > > > > + uint32_t pool_base; > > > > +} TestFixture; > > > > + > > > > +static const uint8_t tx_data[] = {0x10, 0x11, 0x12}; static > > > > const > > > > +uint8_t rx_data[] = {0x30, 0x31, 0x32}; > > > > + > > > > +#define DRAM_BASE 0x80000000 > > > > +#define DRAM_TX_OFFSET 0x1000 > > > > +#define DRAM_RX_OFFSET 0x2000 > > > > + > > > > +static void aspeed_i2c_write(TestFixture *f, uint32_t offset, > > > > uint32_t val) > > > > +{ > > > > + qtest_writel(global_qtest, f->base_addr + f->bus_offset + > > > > offset, val); > > > > +} > > > > + > > > > +static void aspeed_i2c_write_pool(TestFixture *f, uint32_t > > > > offset, > > > > uint8_t val) > > > > +{ > > > > + qtest_writeb(global_qtest, f->base_addr + f->pool_base + > > > > offset, val); > > > > +} > > > > + > > > > +static void aspeed_i2c_write_dma(TestFixture *f, uint32_t > > > > offset, > > > > uint8_t val) > > > > +{ > > > > + qtest_writeb(global_qtest, DRAM_BASE + offset, val); } > > > > + > > > > +static uint32_t aspeed_i2c_read(TestFixture *f, uint32_t > > > > offset) { > > > > + return qtest_readl(global_qtest, f->base_addr + f- > > > > >bus_offset > > > > +offset); } > > > > + > > > > +static uint8_t aspeed_i2c_read_pool(TestFixture *f, uint32_t > > > > offset) > > > > +{ > > > > + return qtest_readb(global_qtest, f->base_addr + f- > > > > >pool_base + > > > > offset); > > > > +} > > > > + > > > > +static uint8_t aspeed_i2c_read_dma(TestFixture *f, uint32_t > > > > offset) > > > > +{ > > > > + return qtest_readb(global_qtest, DRAM_BASE + offset); } > > > > + > > > > +static bool aspeed_i2c_irq(TestFixture *f) { > > > > + return qtest_get_irq(global_qtest, f->irq_no); } > > > > + > > > > +static uint32_t aspeed_i2c_ctrl_offset(const TestData *td) { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + return A_I2CD_FUN_CTRL; > > > > + } else { > > > > + return A_I2CC_FUN_CTRL; > > > > + } > > > > +} > > > > + > > > > +static uint32_t aspeed_i2c_cmd_offset(const TestData *td) { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + return A_I2CD_CMD; > > > > + } else { > > > > + return A_I2CM_CMD; > > > > + } > > > > +} > > > > + > > > > + > > > > +static uint32_t aspeed_i2c_intr_ctrl_offset(const TestData > > > > *td) { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + return A_I2CD_INTR_CTRL; > > > > + } else { > > > > + return A_I2CM_INTR_CTRL; > > > > + } > > > > +} > > > > + > > > > +static uint32_t aspeed_i2c_intr_sts_offset(const TestData *td) > > > > { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + return A_I2CD_INTR_STS; > > > > + } else { > > > > + return A_I2CM_INTR_STS; > > > > + } > > > > +} > > > > + > > > > +static uint32_t aspeed_i2c_pool_ctrl_offset(const TestData > > > > *td) { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + return A_I2CD_POOL_CTRL; > > > > + } else { > > > > + return A_I2CC_POOL_CTRL; > > > > + } > > > > +} > > > > + > > > > +static uint32_t aspeed_i2c_byte_buf_offset(const TestData *td) > > > > { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + return A_I2CD_BYTE_BUF; > > > > + } else { > > > > + return A_I2CC_MS_TXRX_BYTE_BUF; > > > > + } > > > > +} > > > > + > > > > +static void aspeed_i2c_write_slave_addr(TestFixture *f, const > > > > TestData *td, > > > > + uint8_t addr) > > { > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + aspeed_i2c_write(f, A_I2CD_DEV_ADDR, addr); > > > > + } else { > > > > + aspeed_i2c_write(f, A_I2CS_DEV_ADDR, addr); > > > > + } > > > > +} > > > > + > > > > +static void aspeed_i2c_write_dma_addr(TestFixture *f, const > > > > TestData *td, > > > > + uint32_t val, bool > > rx) { > > > > + uint32_t offset; > > > > + > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + offset = A_I2CD_DMA_ADDR; > > > > + } else { > > > > + offset = rx ? A_I2CM_DMA_RX_ADDR : > > A_I2CM_DMA_TX_ADDR; > > > > + } > > > > + aspeed_i2c_write(f, offset, val); } > > > > + > > > > +static void aspeed_i2c_write_dma_len(TestFixture *f, const > > > > TestData *td, > > > > + uint16_t val, bool > > rx) { > > > > + uint32_t reg; > > > > + > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + aspeed_i2c_write(f, A_I2CD_DMA_LEN, val + 1); > > > > + return; > > > > + } > > > > + > > > > + if (rx) { > > > > + reg = FIELD_DP32(0, I2CM_DMA_LEN, RX_BUF_LEN_W1T, 1); > > > > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, RX_BUF_LEN, val); > > > > + } else { > > > > + reg = FIELD_DP32(0, I2CM_DMA_LEN, TX_BUF_LEN_W1T, 1); > > > > + reg = FIELD_DP32(reg, I2CM_DMA_LEN, TX_BUF_LEN, val); > > > > + } > > > > + aspeed_i2c_write(f, A_I2CM_DMA_LEN, reg); } > > > > + > > > > +static uint8_t aspeed_i2c_get_txstate(TestFixture *f, const > > > > TestData *td) > > > > +{ > > > > + uint32_t offset; > > > > + > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + offset = A_I2CD_CMD; > > > > + } else { > > > > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > > > > + } > > > > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), > > > > TX_STATE); > > > > +} > > > > + > > > > +static bool aspeed_i2c_get_busy_sts(TestFixture *f, const > > > > TestData > > > > *td) > > > > +{ > > > > + uint32_t offset; > > > > + > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + offset = A_I2CD_CMD; > > > > + } else { > > > > + offset = A_I2CC_MS_TXRX_BYTE_BUF; > > > > + } > > > > + return SHARED_FIELD_EX32(aspeed_i2c_read(f, offset), > > > > BUS_BUSY_STS); > > > > +} > > > > + > > > > +/* Write a data byte in a byte mode transfer */ static void > > > > +aspeed_i2c_write_byte(TestFixture *f, const TestData *td, > > > > uint8_t val) > > > > +{ > > > > + aspeed_i2c_write(f, aspeed_i2c_byte_buf_offset(td), > > > > + SHARED_FIELD_DP32(0, TX_BUF, val)); } > > > > + > > > > +/* Read a data byte in a byte mode transfer */ static uint8_t > > > > +aspeed_i2c_read_byte(TestFixture *f, const TestData > > > > *td) > > > > +{ > > > > + uint32_t reg; > > > > + > > > > + reg = aspeed_i2c_read(f, aspeed_i2c_byte_buf_offset(td)); > > > > + return SHARED_FIELD_EX32(reg, RX_BUF); } > > > > + > > > > +static void aspeed_i2c_send_start(TestFixture *f, const > > > > TestData > > > > *td, > > > > + uint32_t cmd) { > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); } > > > > + > > > > +static uint8_t qom_i2c_dummy_get_uint8(const char *prop) { > > > > + QDict *r; > > > > + uint8_t u; > > > > + > > > > + r = qtest_qmp(global_qtest, > > > > + "{ 'execute': 'qom-get', 'arguments': " > > > > + "{ 'path': 'i2c-dummy', 'property': %s } }", > > > > + prop); > > > > + u = qdict_get_uint(r, "return"); > > > > + qobject_unref(r); > > > > + return u; > > > > +} > > > > + > > > > +static void qom_i2c_dummy_set_uint8(const char *prop, uint8_t > > > > data) > > > > +{ > > > > + QDict *r; > > > > + > > > > + r = qtest_qmp(global_qtest, > > > > + "{ 'execute': 'qom-set', 'arguments': " > > > > + "{ 'path': 'i2c-dummy', 'property': %s, > > > > 'value': > > > > %d } }", > > > > + prop, data); > > > > + qobject_unref(r); > > > > +} > > > > + > > > > +static void qom_i2c_dummy_set_rx(uint8_t idx, uint8_t data) { > > > > + g_autofree char *prop = g_strdup_printf("rx[%d]", idx); > > > > + > > > > + qom_i2c_dummy_set_uint8(prop, data); } > > > > + > > > > +static uint8_t qom_i2c_dummy_get_tx(int8_t idx) { > > > > + g_autofree char *prop = g_strdup_printf("tx[%d]", idx); > > > > + > > > > + return qom_i2c_dummy_get_uint8(prop); } > > > > + > > > > +#define I2C_ADDR_NACK 0x10 > > > > +#define I2C_ADDR_DUMMY 0x20 > > > > +#define I2C_ADDR_ECHO 0x30 > > > > +#define I2C_ADDR_SLAVE 0x40 > > > > + > > Could these be static helper functions instead of multi-line macros? > They do not need anything a function cannot do, and functions would > be > easier to read and type-checked. I understand the macros make > g_assert > report the caller's line, but the qtest path and the tap output > already > tell us which test failed. > Also, CHECK_I2C_ACK() is not used anywhere, so it can be dropped. > > > > > +#define CHECK_I2C_IDLE(f, td) do > > > > { \ > > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > > I2CD_IDLE); \ > > > > + g_assert_false(aspeed_i2c_get_busy_sts(f, > > > > td)); \ > > > > + g_assert_cmphex(aspeed_i2c_read(f, > > > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > > > + > > > > > > 0); > > \ > > > > + > > > > g_assert_false(aspeed_i2c_irq(f)); > > > > \ > > > > +} while (0) > > > > + > > > > +#define CHECK_I2C_INTR(f, td, reg) do > > > > { \ > > > > + > > > > g_assert_true(aspeed_i2c_irq(f)); > > > > \ > > > > + g_assert_cmphex(aspeed_i2c_read(f, > > > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > > > + > > > > > > reg); > > \ > > > > +} while (0) > > > > + > > > > +#define CHECK_I2C_BUS_ACTIVE(f, td) do > > > > { \ > > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > > I2CD_MACTIVE); \ > > > > + g_assert_true(aspeed_i2c_get_busy_sts(f, > > > > td)); \ > > > > +} while (0) > > > > + > > > > +#define CHECK_I2C_ACK(f, td) do > > > > { \ > > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > > I2CD_MACTIVE); \ > > > > + g_assert_true(aspeed_i2c_get_busy_sts(f, > > > > td)); \ > > > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, TX_ACK, > > > > 1)); \ > > > > +} while (0) > > > > + > > > > +#define CLEAR_I2C_INTR(f, td, reg) do > > > > { \ > > > > + CHECK_I2C_INTR(f, td, > > > > reg); \ > > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), > > > > reg); \ > > > > + g_assert_cmphex(aspeed_i2c_read(f, > > > > aspeed_i2c_intr_sts_offset(td)), ==, \ > > > > + > > > > > > 0); > > \ > > > > +} while (0) > > > > + > > > > +#define SEND_I2C_STOP(f, td) do > > > > { \ > > > > + aspeed_i2c_write(f, > > > > aspeed_i2c_cmd_offset(td), \ > > > > + SHARED_FIELD_DP32(0, M_STOP_CMD, > > > > 1)); \ > > > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, > > > > 1)); \ > > > > + CHECK_I2C_IDLE(f, > > > > > > td); > > \ > > > > +} while (0) > > > > + > > > > +#define CHECK_I2C_DUMMY_DEVICE() do > > > > { \ > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) > > > > { \ > > > > + g_assert_cmphex(qom_i2c_dummy_get_tx(i), ==, > > > > tx_data[i]); \ > > > > + > > > > } > > > > \ > > > > + g_assert_cmphex(qom_i2c_dummy_get_uint8("tx-pos"), > > > > ==, \ > > > > + > > > > > > ARRAY_SIZE(tx_data)); \ > > > > + g_assert_cmphex(qom_i2c_dummy_get_uint8("rx-pos"), > > > > ==, \ > > > > + > > > > > > ARRAY_SIZE(rx_data)); \ > > > > +} while (0) > > > > + > > > > +static void aspeed_i2c_init(TestFixture *f, const TestData > > > > *td) { > > > > + uint32_t reg; > > > > + > > > > + qtest_qmp(global_qtest, "{ 'execute': 'system_reset' }"); > > > > + > > > > + aspeed_i2c_write_slave_addr(f, td, I2C_ADDR_SLAVE); > > > > + > > > The slave address is written before the register > mode is switched: > > For the new register mode tests, aspeed_i2c_write_slave_addr() writes > I2CS_DEV_ADDR (offset 0x40), but the controller is still in old > register > mode at that point, so the write is dropped as a bad offset. The > slave > address is never programmed, and the slave test only passes because > an > earlier test left the controller in new register mode. > > Running the slave test on its own fails every time: > > QTEST_QEMU_BINARY=./qemu-system-arm ./tests/qtest/aspeed_i2c-test \ > -p /arm/ast2600/nack -p /arm/ast2600-new/slave > > ERROR: test_slave: assertion failed > (aspeed_i2c_read(f, A_I2CS_DMA_LEN_STS) == tmp): (0x00000000 == > 0x00020000) > > (/arm/ast2600/nack is only there to start the machine.) > > Thanks, > Jamin > > > > > + reg = FIELD_DP32(0, I2C_CTRL_GLOBAL, SRAM_EN, 1); > > > > + if (td->mode != TEST_I2C_MODE_OLD) { > > > > + reg = FIELD_DP32(reg, I2C_CTRL_GLOBAL, REG_MODE, 1); > > > > + } > > > > + qtest_writel(global_qtest, f->base_addr + > > > > A_I2C_CTRL_GLOBAL, > > > > reg); > > > > + > > > > + reg = SHARED_FIELD_DP32(0, MASTER_EN, 1); > > > > + reg = SHARED_FIELD_DP32(reg, SLAVE_EN, 1); > > > > + aspeed_i2c_write(f, aspeed_i2c_ctrl_offset(td), reg); > > > > + > > > > + /* Enable all interrupts */ > > > > + reg = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > > + reg = SHARED_FIELD_DP32(reg, TX_NAK, 1); > > > > + reg = SHARED_FIELD_DP32(reg, RX_DONE, 1); > > > > + reg = SHARED_FIELD_DP32(reg, ARBIT_LOSS, 1); > > > > + reg = SHARED_FIELD_DP32(reg, NORMAL_STOP, 1); > > > > + reg = SHARED_FIELD_DP32(reg, ABNORMAL, 1); > > > > + if (td->mode != TEST_I2C_MODE_OLD) { > > > > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + reg = FIELD_DP32(reg, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_intr_ctrl_offset(td), reg); > > > > + > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > > + qom_i2c_dummy_set_rx(i, rx_data[i]); > > > > + } > > > > +} > > > > + > > > > +static uint32_t aspeed_i2c_pkt_set_addr_cmd(const TestData > > > > *td, > > > > uint8_t addr) > > > > +{ > > > > + uint32_t cmd; > > > > + > > > > + cmd = FIELD_DP32(0, I2CM_CMD, PKT_OP_EN, 1); > > > > + cmd = FIELD_DP32(cmd, I2CM_CMD, PKT_DEV_ADDR, addr); > > > > + return cmd; > > > > +} > > > > + > > > > +static > > > > +uint32_t aspeed_i2c_byte_set_addr_cmd(TestFixture *f, const > > > > TestData *td, > > > > + uint8_t addr, > > bool is_recv) { > > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > > > + } else { > > > > + aspeed_i2c_write_byte(f, td, addr << 1 | (is_recv ? 1 > > > > : > > > > 0)); > > > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > > > + } > > > > +} > > > > + > > > > +static > > > > +uint32_t aspeed_i2c_buf_set_addr_cmd(TestFixture *f, const > > > > TestData *td, > > > > + uint8_t addr, > > bool is_recv) { > > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > > > + } else { > > > > + aspeed_i2c_write_pool(f, 0, addr << 1 | (is_recv ? 1 : > > > > 0)); > > > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > > > + } > > > > +} > > > > + > > > > +static > > > > +uint32_t aspeed_i2c_dma_set_addr_cmd(TestFixture *f, const > > > > TestData *td, > > > > + uint8_t addr, > > bool is_recv) { > > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > > + return aspeed_i2c_pkt_set_addr_cmd(td, addr); > > > > + } else { > > > > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET, addr << 1 | > > > > (is_recv ? 1 : 0)); > > > > + return SHARED_FIELD_DP32(0, M_TX_CMD, 1); > > > > + } > > > > +} > > > > + > > > > + > > > > +static uint32_t aspeed_i2c_send_addr_status(TestFixture *f, > > > > const > > > > TestData *td) > > > > +{ > > > > + uint32_t sts; > > > > + > > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > > + sts = FIELD_DP32(0, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } else { > > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > > + } > > > > + return sts; > > > > +} > > > > + > > > > +/* > > > > + * Send an address byte to a non existing device. > > > > + * The TX_NAK interrupt should be raised. > > > > + */ > > > > +static void test_nack(TestFixture *f, gconstpointer p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + uint32_t sts, cmd; > > > > + > > > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_NACK, > > > > false); > > > > + aspeed_i2c_send_start(f, td, cmd); > > > > + > > > > + g_assert_cmphex(aspeed_i2c_get_txstate(f, td), ==, > > > > I2CD_MSTART); > > > > + g_assert_false(aspeed_i2c_get_busy_sts(f, td)); > > > > + sts = SHARED_FIELD_DP32(0, TX_NAK, 1); > > > > + if (td->mode == TEST_I2C_MODE_PKT) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_FAIL, 1); > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > > > + SHARED_FIELD_DP32(0, M_STOP_CMD, > > 1)); > > > > + CLEAR_I2C_INTR(f, td, SHARED_FIELD_DP32(0, NORMAL_STOP, > > > > 1)); > > > > + CHECK_I2C_IDLE(f, td); > > > > + qtest_hmp(global_qtest, "system_reset"); } > > > > + > > > > + > > > > +static void test_dummy_byte(TestFixture *f, gconstpointer > > > > p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + uint32_t cmd, sts; > > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > > + > > > > + /* Write device address (Write) */ > > > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > > false); > > > > + aspeed_i2c_send_start(f, td, cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, td)); > > > > + > > > > + /* Write data */ > > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > > I2C_ADDR_DUMMY); > > > > + } else { > > > > + cmd = 0; > > > > + } > > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > > > + aspeed_i2c_write_byte(f, td, tx_data[i]); > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + } > > > > + > > > > + /* Read data */ > > > > + cmd = aspeed_i2c_byte_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > > true); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + sts = aspeed_i2c_send_addr_status(f, td); > > > > + > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > > + if (i == ARRAY_SIZE(rx_data) - 1) { > > > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, > > 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, > > PKT_CMD_DONE, 1); > > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > > I2C_ADDR_DUMMY); > > > > + } else { > > > > + cmd = 0; > > > > + } > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > > rx_data[i]); > > > > + sts = 0; > > > > + } > > > > + > > > > + SEND_I2C_STOP(f, td); > > > > + CHECK_I2C_DUMMY_DEVICE(); > > > > + qtest_hmp(global_qtest, "system_reset"); } > > > > + > > > > +static void test_dummy_buf(TestFixture *f, gconstpointer > > > > p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + uint32_t cmd, sts, pool_ctrl; > > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > > + > > > > + /* Write data with device address */ > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > > > + aspeed_i2c_write_pool(f, i + (pkt_mode ? 0 : 1), > > > > tx_data[i]); > > > > + } > > > > + pool_ctrl = SHARED_FIELD_DP32(0, TX_COUNT, > > > > + ARRAY_SIZE(tx_data) > > - (pkt_mode > > > > ? 1 : 0)); > > > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), > > > > pool_ctrl); > > > > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > > false); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + > > > > + /* Write device address (Read) in non packet mde */ > > > > + if (pkt_mode) { > > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > > I2C_ADDR_DUMMY); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + } else { > > > > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | > > > > 1); > > > > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, > > > > M_TX_CMD, 1)); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, > > > > td)); > > > > + cmd = 0; > > > > + } > > > > + > > > > + /* Read data */ > > > > + pool_ctrl = SHARED_FIELD_DP32(0, RX_SIZE, > > > > + ARRAY_SIZE(rx_data) > > - 1); > > > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), > > > > pool_ctrl); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, RX_BUFF_EN, 1); > > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > > + g_assert_cmphex(aspeed_i2c_read_pool(f, i), ==, > > > > rx_data[i]); > > > > + } > > > > + > > > > + SEND_I2C_STOP(f, td); > > > > + CHECK_I2C_DUMMY_DEVICE(); > > > > + qtest_hmp(global_qtest, "system_reset"); } > > > > + > > > > +static void test_dummy_dma(TestFixture *f, gconstpointer > > > > p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + uint32_t cmd, sts; > > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > > + > > > > + /* Write data with device address*/ > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(tx_data); i++) { > > > > + aspeed_i2c_write_dma(f, DRAM_TX_OFFSET + (pkt_mode ? > > 0 : > > > > 1) + i, > > > > + tx_data[i]); > > > > + } > > > > + aspeed_i2c_write_dma_addr(f, td, DRAM_TX_OFFSET, false); > > > > + aspeed_i2c_write_dma_len(f, td, > > > > + ARRAY_SIZE(tx_data) - > > (pkt_mode ? 1 : > > > > 0), false); > > > > + cmd = aspeed_i2c_dma_set_addr_cmd(f, td, I2C_ADDR_DUMMY, > > > > false); > > > > + > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, TX_DMA_EN, 1); > > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + > > > > + /* Write device address (Read) in non packet mode */ > > > > + if (pkt_mode) { > > > > + cmd = aspeed_i2c_pkt_set_addr_cmd(td, > > I2C_ADDR_DUMMY); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + } else { > > > > + aspeed_i2c_write_byte(f, td, (I2C_ADDR_DUMMY << 1) | > > > > 1); > > > > + aspeed_i2c_send_start(f, td, SHARED_FIELD_DP32(0, > > > > M_TX_CMD, 1)); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, aspeed_i2c_send_addr_status(f, > > > > td)); > > > > + cmd = 0; > > > > + } > > > > + > > > > + /* Read data */ > > > > + aspeed_i2c_write_dma_addr(f, td, DRAM_RX_OFFSET, true); > > > > + aspeed_i2c_write_dma_len(f, td, ARRAY_SIZE(rx_data) - 1, > > > > true); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_RX_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_S_RX_CMD_LAST, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, RX_DMA_EN, 1); > > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + for (uint8_t i = 0; i < ARRAY_SIZE(rx_data); i++) { > > > > + g_assert_cmphex(aspeed_i2c_read_dma(f, > > DRAM_RX_OFFSET + > > > > i), ==, > > > > + rx_data[i]); > > > > + } > > > > + > > > > + SEND_I2C_STOP(f, td); > > > > + CHECK_I2C_DUMMY_DEVICE(); > > > > + qtest_hmp(global_qtest, "system_reset"); } > > > > + > > > > +static void test_slave(TestFixture *f, gconstpointer p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + uint32_t cmd, sts, tmp; > > > > + bool pkt_mode = td->mode == TEST_I2C_MODE_PKT; > > > > + uint8_t offset = pkt_mode ? 0 : 1; > > > > + > > > > + aspeed_i2c_write_pool(f, offset + 0, I2C_ADDR_SLAVE); > > > > + aspeed_i2c_write_pool(f, offset + 1, tx_data[0]); > > > > + aspeed_i2c_write_pool(f, offset + 2, tx_data[1]); > > > > + > > > > + tmp = SHARED_FIELD_DP32(0, TX_COUNT, pkt_mode ? 2 : 3); > > > > + aspeed_i2c_write(f, aspeed_i2c_pool_ctrl_offset(td), tmp); > > > > + > > > > + cmd = aspeed_i2c_buf_set_addr_cmd(f, td, I2C_ADDR_ECHO, > > > > false); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_START_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, M_TX_CMD, 1); > > > > + cmd = SHARED_FIELD_DP32(cmd, TX_BUFF_EN, 1); > > > > + sts = SHARED_FIELD_DP32(0, TX_ACK, 1); > > > > + if (pkt_mode) { > > > > + sts = FIELD_DP32(sts, I2CM_INTR_STS, PKT_CMD_DONE, 1); > > > > + } > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), cmd); > > > > + CHECK_I2C_BUS_ACTIVE(f, td); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + > > > > + /* Send stop and handle i2c-echo response as a slave */ > > > > + if (td->mode == TEST_I2C_MODE_OLD) { > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > > > + SHARED_FIELD_DP32(0, > > M_STOP_CMD, 1)); > > > > + clock_step(1); > > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > > I2C_ADDR_SLAVE << 1); > > > > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > > > > + sts = SHARED_FIELD_DP32(sts, RX_DONE, 1); > > > > + sts = FIELD_DP32(sts, I2CD_INTR_STS, > > SLAVE_ADDR_RX_MATCH, > > > > 1); > > > > + CHECK_I2C_INTR(f, td, sts); > > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), > > > > sts); > > > > + > > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > > tx_data[0]); > > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > > + CHECK_I2C_INTR(f, td, sts); > > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), > > > > sts); > > > > + > > > > + g_assert_cmphex(aspeed_i2c_read_byte(f, td), ==, > > > > tx_data[1]); > > > > + sts = SHARED_FIELD_DP32(0, RX_DONE, 1); > > > > + CHECK_I2C_INTR(f, td, sts); > > > > + aspeed_i2c_write(f, aspeed_i2c_intr_sts_offset(td), > > > > sts); > > > > + } else { > > > > + aspeed_i2c_write(f, A_I2CS_DMA_RX_ADDR, > > DRAM_RX_OFFSET); > > > > + tmp = SHARED_FIELD_DP32(0, RX_DMA_EN, 1); > > > > + aspeed_i2c_write(f, A_I2CS_CMD, tmp); > > > > + > > > > + aspeed_i2c_write(f, aspeed_i2c_cmd_offset(td), > > > > + SHARED_FIELD_DP32(0, > > M_STOP_CMD, 1)); > > > > + clock_step(1); > > > > + > > > > + tmp = FIELD_DP32(0, I2CS_DMA_LEN_STS, RX_LEN, 2); > > > > + g_assert_cmphex(aspeed_i2c_read(f, > > A_I2CS_DMA_LEN_STS), > > > > ==, tmp); > > > > + for (uint8_t i = 0; i < 2; i++) { > > > > + g_assert_cmphex(aspeed_i2c_read_dma(f, > > DRAM_RX_OFFSET > > > > +i), ==, > > > > + tx_data[i]); > > > > + } > > > > + } > > > > + > > > > + sts = SHARED_FIELD_DP32(0, NORMAL_STOP, 1); > > > > + CLEAR_I2C_INTR(f, td, sts); > > > > + CHECK_I2C_IDLE(f, td); > > > > + > > > > + qtest_hmp(global_qtest, "system_reset"); } > > > > + > > > > +static void aspeed_i2c_init_fixture(TestFixture *fixture, > > > > const > > > > TestData *td) > > > > +{ > > > > + switch (td->machine) { > > > > + case TEST_MACHINE_AST2400: > > > > + fixture->base_addr = 0x1E78A000; > > > > + fixture->bus_offset = 0x40; > > > > + fixture->irq_no = 12; > > > > + fixture->pool_base = 0x800; > > > > + break; > > > > + case TEST_MACHINE_AST2500: > > > > + fixture->base_addr = 0x1E78A000; > > > > + fixture->bus_offset = 0x40; > > > > + fixture->irq_no = 12; > > > > + fixture->pool_base = 0x200; > > > > + break; > > > > + case TEST_MACHINE_AST2600: > > > > + fixture->base_addr = 0x1E78A000; > > > > + fixture->bus_offset = 0x80; > > > > + fixture->irq_no = 110; > > > > + fixture->pool_base = 0xC00; > > > > + break; > > > > + default: > > > > + return; > > > > + } > > > > +} > > > > + > > > > +static void aspeed_i2c_init_test(TestFixture *fixture, const > > > > TestData *td) > > > > +{ > > > > + aspeed_i2c_init(fixture, td); > > > > + CHECK_I2C_IDLE(fixture, td); > > > > +} > > > > + > > > > +static void aspeed_i2c_init_new_machine(TestFixture *fixture, > > > > > > + gconstpointer > > p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + const char *machine; > > > > + const char *irq_dev; > > > > + > > > > + aspeed_i2c_init_fixture(fixture, td); > > > > + > > > > + if (global_qtest) { > > > > + qtest_quit(global_qtest); > > > > + } > > > > + > > > > + switch (td->machine) { > > > > + case TEST_MACHINE_AST2400: > > > > + machine = "palmetto-bmc"; > > > > + irq_dev = "/machine/soc/vic"; > > > > + break; > > > > + case TEST_MACHINE_AST2500: > > > > + machine = "ast2500-evb"; > > > > + irq_dev = "/machine/soc/vic"; > > > > + break; > > > > + case TEST_MACHINE_AST2600: > > > > + machine = "ast2600-evb"; > > > > + irq_dev = "/machine/soc/a7mpcore/gic"; > > > > + break; > > > > + default: > > > > + g_test_fail(); > > > > + return; > > > > + } > > > > + > > > > + global_qtest = qtest_initf( > > > > + "-machine %s -nographic " > > > > + "-device i2c-echo,id=i2c- > > > > echo,bus=aspeed.i2c.bus.0,address=%d " > > > > + "-device i2c-dummy-device,id=i2c- > > > > dummy,bus=aspeed.i2c.bus.0,address=%d", > > > > + machine, I2C_ADDR_ECHO, I2C_ADDR_DUMMY); > > > > + qtest_irq_intercept_in(global_qtest, irq_dev); > > > > + > > > > + aspeed_i2c_init_test(fixture, td); } > > > > + > > > > +static void aspeed_i2c_init_machine(TestFixture *fixture, > > > > + gconstpointer > > p_data) { > > > > + const TestData *td = (const TestData *)p_data; > > > > + > > > > + aspeed_i2c_init_fixture(fixture, td); > > > > + aspeed_i2c_init_test(fixture, td); } > > > > + > > > > +static void add_test(const TestData *td, const char *name, > > > > + void (*func)(TestFixture *, const void > > > > *), > > > > + bool setup) > > > > +{ > > > > + g_autofree char *test_name; > > > > + > > > > + test_name = g_strdup_printf("%s/%s", td->name, name); > > > > + > > > > + qtest_add(test_name, TestFixture, td, > > > > + setup ? aspeed_i2c_init_new_machine : > > > > aspeed_i2c_init_machine, > > > > + func, NULL); > > > > +} > > > > + > > > > +#define ADD_TEST(td, test, new) add_test(td, #test, > > > > test_##test, > > > > new) > > > > + > > > > +static const TestData test_data[] = { > > > > + { > > > > + .name = "ast2400", > > > > + .machine = TEST_MACHINE_AST2400, > > > > + }, > > > > + { > > > > + .name = "ast2500", > > > > + .machine = TEST_MACHINE_AST2500, > > > > + }, > > > > + { > > > > + .name = "ast2600", > > > > + .machine = TEST_MACHINE_AST2600, > > > > + }, > > > > + { > > > > + .name = "ast2600-new", > > > > + .machine = TEST_MACHINE_AST2600, > > > > + .mode = TEST_I2C_MODE_NEW, > > > > + }, > > > > + { > > > > + .name = "ast2600-packet", > > > > + .machine = TEST_MACHINE_AST2600, > > > > + .mode = TEST_I2C_MODE_PKT, > > > > + }, > > > > +}; > > > > + > > > > +int main(int argc, char **argv) > > > > +{ > > > > + int ret; > > > > + bool setup; > > > > + > > > > + g_test_init(&argc, &argv, NULL); > > > > + > > > > + for (int i = 0; i < ARRAY_SIZE(test_data); i++) { > > > > + setup = i == 0 || test_data[i].machine != test_data[i > > > > - > > > > 1].machine; > > > > + ADD_TEST(&test_data[i], nack, setup); > > > > + ADD_TEST(&test_data[i], dummy_byte, false); > > > > + ADD_TEST(&test_data[i], dummy_buf, false); > > > > + if (test_data[i].machine != TEST_MACHINE_AST2400) { > > > > + ADD_TEST(&test_data[i], dummy_dma, false); > > > > + } > > > > + ADD_TEST(&test_data[i], slave, false); > > > > + } > > > > + > > > > + ret = g_test_run(); > > > > + qtest_quit(global_qtest); > > > > + return ret; > > > > +} > > > > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > > > > index > > > > 46d4fc1d2b..c496b7c3a7 100644 > > > > --- a/tests/qtest/meson.build > > > > +++ b/tests/qtest/meson.build > > > > @@ -233,12 +233,14 @@ qtests_aspeed = \ > > > > ['aspeed-acry-test', > > > > 'aspeed_gpio-test', > > > > 'aspeed_hace-test', > > > > + 'aspeed_i2c-test', > > > > 'aspeed-sbc-test', > > > > 'aspeed_scu-test', > > > > 'aspeed_smc-test'] > > > > qtests_aspeed64 = \ > > > > ['ast2700-gpio-test', > > > > 'ast2700-hace-test', > > > > + 'aspeed_i2c-test', > > > > 'ast2700-sgpio-test', > > > > 'ast2700-smc-test'] > > > > > > > > ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov ` (5 preceding siblings ...) 2026-10-27 19:50 ` [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers Timur Maksimov @ 2026-10-28 11:07 ` Cédric Le Goater 6 siblings, 0 replies; 33+ messages in thread From: Cédric Le Goater @ 2026-10-28 11:07 UTC (permalink / raw) To: Timur Maksimov, qemu-devel Cc: Paolo Bonzini, Peter Maydell, Jamin Lin, Kane Chen, qemu-arm, Fabiano Rosas, Laurent Vivier Jamin, Could you please check the Fix changes, patch 2-5 ? They appear to be valid additions but I would like a second opinion. Thanks, C. On 10/27/26 20:50, Timur Maksimov wrote: > In non-packet mode, buffer and DMA modes are broken > in the Aspeed I2C controller, > probably because no available driver currently uses them. > This patch fixes them, so byte, buffer, and DMA transfer modes > now work correctly for both register modes, old and new ones. > Tests are provided to prevent possible regressions. > > Signed-off-by: Timur Maksimov <gessingate@gmail.com> > --- > Changes in v2: > - Fix commits description format. Some of them are reordered and clarified. > - Link to v1: https://lore.kernel.org/qemu-devel/20261027-aspeed-i2c-nonpkt-fix-v1-0-076cdde514f8@gmail.com > > --- > Timur Maksimov (6): > hw/i2c: add dummy device > hw/i2c/aspeed: fix BUSY status reading in new register mode > hw/i2c/aspeed: fix I2CD_CMD register reading > hw/i2c/aspeed: fix buffer transfer in non-packet mode > hw/i2c/aspeed: fix DMA transfer in non-packet mode > tests/qtest: Add cases for 32-bit Aspeed I2C controllers > > hw/i2c/Kconfig | 5 + > hw/i2c/aspeed_i2c.c | 23 +- > hw/i2c/i2c-dummy-device.c | 168 +++++++++ > hw/i2c/meson.build | 3 +- > tests/qtest/aspeed_i2c-test.c | 836 ++++++++++++++++++++++++++++++++++++++++++ > tests/qtest/meson.build | 2 + > 6 files changed, 1029 insertions(+), 8 deletions(-) > --- > base-commit: 9b77fa7a4fe86c80d1ff526769c61ada7d28666a > change-id: 20261025-aspeed-i2c-nonpkt-fix-89b852b5c84a > ^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2026-10-29 15:57 UTC | newest] Thread overview: 33+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-27 19:50 [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes Timur Maksimov 2026-10-27 19:50 ` [PATCH v2 1/6] hw/i2c: add dummy device Timur Maksimov 2026-10-28 8:54 ` Cédric Le Goater 2026-10-28 10:17 ` Markus Armbruster 2026-10-28 11:04 ` Cédric Le Goater 2026-10-28 11:26 ` Markus Armbruster 2026-10-28 16:20 ` Timur Maksimov 2026-10-29 7:22 ` Markus Armbruster 2026-10-29 8:20 ` Timur Maksimov 2026-10-29 8:37 ` Cédric Le Goater 2026-10-29 12:45 ` Corey Minyard 2026-10-29 13:04 ` Cédric Le Goater 2026-10-29 15:56 ` Corey Minyard 2026-10-27 19:50 ` [PATCH v2 2/6] hw/i2c/aspeed: fix BUSY status reading in new register mode Timur Maksimov 2026-10-28 8:17 ` Cédric Le Goater 2026-10-28 21:24 ` Cédric Le Goater 2026-10-29 5:21 ` Jamin Lin 2026-10-27 19:50 ` [PATCH v2 3/6] hw/i2c/aspeed: fix I2CD_CMD register reading Timur Maksimov 2026-10-29 5:23 ` Jamin Lin 2026-10-27 19:50 ` [PATCH v2 4/6] hw/i2c/aspeed: fix buffer transfer in non-packet mode Timur Maksimov 2026-10-29 6:33 ` Jamin Lin 2026-10-29 7:23 ` Timur Maksimov 2026-10-29 7:44 ` Jamin Lin 2026-10-29 8:45 ` Timur Maksimov 2026-10-27 19:50 ` [PATCH v2 5/6] hw/i2c/aspeed: fix DMA " Timur Maksimov 2026-10-28 21:32 ` Cédric Le Goater 2026-10-29 6:23 ` Jamin Lin 2026-10-27 19:50 ` [PATCH v2 6/6] tests/qtest: Add cases for 32-bit Aspeed I2C controllers Timur Maksimov 2026-10-28 8:56 ` Cédric Le Goater 2026-10-28 14:47 ` Timur Maksimov 2026-10-29 7:25 ` Jamin Lin 2026-10-29 8:25 ` Timur Maksimov 2026-10-28 11:07 ` [PATCH v2 0/6] hw/i2c/aspeed: fix non-packet buffer and DMA transfer modes 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.