Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spi_nand_flash): Add SIO (full-duplex) and DIO mode support #476

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions spi_nand_flash/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ menu "SPI NAND Flash configuration"
If this option is enabled, any time SPI NAND flash is written then the data will be read
back and verified. This can catch hardware problems with SPI NAND flash, or flash which
was not erased before verification.

endmenu
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,25 @@ static void example_init_nand_flash(spi_nand_flash_device_t **out_handle, spi_de
ESP_LOGI(TAG, "DMA CHANNEL: %d", SPI_DMA_CHAN);
ESP_ERROR_CHECK(spi_bus_initialize(HOST_ID, &bus_config, SPI_DMA_CHAN));

// spi_flags = SPI_DEVICE_HALFDUPLEX -> half duplex
// spi_flags = 0 -> full_duplex
const uint32_t spi_flags = SPI_DEVICE_HALFDUPLEX;

spi_device_interface_config_t devcfg = {
.clock_speed_hz = EXAMPLE_FLASH_FREQ_KHZ * 1000,
.mode = 0,
.spics_io_num = PIN_CS,
.queue_size = 10,
.flags = SPI_DEVICE_HALFDUPLEX,
.flags = spi_flags,
};

spi_device_handle_t spi;
ESP_ERROR_CHECK(spi_bus_add_device(HOST_ID, &devcfg, &spi));

spi_nand_flash_config_t nand_flash_config = {
.device_handle = spi,
.io_mode = SPI_NAND_IO_MODE_SIO,
.flags = spi_flags,
};
spi_nand_flash_device_t *nand_flash_device_handle;
ESP_ERROR_CHECK(spi_nand_flash_init_device(&nand_flash_config, &nand_flash_device_handle));
Expand Down
8 changes: 8 additions & 0 deletions spi_nand_flash/include/spi_nand_flash.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@
extern "C" {
#endif

typedef enum {
SPI_NAND_IO_MODE_SIO = 0,
SPI_NAND_IO_MODE_DOUT,
SPI_NAND_IO_MODE_DIO,
} spi_nand_flash_io_mode_t;

/** @brief Structure to describe how to configure the nand access layer.
@note The spi_device_handle_t must be initialized with the flag SPI_DEVICE_HALFDUPLEX
*/
struct spi_nand_flash_config_t {
spi_device_handle_t device_handle; ///< SPI Device for this nand chip.
uint8_t gc_factor; ///< The gc factor controls the number of blocks to spare block ratio.
///< Lower values will reduce the available space but increase performance
spi_nand_flash_io_mode_t io_mode; ///< set io mode for SPI NAND communication
uint8_t flags; ///< set flag with SPI_DEVICE_HALFDUPLEX for half duplex communcation.
};

typedef struct spi_nand_flash_config_t spi_nand_flash_config_t;
Expand Down
20 changes: 11 additions & 9 deletions spi_nand_flash/priv_include/spi_nand_oper.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <stdint.h>
#include <esp_err.h>
#include <driver/spi_master.h>
#include "nand.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -42,6 +43,7 @@ typedef struct spi_nand_transaction_t spi_nand_transaction_t;
#define CMD_READ_X2 0x3B
#define CMD_READ_X4 0x6B
#define CMD_ERASE_BLOCK 0xD8
#define CMD_READ_DIO 0xBB

#define REG_PROTECT 0xA0
#define REG_CONFIG 0xB0
Expand All @@ -55,16 +57,16 @@ typedef struct spi_nand_transaction_t spi_nand_transaction_t;
#define STAT_ECC1 1 << 5
#define STAT_ECC2 1 << 6

esp_err_t spi_nand_execute_transaction(spi_device_handle_t device, spi_nand_transaction_t *transaction);
esp_err_t spi_nand_execute_transaction(spi_nand_flash_device_t *handle, spi_nand_transaction_t *transaction);

esp_err_t spi_nand_read_register(spi_device_handle_t device, uint8_t reg, uint8_t *val);
esp_err_t spi_nand_write_register(spi_device_handle_t device, uint8_t reg, uint8_t val);
esp_err_t spi_nand_write_enable(spi_device_handle_t device);
esp_err_t spi_nand_read_page(spi_device_handle_t device, uint32_t page);
esp_err_t spi_nand_read(spi_device_handle_t device, uint8_t *data, uint16_t column, uint16_t length);
esp_err_t spi_nand_program_execute(spi_device_handle_t device, uint32_t page);
esp_err_t spi_nand_program_load(spi_device_handle_t device, const uint8_t *data, uint16_t column, uint16_t length);
esp_err_t spi_nand_erase_block(spi_device_handle_t device, uint32_t page);
esp_err_t spi_nand_read_register(spi_nand_flash_device_t *handle, uint8_t reg, uint8_t *val);
esp_err_t spi_nand_write_register(spi_nand_flash_device_t *handle, uint8_t reg, uint8_t val);
esp_err_t spi_nand_write_enable(spi_nand_flash_device_t *handle);
esp_err_t spi_nand_read_page(spi_nand_flash_device_t *handle, uint32_t page);
esp_err_t spi_nand_read(spi_nand_flash_device_t *handle, uint8_t *data, uint16_t column, uint16_t length);
esp_err_t spi_nand_program_execute(spi_nand_flash_device_t *handle, uint32_t page);
esp_err_t spi_nand_program_load(spi_nand_flash_device_t *handle, const uint8_t *data, uint16_t column, uint16_t length);
esp_err_t spi_nand_erase_block(spi_nand_flash_device_t *handle, uint32_t page);

#ifdef __cplusplus
}
Expand Down
6 changes: 3 additions & 3 deletions spi_nand_flash/src/nand.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ static esp_err_t detect_chip(spi_nand_flash_device_t *dev)
.miso_data = &manufacturer_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
spi_nand_execute_transaction(dev, &t);
ESP_LOGD(TAG, "%s: manufacturer_id: %x\n", __func__, manufacturer_id);

switch (manufacturer_id) {
Expand All @@ -49,13 +49,13 @@ static esp_err_t detect_chip(spi_nand_flash_device_t *dev)
static esp_err_t unprotect_chip(spi_nand_flash_device_t *dev)
{
uint8_t status;
esp_err_t ret = spi_nand_read_register(dev->config.device_handle, REG_PROTECT, &status);
esp_err_t ret = spi_nand_read_register(dev, REG_PROTECT, &status);
if (ret != ESP_OK) {
return ret;
}

if (status != 0x00) {
ret = spi_nand_write_register(dev->config.device_handle, REG_PROTECT, 0);
ret = spi_nand_write_register(dev, REG_PROTECT, 0);
}

return ret;
Expand Down
5 changes: 2 additions & 3 deletions spi_nand_flash/src/nand_alliance.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ esp_err_t spi_nand_alliance_init(spi_nand_flash_device_t *dev)
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.address = 1,
.address_bytes = 1,
.dummy_bits = 8,
.address_bytes = 2,
.miso_len = 1,
.miso_data = &device_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
spi_nand_execute_transaction(dev, &t);
dev->chip.erase_block_delay_us = 3000;
dev->chip.program_page_delay_us = 630;
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id);
Expand Down
5 changes: 3 additions & 2 deletions spi_nand_flash/src/nand_gigadevice.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ esp_err_t spi_nand_gigadevice_init(spi_nand_flash_device_t *dev)
uint8_t device_id;
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.dummy_bits = 16,
.address = 0,
.address_bytes = 2,
.miso_len = 1,
.miso_data = &device_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
spi_nand_execute_transaction(dev, &t);
dev->chip.read_page_delay_us = 25;
dev->chip.erase_block_delay_us = 3200;
dev->chip.program_page_delay_us = 380;
Expand Down
52 changes: 26 additions & 26 deletions spi_nand_flash/src/nand_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static esp_err_t s_verify_write(spi_nand_flash_device_t *handle, const uint8_t *
uint8_t *temp_buf = NULL;
temp_buf = heap_caps_malloc(length, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem");
if (spi_nand_read(handle->config.device_handle, temp_buf, offset, length)) {
if (spi_nand_read(handle, temp_buf, offset, length)) {
ESP_LOGE(TAG, "%s: Failed to read nand flash to verify previous write", __func__);
free(temp_buf);
return ESP_FAIL;
Expand All @@ -39,15 +39,15 @@ static esp_err_t s_verify_write(spi_nand_flash_device_t *handle, const uint8_t *
}
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE

static esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_operation_time_us, uint8_t *status_out)
static esp_err_t wait_for_ready(spi_nand_flash_device_t *dev, uint32_t expected_operation_time_us, uint8_t *status_out)
{
if (expected_operation_time_us < ROM_WAIT_THRESHOLD_US) {
esp_rom_delay_us(expected_operation_time_us);
}

while (true) {
uint8_t status;
ESP_RETURN_ON_ERROR(spi_nand_read_register(device, REG_STATUS, &status), TAG, "");
ESP_RETURN_ON_ERROR(spi_nand_read_register(dev, REG_STATUS, &status), TAG, "");

if ((status & STAT_BUSY) == 0) {
if (status_out) {
Expand All @@ -66,16 +66,16 @@ static esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_op

static esp_err_t read_page_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out)
{
ESP_RETURN_ON_ERROR(spi_nand_read_page(dev->config.device_handle, page), TAG, "");
ESP_RETURN_ON_ERROR(spi_nand_read_page(dev, page), TAG, "");

return wait_for_ready(dev->config.device_handle, dev->chip.read_page_delay_us, status_out);
return wait_for_ready(dev, dev->chip.read_page_delay_us, status_out);
}

static esp_err_t program_execute_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out)
{
ESP_RETURN_ON_ERROR(spi_nand_program_execute(dev->config.device_handle, page), TAG, "");
ESP_RETURN_ON_ERROR(spi_nand_program_execute(dev, page), TAG, "");

return wait_for_ready(dev->config.device_handle, dev->chip.program_page_delay_us, status_out);
return wait_for_ready(dev, dev->chip.program_page_delay_us, status_out);
}

esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, uint32_t block, bool *is_bad_status)
Expand All @@ -87,7 +87,7 @@ esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, uint32_t block, bool *is_
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, first_block_page, NULL), fail, TAG, "");

// Read the first 2 bytes on the OOB of the first page in the block. This should be 0xFFFF for a good block
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, (uint8_t *) &bad_block_indicator, handle->chip.page_size, 2),
ESP_GOTO_ON_ERROR(spi_nand_read(handle, (uint8_t *) &bad_block_indicator, handle->chip.page_size, 2),
fail, TAG, "");

ESP_LOGD(TAG, "is_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", block, first_block_page, bad_block_indicator);
Expand All @@ -113,18 +113,18 @@ esp_err_t nand_mark_bad(spi_nand_flash_device_t *handle, uint32_t block)
ESP_LOGD(TAG, "mark_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", block, first_block_page, bad_block_indicator);

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, first_block_page, NULL), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, first_block_page),
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle, first_block_page),
fail, TAG, "");
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->chip.erase_block_delay_us, &status),
ESP_GOTO_ON_ERROR(wait_for_ready(handle, handle->chip.erase_block_delay_us, &status),
fail, TAG, "");
if ((status & STAT_ERASE_FAILED) != 0) {
ret = ESP_ERR_NOT_FINISHED;
goto fail;
}

ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, (const uint8_t *) &bad_block_indicator,
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle, (const uint8_t *) &bad_block_indicator,
handle->chip.page_size, 2),
fail, TAG, "");
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, first_block_page, NULL), fail, TAG, "");
Expand All @@ -147,10 +147,10 @@ esp_err_t nand_erase_chip(spi_nand_flash_device_t *handle)
uint8_t status;

