/* * Copyright (c) 2020 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #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);