Dozingfiretruck 4 år sedan
förälder
incheckning
3551669193

+ 120 - 0
components/sfud/luat_lib_sfud.c

@@ -0,0 +1,120 @@
+
+#include "luat_base.h"
+#include "luat_spi.h"
+#include "sfud.h"
+
+#define LUAT_LOG_TAG "luat.sfud"
+#include "luat_log.h"
+
+luat_spi_t sfud_spi_flash;
+
+static int l_sfud_init(lua_State *L){
+
+    sfud_spi_flash.id = luaL_checkinteger(L, 1);
+    sfud_spi_flash.cs = luaL_checkinteger(L, 2);
+    sfud_spi_flash.bandrate = luaL_checkinteger(L, 3);
+    // sfud_spi_flash.id = 0;
+    // sfud_spi_flash.cs = 20; // 默认无
+    sfud_spi_flash.CPHA = 1; // CPHA0
+    sfud_spi_flash.CPOL = 1; // CPOL0
+    sfud_spi_flash.dataw = 8; // 8bit
+    // sfud_spi_flash.bandrate = 20 * 1000 * 1000; // 2000000U
+    sfud_spi_flash.bit_dict = 1; // MSB=1, LSB=0
+    sfud_spi_flash.master = 1; // master=1,slave=0
+    sfud_spi_flash.mode = 1; // FULL=1, half=0
+    luat_spi_setup(&sfud_spi_flash);
+
+    int re = sfud_init();
+    lua_pushboolean(L, re == 0 ? 1 : 0);
+    return 1;
+}
+
+static int l_sfud_get_device_num(lua_State *L){
+    int re = sfud_get_device_num();
+    lua_pushinteger(L, re);
+    return 1;
+}
+
+static int l_sfud_get_device(lua_State *L){
+    sfud_flash *flash = sfud_get_device(luaL_checkinteger(L, 1));
+    lua_pushlightuserdata(L, flash);
+    return 1;
+}
+
+static int l_sfud_get_device_table(lua_State *L){
+    sfud_flash *flash = sfud_get_device_table();
+    lua_pushlightuserdata(L, flash);
+    return 1;
+}
+
+static int l_sfud_chip_erase(lua_State *L){
+    const sfud_flash *flash = lua_touserdata(L, 1);
+    sfud_err re = sfud_chip_erase(flash);
+    lua_pushinteger(L, re);
+    return 1;
+}
+
+static int l_sfud_erase(lua_State *L){
+    const sfud_flash *flash = lua_touserdata(L, 1);
+    uint32_t addr = luaL_checkinteger(L, 2);
+    size_t size = luaL_checkinteger(L, 3);
+    sfud_err re = sfud_erase(flash,addr,size);
+    lua_pushinteger(L, re);
+    return 1;
+}
+
+
+static int l_sfud_read(lua_State *L){
+    const sfud_flash *flash = lua_touserdata(L, 1);
+    uint32_t addr = luaL_checkinteger(L, 2);
+    size_t size = luaL_checkinteger(L, 3);
+    uint8_t* data = (uint8_t*)luat_heap_malloc(size);
+    sfud_err re = sfud_read(flash, addr, size,data);
+    if(re != SFUD_SUCCESS){
+        size = 0;
+        LLOGD("sfud_read re %d", re);
+    }
+    lua_pushlstring(L, data, size);
+    luat_heap_free(data);
+    return 1;
+}
+    
+static int l_sfud_write(lua_State *L){
+    const sfud_flash *flash = lua_touserdata(L, 1);
+    uint32_t addr = luaL_checkinteger(L, 2);
+    size_t size = 0;
+    const char* data = luaL_checklstring(L, 3, &size);
+    sfud_err re = sfud_write(flash, addr, size,data);
+    lua_pushinteger(L, re);
+    return 1;
+}
+
+static int l_sfud_erase_write(lua_State *L){
+    const sfud_flash *flash = lua_touserdata(L, 1);
+    uint32_t addr = luaL_checkinteger(L, 2);
+    size_t size = 0;
+    const char* data = luaL_checklstring(L, 3, &size);
+    sfud_err re = sfud_erase_write(flash, addr, size,data);
+    lua_pushinteger(L, re);
+    return 1;
+}
+
+#include "rotable.h"
+static const rotable_Reg reg_sfud[] =
+{
+    { "init",       l_sfud_init,        0},
+    { "get_device_num",       l_sfud_get_device_num,        0},
+    { "get_device",       l_sfud_get_device,        0},
+    { "get_device_table",       l_sfud_get_device_table,        0},
+    { "erase",       l_sfud_erase,        0},
+    { "chip_erase",       l_sfud_chip_erase,        0},
+    { "read",       l_sfud_read,        0},
+    { "write",       l_sfud_write,        0},
+    { "erase_write",       l_sfud_erase_write,        0},
+	{ NULL, NULL, 0}
+};
+
+LUAMOD_API int luaopen_sfud( lua_State *L ) {
+    luat_newlib(L, reg_sfud);
+    return 1;
+}

+ 1040 - 0
components/sfud/sfud.c

