/* * Copyright (c) 2024 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ /* * @addtogroup t_i2c_basic * @{ * @defgroup t_i2c_read_write test_i2c_read_write * @brief TestPurpose: verify I2C master can read and write * @} */ #include #include #include #include #define RAM_ADDR (0b10100010 >> 1) #if DT_NODE_HAS_STATUS(DT_ALIAS(i2c_ram), okay) #define I2C_DEV_NODE DT_ALIAS(i2c_ram) #define TX_DATA_OFFSET 2 static uint8_t tx_data[9] = {0x00, 0x00, 'Z', 'e', 'p', 'h', 'y', 'r', '\n'}; static uint8_t rx_cmd[2] = {0x00, 0x00}; #else #error "Please set the correct I2C device and alias for i2c_ram to be status okay" #endif uint32_t i2c_cfg = I2C_SPEED_SET(I2C_SPEED_STANDARD) | I2C_MODE_CONTROLLER; struct i2c_msg msgs[2]; uint8_t rx_data[7]; const struct device *i2c_dev = DEVICE_DT_GET(I2C_DEV_NODE); /* Address from datasheet is 0b1010xxxr where x bits are additional * memory address bits and r is the r/w i2c bit. * * However... the address needs to be shifted into the lower 7 bits as * Zephyr expects a 7bit device address and shifts this left to set the * i2c r/w bit. */ static void *i2c_ram_setup(void) { int ret; uint32_t i2c_cfg_tmp; zassert_true(device_is_ready(i2c_dev), "I2C device is not read"); /* 1. Verify i2c_configure() */ zassert_ok(i2c_configure(i2c_dev, i2c_cfg), "I2C config failed"); /* 2. Verify i2c_get_config(), optional API */ ret = i2c_get_config(i2c_dev, &i2c_cfg_tmp); if (ret != -ENOSYS) { zassert_equal(i2c_cfg, i2c_cfg_tmp, "I2C get_config returned invalid config"); } return NULL; } static uint16_t addr; static void i2c_ram_before(void *f) { tx_data[0] = (addr >> 8) & 0xFF; tx_data[1] = (addr) & 0xFF; rx_cmd[0] = (addr >> 8) & 0xFF; rx_cmd[1] = (addr) & 0xFF; addr += ARRAY_SIZE(tx_data) - TX_DATA_OFFSET; memset(rx_data, 0, ARRAY_SIZE(rx_data)); } ZTEST(i2c_ram, test_ram_transfer) { TC_PRINT("ram using i2c_transfer from thread %p addr %x\n", k_current_get(), addr); msgs[0].buf = tx_data; msgs[0].len = ARRAY_SIZE(tx_data); msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP; zassert_ok(i2c_transfer(i2c_dev, msgs, 1, RAM_ADDR), "I2C write to fram failed"); /* Write the address and read the data back */ msgs[0].buf = rx_cmd; msgs[0].len = ARRAY_SIZE(rx_cmd); msgs[0].flags = I2C_MSG_WRITE; msgs[1].buf = rx_data; msgs[1].len = 7; msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP; zassert_ok(i2c_transfer(i2c_dev, msgs, 2, RAM_ADDR), "I2C read from fram failed"); zassert_equal(memcmp(&tx_data[TX_DATA_OFFSET], &rx_data[0], ARRAY_SIZE(rx_data)), 0, "Written and Read data should match"); } ZTEST(i2c_ram, test_ram_write_read) { TC_PRINT("ram using i2c_write and i2c_write_read from thread %p addr %x\n", k_current_get(), addr); zassert_ok(i2c_write(i2c_dev, tx_data, ARRAY_SIZE(tx_data), RAM_ADDR), "I2C write to fram failed"); zassert_ok(i2c_write_read(i2c_dev, RAM_ADDR, rx_cmd, ARRAY_SIZE(rx_cmd), rx_data, ARRAY_SIZE(rx_data)), "I2C read from fram failed"); zassert_equal(memcmp(&tx_data[TX_DATA_OFFSET], &rx_data[0], ARRAY_SIZE(rx_data)), 0, "Written and Read data should match"); } #ifdef CONFIG_I2C_CALLBACK K_SEM_DEFINE(transfer_sem, 0, 1); static void i2c_ram_transfer_cb(const struct device *dev, int result, void *data) { struct k_sem *s = data; k_sem_give(s); } ZTEST(i2c_ram, test_ram_transfer_cb) { msgs[0].buf = tx_data; msgs[0].len = ARRAY_SIZE(tx_data); msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP; zassert_ok(i2c_transfer_cb(i2c_dev, msgs, 1, RAM_ADDR, i2c_ram_transfer_cb, &transfer_sem), "I2C write to fram failed"); k_sem_take(&transfer_sem, K_FOREVER); /* Write the address and read the data back */ msgs[0].buf = rx_cmd; msgs[0].len = ARRAY_SIZE(rx_cmd); msgs[0].flags = I2C_MSG_WRITE; msgs[1].buf = rx_data; msgs[1].len = ARRAY_SIZE(rx_data); msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP; zassert_ok(i2c_transfer_cb(i2c_dev, msgs, 2, RAM_ADDR, i2c_ram_transfer_cb, &transfer_sem), "I2C read from fram failed"); k_sem_take(&transfer_sem, K_FOREVER); zassert_equal(memcmp(&tx_data[TX_DATA_OFFSET], &rx_data[0], ARRAY_SIZE(rx_data)), 0, "Written and Read data should match"); } #endif /* CONFIG_I2C_CALLBACK */ #ifdef CONFIG_I2C_RTIO #include I2C_IODEV_DEFINE(i2c_iodev, I2C_DEV_NODE, RAM_ADDR); RTIO_DEFINE(i2c_rtio, 2, 2); ZTEST(i2c_ram, test_ram_rtio) { struct rtio_sqe *wr_sqe, *rd_sqe; struct rtio_cqe *wr_cqe, *rd_cqe; TC_PRINT("submitting write from thread %p addr %x\n", k_current_get(), addr); wr_sqe = rtio_sqe_acquire(&i2c_rtio); wr_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP; rtio_sqe_prep_write(wr_sqe, &i2c_iodev, 0, tx_data, ARRAY_SIZE(tx_data), tx_data); zassert_ok(rtio_submit(&i2c_rtio, 1), "submit should succeed"); wr_cqe = rtio_cqe_consume(&i2c_rtio); zassert_ok(wr_cqe->result, "i2c write should succeed"); rtio_cqe_release(&i2c_rtio, wr_cqe); /* Write the address and read the data back */ msgs[0].len = ARRAY_SIZE(rx_cmd); msgs[0].flags = I2C_MSG_WRITE; msgs[1].buf = rx_data; msgs[1].len = ARRAY_SIZE(rx_data); msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP; wr_sqe = rtio_sqe_acquire(&i2c_rtio); wr_sqe->flags |= RTIO_SQE_TRANSACTION; rd_sqe = rtio_sqe_acquire(&i2c_rtio); rd_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; rtio_sqe_prep_write(wr_sqe, &i2c_iodev, 0, rx_cmd, ARRAY_SIZE(rx_cmd), rx_cmd); rtio_sqe_prep_read(rd_sqe, &i2c_iodev, 0, rx_data, ARRAY_SIZE(rx_data), rx_data); zassert_ok(rtio_submit(&i2c_rtio, 2), "submit should succeed"); wr_cqe = rtio_cqe_consume(&i2c_rtio); rd_cqe = rtio_cqe_consume(&i2c_rtio); zassert_ok(wr_cqe->result, "i2c write should succeed"); zassert_ok(rd_cqe->result, "i2c read should succeed"); rtio_cqe_release(&i2c_rtio, wr_cqe); rtio_cqe_release(&i2c_rtio, rd_cqe); zassert_equal(memcmp(&tx_data[TX_DATA_OFFSET], &rx_data[0], ARRAY_SIZE(rx_data)), 0, "Written and Read data should match"); } static enum isr_rtio_state { INIT, WRITE_WAIT, READ_CMD_WAIT, READ_DATA_WAIT, DONE } isr_state = INIT; K_SEM_DEFINE(ram_rtio_isr_sem, 0, 1); void ram_rtio_isr(struct k_timer *tid) { struct rtio_sqe *wr_sqe, *rd_sqe; struct rtio_cqe *wr_cqe, *rd_cqe; switch (isr_state) { case INIT: TC_PRINT("timer submitting write, addr %x\n", addr); wr_sqe = rtio_sqe_acquire(&i2c_rtio); wr_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP; rtio_sqe_prep_write(wr_sqe, &i2c_iodev, 0, tx_data, ARRAY_SIZE(tx_data), tx_data); zassert_ok(rtio_submit(&i2c_rtio, 0), "submit should succeed"); isr_state += 1; break; case WRITE_WAIT: wr_cqe = rtio_cqe_consume(&i2c_rtio); if (wr_cqe) { TC_PRINT("timer checking write result, submitting read\n"); zassert_ok(wr_cqe->result, "i2c write should succeed"); rtio_cqe_release(&i2c_rtio, wr_cqe); /* Write the address and read the data back */ msgs[0].len = ARRAY_SIZE(rx_cmd); msgs[0].flags = I2C_MSG_WRITE; msgs[1].buf = rx_data; msgs[1].len = ARRAY_SIZE(rx_data); msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP; wr_sqe = rtio_sqe_acquire(&i2c_rtio); wr_sqe->flags |= RTIO_SQE_TRANSACTION; rd_sqe = rtio_sqe_acquire(&i2c_rtio); rd_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; rtio_sqe_prep_write(wr_sqe, &i2c_iodev, 0, rx_cmd, ARRAY_SIZE(rx_cmd), rx_cmd); rtio_sqe_prep_read(rd_sqe, &i2c_iodev, 0, rx_data, ARRAY_SIZE(rx_data), rx_data); zassert_ok(rtio_submit(&i2c_rtio, 0), "submit should succeed"); isr_state += 1; } break; case READ_CMD_WAIT: wr_cqe = rtio_cqe_consume(&i2c_rtio); if (wr_cqe) { TC_PRINT("read command complete\n"); zassert_ok(wr_cqe->result, "i2c read command should succeed"); rtio_cqe_release(&i2c_rtio, wr_cqe); isr_state += 1; } break; case READ_DATA_WAIT: rd_cqe = rtio_cqe_consume(&i2c_rtio); if (rd_cqe) { TC_PRINT("read data complete\n"); zassert_ok(rd_cqe->result, "i2c read data should succeed"); rtio_cqe_release(&i2c_rtio, rd_cqe); isr_state += 1; k_sem_give(&ram_rtio_isr_sem); k_timer_stop(tid); } break; default: zassert_ok(-1, "Should not get here"); } } K_TIMER_DEFINE(ram_rtio_isr_timer, ram_rtio_isr, NULL); ZTEST(i2c_ram, test_ram_rtio_isr) { k_timer_start(&ram_rtio_isr_timer, K_MSEC(1), K_MSEC(1)); k_sem_take(&ram_rtio_isr_sem, K_FOREVER); } #endif /* CONFIG_I2C_RTIO */ ZTEST_SUITE(i2c_ram, NULL, i2c_ram_setup, i2c_ram_before, NULL, NULL);