for (int i = 0; i < handle->chip.num_blocks; i++) {
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), end, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, i * (1 << handle->chip.log2_ppb)),
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle), end, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle, i * (1 << handle->chip.log2_ppb)),
end, TAG, "");
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->chip.erase_block_delay_us, &status),
ESP_GOTO_ON_ERROR(wait_for_ready(handle, handle->chip.erase_block_delay_us, &status),
end, TAG, "");
if ((status & STAT_ERASE_FAILED) != 0) {
ret = ESP_ERR_NOT_FINISHED;
Expand All @@ -171,10 +171,10 @@ esp_err_t nand_erase_block(spi_nand_flash_device_t *handle, uint32_t block)

uint32_t first_block_page = block * (1 << handle->chip.log2_ppb);

ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, first_block_page),
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle, first_block_page),
fail, TAG, "");
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle,
ESP_GOTO_ON_ERROR(wait_for_ready(handle,
handle->chip.erase_block_delay_us, &status),
fail, TAG, "");

Expand All @@ -196,10 +196,10 @@ esp_err_t nand_prog(spi_nand_flash_device_t *handle, uint32_t page, const uint8_
uint8_t status;

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, NULL), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, data, 0, handle->chip.page_size),
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle, data, 0, handle->chip.page_size),
fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, (uint8_t *)&used_marker,
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle, (uint8_t *)&used_marker,
handle->chip.page_size + 2, 2),
fail, TAG, "");
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, page, &status), fail, TAG, "");
Expand Down Expand Up @@ -232,7 +232,7 @@ esp_err_t nand_is_free(spi_nand_flash_device_t *handle, uint32_t page, bool *is_
uint16_t used_marker;

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, NULL), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, (uint8_t *)&used_marker,
ESP_GOTO_ON_ERROR(spi_nand_read(handle, (uint8_t *)&used_marker,
handle->chip.page_size + 2, 2),
fail, TAG, "");