@@ -0,0 +1,1040 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: serial flash operate functions by SFUD lib.
+ * Created on: 2016-04-23
+ */
+
+#include "sfud.h"
+#include <string.h>
+
+/* send dummy data for read data */
+#define DUMMY_DATA                               0xFF
+
+#ifndef SFUD_FLASH_DEVICE_TABLE
+#error "Please configure the flash device information table in (in sfud_cfg.h)."
+#endif
+
+/* user configured flash device information table */
+static sfud_flash flash_table[] = SFUD_FLASH_DEVICE_TABLE;
+/* supported manufacturer information table */
+static const sfud_mf mf_table[] = SFUD_MF_TABLE;
+
+#ifdef SFUD_USING_FLASH_INFO_TABLE
+/* supported flash chip information table */
+static const sfud_flash_chip flash_chip_table[] = SFUD_FLASH_CHIP_TABLE;
+#endif
+
+#ifdef SFUD_USING_QSPI
+/**
+ * flash read data mode
+ */
+enum sfud_qspi_read_mode {
+    NORMAL_SPI_READ = 1 << 0,               /**< mormal spi read mode */
+    DUAL_OUTPUT = 1 << 1,                   /**< qspi fast read dual output */
+    DUAL_IO = 1 << 2,                       /**< qspi fast read dual input/output */
+    QUAD_OUTPUT = 1 << 3,                   /**< qspi fast read quad output */
+    QUAD_IO = 1 << 4,                       /**< qspi fast read quad input/output */
+};
+
+/* QSPI flash chip's extended information table */
+static const sfud_qspi_flash_ext_info qspi_flash_ext_info_table[] = SFUD_FLASH_EXT_INFO_TABLE;
+#endif /* SFUD_USING_QSPI */
+
+static sfud_err software_init(const sfud_flash *flash);
+static sfud_err hardware_init(sfud_flash *flash);
+static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size, uint16_t write_gran,
+        const uint8_t *data);
+static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
+static sfud_err wait_busy(const sfud_flash *flash);
+static sfud_err reset(const sfud_flash *flash);
+static sfud_err read_jedec_id(sfud_flash *flash);
+static sfud_err set_write_enabled(const sfud_flash *flash, bool enabled);
+static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled);
+static void make_adress_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array);
+
+/* ../port/sfup_port.c */
+extern void sfud_log_debug(const char *file, const long line, const char *format, ...);
+extern void sfud_log_info(const char *format, ...);
+
+/**
+ * SFUD initialize by flash device
+ *
+ * @param flash flash device
+ *
+ * @return result
+ */
+sfud_err sfud_device_init(sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+
+    /* hardware initialize */
+    result = hardware_init(flash);
+    if (result == SFUD_SUCCESS) {
+        result = software_init(flash);
+    }
+    if (result == SFUD_SUCCESS) {
+        flash->init_ok = true;
+        SFUD_INFO("%s flash device is initialize success.", flash->name);
+    } else {
+        flash->init_ok = false;
+        SFUD_INFO("Error: %s flash device is initialize fail.", flash->name);
+    }
+
+    return result;
+}
+
+/**
+ * SFUD library initialize.
+ *
+ * @return result
+ */
+sfud_err sfud_init(void) {
+    sfud_err cur_flash_result = SFUD_SUCCESS, all_flash_result = SFUD_SUCCESS;
+    size_t i;
+
+    SFUD_DEBUG("Start initialize Serial Flash Universal Driver(SFUD) V%s.", SFUD_SW_VERSION);
+    SFUD_DEBUG("You can get the latest version on https://github.com/armink/SFUD .");
+    /* initialize all flash device in flash device table */
+    for (i = 0; i < sizeof(flash_table) / sizeof(sfud_flash); i++) {
+        /* initialize flash device index of flash device information table */
+        flash_table[i].index = i;
+        cur_flash_result = sfud_device_init(&flash_table[i]);
+
+        if (cur_flash_result != SFUD_SUCCESS) {
+            all_flash_result = cur_flash_result;
+        }
+    }
+
+    return all_flash_result;
+}
+
+/**
+ * get flash device by its index which in the flash information table
+ *
+ * @param index the index which in the flash information table  @see flash_table
+ *
+ * @return flash device
+ */
+sfud_flash *sfud_get_device(size_t index) {
+    if (index < sfud_get_device_num()) {
+        return &flash_table[index];
+    } else {
+        return NULL;
+    }
+}
+
+/**
+ * get flash device total number on flash device information table  @see flash_table
+ *
+ * @return flash device total number
+ */
+size_t sfud_get_device_num(void) {
+    return sizeof(flash_table) / sizeof(sfud_flash);
+}
+
+/**
+ * get flash device information table  @see flash_table
+ *
+ * @return flash device table pointer
+ */
+const sfud_flash *sfud_get_device_table(void) {
+    return flash_table;
+}
+
+#ifdef SFUD_USING_QSPI
+static void qspi_set_read_cmd_format(sfud_flash *flash, uint8_t ins, uint8_t ins_lines, uint8_t addr_lines,
+        uint8_t dummy_cycles, uint8_t data_lines) {
+    /* if medium size greater than 16Mb, use 4-Byte address, instruction should be added one */
+    if (flash->chip.capacity <= 0x1000000) {
+        flash->read_cmd_format.instruction = ins;
+        flash->read_cmd_format.address_size = 24;
+    } else {
+        flash->read_cmd_format.instruction = ins + 1;
+        flash->read_cmd_format.address_size = 32;
+    }
+
+    flash->read_cmd_format.instruction_lines = ins_lines;
+    flash->read_cmd_format.address_lines = addr_lines;
+    flash->read_cmd_format.alternate_bytes_lines = 0;
+    flash->read_cmd_format.dummy_cycles = dummy_cycles;
+    flash->read_cmd_format.data_lines = data_lines;
+}
+
+/**
+ * Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode.
+ *
+ * it will find the appropriate fast-read instruction to replace the read instruction(0x03)
+ * fast-read instruction @see SFUD_FLASH_EXT_INFO_TABLE
+ *
+ * @note When Flash is in QSPI mode, the method must be called after sfud_device_init().
+ *
+ * @param flash flash device
+ * @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4
+ *
+ * @return result
+ */
+sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width) {
+    size_t i = 0;
+    uint8_t read_mode = NORMAL_SPI_READ;
+    sfud_err result = SFUD_SUCCESS;
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4);
+
+    /* get read_mode, If don't found, the default is SFUD_QSPI_NORMAL_SPI_READ */
+    for (i = 0; i < sizeof(qspi_flash_ext_info_table) / sizeof(sfud_qspi_flash_ext_info); i++) {
+        if ((qspi_flash_ext_info_table[i].mf_id == flash->chip.mf_id)
+                && (qspi_flash_ext_info_table[i].type_id == flash->chip.type_id)
+                && (qspi_flash_ext_info_table[i].capacity_id == flash->chip.capacity_id)) {
+            read_mode = qspi_flash_ext_info_table[i].read_mode;
+        }
+    }
+
+    /* determine qspi supports which read mode and set read_cmd_format struct */
+    switch (data_line_width) {
+    case 1:
+        qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
+        break;
+    case 2:
+        if (read_mode & DUAL_IO) {
+            qspi_set_read_cmd_format(flash, SFUD_CMD_DUAL_IO_READ_DATA, 1, 2, 8, 2);
+        } else if (read_mode & DUAL_OUTPUT) {
+            qspi_set_read_cmd_format(flash, SFUD_CMD_DUAL_OUTPUT_READ_DATA, 1, 1, 8, 2);
+        } else {
+            qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
+        }
+        break;
+    case 4:
+        if (read_mode & QUAD_IO) {
+            qspi_set_read_cmd_format(flash, SFUD_CMD_QUAD_IO_READ_DATA, 1, 4, 6, 4);
+        } else if (read_mode & QUAD_OUTPUT) {
+            qspi_set_read_cmd_format(flash, SFUD_CMD_QUAD_OUTPUT_READ_DATA, 1, 1, 8, 4);
+        } else {
+            qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
+        }
+        break;
+    }
+
+    return result;
+}
+#endif /* SFUD_USING_QSPI */
+
+/**
+ * hardware initialize
+ */
+static sfud_err hardware_init(sfud_flash *flash) {
+    extern sfud_err sfud_spi_port_init(sfud_flash * flash);
+
+    sfud_err result = SFUD_SUCCESS;
+    size_t i;
+
+    SFUD_ASSERT(flash);
+
+    result = sfud_spi_port_init(flash);
+    if (result != SFUD_SUCCESS) {
+        return result;
+    }
+
+#ifdef SFUD_USING_QSPI
+    /* set default read instruction */
+    flash->read_cmd_format.instruction = SFUD_CMD_READ_DATA;
+#endif /* SFUD_USING_QSPI */
+
+    /* SPI write read function must be initialize */
+    SFUD_ASSERT(flash->spi.wr);
+    /* if the user don't configure flash chip information then using SFDP parameter or static flash parameter table */
+    if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0
+            || flash->chip.erase_gran_cmd == 0) {
+        /* read JEDEC ID include manufacturer ID, memory type ID and flash capacity ID */
+        result = read_jedec_id(flash);
+        if (result != SFUD_SUCCESS) {
+            return result;
+        }
+
+#ifdef SFUD_USING_SFDP
+        extern bool sfud_read_sfdp(sfud_flash *flash);
+        /* read SFDP parameters */
+        if (sfud_read_sfdp(flash)) {
+            flash->chip.name = NULL;
+            flash->chip.capacity = flash->sfdp.capacity;
+            /* only 1 byte or 256 bytes write mode for SFDP */
+            if (flash->sfdp.write_gran == 1) {
+                flash->chip.write_mode = SFUD_WM_BYTE;
+            } else {
+                flash->chip.write_mode = SFUD_WM_PAGE_256B;
+            }
+            /* find the the smallest erase sector size for eraser. then will use this size for erase granularity */
+            flash->chip.erase_gran = flash->sfdp.eraser[0].size;
+            flash->chip.erase_gran_cmd = flash->sfdp.eraser[0].cmd;
+            for (i = 1; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
+                if (flash->sfdp.eraser[i].size != 0 && flash->chip.erase_gran > flash->sfdp.eraser[i].size) {
+                    flash->chip.erase_gran = flash->sfdp.eraser[i].size;
+                    flash->chip.erase_gran_cmd = flash->sfdp.eraser[i].cmd;
+                }
+            }
+        } else {
+#endif
+
+#ifdef SFUD_USING_FLASH_INFO_TABLE
+            /* read SFDP parameters failed then using SFUD library provided static parameter */
+            for (i = 0; i < sizeof(flash_chip_table) / sizeof(sfud_flash_chip); i++) {
+                if ((flash_chip_table[i].mf_id == flash->chip.mf_id)
+                        && (flash_chip_table[i].type_id == flash->chip.type_id)
+                        && (flash_chip_table[i].capacity_id == flash->chip.capacity_id)) {
+                    flash->chip.name = flash_chip_table[i].name;
+                    flash->chip.capacity = flash_chip_table[i].capacity;
+                    flash->chip.write_mode = flash_chip_table[i].write_mode;
+                    flash->chip.erase_gran = flash_chip_table[i].erase_gran;
+                    flash->chip.erase_gran_cmd = flash_chip_table[i].erase_gran_cmd;
+                    break;
+                }
+            }
+#endif
+
+#ifdef SFUD_USING_SFDP
+        }
+#endif
+
+    }
+
+    if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0
+            || flash->chip.erase_gran_cmd == 0) {
+        SFUD_INFO("Warning: This flash device is not found or not support.");
+        return SFUD_ERR_NOT_FOUND;
+    } else {
+        const char *flash_mf_name = NULL;
+        /* find the manufacturer information */
+        for (i = 0; i < sizeof(mf_table) / sizeof(sfud_mf); i++) {
+            if (mf_table[i].id == flash->chip.mf_id) {
+                flash_mf_name = mf_table[i].name;
+                break;
+            }
+        }
+        /* print manufacturer and flash chip name */
+        if (flash_mf_name && flash->chip.name) {
+            SFUD_INFO("Find a %s %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.name,
+                    flash->chip.capacity);
+        } else if (flash_mf_name) {
+            SFUD_INFO("Find a %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.capacity);
+        } else {
+            SFUD_INFO("Find a flash chip. Size is %ld bytes.", flash->chip.capacity);
+        }
+    }
+
+    /* reset flash device */
+    result = reset(flash);
+    if (result != SFUD_SUCCESS) {
+        return result;
+    }
+
+    /* The flash all blocks is protected,so need change the flash status to unprotected before write and erase operate. */
+    if (flash->chip.write_mode & SFUD_WM_AAI) {
+        result = sfud_write_status(flash, true, 0x00);
+    } else {
+        /* MX25L3206E */
+        if ((0xC2 == flash->chip.mf_id) && (0x20 == flash->chip.type_id) && (0x16 == flash->chip.capacity_id)) {
+            result = sfud_write_status(flash, false, 0x00);
+        }
+    }
+    if (result != SFUD_SUCCESS) {
+        return result;
+    }    
+
+    /* if the flash is large than 16MB (256Mb) then enter in 4-Byte addressing mode */
+    if (flash->chip.capacity > (1L << 24)) {
+        result = set_4_byte_address_mode(flash, true);
+    } else {
+        flash->addr_in_4_byte = false;
+    }
+
+    return result;
+}
+
+/**
+ * software initialize
+ *
+ * @param flash flash device
+ *
+ * @return result
+ */
+static sfud_err software_init(const sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+
+    SFUD_ASSERT(flash);
+
+    return result;
+}
+
+/**
+ * read flash data
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size read size
+ * @param data read data pointer
+ *
+ * @return result
+ */
+sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[5], cmd_size;
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(data);
+    /* must be call this function after initialize OK */
+    SFUD_ASSERT(flash->init_ok);
+    /* check the flash address bound */
+    if (addr + size > flash->chip.capacity) {
+        SFUD_INFO("Error: Flash address is out of bound.");
+        return SFUD_ERR_ADDR_OUT_OF_BOUND;
+    }
+    /* lock SPI */
+    if (spi->lock) {
+        spi->lock(spi);
+    }
+
+    result = wait_busy(flash);
+
+    if (result == SFUD_SUCCESS) {
+#ifdef SFUD_USING_QSPI
+        if (flash->read_cmd_format.instruction != SFUD_CMD_READ_DATA) {
+            result = spi->qspi_read(spi, addr, (sfud_qspi_read_cmd_format *)&flash->read_cmd_format, data, size);
+        } else
+#endif
+        {
+            cmd_data[0] = SFUD_CMD_READ_DATA;
+            make_adress_byte_array(flash, addr, &cmd_data[1]);
+            cmd_size = flash->addr_in_4_byte ? 5 : 4;
+            result = spi->wr(spi, cmd_data, cmd_size, data, size);
+        }
+    }
+    /* unlock SPI */
+    if (spi->unlock) {
+        spi->unlock(spi);
+    }
+
+    return result;
+}
+
+/**
+ * erase all flash data
+ *
+ * @param flash flash device
+ *
+ * @return result
+ */
+sfud_err sfud_chip_erase(const sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[4];
+
+    SFUD_ASSERT(flash);
+    /* must be call this function after initialize OK */
+    SFUD_ASSERT(flash->init_ok);
+    /* lock SPI */
+    if (spi->lock) {
+        spi->lock(spi);
+    }
+
+    /* set the flash write enable */
+    result = set_write_enabled(flash, true);
+    if (result != SFUD_SUCCESS) {
+        goto __exit;
+    }
+
+    cmd_data[0] = SFUD_CMD_ERASE_CHIP;
+    /* dual-buffer write, like AT45DB series flash chip erase operate is different for other flash */
+    if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER) {
+        cmd_data[1] = 0x94;
+        cmd_data[2] = 0x80;
+        cmd_data[3] = 0x9A;
+        result = spi->wr(spi, cmd_data, 4, NULL, 0);
+    } else {
+        result = spi->wr(spi, cmd_data, 1, NULL, 0);
+    }
+    if (result != SFUD_SUCCESS) {
+        SFUD_INFO("Error: Flash chip erase SPI communicate error.");
+        goto __exit;
+    }
+    result = wait_busy(flash);
+
+__exit:
+    /* set the flash write disable */
+    set_write_enabled(flash, false);
+    /* unlock SPI */
+    if (spi->unlock) {
+        spi->unlock(spi);
+    }
+
+    return result;
+}
+
+/**
+ * erase flash data
+ *
+ * @note It will erase align by erase granularity.
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size erase size
+ *
+ * @return result
+ */
+sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size) {
+    extern size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr, size_t erase_size);
+
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[5], cmd_size, cur_erase_cmd;
+    size_t cur_erase_size;
+
+    SFUD_ASSERT(flash);
+    /* must be call this function after initialize OK */
+    SFUD_ASSERT(flash->init_ok);
+    /* check the flash address bound */
+    if (addr + size > flash->chip.capacity) {
+        SFUD_INFO("Error: Flash address is out of bound.");
+        return SFUD_ERR_ADDR_OUT_OF_BOUND;
+    }
+
+    if (addr == 0 && size == flash->chip.capacity) {
+        return sfud_chip_erase(flash);
+    }
+
+    /* lock SPI */
+    if (spi->lock) {
+        spi->lock(spi);
+    }
+
+    /* loop erase operate. erase unit is erase granularity */
+    while (size) {
+        /* if this flash is support SFDP parameter, then used SFDP parameter supplies eraser */
+#ifdef SFUD_USING_SFDP
+        size_t eraser_index;
+        if (flash->sfdp.available) {
+            /* get the suitable eraser for erase process from SFDP parameter */
+            eraser_index = sfud_sfdp_get_suitable_eraser(flash, addr, size);
+            cur_erase_cmd = flash->sfdp.eraser[eraser_index].cmd;
+            cur_erase_size = flash->sfdp.eraser[eraser_index].size;
+        } else {
+#else
+        {
+#endif
+            cur_erase_cmd = flash->chip.erase_gran_cmd;
+            cur_erase_size = flash->chip.erase_gran;
+        }
+        /* set the flash write enable */
+        result = set_write_enabled(flash, true);
+        if (result != SFUD_SUCCESS) {
+            goto __exit;
+        }
+
+        cmd_data[0] = cur_erase_cmd;
+        make_adress_byte_array(flash, addr, &cmd_data[1]);
+        cmd_size = flash->addr_in_4_byte ? 5 : 4;
+        result = spi->wr(spi, cmd_data, cmd_size, NULL, 0);
+        if (result != SFUD_SUCCESS) {
+            SFUD_INFO("Error: Flash erase SPI communicate error.");
+            goto __exit;
+        }
+        result = wait_busy(flash);
+        if (result != SFUD_SUCCESS) {
+            goto __exit;
+        }
+        /* make erase align and calculate next erase address */
+        if (addr % cur_erase_size != 0) {
+            if (size > cur_erase_size - (addr % cur_erase_size)) {
+                size -= cur_erase_size - (addr % cur_erase_size);
+                addr += cur_erase_size - (addr % cur_erase_size);
+            } else {
+                goto __exit;
+            }
+        } else {
+            if (size > cur_erase_size) {
+                size -= cur_erase_size;
+                addr += cur_erase_size;
+            } else {
+                goto __exit;
+            }
+        }
+    }
+
+__exit:
+    /* set the flash write disable */
+    set_write_enabled(flash, false);
+    /* unlock SPI */
+    if (spi->unlock) {
+        spi->unlock(spi);
+    }
+
+    return result;
+}
+
+/**
+ * write flash data (no erase operate) for write 1 to 256 bytes per page mode or byte write mode
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size write size
+ * @param write_gran write granularity bytes, only support 1 or 256
+ * @param data write data
+ *
+ * @return result
+ */
+static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size, uint16_t write_gran,
+        const uint8_t *data) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    static uint8_t cmd_data[5 + SFUD_WRITE_MAX_PAGE_SIZE];
+    uint8_t cmd_size;
+    size_t data_size;
+
+    SFUD_ASSERT(flash);
+    /* only support 1 or 256 */
+    SFUD_ASSERT(write_gran == 1 || write_gran == 256);
+    /* must be call this function after initialize OK */
+    SFUD_ASSERT(flash->init_ok);
+    /* check the flash address bound */
+    if (addr + size > flash->chip.capacity) {
+        SFUD_INFO("Error: Flash address is out of bound.");
+        return SFUD_ERR_ADDR_OUT_OF_BOUND;
+    }
+    /* lock SPI */
+    if (spi->lock) {
+        spi->lock(spi);
+    }
+
+    /* loop write operate. write unit is write granularity */
+    while (size) {
+        /* set the flash write enable */
+        result = set_write_enabled(flash, true);
+        if (result != SFUD_SUCCESS) {
+            goto __exit;
+        }
+        cmd_data[0] = SFUD_CMD_PAGE_PROGRAM;
+        make_adress_byte_array(flash, addr, &cmd_data[1]);
+        cmd_size = flash->addr_in_4_byte ? 5 : 4;
+
+        /* make write align and calculate next write address */
+        if (addr % write_gran != 0) {
+            if (size > write_gran - (addr % write_gran)) {
+                data_size = write_gran - (addr % write_gran);
+            } else {
+                data_size = size;
+            }
+        } else {
+            if (size > write_gran) {
+                data_size = write_gran;
+            } else {
+                data_size = size;
+            }
+        }
+        size -= data_size;
+        addr += data_size;
+
+        memcpy(&cmd_data[cmd_size], data, data_size);
+
+        result = spi->wr(spi, cmd_data, cmd_size + data_size, NULL, 0);
+        if (result != SFUD_SUCCESS) {
+            SFUD_INFO("Error: Flash write SPI communicate error.");
+            goto __exit;
+        }
+        result = wait_busy(flash);
+        if (result != SFUD_SUCCESS) {
+            goto __exit;
+        }
+        data += data_size;
+    }
+
+__exit:
+    /* set the flash write disable */
+    set_write_enabled(flash, false);
+    /* unlock SPI */
+    if (spi->unlock) {
+        spi->unlock(spi);
+    }
+
+    return result;
+}
+
+/**
+ * write flash data (no erase operate) for auto address increment mode
+ *
+ * If the address is odd number, it will place one 0xFF before the start of data for protect the old data.
+ * If the latest remain size is 1, it will append one 0xFF at the end of data for protect the old data.
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size write size
+ * @param data write data
+ *
+ * @return result
+ */
+static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[8], cmd_size;
+    bool first_write = true;
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(flash->init_ok);
+    /* check the flash address bound */
+    if (addr + size > flash->chip.capacity) {
+        SFUD_INFO("Error: Flash address is out of bound.");
+        return SFUD_ERR_ADDR_OUT_OF_BOUND;
+    }
+    /* lock SPI */
+    if (spi->lock) {
+        spi->lock(spi);
+    }
+    /* The address must be even for AAI write mode. So it must write one byte first when address is odd. */
+    if (addr % 2 != 0) {
+        result = page256_or_1_byte_write(flash, addr++, 1, 1, data++);
+        if (result != SFUD_SUCCESS) {
+            goto __exit;
+        }
+        size--;
+    }
+    /* set the flash write enable */
+    result = set_write_enabled(flash, true);
+    if (result != SFUD_SUCCESS) {
+        goto __exit;
+    }
+    /* loop write operate. */
+    cmd_data[0] = SFUD_CMD_AAI_WORD_PROGRAM;
+    while (size >= 2) {
+        if (first_write) {
+            make_adress_byte_array(flash, addr, &cmd_data[1]);
+            cmd_size = flash->addr_in_4_byte ? 5 : 4;
+            cmd_data[cmd_size] = *data;
+            cmd_data[cmd_size + 1] = *(data + 1);
+            first_write = false;
+        } else {
+            cmd_size = 1;
+            cmd_data[1] = *data;
+            cmd_data[2] = *(data + 1);
+        }
+
+        result = spi->wr(spi, cmd_data, cmd_size + 2, NULL, 0);
+        if (result != SFUD_SUCCESS) {
+            SFUD_INFO("Error: Flash write SPI communicate error.");
+            goto __exit;
+        }
+
+        result = wait_busy(flash);
+        if (result != SFUD_SUCCESS) {
+            goto __exit;
+        }
+
+        size -= 2;
+        addr += 2;
+        data += 2;
+    }
+    /* set the flash write disable for exit AAI mode */
+    result = set_write_enabled(flash, false);
+    /* write last one byte data when origin write size is odd */
+    if (result == SFUD_SUCCESS && size == 1) {
+        result = page256_or_1_byte_write(flash, addr, 1, 1, data);
+    }
+
+__exit:
+    if (result != SFUD_SUCCESS) {
+        set_write_enabled(flash, false);
+    }
+    /* unlock SPI */
+    if (spi->unlock) {
+        spi->unlock(spi);
+    }
+
+    return result;
+}
+
+/**
+ * write flash data (no erase operate)
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size write size
+ * @param data write data
+ *
+ * @return result
+ */
+sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) {
+    sfud_err result = SFUD_SUCCESS;
+
+    if (flash->chip.write_mode & SFUD_WM_PAGE_256B) {
+        result = page256_or_1_byte_write(flash, addr, size, 256, data);
+    } else if (flash->chip.write_mode & SFUD_WM_AAI) {
+        result = aai_write(flash, addr, size, data);
+    } else if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER) {
+        //TODO dual-buffer write mode
+    }
+
+    return result;
+}
+
+/**
+ * erase and write flash data
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size write size
+ * @param data write data
+ *
+ * @return result
+ */
+sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) {
+    sfud_err result = SFUD_SUCCESS;
+
+    result = sfud_erase(flash, addr, size);
+
+    if (result == SFUD_SUCCESS) {
+        result = sfud_write(flash, addr, size, data);
+    }
+
+    return result;
+}
+
+static sfud_err reset(const sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[2];
+
+    SFUD_ASSERT(flash);
+
+    cmd_data[0] = SFUD_CMD_ENABLE_RESET;
+    result = spi->wr(spi, cmd_data, 1, NULL, 0);
+    if (result == SFUD_SUCCESS) {
+        result = wait_busy(flash);
+    } else {
+        SFUD_INFO("Error: Flash device reset failed.");
+        return result;
+    }
+
+    cmd_data[1] = SFUD_CMD_RESET;
+    result = spi->wr(spi, &cmd_data[1], 1, NULL, 0);
+
+    if (result == SFUD_SUCCESS) {
+        result = wait_busy(flash);
+    }
+
+    if (result == SFUD_SUCCESS) {
+        SFUD_DEBUG("Flash device reset success.");
+    } else {
+        SFUD_INFO("Error: Flash device reset failed.");
+    }
+
+    return result;
+}
+
+static sfud_err read_jedec_id(sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[1], recv_data[3];
+
+    SFUD_ASSERT(flash);
+
+    cmd_data[0] = SFUD_CMD_JEDEC_ID;
+    result = spi->wr(spi, cmd_data, sizeof(cmd_data), recv_data, sizeof(recv_data));
+    if (result == SFUD_SUCCESS) {
+        flash->chip.mf_id = recv_data[0];
+        flash->chip.type_id = recv_data[1];
+        flash->chip.capacity_id = recv_data[2];
+        SFUD_DEBUG("The flash device manufacturer ID is 0x%02X, memory type ID is 0x%02X, capacity ID is 0x%02X.",
+                flash->chip.mf_id, flash->chip.type_id, flash->chip.capacity_id);
+    } else {
+        SFUD_INFO("Error: Read flash device JEDEC ID error.");
+    }
+
+    return result;
+}
+
+/**
+ * set the flash write enable or write disable
+ *
+ * @param flash flash device
+ * @param enabled true: enable  false: disable
+ *
+ * @return result
+ */
+static sfud_err set_write_enabled(const sfud_flash *flash, bool enabled) {
+    sfud_err result = SFUD_SUCCESS;
+    uint8_t cmd, register_status;
+
+    SFUD_ASSERT(flash);
+
+    if (enabled) {
+        cmd = SFUD_CMD_WRITE_ENABLE;
+    } else {
+        cmd = SFUD_CMD_WRITE_DISABLE;
+    }
+
+    result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
+
+    if (result == SFUD_SUCCESS) {
+        result = sfud_read_status(flash, &register_status);
+    }
+
+    if (result == SFUD_SUCCESS) {
+        if (enabled && (register_status & SFUD_STATUS_REGISTER_WEL) == 0) {
+            SFUD_INFO("Error: Can't enable write status.");
+            return SFUD_ERR_WRITE;
+        } else if (!enabled && (register_status & SFUD_STATUS_REGISTER_WEL) != 0) {
+            SFUD_INFO("Error: Can't disable write status.");
+            return SFUD_ERR_WRITE;
+        }
+    }
+
+    return result;
+}
+
+/**
+ * enable or disable 4-Byte addressing for flash
+ *
+ * @note The 4-Byte addressing just supported for the flash capacity which is large then 16MB (256Mb).
+ *
+ * @param flash flash device
+ * @param enabled true: enable   false: disable
+ *
+ * @return result
+ */
+static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled) {
+    sfud_err result = SFUD_SUCCESS;
+    uint8_t cmd;
+
+    SFUD_ASSERT(flash);
+
+    /* set the flash write enable */
+    result = set_write_enabled(flash, true);
+    if (result != SFUD_SUCCESS) {
+        return result;
+    }
+
+    if (enabled) {
+        cmd = SFUD_CMD_ENTER_4B_ADDRESS_MODE;
+    } else {
+        cmd = SFUD_CMD_EXIT_4B_ADDRESS_MODE;
+    }
+
+    result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
+
+    if (result == SFUD_SUCCESS) {
+        flash->addr_in_4_byte = enabled ? true : false;
+        SFUD_DEBUG("%s 4-Byte addressing mode success.", enabled ? "Enter" : "Exit");
+    } else {
+        SFUD_INFO("Error: %s 4-Byte addressing mode failed.", enabled ? "Enter" : "Exit");
+    }
+
+    return result;
+}
+
+/**
+ * read flash register status
+ *
+ * @param flash flash device
+ * @param status register status
+ *
+ * @return result
+ */
+sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status) {
+    uint8_t cmd = SFUD_CMD_READ_STATUS_REGISTER;
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(status);
+
+    return flash->spi.wr(&flash->spi, &cmd, 1, status, 1);
+}
+
+static sfud_err wait_busy(const sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+    uint8_t status;
+    size_t retry_times = flash->retry.times;
+
+    SFUD_ASSERT(flash);
+
+    while (true) {
+        result = sfud_read_status(flash, &status);
+        if (result == SFUD_SUCCESS && ((status & SFUD_STATUS_REGISTER_BUSY)) == 0) {
+            break;
+        }
+        /* retry counts */
+        SFUD_RETRY_PROCESS(flash->retry.delay, retry_times, result);
+    }
+
+    if (result != SFUD_SUCCESS || ((status & SFUD_STATUS_REGISTER_BUSY)) != 0) {
+        SFUD_INFO("Error: Flash wait busy has an error.");
+    }
+
+    return result;
+}
+
+static void make_adress_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array) {
+    uint8_t len, i;
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(array);
+
+    len = flash->addr_in_4_byte ? 4 : 3;
+
+    for (i = 0; i < len; i++) {
+        array[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF;
+    }
+}
+
+/**
+ * write status register
+ *
+ * @param flash flash device
+ * @param is_volatile true: volatile mode, false: non-volatile mode
+ * @param status register status
+ *
+ * @return result
+ */
+sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status) {
+    sfud_err result = SFUD_SUCCESS;
+    const sfud_spi *spi = &flash->spi;
+    uint8_t cmd_data[2];
+
+    SFUD_ASSERT(flash);
+
+    if (is_volatile) {
+        cmd_data[0] = SFUD_VOLATILE_SR_WRITE_ENABLE;
+        result = spi->wr(spi, cmd_data, 1, NULL, 0);
+    } else {
+        result = set_write_enabled(flash, true);
+    }
+
+    if (result == SFUD_SUCCESS) {
+        cmd_data[0] = SFUD_CMD_WRITE_STATUS_REGISTER;
+        cmd_data[1] = status;
+        result = spi->wr(spi, cmd_data, 2, NULL, 0);
+    }
+
+    if (result != SFUD_SUCCESS) {
+        SFUD_INFO("Error: Write_status register failed.");
+    }
+
+    return result;
+}

