Test all supported register and transfer modes. Signed-off-by: Timur Maksimov [email protected] --- 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 <[email protected]> + * + * 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 46685d6d46..f4d328ee4e 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
