blob: feac946c46ef8c1a3d3414d43d0376df7d98a481 [file] [log] [blame]
/*
* Copyright (c) 2020 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/drivers/spi.h>
#define TEST_FREQ_HZ 24000000U
#define W25Q128_JEDEC_ID 0x001840efU
#define TEST_BUF_SIZE 4096U
/* #define MAX_SPI_BUF 8 */
#define SPI_STATUS1_BUSY 0x01U
#define SPI_STATUS1_WEL 0x02U
#define SPI_STATUS2_QE 0x02U
#define SPI_READ_JEDEC_ID 0x9FU
#define SPI_READ_STATUS1 0x05U
#define SPI_READ_STATUS2 0x35U
#define SPI_WRITE_STATUS2 0x31U
#define SPI_WRITE_ENABLE_VS 0x50U
#define SPI_WRITE_ENABLE 0x06U
#define SPI_SECTOR_ERASE 0x20U
#define SPI_SINGLE_WRITE_DATA 0x02U
#define SPI_QUAD_WRITE_DATA 0x32U
/* bits[7:0] = spi opcode,
* bits[15:8] = bytes number of clocks with data lines tri-stated
*/
#define SPI_FAST_READ_DATA 0x080BU
#define SPI_DUAL_FAST_READ_DATA 0x083BU
#define SPI_QUAD_FAST_READ_DATA 0x086BU
#define SPI_OCTAL_QUAD_READ_DATA 0xE3U
#define BUF_SIZE 11
uint8_t buffer_tx[] = "0123456789\0";
#define BUF_SIZE_2 7
uint8_t buffer_tx_2[] = "abcdef\0";
#define SPI_TEST_ADDRESS 0x000010U
#define SPI_TEST_ADDRESS_2 0x000020U
static uint8_t safbuf[TEST_BUF_SIZE] __aligned(4);
static uint8_t safbuf2[TEST_BUF_SIZE] __aligned(4);
static const struct device *const spi_dev = DEVICE_DT_GET(DT_NODELABEL(spi0));
/* static struct spi_buf spi_bufs[MAX_SPI_BUF]; */
static const struct spi_config spi_cfg_single = {
.frequency = TEST_FREQ_HZ,
.operation = (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8)
| SPI_LINES_SINGLE),
};
static const struct spi_config spi_cfg_single_hold_cs = {
.frequency = TEST_FREQ_HZ,
.operation = (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8)
| SPI_LINES_SINGLE | SPI_HOLD_ON_CS),
};
static const struct spi_config spi_cfg_dual = {
.frequency = TEST_FREQ_HZ,
.operation = (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8)
| SPI_LINES_DUAL),
};
static const struct spi_config spi_cfg_quad = {
.frequency = TEST_FREQ_HZ,
.operation = (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8)
| SPI_LINES_QUAD),
};
static void spi_single_init(void)
{
zassert_true(device_is_ready(spi_dev), "SPI controller device is not ready");
}
static void clear_buffers(void)
{
memset(safbuf, 0, sizeof(safbuf));
memset(safbuf2, 0, sizeof(safbuf2));
}
/* Compute the number of bytes required to generate the requested number of
* SPI clocks based on single, dual, or quad mode.
* mode = 1(full-duplex), 2(dual), 4(quad)
* full-duplex: 8 clocks per byte
* dual: 4 clocks per byte
* quad: 2 clocks per byte
*/
static uint32_t spi_clocks_to_bytes(uint32_t spi_clocks, uint8_t mode)
{
uint32_t nbytes;
if (mode == 4u) {
nbytes = spi_clocks / 2U;
} else if (mode == 2u) {
nbytes = spi_clocks / 4U;
} else {
nbytes = spi_clocks / 8U;
}
return nbytes;
}
static int spi_flash_address_format(uint8_t *dest, size_t destsz,
uint32_t spi_addr, size_t addrsz)
{
if (!dest || (addrsz == 0) || (addrsz > 4U) || (addrsz > destsz)) {
return -EINVAL;
}
for (size_t i = 0; i < addrsz; i++) {
dest[i] = (uint8_t)((spi_addr >> ((addrsz - (i + 1U)) * 8U)) & 0xffU);
}
return 0;
}
static int spi_flash_read_status(const struct device *dev, uint8_t opcode, uint8_t *status)
{
struct spi_buf spi_bufs[2] = { 0 };
uint32_t txdata = 0;
uint32_t rxdata = 0;
int ret = 0;
txdata = opcode;
spi_bufs[0].buf = &txdata;
spi_bufs[0].len = 1U;
spi_bufs[1].buf = &rxdata;
spi_bufs[1].len = 1U;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 2U,
};
const struct spi_buf_set rxset = {
.buffers = &spi_bufs[0],
.count = 2U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, &rxset);
if (ret) {
return ret;
}
if (status) {
*status = (uint8_t)(rxdata & 0xffu);
}
return 0;
}
static int spi_flash_write_status(const struct device *dev, uint8_t opcode, uint8_t spi_status)
{
struct spi_buf spi_bufs[1] = { 0 };
uint32_t txdata = 0;
int ret = 0;
txdata = spi_status;
txdata <<= 8U;
txdata |= opcode;
spi_bufs[0].buf = &txdata;
spi_bufs[0].len = 2U;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 1U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, NULL);
if (ret) {
return ret;
}
return 0;
}
static int spi_flash_tx_one_byte_cmd(const struct device *dev, uint8_t opcode)
{
struct spi_buf spi_bufs[1] = { 0 };
uint32_t txdata = 0;
int ret = 0;
txdata = opcode;
spi_bufs[0].buf = &txdata;
spi_bufs[0].len = 1U;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 1U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, NULL);
if (ret) {
return ret;
}
return 0;
}
/**
* @brief Test spi device
* @details
* - Find spi device
* - Read flash jedec id
*/
ZTEST_USER(spi, test_spi_device)
{
struct spi_buf spi_bufs[2] = { 0 };
uint32_t txdata = 0;
uint32_t jedec_id = 0;
int ret = 0;
/* read jedec id */
txdata = SPI_READ_JEDEC_ID;
spi_bufs[0].buf = &txdata;
spi_bufs[0].len = 1U;
spi_bufs[1].buf = &jedec_id;
spi_bufs[1].len = 3U;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 2U,
};
const struct spi_buf_set rxset = {
.buffers = &spi_bufs[0],
.count = 2U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, &rxset);
zassert_true(ret == 0, "Read JEDEC ID spi_transceive failure: "
"error %d", ret);
zassert_true(jedec_id == W25Q128_JEDEC_ID,
"JEDEC ID doesn't match: expected 0x%08x, read 0x%08x",
W25Q128_JEDEC_ID, jedec_id);
}
/**
* @brief Test spi sector erase
* @details
* - write enable
* - erase data in flash device
* - read register1 and wait for erase operation completed
*/
ZTEST_USER(spi_sector_erase, test_spi_sector_erase)
{
struct spi_buf spi_bufs[2] = { 0 };
int ret = 0;
uint8_t spi_status = 0;
clear_buffers();
/* write enable */
ret = spi_flash_tx_one_byte_cmd(spi_dev, SPI_WRITE_ENABLE);
zassert_true(ret == 0, "Send write enable spi_transceive failure: error %d", ret);
/* erase data start from address SPI_TEST_ADDRESS */
safbuf[0] = SPI_SECTOR_ERASE;
spi_flash_address_format(&safbuf[1], 4U, SPI_TEST_ADDRESS, 3U);
spi_bufs[0].buf = &safbuf;
spi_bufs[0].len = 4;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 1U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, NULL);
zassert_true(ret == 0, "Send sector erase data spi_transceive failure: error %d", ret);
/* read SPI flash status register1 to check whether erase operation completed */
spi_status = SPI_STATUS1_BUSY;
while (spi_status & SPI_STATUS1_BUSY) {
ret = spi_flash_read_status(spi_dev, SPI_READ_STATUS1, &spi_status);
zassert_true(ret == 0, "Send read register1 spi_transceive "
"failure: error %d", ret);
}
}
/**
* @brief Write data into flash using spi api
* @details
* - flash write enable
* - write data into flash using spi api
*/
static void test_spi_single_write(void)
{
struct spi_buf spi_bufs[1] = { 0 };
int ret = 0;
uint8_t spi_status = 0;
clear_buffers();
ret = spi_flash_tx_one_byte_cmd(spi_dev, SPI_WRITE_ENABLE);
zassert_true(ret == 0, "Send write enable spi_transceive failure: "
"error %d", ret);
/* write data start from address SPI_TEST_ADDRESS */
safbuf[0] = SPI_SINGLE_WRITE_DATA;
spi_flash_address_format(&safbuf[1], 4U, SPI_TEST_ADDRESS, 3U);
memcpy(&safbuf[4], buffer_tx, BUF_SIZE);
spi_bufs[0].buf = &safbuf;
spi_bufs[0].len = 4U + BUF_SIZE;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 1U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, NULL);
zassert_true(ret == 0, "Send write data spi_transceive failure: error %d", ret);
/* read register1 to check whether program operation completed */
spi_status = SPI_STATUS1_BUSY;
while (spi_status & SPI_STATUS1_BUSY) {
ret = spi_flash_read_status(spi_dev, SPI_READ_STATUS1, &spi_status);
zassert_true(ret == 0, "Read SPI flash STATUS opcode 0x%02x error: %d",
SPI_READ_STATUS1, ret);
}
}
/**
* @brief Read data from flash using spi single mode
* @details
* - read data using spi single mode
* - check read buffer data whether correct
* @note SPI flash fast instructions require a certain number of SPI clocks
* to be generated with I/O lines tri-stated after the address has been
* transmitted. The purpose is allow SPI flash time to move get data ready
* and enable its output line(s). The MCHP XEC SPI driver can do this by
* specifying a struct spi_buf with buf pointer set to NULL and length set
* to the number of bytes which will generate the required number of clocks.
* For full-duplex one byte is 8 clocks, dual one byte is 4 clocks, and for
* quad one byte is 2 clocks.
*/
ZTEST_USER(spi, test_spi_single_read)
{
struct spi_buf spi_bufs[3] = { 0 };
int ret = 0;
uint16_t spi_opcode = 0;
uint8_t cnt = 0;
clear_buffers();
/* bits[7:0] = opcode,
* bits[15:8] = number of SPI clocks with I/O lines tri-stated after
* address transmit before data read phase.
*/
spi_opcode = SPI_FAST_READ_DATA;
/* read data using spi single mode */
/* set the spi operation code and address */
safbuf[0] = spi_opcode & 0xFFU;
spi_flash_address_format(&safbuf[1], 4U, SPI_TEST_ADDRESS, 3U);
spi_bufs[cnt].buf = &safbuf;
spi_bufs[cnt].len = 4U;
/* set the dummy clocks */
if (spi_opcode & 0xFF00U) {
cnt++;
spi_bufs[cnt].buf = NULL;
spi_bufs[cnt].len = spi_clocks_to_bytes(((spi_opcode >> 8) & 0xffU), 1u);
}
cnt++;
spi_bufs[cnt].buf = &safbuf2;
spi_bufs[cnt].len = BUF_SIZE;
cnt++; /* total number of buffers */
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = cnt,
};
const struct spi_buf_set rxset = {
.buffers = &spi_bufs[0],
.count = cnt,
};
ret = spi_transceive(spi_dev, &spi_cfg_single, &txset, &rxset);
zassert_true(ret == 0, "Send fast read data spi_transceive failure: error %d", ret);
/* check read buffer data whether correct */
zassert_true(memcmp(buffer_tx, safbuf2, BUF_SIZE) == 0,
"Buffer read data is different to write data");
}
static void spi_dual_init(void)
{
zassert_true(device_is_ready(spi_dev), "SPI controller device is not ready");
}
/**
* @brief Read data from flash using spi dual mode
* @details
* - read data using spi dual mode
* - check read buffer data whether correct
*/
ZTEST_USER(spi, test_spi_dual_read)
{
struct spi_buf spi_bufs[3] = { 0 };
int ret = 0;
uint16_t spi_opcode = 0;
uint8_t cnt = 0;
clear_buffers();
spi_dual_init();
spi_opcode = SPI_DUAL_FAST_READ_DATA;
/* read data using spi dual mode */
/* set the spi operation code and address */
safbuf[0] = spi_opcode & 0xFFU;
spi_flash_address_format(&safbuf[1], 4U, SPI_TEST_ADDRESS, 3U);
spi_bufs[cnt].buf = &safbuf;
spi_bufs[cnt].len = 4U;
/* set the dummy clocks */
if (spi_opcode & 0xFF00U) {
cnt++;
spi_bufs[cnt].buf = NULL;
spi_bufs[cnt].len = spi_clocks_to_bytes(((spi_opcode >> 8) & 0xffU), 1u);
}
cnt++;
spi_bufs[cnt].buf = &safbuf2;
spi_bufs[cnt].len = BUF_SIZE;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = cnt,
};
const struct spi_buf_set rxset = {
.buffers = &spi_bufs[cnt],
.count = 1U,
};
/* send opcode, address, and tri-state clocks using single mode */
ret = spi_transceive(spi_dev, &spi_cfg_single_hold_cs, &txset, NULL);
zassert_true(ret == 0, "Send fast read data spi_transceive failure: error %d", ret);
/* get read data using dual mode */
ret = spi_transceive(spi_dev, &spi_cfg_dual, NULL, &rxset);
zassert_true(ret == 0, "Receive fast read data spi_transceive failure: error %d", ret);
/* check read buffer data whether correct */
zassert_true(memcmp(buffer_tx, safbuf2, BUF_SIZE) == 0,
"Buffer read data is different to write data");
/* release spi device */
ret = spi_release(spi_dev, &spi_cfg_single);
zassert_true(ret == 0, "Spi release failure: error %d", ret);
}
/**
* @brief Write data into flash using spi quad mode
* @details
* - check and make sure spi quad mode is enabled
* - write data using spi quad mode
*/
static void test_spi_quad_write(void)
{
struct spi_buf spi_bufs[2] = { 0 };
int ret = 0;
uint8_t spi_status = 0;
uint8_t spi_status2 = 0;
clear_buffers();
/* read register2 to judge whether quad mode is enabled */
ret = spi_flash_read_status(spi_dev, SPI_READ_STATUS2, &spi_status2);
zassert_true(ret == 0, "SPI read flash STATUS2 failure: error %d", ret);
/* set register2 QE=1 to enable quad mode. We write the volatile STATUS2 register
* not the normal STATUS2 which retains the value across a power cycle.
*/
if ((spi_status2 & SPI_STATUS2_QE) == 0U) {
ret = spi_flash_tx_one_byte_cmd(spi_dev, SPI_WRITE_ENABLE_VS);
zassert_true(ret == 0, "Send write enable volatile spi_transceive failure: "
"error %d", ret);
spi_status2 |= SPI_STATUS2_QE;
ret = spi_flash_write_status(spi_dev, SPI_WRITE_STATUS2, spi_status2);
zassert_true(ret == 0, "Write spi status2 QE=1 spi_transceive failure: "
"error %d", ret);
/* read register2 to confirm quad mode is enabled */
spi_status2 = 0u;
ret = spi_flash_read_status(spi_dev, SPI_READ_STATUS2, &spi_status2);
zassert_true(ret == 0, "Read register2 status spi_transceive failure: "
"error %d", ret);
zassert_true((spi_status2 & SPI_STATUS2_QE) == SPI_STATUS2_QE,
"Enable QSPI mode failure");
}
/* write enable */
ret = spi_flash_tx_one_byte_cmd(spi_dev, SPI_WRITE_ENABLE);
zassert_true(ret == 0, "Send write enable spi_transceive failure: error %d", ret);
/* write data using spi quad mode */
/* send quad write opcode and address using single mode */
safbuf[0] = SPI_QUAD_WRITE_DATA;
spi_flash_address_format(&safbuf[1], 4U, SPI_TEST_ADDRESS_2, 3U);
spi_bufs[0].buf = &safbuf;
spi_bufs[0].len = 4;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = 1U,
};
ret = spi_transceive(spi_dev, &spi_cfg_single_hold_cs, &txset, NULL);
zassert_true(ret == 0, "Send quad write data spi_transceive failure: error %d", ret);
/* send data using quad mode */
memcpy(&safbuf[0], buffer_tx_2, BUF_SIZE_2);
spi_bufs[0].buf = &safbuf;
spi_bufs[0].len = BUF_SIZE_2;
ret = spi_transceive(spi_dev, &spi_cfg_quad, &txset, NULL);
zassert_true(ret == 0, "Send quad write data spi_transceive failure: error %d", ret);
/* call SPI release API to clear SPI CS Hold On lock */
ret = spi_release(spi_dev, &spi_cfg_single);
zassert_true(ret == 0, "Spi release failure: error %d", ret);
/* poll busy bit in flash status1 register */
spi_status = SPI_STATUS1_BUSY;
while (spi_status & SPI_STATUS1_BUSY) {
ret = spi_flash_read_status(spi_dev, SPI_READ_STATUS1, &spi_status);
zassert_true(ret == 0, "Read flash STATUS1 register error %d", ret);
}
}
/**
* @brief Read data from flash using spi quad mode
* @details
* - read data using spi quad mode
* - check read buffer data whether correct
*/
ZTEST_USER(spi_quad, test_spi_quad_read)
{
struct spi_buf spi_bufs[3] = {0};
int ret = 0;
uint16_t spi_opcode = 0;
uint8_t cnt = 0;
clear_buffers();
spi_opcode = SPI_QUAD_FAST_READ_DATA;
/* read data using spi quad mode
* Transmit opcode, address, and tri-state clocks using full-duplex mode
* with driver Hold CS ON flag.
* Next, read data using dual configuration.
* Call driver release API to release lock set by Hold CS ON flag.
*/
/* set the spi operation code and address */
safbuf[0] = spi_opcode & 0xFFU;
spi_flash_address_format(&safbuf[1], 4U, SPI_TEST_ADDRESS_2, 3U);
spi_bufs[cnt].buf = &safbuf;
spi_bufs[cnt].len = 4U;
/* set the dummy clocks */
if (spi_opcode & 0xFF00U) {
cnt++;
spi_bufs[cnt].buf = NULL;
spi_bufs[cnt].len = spi_clocks_to_bytes(((spi_opcode >> 8) & 0xffU), 1u);
}
cnt++;
spi_bufs[cnt].buf = &safbuf2;
spi_bufs[cnt].len = BUF_SIZE_2;
const struct spi_buf_set txset = {
.buffers = &spi_bufs[0],
.count = cnt,
};
const struct spi_buf_set rxset = {
.buffers = &spi_bufs[cnt],
.count = 1U,
};
/* send opcode and address using single mode with Hold CS ON flag */
ret = spi_transceive(spi_dev, &spi_cfg_single_hold_cs, &txset, NULL);
zassert_true(ret == 0, "Send fast read data spi_transceive failure: error %d", ret);
/* read data using quad mode */
ret = spi_transceive(spi_dev, &spi_cfg_quad, NULL, &rxset);
zassert_true(ret == 0, "Receive fast read data spi_transceive failure: error %d", ret);
/* release spi device */
ret = spi_release(spi_dev, &spi_cfg_single);
zassert_true(ret == 0, "Spi release failure: error %d", ret);
/* check read buffer data whether correct */
zassert_true(memcmp(buffer_tx_2, safbuf2, BUF_SIZE_2) == 0,
"Buffer read data is different to write data");
}
void *spi_setup(void)
{
spi_single_init();
return NULL;
}
void *spi_single_setup(void)
{
spi_single_init();
/* The writing test goes
* first berfore testing
* the reading.
*/
test_spi_single_write();
return NULL;
}
void *spi_quad_setup(void)
{
spi_dual_init();
/* The writing test goes
* first berfore testing
* the reading.
*/
test_spi_quad_write();
return NULL;
}
/* Test assumes flash test regions is in erased state */
ZTEST_SUITE(spi, NULL, spi_single_setup, NULL, NULL, NULL);
ZTEST_SUITE(spi_quad, NULL, spi_quad_setup, NULL, NULL, NULL);
ZTEST_SUITE(spi_sector_erase, NULL, spi_setup, NULL, NULL, NULL);