+ 178 - 0
components/sfud/sfud.h

@@ -0,0 +1,178 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: It is an head file for this library. You can see all of the functions which can be called by user.
+ * Created on: 2016-04-23
+ */
+
+#ifndef _SFUD_H_
+#define _SFUD_H_
+
+#include "sfud_def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ../src/sfup.c */
+/**
+ * SFUD library initialize.
+ *
+ * @return result
+ */
+sfud_err sfud_init(void);
+
+/**
+ * SFUD initialize by flash device
+ *
+ * @param flash flash device
+ *
+ * @return result
+ */
+sfud_err sfud_device_init(sfud_flash *flash);
+
+/**
+ * get flash device by its index which in the flash information table
+ *
+ * @param index the index which in the flash information table  @see flash_table
+ *
+ * @return flash device
+ */
+sfud_flash *sfud_get_device(size_t index);
+
+/**
+ * get flash device total number on flash device information table  @see flash_table
+ *
+ * @return flash device total number
+ */
+size_t sfud_get_device_num(void);
+
+/**
+ * get flash device information table  @see flash_table
+ *
+ * @return flash device table pointer
+ */
+const sfud_flash *sfud_get_device_table(void);
+
+#ifdef SFUD_USING_QSPI
+/**
+ * Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode.
+ *
+ * it will find the appropriate fast-read instruction to replace the read instruction(0x03)
+ * fast-read instruction @see SFUD_FLASH_EXT_INFO_TABLE
+ *
+ * @note When Flash is in QSPI mode, the method must be called after sfud_device_init().
+ *
+ * @param flash flash device
+ * @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4
+ *
+ * @return result
+ */
+sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width);
+#endif /* SFUD_USING_QSPI */
+
+/**
+ * read flash data
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size read size
+ * @param data read data pointer
+ *
+ * @return result
+ */
+sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data);
+
+/**
+ * erase flash data
+ *
+ * @note It will erase align by erase granularity.
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size erase size
+ *
+ * @return result
+ */
+sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size);
+
+/**
+ * write flash data (no erase operate)
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param data write data
+ * @param size write size
+ *
+ * @return result
+ */
+sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
+
+/**
+ * erase and write flash data
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param size write size
+ * @param data write data
+ *
+ * @return result
+ */
+sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
+
+/**
+ * erase all flash data
+ *
+ * @param flash flash device
+ *
+ * @return result
+ */
+sfud_err sfud_chip_erase(const sfud_flash *flash);
+
+/**
+ * read flash register status
+ *
+ * @param flash flash device
+ * @param status register status
+ *
+ * @return result
+ */
+sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status);
+
+/**
+ * write status register
+ *
+ * @param flash flash device
+ * @param is_volatile true: volatile mode, false: non-volatile mode
+ * @param status register status
+ *
+ * @return result
+ */
+sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFUD_H_ */

