blob: 5b9dbb77ce381ae394b3c755625d62fbb5208bfb [file] [log] [blame]
/*
* 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 <zephyr/drivers/i2c.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <zephyr/tc_util.h>
#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 <zephyr/rtio/rtio.h>
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);