Expand Down Expand Up @@ -288,7 +288,7 @@ esp_err_t nand_read(spi_nand_flash_device_t *handle, uint32_t page, size_t offse
return ESP_FAIL;
}

ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, data, offset, length), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_read(handle, data, offset, length), fail, TAG, "");

return ret;
fail:
Expand All @@ -312,7 +312,7 @@ esp_err_t nand_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst)
return ESP_FAIL;
}

ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, dst, &status), fail, TAG, "");

if ((status & STAT_PROGRAM_FAILED) != 0) {
Expand All @@ -324,7 +324,7 @@ esp_err_t nand_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst)
// First read src page data from cache to temp_buf
temp_buf = heap_caps_malloc(handle->chip.page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem");
if (spi_nand_read(handle->config.device_handle, temp_buf, 0, handle->chip.page_size)) {
if (spi_nand_read(handle, temp_buf, 0, handle->chip.page_size)) {
ESP_LOGE(TAG, "%s: Failed to read src_page=%"PRIu32"", __func__, src);
goto fail;
}
Expand Down
5 changes: 3 additions & 2 deletions spi_nand_flash/src/nand_micron.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ esp_err_t spi_nand_micron_init(spi_nand_flash_device_t *dev)
uint8_t device_id;
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.dummy_bits = 16,
.address = 0,
.address_bytes = 2,
.miso_len = 1,
.miso_data = &device_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
spi_nand_execute_transaction(dev, &t);
dev->chip.ecc_data.ecc_status_reg_len_in_bits = 3;
dev->chip.erase_block_delay_us = 2000;
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id);
Expand Down
5 changes: 3 additions & 2 deletions spi_nand_flash/src/nand_winbond.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ esp_err_t spi_nand_winbond_init(spi_nand_flash_device_t *dev)
uint8_t device_id_buf[2];
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.dummy_bits = 16,
.address = 0,
.address_bytes = 2,
.miso_len = 2,
.miso_data = device_id_buf,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
spi_nand_execute_transaction(dev, &t);
uint16_t device_id = (device_id_buf[0] << 8) + device_id_buf[1];
dev->chip.read_page_delay_us = 10;
dev->chip.erase_block_delay_us = 2500;
Expand Down
Loading