+ 51 - 0
components/sfud/sfud_cfg.h

@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: It is the configure head file for this library.
+ * Created on: 2016-04-23
+ */
+
+#ifndef _SFUD_CFG_H_
+#define _SFUD_CFG_H_
+
+// #define SFUD_DEBUG_MODE
+
+#define SFUD_USING_SFDP
+
+#define SFUD_USING_FLASH_INFO_TABLE
+
+#define SFUD_FLASH_DEVICE_TABLE {0}
+
+// enum {
+//     SFUD_XXXX_DEVICE_INDEX = 0,
+// };
+
+// #define SFUD_FLASH_DEVICE_TABLE                                                \
+// {                                                                              \
+//     [SFUD_XXXX_DEVICE_INDEX] = {.name = "XXXX", .spi.name = "SPIX"},           \
+// }
+
+// #define SFUD_USING_QSPI
+
+#endif /* _SFUD_CFG_H_ */

+ 296 - 0
components/sfud/sfud_def.h

@@ -0,0 +1,296 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: It is the macro definition head file for this library.
+ * Created on: 2016-04-23
+ */
+
+#ifndef _SFUD_DEF_H_
+#define _SFUD_DEF_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sfud_cfg.h>
+#include "sfud_flash_def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* debug print function. Must be implement by user. */
+#ifdef SFUD_DEBUG_MODE
+#ifndef SFUD_DEBUG
+#define SFUD_DEBUG(...) sfud_log_debug(__FILE__, __LINE__, __VA_ARGS__)
+#endif /* SFUD_DEBUG */
+#else
+#define SFUD_DEBUG(...)
+#endif /* SFUD_DEBUG_MODE */
+
+#ifndef SFUD_INFO
+#define SFUD_INFO(...)  sfud_log_info(__VA_ARGS__)
+#endif
+
+/* assert for developer. */
+#ifdef SFUD_DEBUG_MODE
+#define SFUD_ASSERT(EXPR)                                                      \
+if (!(EXPR))                                                                   \
+{                                                                              \
+    SFUD_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__);          \
+    while (1);                                                                 \
+}
+#else
+#define SFUD_ASSERT(EXPR)
+#endif
+
+/**
+ * retry process
+ *
+ * @param delay delay function for every retry. NULL will not delay for every retry.
+ * @param retry retry counts
+ * @param result SFUD_ERR_TIMEOUT: retry timeout
+ */
+#define SFUD_RETRY_PROCESS(delay, retry, result)                               \
+    void (*__delay_temp)(void) = (void (*)(void))delay;                        \
+    if (retry == 0) {result = SFUD_ERR_TIMEOUT;break;}                         \
+    else {if (__delay_temp) {__delay_temp();} retry --;}
+
+/* software version number */
+#define SFUD_SW_VERSION                             "1.1.0"
+/*
+ * all defined supported command
+ */
+#ifndef SFUD_CMD_WRITE_ENABLE
+#define SFUD_CMD_WRITE_ENABLE                          0x06
+#endif
+
+#ifndef SFUD_CMD_WRITE_DISABLE
+#define SFUD_CMD_WRITE_DISABLE                         0x04
+#endif
+
+#ifndef SFUD_CMD_READ_STATUS_REGISTER
+#define SFUD_CMD_READ_STATUS_REGISTER                  0x05
+#endif
+
+#ifndef SFUD_VOLATILE_SR_WRITE_ENABLE
+#define SFUD_VOLATILE_SR_WRITE_ENABLE                  0x50
+#endif
+
+#ifndef SFUD_CMD_WRITE_STATUS_REGISTER
+#define SFUD_CMD_WRITE_STATUS_REGISTER                 0x01
+#endif
+
+#ifndef SFUD_CMD_PAGE_PROGRAM
+#define SFUD_CMD_PAGE_PROGRAM                          0x02
+#endif
+
+#ifndef SFUD_CMD_AAI_WORD_PROGRAM
+#define SFUD_CMD_AAI_WORD_PROGRAM                      0xAD
+#endif
+
+#ifndef SFUD_CMD_ERASE_CHIP
+#define SFUD_CMD_ERASE_CHIP                            0xC7
+#endif
+
+#ifndef SFUD_CMD_READ_DATA
+#define SFUD_CMD_READ_DATA                             0x03
+#endif
+
+#ifndef SFUD_CMD_DUAL_OUTPUT_READ_DATA 
+#define SFUD_CMD_DUAL_OUTPUT_READ_DATA                 0x3B
+#endif
+
+#ifndef SFUD_CMD_DUAL_IO_READ_DATA 
+#define SFUD_CMD_DUAL_IO_READ_DATA                     0xBB
+#endif
+
+#ifndef SFUD_CMD_QUAD_IO_READ_DATA
+#define SFUD_CMD_QUAD_IO_READ_DATA                     0xEB
+#endif
+
+#ifndef SFUD_CMD_QUAD_OUTPUT_READ_DATA
+#define SFUD_CMD_QUAD_OUTPUT_READ_DATA                 0x6B
+#endif
+
+#ifndef SFUD_CMD_MANUFACTURER_DEVICE_ID
+#define SFUD_CMD_MANUFACTURER_DEVICE_ID                0x90
+#endif
+
+#ifndef SFUD_CMD_JEDEC_ID
+#define SFUD_CMD_JEDEC_ID                              0x9F
+#endif
+
+#ifndef SFUD_CMD_READ_UNIQUE_ID
+#define SFUD_CMD_READ_UNIQUE_ID                        0x4B
+#endif
+
+#ifndef SFUD_CMD_READ_SFDP_REGISTER
+#define SFUD_CMD_READ_SFDP_REGISTER                    0x5A
+#endif
+
+#ifndef SFUD_CMD_ENABLE_RESET
+#define SFUD_CMD_ENABLE_RESET                          0x66
+#endif
+
+#ifndef SFUD_CMD_RESET
+#define SFUD_CMD_RESET                                 0x99
+#endif
+
+#ifndef SFUD_CMD_ENTER_4B_ADDRESS_MODE
+#define SFUD_CMD_ENTER_4B_ADDRESS_MODE                 0xB7
+#endif
+
+#ifndef SFUD_CMD_EXIT_4B_ADDRESS_MODE
+#define SFUD_CMD_EXIT_4B_ADDRESS_MODE                  0xE9
+#endif
+
+#ifndef SFUD_WRITE_MAX_PAGE_SIZE
+#define SFUD_WRITE_MAX_PAGE_SIZE                        256
+#endif
+
+/* send dummy data for read data */
+#ifndef SFUD_DUMMY_DATA
+#define SFUD_DUMMY_DATA                                0xFF
+#endif
+
+/* maximum number of erase type support on JESD216 (V1.0) */
+#define SFUD_SFDP_ERASE_TYPE_MAX_NUM                      4
+
+/**
+ * status register bits
+ */
+enum {
+    SFUD_STATUS_REGISTER_BUSY = (1 << 0),                  /**< busing */
+    SFUD_STATUS_REGISTER_WEL = (1 << 1),                   /**< write enable latch */
+    SFUD_STATUS_REGISTER_SRP = (1 << 7),                   /**< status register protect */
+};
+
+/**
+ * error code
+ */
+typedef enum {
+    SFUD_SUCCESS = 0,                                      /**< success */
+    SFUD_ERR_NOT_FOUND = 1,                                /**< not found or not supported */
+    SFUD_ERR_WRITE = 2,                                    /**< write error */
+    SFUD_ERR_READ = 3,                                     /**< read error */
+    SFUD_ERR_TIMEOUT = 4,                                  /**< timeout error */
+    SFUD_ERR_ADDR_OUT_OF_BOUND = 5,                        /**< address is out of flash bound */
+} sfud_err;
+
+#ifdef SFUD_USING_QSPI
+/**
+ * QSPI flash read cmd format
+ */
+typedef struct {
+    uint8_t instruction;
+    uint8_t instruction_lines;
+    uint8_t address_size;
+    uint8_t address_lines;
+    uint8_t alternate_bytes_lines;
+    uint8_t dummy_cycles;
+    uint8_t data_lines;
+} sfud_qspi_read_cmd_format;
+#endif /* SFUD_USING_QSPI */
+
+/* SPI bus write read data function type */
+typedef sfud_err (*spi_write_read_func)(const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, size_t read_size);
+
+#ifdef SFUD_USING_SFDP
+/**
+ * the SFDP (Serial Flash Discoverable Parameters) parameter info which used on this library
+ */
+typedef struct {
+    bool available;                              /**< available when read SFDP OK */
+    uint8_t major_rev;                           /**< SFDP Major Revision */
+    uint8_t minor_rev;                           /**< SFDP Minor Revision */
+    uint16_t write_gran;                         /**< write granularity (bytes) */
+    uint8_t erase_4k;                            /**< 4 kilobyte erase is supported throughout the device */
+    uint8_t erase_4k_cmd;                        /**< 4 Kilobyte erase command */
+    bool sr_is_non_vola;                         /**< status register is supports non-volatile */
+    uint8_t vola_sr_we_cmd;                      /**< volatile status register write enable command */
+    bool addr_3_byte;                            /**< supports 3-Byte addressing */
+    bool addr_4_byte;                            /**< supports 4-Byte addressing */
+    uint32_t capacity;                           /**< flash capacity (bytes) */
+    struct {
+        uint32_t size;                           /**< erase sector size (bytes). 0x00: not available */
+        uint8_t cmd;                             /**< erase command */
+    } eraser[SFUD_SFDP_ERASE_TYPE_MAX_NUM];      /**< supported eraser types table */
+    //TODO lots of fast read-related stuff (like modes supported and number of wait states/dummy cycles needed in each)
+} sfud_sfdp, *sfud_sfdp_t;
+#endif
+
+/**
+ * SPI device
+ */
+typedef struct __sfud_spi {
+    /* SPI device name */
+    char *name;
+    /* SPI bus write read data function */
+    sfud_err (*wr)(const struct __sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
+                   size_t read_size);
+#ifdef SFUD_USING_QSPI
+    /* QSPI fast read function */
+    sfud_err (*qspi_read)(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
+                          uint8_t *read_buf, size_t read_size);
+#endif
+    /* lock SPI bus */
+    void (*lock)(const struct __sfud_spi *spi);
+    /* unlock SPI bus */
+    void (*unlock)(const struct __sfud_spi *spi);
+    /* some user data */
+    void *user_data;
+} sfud_spi, *sfud_spi_t;
+
+/**
+ * serial flash device
+ */
+typedef struct {
+    char *name;                                  /**< serial flash name */
+    size_t index;                                /**< index of flash device information table  @see flash_table */
+    sfud_flash_chip chip;                        /**< flash chip information */
+    sfud_spi spi;                                /**< SPI device */
+    bool init_ok;                                /**< initialize OK flag */
+    bool addr_in_4_byte;                         /**< flash is in 4-Byte addressing */
+    struct {
+        void (*delay)(void);                     /**< every retry's delay */
+        size_t times;                            /**< default times for error retry */
+    } retry;
+    void *user_data;                             /**< some user data */
+
+#ifdef SFUD_USING_QSPI
+    sfud_qspi_read_cmd_format read_cmd_format;   /**< fast read cmd format */
+#endif
+
+#ifdef SFUD_USING_SFDP
+    sfud_sfdp sfdp;                              /**< serial flash discoverable parameters by JEDEC standard */
+#endif
+
+} sfud_flash, *sfud_flash_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFUD_DEF_H_ */

+ 193 - 0
components/sfud/sfud_flash_def.h

@@ -0,0 +1,193 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: It is the flash types and specification macro definition head file for this library.
+ * Created on: 2016-06-09
+ */
+
+#ifndef _SFUD_FLASH_DEF_H_
+#define _SFUD_FLASH_DEF_H_
+
+#include <stdint.h>
+#include <sfud_cfg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * flash program(write) data mode
+ */
+enum sfud_write_mode {
+    SFUD_WM_PAGE_256B = 1 << 0,                            /**< write 1 to 256 bytes per page */
+    SFUD_WM_BYTE = 1 << 1,                                 /**< byte write */
+    SFUD_WM_AAI = 1 << 2,                                  /**< auto address increment */
+    SFUD_WM_DUAL_BUFFER = 1 << 3,                          /**< dual-buffer write, like AT45DB series */
+};
+
+/* manufacturer information */
+typedef struct {
+    char *name;
+    uint8_t id;
+} sfud_mf;
+
+/* flash chip information */
+typedef struct {
+    char *name;                                  /**< flash chip name */
+    uint8_t mf_id;                               /**< manufacturer ID */
+    uint8_t type_id;                             /**< memory type ID */
+    uint8_t capacity_id;                         /**< capacity ID */
+    uint32_t capacity;                           /**< flash capacity (bytes) */
+    uint16_t write_mode;                         /**< write mode @see sfud_write_mode */
+    uint32_t erase_gran;                         /**< erase granularity (bytes) */
+    uint8_t erase_gran_cmd;                      /**< erase granularity size block command */
+} sfud_flash_chip;
+
+#ifdef SFUD_USING_QSPI
+/* QSPI flash chip's extended information compared with SPI flash */
+typedef struct {
+    uint8_t mf_id;                               /**< manufacturer ID */
+    uint8_t type_id;                             /**< memory type ID */
+    uint8_t capacity_id;                         /**< capacity ID */
+    uint8_t read_mode;                           /**< supported read mode on this qspi flash chip */
+} sfud_qspi_flash_ext_info;
+#endif
+
+/* SFUD support manufacturer JEDEC ID */
+#define SFUD_MF_ID_CYPRESS                             0x01
+#define SFUD_MF_ID_FUJITSU                             0x04
+#define SFUD_MF_ID_EON                                 0x1C
+#define SFUD_MF_ID_ATMEL                               0x1F
+#define SFUD_MF_ID_MICRON                              0x20
+#define SFUD_MF_ID_AMIC                                0x37
+#define SFUD_MF_ID_NOR_MEM                             0x52
+#define SFUD_MF_ID_SANYO                               0x62
+#define SFUD_MF_ID_INTEL                               0x89
+#define SFUD_MF_ID_ESMT                                0x8C
+#define SFUD_MF_ID_FUDAN                               0xA1
+#define SFUD_MF_ID_HYUNDAI                             0xAD
+#define SFUD_MF_ID_SST                                 0xBF
+#define SFUD_MF_ID_MICRONIX                            0xC2
+#define SFUD_MF_ID_GIGADEVICE                          0xC8
+#define SFUD_MF_ID_ISSI                                0xD5
+#define SFUD_MF_ID_WINBOND                             0xEF
+
+/* SFUD supported manufacturer information table */
+#define SFUD_MF_TABLE                                     \
+{                                                         \
+    {"Cypress",    SFUD_MF_ID_CYPRESS},                   \
+    {"Fujitsu",    SFUD_MF_ID_FUJITSU},                   \
+    {"EON",        SFUD_MF_ID_EON},                       \
+    {"Atmel",      SFUD_MF_ID_ATMEL},                     \
+    {"Micron",     SFUD_MF_ID_MICRON},                    \
+    {"AMIC",       SFUD_MF_ID_AMIC},                      \
+    {"Sanyo",      SFUD_MF_ID_SANYO},                     \
+    {"Intel",      SFUD_MF_ID_INTEL},                     \
+    {"ESMT",       SFUD_MF_ID_ESMT},                      \
+    {"Fudan",      SFUD_MF_ID_FUDAN},                     \
+    {"Hyundai",    SFUD_MF_ID_HYUNDAI},                   \
+    {"SST",        SFUD_MF_ID_SST},                       \
+    {"GigaDevice", SFUD_MF_ID_GIGADEVICE},                \
+    {"ISSI",       SFUD_MF_ID_ISSI},                      \
+    {"Winbond",    SFUD_MF_ID_WINBOND},                   \
+    {"Micronix",   SFUD_MF_ID_MICRONIX},                  \
+    {"Nor-Mem",    SFUD_MF_ID_NOR_MEM},                   \
+}
+
+#ifdef SFUD_USING_FLASH_INFO_TABLE
+/* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
+ * then the SFUD will find the flash chip information by this table. You can add other flash to here then
+ *  notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
+ * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
+ */
+#define SFUD_FLASH_CHIP_TABLE                                                                                       \
+{                                                                                                                   \
+    {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81},      \
+    {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
+    {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
+    {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
+    {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
+    {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
+    {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
+    {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
+    {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
+    {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
+    {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
+    {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
+    {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                      \
+    {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
+    {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
+    {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
+    {"GD25Q32C", SFUD_MF_ID_GIGADEVICE, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
+    {"GD25Q80C", SFUD_MF_ID_GIGADEVICE, 0x40, 0x14, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
+    {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
+    {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
+    {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
+    {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},                     \
+    {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
+    {"NM25Q128EV", SFUD_MF_ID_NOR_MEM, 0x21, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
+}
+#endif /* SFUD_USING_FLASH_INFO_TABLE */
+
+#ifdef SFUD_USING_QSPI
+/* This table saves flash read-fast instructions in QSPI mode, 
+ * SFUD can use this table to select the most appropriate read instruction for flash.
+ * | mf_id | type_id | capacity_id | qspi_read_mode |
+ */
+#define SFUD_FLASH_EXT_INFO_TABLE                                                                  \
+{                                                                                                  \
+    /* W25Q40BV */                                                                                 \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x13, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
+    /* W25Q80JV */                                                                                 \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
+    /* W25Q16BV */                                                                                 \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
+    /* W25Q32BV */                                                                                 \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_OUTPUT|QUAD_IO},             \
+    /* W25Q64JV */                                                                                 \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
+    /* W25Q128JV */                                                                                \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
+    /* W25Q256FV */                                                                                \
+    {SFUD_MF_ID_WINBOND, 0x40, 0x19, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
+    /* EN25Q32B */                                                                                 \
+    {SFUD_MF_ID_EON, 0x30, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_IO},                             \
+    /* S25FL216K */                                                                                \
+    {SFUD_MF_ID_CYPRESS, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
+    /* A25L080 */                                                                                  \
+    {SFUD_MF_ID_AMIC, 0x30, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO},                            \
+    /* A25LQ64 */                                                                                  \
+    {SFUD_MF_ID_AMIC, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_IO},                    \
+    /* MX25L3206E and KH25L3206E */                                                                \
+    {SFUD_MF_ID_MICRONIX, 0x20, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT},                                \
+    /* GD25Q64B */                                                                                 \
+    {SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT},                              \
+}
+#endif /* SFUD_USING_QSPI */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFUD_FLASH_DEF_H_ */

+ 143 - 0
components/sfud/sfud_port.c

@@ -0,0 +1,143 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: Portable interface for each platform.
+ * Created on: 2016-04-23
+ */
+
+#include <sfud.h>
+#include <stdarg.h>
+
+#include "luat_spi.h"
+
+static char log_buf[256];
+
+void sfud_log_debug(const char *file, const long line, const char *format, ...);
+
+/**
+ * SPI write data then read data
+ */
+static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
+        size_t read_size) {
+    sfud_err result = SFUD_SUCCESS;
+    luat_spi_t* spi_flash = (luat_spi_t*) (spi->user_data);
+    if (write_size) {
+        SFUD_ASSERT(write_buf);
+    }
+    if (read_size) {
+        SFUD_ASSERT(read_buf);
+    }
+
+    if (write_size && read_size) {
+        if (luat_spi_transfer(spi_flash -> id, write_buf, read_buf, read_size) <= 0) {
+            result = SFUD_ERR_TIMEOUT;
+        }
+    } else if (write_size) {
+        if (luat_spi_send(spi_flash -> id,  write_buf, write_size) <= 0) {
+            result = SFUD_ERR_WRITE;
+        }
+    } else {
+        if (luat_spi_recv(spi_flash -> id, read_buf, read_size) <= 0) {
+            result = SFUD_ERR_READ;
+        }
+    }
+
+    return result;
+}
+
+#ifdef SFUD_USING_QSPI
+/**
+ * read flash data by QSPI
+ */
+static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
+        uint8_t *read_buf, size_t read_size) {
+    sfud_err result = SFUD_SUCCESS;
+
+    /**
+     * add your qspi read flash data code
+     */
+
+    return result;
+}
+#endif /* SFUD_USING_QSPI */
+
+/* about 100 microsecond delay */
+static void retry_delay_100us(void) {
+    uint32_t delay = 120;
+    while(delay--);
+}
+
+sfud_err sfud_spi_port_init(sfud_flash *flash) {
+    sfud_err result = SFUD_SUCCESS;
+
+    extern luat_spi_t sfud_spi_flash;
+    /* port SPI device interface */
+    flash->spi.wr = spi_write_read;
+    // flash->spi.user_data = flash;
+    flash->spi.user_data = &sfud_spi_flash;
+    /* 100 microsecond delay */
+    flash->retry.delay = retry_delay_100us;
+    /* 60 seconds timeout */
+    flash->retry.times = 60 * 10000;
+
+    return result;
+}
+
+/**
+ * This function is print debug info.
+ *
+ * @param file the file which has call this function
+ * @param line the line number which has call this function
+ * @param format output format
+ * @param ... args
+ */
+void sfud_log_debug(const char *file, const long line, const char *format, ...) {
+    va_list args;
+
+    /* args point to the first variable parameter */
+    va_start(args, format);
+    printf("[SFUD](%s:%ld) ", file, line);
+    /* must use vprintf to print */
+    vsnprintf(log_buf, sizeof(log_buf), format, args);
+    printf("%s\n", log_buf);
+    va_end(args);
+}
+
+/**
+ * This function is print routine info.
+ *
+ * @param format output format
+ * @param ... args
+ */
+void sfud_log_info(const char *format, ...) {
+    va_list args;
+
+    /* args point to the first variable parameter */
+    va_start(args, format);
+    printf("[SFUD]");
+    /* must use vprintf to print */
+    vsnprintf(log_buf, sizeof(log_buf), format, args);
+    printf("%s\n", log_buf);
+    va_end(args);
+}

+ 387 - 0
components/sfud/sfud_sfdp.c

@@ -0,0 +1,387 @@
+/*
+ * This file is part of the Serial Flash Universal Driver Library.
+ *
+ * Copyright (c) 2016, Armink, <armink.ztl@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Function: Analyze the SFDP (Serial Flash Discoverable Parameters) which from JESD216/A/B (V1.X) standard.
+ *           JESD216  (V1.0) document: http://www.jedec.org/sites/default/files/docs/JESD216.pdf
+ *           JESD216A (V1.5) document: http://www.jedec.org/sites/default/files/docs/JESD216A.pdf
+ *           JESD216B (V1.6) document: http://www.jedec.org/sites/default/files/docs/JESD216B.pdf
+ *
+ * Created on: 2016-05-26
+ */
+
+#include "sfud.h"
+
+/**
+ * JEDEC Standard JESD216 Terms and definitions:
+ *
+ * DWORD: Four consecutive 8-bit bytes used as the basic 32-bit building block for headers and parameter tables.
+ *
+ * Sector: The minimum granularity - size and alignment - of an area that can be erased in the data array
+ * of a flash memory device. Different areas within the address range of the data array may have a different
+ * minimum erase granularity (sector size).
+ */
+
+#ifdef SFUD_USING_SFDP
+
+/* support maximum SFDP major revision by driver */
+#define SUPPORT_MAX_SFDP_MAJOR_REV                  1
+/* the JEDEC basic flash parameter table length is 9 DWORDs (288-bit) on JESD216 (V1.0) initial release standard */
+#define BASIC_TABLE_LEN                             9
+/* the smallest eraser in SFDP eraser table */
+#define SMALLEST_ERASER_INDEX                       0
+/**
+ *  SFDP parameter header structure
+ */
+typedef struct {
+    uint8_t id;                                  /**< Parameter ID LSB */
+    uint8_t minor_rev;                           /**< Parameter minor revision */
+    uint8_t major_rev;                           /**< Parameter major revision */
+    uint8_t len;                                 /**< Parameter table length(in double words) */
+    uint32_t ptp;                                /**< Parameter table 24bit pointer (byte address) */
+} sfdp_para_header;
+
+static sfud_err read_sfdp_data(const sfud_flash *flash, uint32_t addr, uint8_t *read_buf, size_t size);
+static bool read_sfdp_header(sfud_flash *flash);
+static bool read_basic_header(const sfud_flash *flash, sfdp_para_header *basic_header);
+static bool read_basic_table(sfud_flash *flash, sfdp_para_header *basic_header);
+
+/* ../port/sfup_port.c */
+extern void sfud_log_debug(const char *file, const long line, const char *format, ...);
+extern void sfud_log_info(const char *format, ...);
+
+/**
+ * Read SFDP parameter information
+ *
+ * @param flash flash device
+ *
+ * @return true: read OK
+ */
+bool sfud_read_sfdp(sfud_flash *flash) {
+    SFUD_ASSERT(flash);
+
+    /* JEDEC basic flash parameter header */
+    sfdp_para_header basic_header;
+    if (read_sfdp_header(flash) && read_basic_header(flash, &basic_header)) {
+        return read_basic_table(flash, &basic_header);
+    } else {
+        SFUD_INFO("Warning: Read SFDP parameter header information failed. The %s is not support JEDEC SFDP.", flash->name);
+        return false;
+    }
+}
+
+/**
+ * Read SFDP parameter header
+ *
+ * @param flash flash device
+ *
+ * @return true: read OK
+ */
+static bool read_sfdp_header(sfud_flash *flash) {
+    sfud_sfdp *sfdp = &flash->sfdp;
+    /* The SFDP header is located at address 000000h of the SFDP data structure.
+     * It identifies the SFDP Signature, the number of parameter headers, and the SFDP revision numbers. */
+    /* sfdp parameter header address */
+    uint32_t header_addr = 0;
+    /* each parameter header being 2 DWORDs (64-bit) */
+    uint8_t header[2 * 4] = { 0 };
+
+    SFUD_ASSERT(flash);
+
+    sfdp->available = false;
+    /* read SFDP header */
+    if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != SFUD_SUCCESS) {
+        SFUD_INFO("Error: Can't read SFDP header.");
+        return false;
+    }
+    /* check SFDP header */
+    if (!(header[0] == 'S' &&
+          header[1] == 'F' &&
+          header[2] == 'D' &&
+          header[3] == 'P')) {
+        SFUD_DEBUG("Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').");
+        return false;
+    }
+    sfdp->minor_rev = header[4];
+    sfdp->major_rev = header[5];
+    if (sfdp->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) {
+        SFUD_INFO("Error: This reversion(V%d.%d) SFDP is not supported.", sfdp->major_rev, sfdp->minor_rev);
+        return false;
+    }
+    SFUD_DEBUG("Check SFDP header is OK. The reversion is V%d.%d, NPN is %d.", sfdp->major_rev, sfdp->minor_rev,
+            header[6]);
+
+    return true;
+}
+
+/**
+ * Read JEDEC basic parameter header
+ *
+ * @param flash flash device
+ *
+ * @return true: read OK
+ */
+static bool read_basic_header(const sfud_flash *flash, sfdp_para_header *basic_header) {
+    /* The basic parameter header is mandatory, is defined by this standard, and starts at byte offset 08h. */
+    uint32_t header_addr = 8;
+    /* each parameter header being 2 DWORDs (64-bit) */
+    uint8_t header[2 * 4] = { 0 };
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(basic_header);
+
+    /* read JEDEC basic flash parameter header */
+    if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != SFUD_SUCCESS) {
+        SFUD_INFO("Error: Can't read JEDEC basic flash parameter header.");
+        return false;
+    }
+    basic_header->id        = header[0];
+    basic_header->minor_rev = header[1];
+    basic_header->major_rev = header[2];
+    basic_header->len       = header[3];
+    basic_header->ptp       = (long)header[4] | (long)header[5] << 8 | (long)header[6] << 16;
+    /* check JEDEC basic flash parameter header */
+    if (basic_header->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) {
+        SFUD_INFO("Error: This reversion(V%d.%d) JEDEC basic flash parameter header is not supported.",
+                basic_header->major_rev, basic_header->minor_rev);
+        return false;
+    }
+    if (basic_header->len < BASIC_TABLE_LEN) {
+        SFUD_INFO("Error: The JEDEC basic flash parameter table length (now is %d) error.", basic_header->len);
+        return false;
+    }
+    SFUD_DEBUG("Check JEDEC basic flash parameter header is OK. The table id is %d, reversion is V%d.%d,"
+            " length is %d, parameter table pointer is 0x%06lX.", basic_header->id, basic_header->major_rev,
+            basic_header->minor_rev, basic_header->len, basic_header->ptp);
+
+    return true;
+}
+
+/**
+ * Read JEDEC basic parameter table
+ *
+ * @param flash flash device
+ *
+ * @return true: read OK
+ */
+static bool read_basic_table(sfud_flash *flash, sfdp_para_header *basic_header) {
+    sfud_sfdp *sfdp = &flash->sfdp;
+    /* parameter table address */
+    uint32_t table_addr = basic_header->ptp;
+    /* parameter table */
+    uint8_t table[BASIC_TABLE_LEN * 4] = { 0 }, i, j;
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(basic_header);
+
+    /* read JEDEC basic flash parameter table */
+    if (read_sfdp_data(flash, table_addr, table, sizeof(table)) != SFUD_SUCCESS) {
+        SFUD_INFO("Warning: Can't read JEDEC basic flash parameter table.");
+        return false;
+    }
+    /* print JEDEC basic flash parameter table info */
+    SFUD_DEBUG("JEDEC basic flash parameter table info:");
+    SFUD_DEBUG("MSB-LSB  3    2    1    0");
+    for (i = 0; i < BASIC_TABLE_LEN; i++) {
+        SFUD_DEBUG("[%04d] 0x%02X 0x%02X 0x%02X 0x%02X", i + 1, table[i * 4 + 3], table[i * 4 + 2], table[i * 4 + 1],
+                table[i * 4]);
+    }
+
+    /* get block/sector 4 KB erase supported and command */
+    sfdp->erase_4k_cmd = table[1];
+    switch (table[0] & 0x03) {
+    case 1:
+        sfdp->erase_4k = true;
+        SFUD_DEBUG("4 KB Erase is supported throughout the device. Command is 0x%02X.", sfdp->erase_4k_cmd);
+        break;
+    case 3:
+        sfdp->erase_4k = false;
+        SFUD_DEBUG("Uniform 4 KB erase is unavailable for this device.");
+        break;
+    default:
+        SFUD_INFO("Error: Uniform 4 KB erase supported information error.");
+        return false;
+    }
+    /* get write granularity */
+    //TODO ĿǰΪ 1.0 ���ṩ�ķ�ʽ������֧�� V1.5 �����ϵķ�ʽ��ȡ page size
+    switch ((table[0] & (0x01 << 2)) >> 2) {
+    case 0:
+        sfdp->write_gran = 1;
+        SFUD_DEBUG("Write granularity is 1 byte.");
+        break;
+    case 1:
+        sfdp->write_gran = 256;
+        SFUD_DEBUG("Write granularity is 64 bytes or larger.");
+        break;
+    }
+    /* volatile status register block protect bits */
+    switch ((table[0] & (0x01 << 3)) >> 3) {
+    case 0:
+        /* Block Protect bits in device's status register are solely non-volatile or may be
+         * programmed either as volatile using the 50h instruction for write enable or non-volatile
+         * using the 06h instruction for write enable.
+         */
+        sfdp->sr_is_non_vola = true;
+        SFUD_DEBUG("Target flash status register is non-volatile.");
+        break;
+    case 1:
+        /* block protect bits in device's status register are solely volatile. */
+        sfdp->sr_is_non_vola = false;
+        SFUD_DEBUG("Block Protect bits in device's status register are solely volatile.");
+        /* write enable instruction select for writing to volatile status register */
+        switch ((table[0] & (0x01 << 4)) >> 4) {
+        case 0:
+            sfdp->vola_sr_we_cmd = SFUD_VOLATILE_SR_WRITE_ENABLE;
+            SFUD_DEBUG("Flash device requires instruction 50h as the write enable prior "
+                    "to performing a volatile write to the status register.");
+            break;
+        case 1:
+            sfdp->vola_sr_we_cmd = SFUD_CMD_WRITE_ENABLE;
+            SFUD_DEBUG("Flash device requires instruction 06h as the write enable prior "
+                    "to performing a volatile write to the status register.");
+            break;
+        }
+        break;
+    }
+    /* get address bytes, number of bytes used in addressing flash array read, write and erase. */
+    switch ((table[2] & (0x03 << 1)) >> 1) {
+    case 0:
+        sfdp->addr_3_byte = true;
+        sfdp->addr_4_byte = false;
+        SFUD_DEBUG("3-Byte only addressing.");
+        break;
+    case 1:
+        sfdp->addr_3_byte = true;
+        sfdp->addr_4_byte = true;
+        SFUD_DEBUG("3- or 4-Byte addressing.");
+        break;
+    case 2:
+        sfdp->addr_3_byte = false;
+        sfdp->addr_4_byte = true;
+        SFUD_DEBUG("4-Byte only addressing.");
+        break;
+    default:
+        sfdp->addr_3_byte = false;
+        sfdp->addr_4_byte = false;
+        SFUD_INFO("Error: Read address bytes error!");
+        return false;
+    }
+    /* get flash memory capacity */
+    uint32_t table2_temp = ((long)table[7] << 24) | ((long)table[6] << 16) | ((long)table[5] << 8) | (long)table[4];
+    switch ((table[7] & (0x01 << 7)) >> 7) {
+    case 0:
+        sfdp->capacity = 1 + (table2_temp >> 3);
+        break;
+    case 1:
+        table2_temp &= 0x7FFFFFFF;
+        if (table2_temp > sizeof(sfdp->capacity) * 8 + 3) {
+            sfdp->capacity = 0;
+            SFUD_INFO("Error: The flash capacity is grater than 32 Gb/ 4 GB! Not Supported.");
+            return false;
+        }
+        sfdp->capacity = 1L << (table2_temp - 3);
+        break;
+    }
+    SFUD_DEBUG("Capacity is %ld Bytes.", sfdp->capacity);
+    /* get erase size and erase command  */
+    for (i = 0, j = 0; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
+        if (table[28 + 2 * i] != 0x00) {
+            sfdp->eraser[j].size = 1L << table[28 + 2 * i];
+            sfdp->eraser[j].cmd = table[28 + 2 * i + 1];
+            SFUD_DEBUG("Flash device supports %ldKB block erase. Command is 0x%02X.", sfdp->eraser[j].size / 1024,
+                    sfdp->eraser[j].cmd);
+            j++;
+        }
+    }
+    /* sort the eraser size from small to large */
+    for (i = 0, j = 0; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
+        if (sfdp->eraser[i].size) {
+            for (j = i + 1; j < SFUD_SFDP_ERASE_TYPE_MAX_NUM; j++) {
+                if (sfdp->eraser[j].size != 0 && sfdp->eraser[i].size > sfdp->eraser[j].size) {
+                    /* swap the small eraser */
+                    uint32_t temp_size = sfdp->eraser[i].size;
+                    uint8_t temp_cmd = sfdp->eraser[i].cmd;
+                    sfdp->eraser[i].size = sfdp->eraser[j].size;
+                    sfdp->eraser[i].cmd = sfdp->eraser[j].cmd;
+                    sfdp->eraser[j].size = temp_size;
+                    sfdp->eraser[j].cmd = temp_cmd;
+                }
+            }
+        }
+    }
+
+    sfdp->available = true;
+    return true;
+}
+
+static sfud_err read_sfdp_data(const sfud_flash *flash, uint32_t addr, uint8_t *read_buf, size_t size) {
+    uint8_t cmd[] = {
+            SFUD_CMD_READ_SFDP_REGISTER,
+            (addr >> 16) & 0xFF,
+            (addr >> 8) & 0xFF,
+            (addr >> 0) & 0xFF,
+            SFUD_DUMMY_DATA,
+    };
+
+    SFUD_ASSERT(flash);
+    SFUD_ASSERT(addr < 1L << 24);
+    SFUD_ASSERT(read_buf);
+    SFUD_ASSERT(flash->spi.wr);
+
+    return flash->spi.wr(&flash->spi, cmd, sizeof(cmd), read_buf, size);
+}
+
+/**
+ * get the most suitable eraser for erase process from SFDP parameter
+ *
+ * @param flash flash device
+ * @param addr start address
+ * @param erase_size will be erased size
+ *
+ * @return the eraser index of SFDP eraser table  @see sfud_sfdp.eraser[]
+ */
+size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr, size_t erase_size) {
+    size_t index = SMALLEST_ERASER_INDEX, i;
+    /* only used when flash supported SFDP */
+    SFUD_ASSERT(flash->sfdp.available);
+    /* the address isn't align by smallest eraser's size, then use the smallest eraser */
+    if (addr % flash->sfdp.eraser[SMALLEST_ERASER_INDEX].size) {
+        return SMALLEST_ERASER_INDEX;
+    }
+    /* Find the suitable eraser.
+     * The largest size eraser is at the end of eraser table.
+     * In order to decrease erase command counts, so the find process is from the end of eraser table. */
+    for (i = SFUD_SFDP_ERASE_TYPE_MAX_NUM - 1;; i--) {
+        if ((flash->sfdp.eraser[i].size != 0) && (erase_size >= flash->sfdp.eraser[i].size)
+                && (addr % flash->sfdp.eraser[i].size == 0)) {
+            index = i;
+            break;
+        }
+        if (i == SMALLEST_ERASER_INDEX) {
+            break;
+        }
+    }
+    return index;
+}
+
+#endif /* SFUD_USING_SFDP */

+ 2 - 0
luat/include/luat_base.h

@@ -81,6 +81,8 @@ LUAMOD_API int luaopen_spi( lua_State *L );
 LUAMOD_API int luaopen_disp( lua_State *L );
 /** 加载u8g2库, 可选, 会依赖i2c和spi*/
 LUAMOD_API int luaopen_u8g2( lua_State *L );
+/** 加载sfud库, 可选, 会依赖spi*/
+LUAMOD_API int luaopen_sfud( lua_State *L );
 /** 加载utest库, 预留*/
 LUAMOD_API int luaopen_utest( lua_State *L );
 /** 加载mqtt库, 预留*/