diff --git a/components/hal/esp32/include/hal/spi_flash_ll.h b/components/hal/esp32/include/hal/spi_flash_ll.h index 656425e80c..b9ab67ef6d 100644 --- a/components/hal/esp32/include/hal/spi_flash_ll.h +++ b/components/hal/esp32/include/hal/spi_flash_ll.h @@ -316,17 +316,18 @@ static inline void spi_flash_ll_set_mosi_bitlen(spi_dev_t *dev, uint32_t bitlen) } /** - * Set the command with fixed length (8 bits). + * Set the command. * * @param dev Beginning address of the peripheral registers. * @param command Command to send + * @param bitlen Length of the command */ -static inline void spi_flash_ll_set_command8(spi_dev_t *dev, uint8_t command) +static inline void spi_flash_ll_set_command(spi_dev_t *dev, uint8_t command, uint32_t bitlen) { dev->user.usr_command = 1; typeof(dev->user2) user2 = { .usr_command_value = command, - .usr_command_bitlen = (8 - 1), + .usr_command_bitlen = (bitlen - 1), }; dev->user2 = user2; } @@ -362,7 +363,14 @@ static inline void spi_flash_ll_set_addr_bitlen(spi_dev_t *dev, uint32_t bitlen) */ static inline void spi_flash_ll_set_usr_address(spi_dev_t *dev, uint32_t addr, int bit_len) { - dev->addr = (addr << (32 - bit_len)); + // The blank region should be all ones + if (bit_len >= 32) { + dev->addr = addr; + dev->slv_wr_status = UINT32_MAX; + } else { + uint32_t padding_ones = (bit_len == 32? 0 : UINT32_MAX >> bit_len); + dev->addr = (addr << (32 - bit_len)) | padding_ones; + } } /** @@ -388,6 +396,12 @@ static inline void spi_flash_ll_set_dummy(spi_dev_t *dev, uint32_t dummy_n) dev->user1.usr_dummy_cyclelen = dummy_n - 1; } +static inline void spi_flash_ll_set_hold(spi_dev_t *dev, uint32_t hold_n) +{ + dev->ctrl2.hold_time = hold_n; + dev->user.cs_hold = (hold_n > 0? 1: 0); +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s2/include/hal/gpspi_flash_ll.h b/components/hal/esp32s2/include/hal/gpspi_flash_ll.h index aa9482251a..fce8aa921f 100644 --- a/components/hal/esp32s2/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32s2/include/hal/gpspi_flash_ll.h @@ -275,17 +275,18 @@ static inline void gpspi_flash_ll_set_mosi_bitlen(spi_dev_t *dev, uint32_t bitle } /** - * Set the command with fixed length (8 bits). + * Set the command. * * @param dev Beginning address of the peripheral registers. * @param command Command to send + * @param bitlen Length of the command */ -static inline void gpspi_flash_ll_set_command8(spi_dev_t *dev, uint8_t command) +static inline void gpspi_flash_ll_set_command(spi_dev_t *dev, uint8_t command, uint32_t bitlen) { dev->user.usr_command = 1; typeof(dev->user2) user2 = { .usr_command_value = command, - .usr_command_bitlen = (8 - 1), + .usr_command_bitlen = (bitlen - 1), }; dev->user2 = user2; } @@ -321,7 +322,9 @@ static inline void gpspi_flash_ll_set_addr_bitlen(spi_dev_t *dev, uint32_t bitle */ static inline void gpspi_flash_ll_set_usr_address(spi_dev_t *dev, uint32_t addr, uint32_t bitlen) { - dev->addr = (addr << (32 - bitlen)); + // The blank region should be all ones + uint32_t padding_ones = (bitlen == 32? 0 : UINT32_MAX >> bitlen); + dev->addr = (addr << (32 - bitlen)) | padding_ones; } /** @@ -361,6 +364,12 @@ static inline void gpspi_flash_ll_set_dummy_out(spi_dev_t *dev, uint32_t out_en, dev->ctrl.d_pol = out_lev; } +static inline void gpspi_flash_ll_set_hold(spi_dev_t *dev, uint32_t hold_n) +{ + dev->ctrl2.cs_hold_time = hold_n - 1; + dev->user.cs_hold = (hold_n > 0? 1: 0); +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s2/include/hal/spi_flash_ll.h b/components/hal/esp32s2/include/hal/spi_flash_ll.h index 8cb6cf7f8a..8666598944 100644 --- a/components/hal/esp32s2/include/hal/spi_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spi_flash_ll.h @@ -54,6 +54,7 @@ typedef union { } spi_flash_ll_clock_reg_t; #ifdef GPSPI_BUILD + #define spi_flash_ll_reset(dev) gpspi_flash_ll_reset((spi_dev_t*)dev) #define spi_flash_ll_cmd_is_done(dev) gpspi_flash_ll_cmd_is_done((spi_dev_t*)dev) #define spi_flash_ll_get_buffer_data(dev, buffer, read_len) gpspi_flash_ll_get_buffer_data((spi_dev_t*)dev, buffer, read_len) @@ -66,14 +67,17 @@ typedef union { #define spi_flash_ll_set_clock(dev, clk) gpspi_flash_ll_set_clock((spi_dev_t*)dev, (gpspi_flash_ll_clock_reg_t*)clk) #define spi_flash_ll_set_miso_bitlen(dev, bitlen) gpspi_flash_ll_set_miso_bitlen((spi_dev_t*)dev, bitlen) #define spi_flash_ll_set_mosi_bitlen(dev, bitlen) gpspi_flash_ll_set_mosi_bitlen((spi_dev_t*)dev, bitlen) -#define spi_flash_ll_set_command8(dev, cmd) gpspi_flash_ll_set_command8((spi_dev_t*)dev, cmd) +#define spi_flash_ll_set_command(dev, cmd, bitlen) gpspi_flash_ll_set_command((spi_dev_t*)dev, cmd, bitlen) #define spi_flash_ll_set_addr_bitlen(dev, bitlen) gpspi_flash_ll_set_addr_bitlen((spi_dev_t*)dev, bitlen) #define spi_flash_ll_get_addr_bitlen(dev) gpspi_flash_ll_get_addr_bitlen((spi_dev_t*)dev) #define spi_flash_ll_set_address(dev, addr) gpspi_flash_ll_set_address((spi_dev_t*)dev, addr) #define spi_flash_ll_set_usr_address(dev, addr, bitlen) gpspi_flash_ll_set_usr_address((spi_dev_t*)dev, addr, bitlen) #define spi_flash_ll_set_dummy(dev, dummy) gpspi_flash_ll_set_dummy((spi_dev_t*)dev, dummy) #define spi_flash_ll_set_dummy_out(dev, en, lev) gpspi_flash_ll_set_dummy_out((spi_dev_t*)dev, en, lev) +#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n) + #else + #define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev) #define spi_flash_ll_cmd_is_done(dev) spimem_flash_ll_cmd_is_done((spi_mem_dev_t*)dev) #define spi_flash_ll_erase_chip(dev) spimem_flash_ll_erase_chip((spi_mem_dev_t*)dev) @@ -91,13 +95,15 @@ typedef union { #define spi_flash_ll_set_clock(dev, clk) spimem_flash_ll_set_clock((spi_mem_dev_t*)dev, (spimem_flash_ll_clock_reg_t*)clk) #define spi_flash_ll_set_miso_bitlen(dev, bitlen) spimem_flash_ll_set_miso_bitlen((spi_mem_dev_t*)dev, bitlen) #define spi_flash_ll_set_mosi_bitlen(dev, bitlen) spimem_flash_ll_set_mosi_bitlen((spi_mem_dev_t*)dev, bitlen) -#define spi_flash_ll_set_command8(dev, cmd) spimem_flash_ll_set_command8((spi_mem_dev_t*)dev, cmd) +#define spi_flash_ll_set_command(dev, cmd, bitlen) spimem_flash_ll_set_command((spi_mem_dev_t*)dev, cmd, bitlen) #define spi_flash_ll_set_addr_bitlen(dev, bitlen) spimem_flash_ll_set_addr_bitlen((spi_mem_dev_t*)dev, bitlen) #define spi_flash_ll_get_addr_bitlen(dev) spimem_flash_ll_get_addr_bitlen((spi_mem_dev_t*) dev) #define spi_flash_ll_set_address(dev, addr) spimem_flash_ll_set_address((spi_mem_dev_t*)dev, addr) -#define spi_flash_ll_set_usr_address(dev, addr, bitlen) spimem_flash_ll_set_address((spi_mem_dev_t*)dev, addr) +#define spi_flash_ll_set_usr_address(dev, addr, bitlen) spimem_flash_ll_set_usr_address((spi_mem_dev_t*)dev, addr, bitlen) #define spi_flash_ll_set_dummy(dev, dummy) spimem_flash_ll_set_dummy((spi_mem_dev_t*)dev, dummy) #define spi_flash_ll_set_dummy_out(dev, en, lev) spimem_flash_ll_set_dummy_out((spi_mem_dev_t*)dev, en, lev) +#define spi_flash_ll_set_hold(dev, hold_n) spimem_flash_ll_set_hold((spi_mem_dev_t*)dev, hold_n) + #endif #ifdef __cplusplus diff --git a/components/hal/esp32s2/include/hal/spimem_flash_ll.h b/components/hal/esp32s2/include/hal/spimem_flash_ll.h index 90f808aa62..d2f7e6f3d0 100644 --- a/components/hal/esp32s2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spimem_flash_ll.h @@ -308,17 +308,18 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t } /** - * Set the command with fixed length (8 bits). + * Set the command. * * @param dev Beginning address of the peripheral registers. * @param command Command to send + * @param bitlen Length of the command */ -static inline void spimem_flash_ll_set_command8(spi_mem_dev_t *dev, uint8_t command) +static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen) { dev->user.usr_command = 1; typeof(dev->user2) user2 = { .usr_command_value = command, - .usr_command_bitlen = (8 - 1), + .usr_command_bitlen = (bitlen - 1), }; dev->user2 = user2; } @@ -357,6 +358,18 @@ static inline void spimem_flash_ll_set_address(spi_mem_dev_t *dev, uint32_t addr dev->addr = addr; } +/** + * Set the address to send in user mode. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param addr Address to send + */ +static inline void spimem_flash_ll_set_usr_address(spi_mem_dev_t *dev, uint32_t addr, uint32_t bitlen) +{ + (void)bitlen; + spimem_flash_ll_set_address(dev, addr); +} + /** * Set the length of dummy cycles. * @@ -383,6 +396,13 @@ static inline void spimem_flash_ll_set_dummy_out(spi_mem_dev_t *dev, uint32_t ou dev->ctrl.d_pol = out_lev; } +static inline void spimem_flash_ll_set_hold(spi_mem_dev_t *dev, uint32_t hold_n) +{ + dev->ctrl2.cs_hold_time = hold_n - 1; + dev->user.cs_hold = (hold_n > 0? 1: 0); +} + + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/spi_flash_hal.h b/components/hal/include/hal/spi_flash_hal.h index 6306294740..c17ed9861d 100644 --- a/components/hal/include/hal/spi_flash_hal.h +++ b/components/hal/include/hal/spi_flash_hal.h @@ -40,10 +40,17 @@ typedef struct { spi_flash_host_inst_t inst; ///< Host instance, containing host data and function pointer table. May update with the host (hardware version). spi_dev_t *spi; ///< Pointer to SPI peripheral registers (SP1, SPI2 or SPI3). Set before initialisation. int cs_num; ///< Which cs pin is used, 0-2. - int extra_dummy; ///< Pre-calculated extra dummy used for compensation + struct { + uint8_t extra_dummy; ///< Pre-calculated extra dummy used for compensation + uint8_t reserved1; ///< Reserved, set to 0. + uint8_t cs_hold; ///< CS hold time config used by the host + uint8_t reserved2; ///< Reserved, set to 0. + }; spi_flash_ll_clock_reg_t clock_conf; ///< Pre-calculated clock configuration value - uint32_t reserved_config[2]; ///< The ROM has reserved some memory for configurations with one set of driver code. (e.g. QPI mode, 64-bit address mode, etc.) + esp_flash_io_mode_t base_io_mode; ///< Default IO mode mask for common commands + uint32_t reserved_config[1]; ///< The ROM has reserved some memory for configurations with one set of driver code. (e.g. QPI mode, 64-bit address mode, etc.) } spi_flash_hal_context_t; +_Static_assert(sizeof(spi_flash_hal_context_t) == 28, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM"); /// Configuration structure for the SPI driver. typedef struct { @@ -52,6 +59,7 @@ typedef struct { bool iomux; ///< Whether the IOMUX is used, used for timing compensation. int input_delay_ns; ///< Input delay on the MISO pin after the launch clock, used for timing compensation. esp_flash_speed_t speed;///< SPI flash clock speed to work at. + uint32_t cs_hold; ///< CS hold time config used by the host } spi_flash_hal_config_t; /** @@ -98,7 +106,7 @@ void spi_flash_hal_erase_chip(spi_flash_host_inst_t *host); /** * Erase a specific sector by its start address through the sector erase (20h) - * command. + * command. For 24bit address only. * * @param host The driver context. * @param start_address Start address of the sector to erase. @@ -107,7 +115,7 @@ void spi_flash_hal_erase_sector(spi_flash_host_inst_t *host, uint32_t start_addr /** * Erase a specific 64KB block by its start address through the 64KB block - * erase (D8h) command. + * erase (D8h) command. For 24bit address only. * * @param host The driver context. * @param start_address Start address of the block to erase. @@ -115,7 +123,7 @@ void spi_flash_hal_erase_sector(spi_flash_host_inst_t *host, uint32_t start_addr void spi_flash_hal_erase_block(spi_flash_host_inst_t *host, uint32_t start_address); /** - * Program a page of the flash using the page program (02h) command. + * Program a page of the flash using the page program (02h) command. For 24bit address only. * * @param host The driver context. * @param address Address of the page to program diff --git a/components/hal/include/hal/spi_flash_types.h b/components/hal/include/hal/spi_flash_types.h index d2efe3e8bb..b763ee3d4a 100644 --- a/components/hal/include/hal/spi_flash_types.h +++ b/components/hal/include/hal/spi_flash_types.h @@ -15,6 +15,7 @@ #pragma once #include +#include #include "esp_flash_err.h" #ifdef __cplusplus @@ -23,13 +24,19 @@ extern "C" { /** Definition of a common transaction. Also holds the return value. */ typedef struct { - uint8_t command; ///< Command to send, always 8bits + uint8_t reserved; ///< Reserved, must be 0. uint8_t mosi_len; ///< Output data length, in bytes uint8_t miso_len; ///< Input data length, in bytes uint8_t address_bitlen; ///< Length of address in bits, set to 0 if command does not need an address uint32_t address; ///< Address to perform operation on const uint8_t *mosi_data; ///< Output data to salve uint8_t *miso_data; ///< [out] Input data from slave, little endian + uint32_t flags; ///< Flags for this transaction. Set to 0 for now. +#define SPI_FLASH_TRANS_FLAG_CMD16 BIT(0) ///< Send command of 16 bits +#define SPI_FLASH_TRANS_FLAG_IGNORE_BASEIO BIT(1) ///< Not applying the basic io mode configuration for this transaction +#define SPI_FLASH_TRANS_FLAG_BYTE_SWAP BIT(2) ///< Used for DTR mode, to swap the bytes of a pair of rising/falling edge + uint16_t command; ///< Command to send + uint8_t dummy_bitlen; ///< Basic dummy bits to use } spi_flash_trans_t; /** @@ -53,6 +60,9 @@ typedef enum { ///Lowest speed supported by the driver, currently 5 MHz #define ESP_FLASH_SPEED_MIN ESP_FLASH_5MHZ +// These bits are not quite like "IO mode", but are able to be appended into the io mode and used by the HAL. +#define SPI_FLASH_CONFIG_CONF_BITS BIT(31) ///< OR the io_mode with this mask, to enable the dummy output feature or replace the first several dummy bits into address to meet the requirements of conf bits. (Used in DIO/QIO/OIO mode) + /** @brief Mode used for reading from SPI flash */ typedef enum { SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index b545cbb8d9..df017f6843 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// HAL for SPI Flash (non-IRAM part) +// The IRAM part is in spi_flash_hal_iram.c, spi_flash_hal_gpspi.c, spi_flash_hal_common.inc. + #include #include "hal/spi_flash_hal.h" #include "string.h" @@ -87,6 +90,7 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_ .cs_num = cfg->cs_num, .extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, clock_cfg.freq), .clock_conf = clock_cfg.clock_reg_val, + .cs_hold = cfg->cs_hold, }; ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy); diff --git a/components/hal/spi_flash_hal_common.inc b/components/hal/spi_flash_hal_common.inc index 83fe798de2..3bc1b27d32 100644 --- a/components/hal/spi_flash_hal_common.inc +++ b/components/hal/spi_flash_hal_common.inc @@ -16,6 +16,7 @@ #include "hal/spi_flash_hal.h" #include "string.h" #include "hal/hal_defs.h" +#include "soc/soc_caps.h" #include "sdkconfig.h" #define ADDRESS_MASK_24BIT 0xFFFFFF @@ -26,6 +27,12 @@ static inline spi_dev_t *get_spi_dev(spi_flash_host_inst_t *host) return ((spi_flash_hal_context_t*)host)->spi; } +static inline int get_host_id(spi_flash_host_inst_t* host) +{ + spi_dev_t *dev = get_spi_dev(host); + return spi_flash_ll_hw_get_id(dev); +} + void spi_flash_hal_poll_cmd_done(spi_flash_host_inst_t *host) { while (!spi_flash_ll_cmd_is_done(get_spi_dev(host))) { @@ -35,10 +42,13 @@ void spi_flash_hal_poll_cmd_done(spi_flash_host_inst_t *host) esp_err_t spi_flash_hal_device_config(spi_flash_host_inst_t *host) { + spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host; spi_dev_t *dev = get_spi_dev(host); spi_flash_ll_reset(dev); - spi_flash_ll_set_cs_pin(dev, ((spi_flash_hal_context_t*)host)->cs_num); - spi_flash_ll_set_clock(dev, &((spi_flash_hal_context_t*)host)->clock_conf); + spi_flash_ll_set_cs_pin(dev, ctx->cs_num); + spi_flash_ll_set_clock(dev, &ctx->clock_conf); + int cs_hold = ctx->cs_hold; + spi_flash_ll_set_hold(dev, cs_hold); return ESP_OK; } @@ -52,32 +62,39 @@ esp_err_t spi_flash_hal_configure_host_io_mode( spi_dev_t *dev = get_spi_dev(host); int host_id = spi_flash_ll_hw_get_id(dev); + uint32_t extra_bits = io_mode & 0xFFFF0000; + io_mode = io_mode & 0xFFFF; + + /* + * Some flash chips, when working under some IO modes (DIO, QIO and OIO in the future), treat + * the first 8 bits of the dummy bits as the bits. When the bits meet some pattern, the chip + * will go into a "continuous (XIP)" mode, where the command field will be skipped in the next + * transaction. We have to output all ones in these cycles because we don't need this feature. + */ + bool conf_required = ((extra_bits & SPI_FLASH_CONFIG_CONF_BITS) != 0); + if (!SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(host_id) && io_mode > SPI_FLASH_FASTRD) { return ESP_ERR_NOT_SUPPORTED; } - if (addr_bitlen > 24 && SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT(host_id)) { - /* - * The extra address bits (24-addr_bitlen) are used to control the M7-M0 bits right after - * the address field, to avoid the flash going into continuous read mode. - * - * On ESP32-S2 the MEMSPI (that SUPPORT_CONTROL_DUMMY_OUTPUT), the least significant - * addr_bitlen bits of the address will be used, instead of the MSBs. The driver is - * required to set the address according to the extra address bits. - * - * To reduce the time consuming for the read() function to calculate the shift of address, - * the addr_bitlen is kept to 24 bits. And the CONTROL_DUMMY_OUTPUT feature is used to - * control those bits instead. - */ - //This block is only reached when SPI_FLASH_QIO or SPI_FLASH_DIO - assert(io_mode == SPI_FLASH_DIO || io_mode == SPI_FLASH_QIO); +#if SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT + // The CONTROL_DUMMY_OUTPUT feature is used to control M7-M0 bits. + spi_flash_ll_set_dummy_out(dev, (conf_required? 1: 0), 1); +#else + // On ESP32, dummy output is not supported. These dummy bits will be moved into the address + // phase (and appended as ones). + if (conf_required) { int line_width = (io_mode == SPI_FLASH_DIO? 2: 4); - dummy_cyclelen_base += (addr_bitlen - 24) / line_width; - addr_bitlen = 24; - spi_flash_ll_set_dummy_out(dev, 1, 1); + dummy_cyclelen_base -= 4 / line_width; + addr_bitlen += 4; //extra 4 bits indicate the conf bits is included } +#endif - spi_flash_ll_set_command8(dev, command); + if (command >= 0x100) { + spi_flash_ll_set_command(dev, command, 16); + } else { + spi_flash_ll_set_command(dev, command, 8); + } spi_flash_ll_set_addr_bitlen(dev, addr_bitlen); // Add dummy cycles to compensate for latency of GPIO matrix and external delay, if necessary... spi_flash_ll_set_dummy(dev, COMPUTE_DUMMY_CYCLELEN(host, dummy_cyclelen_base)); @@ -90,16 +107,23 @@ esp_err_t spi_flash_hal_configure_host_io_mode( esp_err_t spi_flash_hal_common_command(spi_flash_host_inst_t *host, spi_flash_trans_t *trans) { - host->driver->configure_host_io_mode(host, trans->command, trans->address_bitlen, 0, SPI_FLASH_FASTRD); - spi_dev_t *dev = get_spi_dev(host); + esp_flash_io_mode_t io_mode = ((spi_flash_hal_context_t*)host)->base_io_mode; + uint16_t command = trans->command; + uint8_t dummy_bitlen = trans->dummy_bitlen; - //disable dummy if no input phase - if (trans->miso_len == 0) { - spi_flash_ll_set_dummy(dev, 0); + if ((trans->flags & SPI_FLASH_TRANS_FLAG_IGNORE_BASEIO) != 0) { + io_mode = 0; + } + + host->driver->configure_host_io_mode(host, command, trans->address_bitlen, dummy_bitlen, io_mode); + + spi_flash_ll_set_usr_address(dev, trans->address, trans->address_bitlen); + //No extra dummy cycles for compensation if no input data + if (trans->miso_len == 0) { + spi_flash_ll_set_dummy(dev, dummy_bitlen); } - spi_flash_ll_set_usr_address(dev, (trans->address & ADDRESS_MASK_24BIT), spi_flash_ll_get_addr_bitlen(dev)); spi_flash_ll_set_mosi_bitlen(dev, trans->mosi_len * 8); spi_flash_ll_set_buffer_data(dev, trans->mosi_data, trans->mosi_len); @@ -114,7 +138,9 @@ esp_err_t spi_flash_hal_read(spi_flash_host_inst_t *host, void *buffer, uint32_t { spi_dev_t *dev = get_spi_dev(host); int bitlen = spi_flash_ll_get_addr_bitlen(dev); - spi_flash_ll_set_usr_address(dev, address << (bitlen - 24), bitlen); + //Only 24-bit and 32-bit address are supported. The extra length are for M7-M0, which should be + //filled with ones by the function below + spi_flash_ll_set_usr_address(dev, address, bitlen & (~7)); spi_flash_ll_set_miso_bitlen(dev, read_len * 8); spi_flash_ll_user_start(dev); host->driver->poll_cmd_done(host); diff --git a/components/hal/spi_flash_hal_gpspi.c b/components/hal/spi_flash_hal_gpspi.c index c26321b437..09a3a28a4b 100644 --- a/components/hal/spi_flash_hal_gpspi.c +++ b/components/hal/spi_flash_hal_gpspi.c @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +// HAL for +// - GPSPI (SP2, SPI3) on ESP32-S2 and later +// The common part is in spi_flash_hal_common.inc + #define GPSPI_BUILD #define spi_flash_hal_common_command spi_flash_hal_gpspi_common_command diff --git a/components/hal/spi_flash_hal_iram.c b/components/hal/spi_flash_hal_iram.c index 8660d9c9e6..3181672080 100644 --- a/components/hal/spi_flash_hal_iram.c +++ b/components/hal/spi_flash_hal_iram.c @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +// HAL for +// - MEMSPI +// - SPI1~3 on ESP32 +// The common part is in spi_flash_hal_common.inc + #include "spi_flash_hal_common.inc" void spi_flash_hal_erase_chip(spi_flash_host_inst_t *host) @@ -21,6 +26,7 @@ void spi_flash_hal_erase_chip(spi_flash_host_inst_t *host) host->driver->poll_cmd_done(host); } +// Only support 24bit address void spi_flash_hal_erase_sector(spi_flash_host_inst_t *host, uint32_t start_address) { spi_dev_t *dev = get_spi_dev(host); @@ -30,6 +36,7 @@ void spi_flash_hal_erase_sector(spi_flash_host_inst_t *host, uint32_t start_addr host->driver->poll_cmd_done(host); } +// Only support 24bit address void spi_flash_hal_erase_block(spi_flash_host_inst_t *host, uint32_t start_address) { spi_dev_t *dev = get_spi_dev(host); @@ -39,6 +46,7 @@ void spi_flash_hal_erase_block(spi_flash_host_inst_t *host, uint32_t start_addre host->driver->poll_cmd_done(host); } +// Only support 24bit address void spi_flash_hal_program_page(spi_flash_host_inst_t *host, const void *buffer, uint32_t address, uint32_t length) { spi_dev_t *dev = get_spi_dev(host); diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 76d778e71c..eb9f7a336f 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -183,7 +183,7 @@ #define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(spi_host) ({(void)spi_host; 1;}) // Peripheral doesn't support output given level during its "dummy phase" -#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT(spi_host) ({(void)spi_host; 0;}) +#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 0 /*-------------------------- TIMER GROUP CAPS --------------------------------*/ // No contents here diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 1dfc101d13..40f0a424e3 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -178,7 +178,7 @@ // Peripheral supports output given level during its "dummy phase" // Only SPI1 supports this feature -#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT(host_id) ((host_id) == 0) +#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 1 #define SOC_MEMSPI_IS_INDEPENDENT 1 diff --git a/components/soc/esp32s3/include/soc/spi_caps.h b/components/soc/esp32s3/include/soc/spi_caps.h index 740b3e8bfb..b18538e6e6 100644 --- a/components/soc/esp32s3/include/soc/spi_caps.h +++ b/components/soc/esp32s3/include/soc/spi_caps.h @@ -30,6 +30,6 @@ #define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(spi_dev) (!((void*)spi_dev == (void*)&GPSPI3)) // Peripheral supports output given level during its "dummy phase" -#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT(spi_dev) (!((void*)spi_dev == (void*)&SPIMEM1)) +#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 1 #define SOC_MEMSPI_IS_INDEPENDENT 1 \ No newline at end of file diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 406679bcef..0a22e6fcb4 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -31,6 +31,7 @@ else() "spi_flash_chip_issi.c" "spi_flash_chip_mxic.c" "spi_flash_chip_gd.c" + "spi_flash_chip_winbond.c" "memspi_host_driver.c") list(APPEND cache_srcs diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index c52729d9d3..eb34c3790a 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -188,6 +188,14 @@ menu "SPI Flash driver" size. Note that the default chip driver supports the GD chips with product ID 60H. + config SPI_FLASH_SUPPORT_WINBOND_CHIP + bool "Winbond" + default y + help + Enable this to support auto detection of Winbond chips if chip vendor not directly + given by ``chip_drv`` member of the chip struct. This adds support for variant + chips, however will extend detecting time. + endmenu #auto detect flash chips endmenu diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 79e242e160..9521c9c3cf 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -149,6 +149,11 @@ bool esp_flash_chip_driver_initialized(const esp_flash_t *chip) esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip) { + // Chip init flow + // 1. Read chip id + // 2. (optional) Detect chip vendor + // 3. Get basic parameters of the chip (size, dummy count, etc.) + // 4. Init chip into desired mode (without breaking the cache!) esp_err_t err = ESP_OK; if (chip == NULL || chip->host == NULL || chip->host->driver == NULL || ((memspi_host_inst_t*)chip->host)->spi == NULL) { @@ -201,22 +206,33 @@ esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip) return rom_spiflash_api_funcs->end(chip, err); } -//this is not public, but useful in unit tests -esp_err_t IRAM_ATTR esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id) +static esp_err_t IRAM_ATTR read_id_core(esp_flash_t* chip, uint32_t* out_id, bool sanity_check) { + bool installed = esp_flash_chip_driver_initialized(chip); esp_err_t err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } - // Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner - // function fails if it sees all-ones or all-zeroes.) - err = chip->host->driver->read_id(chip->host, flash_id); + esp_err_t (*read_id_func)(void*, uint32_t*); + void* read_id_arg; + if (installed && chip->chip_drv->read_id) { + read_id_func = (void*)chip->chip_drv->read_id; + read_id_arg = (void*)chip; + } else { + //default option if the chip is not detected/chosen yet. + read_id_func = (void*)chip->host->driver->read_id; + read_id_arg = (void*)chip->host; + } - if (err == ESP_OK) { // check we see the same ID twice, in case of transient power-on errors + // Inner function fails if it sees all-ones or all-zeroes. + err = read_id_func(read_id_arg, out_id); + + if (sanity_check && err == ESP_OK) { + // Send RDID command twice, check for a matching result and retry in case we just powered on uint32_t new_id; - err = chip->host->driver->read_id(chip->host, &new_id); - if (err == ESP_OK && (new_id != *flash_id)) { + err = read_id_func(read_id_arg, &new_id); + if (err == ESP_OK && (new_id != *out_id)) { err = ESP_ERR_FLASH_NOT_INITIALISED; } } @@ -224,6 +240,23 @@ esp_err_t IRAM_ATTR esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id return rom_spiflash_api_funcs->end(chip, err); } +// Faster version with sanity check. +// Called in esp_flash_init and unit test (though not public) +esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* out_id) +{ + return read_id_core(chip, out_id, true); +} + +esp_err_t esp_flash_read_id(esp_flash_t* chip, uint32_t* out_id) +{ + esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); + //Accept uninitialized chip when reading chip id + if (err != ESP_OK && !(err == ESP_ERR_FLASH_NOT_INITIALISED && chip != NULL)) return err; + if (out_id == NULL) return ESP_ERR_INVALID_ARG; + + return read_id_core(chip, out_id, false); +} + static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip) { esp_err_t err; @@ -271,23 +304,6 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip) } \ } while (0) -esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id) -{ - esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); - //Accept uninitialized chip when reading chip id - if (err != ESP_OK && !(err == ESP_ERR_FLASH_NOT_INITIALISED && chip != NULL)) return err; - if (out_id == NULL) return ESP_ERR_INVALID_ARG; - - err = rom_spiflash_api_funcs->start(chip); - if (err != ESP_OK) { - return err; - } - - err = chip->host->driver->read_id(chip->host, out_id); - - return rom_spiflash_api_funcs->end(chip, err); -} - esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size) { esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index f0d48a62d9..5b2e5ee27f 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -59,6 +59,7 @@ __attribute__((unused)) static const char TAG[] = "spi_flash"; #define DEFAULT_FLASH_MODE SPI_FLASH_FASTRD #endif +//TODO: modify cs hold to meet requirements of all chips!!! #if CONFIG_IDF_TARGET_ESP32 #define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \ .host_id = SPI_HOST,\ diff --git a/components/spi_flash/include/memspi_host_driver.h b/components/spi_flash/include/memspi_host_driver.h index 46336f67ce..80cc55239c 100644 --- a/components/spi_flash/include/memspi_host_driver.h +++ b/components/spi_flash/include/memspi_host_driver.h @@ -107,7 +107,7 @@ esp_err_t memspi_host_flush_cache(spi_flash_host_inst_t *host, uint32_t addr, ui void memspi_host_erase_chip(spi_flash_host_inst_t *host); /** - * Erase a sector starting from a given address. + * Erase a sector starting from a given address. For 24bit address only. * * @param host The driver context. * @param start_address Starting address of the sector. @@ -115,7 +115,7 @@ void memspi_host_erase_chip(spi_flash_host_inst_t *host); void memspi_host_erase_sector(spi_flash_host_inst_t *host, uint32_t start_address); /** - * Erase a block starting from a given address. + * Erase a block starting from a given address. For 24bit address only. * * @param host The driver context. * @param start_address Starting address of the block. @@ -123,7 +123,7 @@ void memspi_host_erase_sector(spi_flash_host_inst_t *host, uint32_t start_addres void memspi_host_erase_block(spi_flash_host_inst_t *host, uint32_t start_address); /** - * Program a page with contents of a buffer. + * Program a page with contents of a buffer. For 24bit address only. * * @param host The driver context. * @param buffer Buffer which contains the data to be flashed. diff --git a/components/spi_flash/include/spi_flash_chip_driver.h b/components/spi_flash/include/spi_flash_chip_driver.h index 7d875b4c10..349adbe571 100644 --- a/components/spi_flash/include/spi_flash_chip_driver.h +++ b/components/spi_flash/include/spi_flash_chip_driver.h @@ -29,6 +29,10 @@ typedef struct { uint32_t page_program_timeout; ///< Timeout for page program operation } flash_chip_op_timeout_t; +typedef enum { + SPI_FLASH_REG_STATUS = 1, +} spi_flash_register_t; + /** @brief SPI flash chip driver definition structure. * * The chip driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some @@ -167,6 +171,17 @@ struct spi_flash_chip_t { * enabled, otherwise disabled */ esp_err_t (*get_io_mode)(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode); + + /* + * Read the chip ID. Called when chip driver is set, but we want to know the exact chip id (to + * get the size, etc.). + */ + esp_err_t (*read_id)(esp_flash_t *chip, uint32_t* out_chip_id); + + /* + * Read the requested register (status, etc.). + */ + esp_err_t (*read_reg)(esp_flash_t *chip, spi_flash_register_t reg_id, uint32_t* out_reg); }; /* Pointer to an array of pointers to all known drivers for flash chips. This array is used diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h index 372a1e1bb6..1c76ec1e44 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -189,6 +189,16 @@ esp_err_t spi_flash_chip_generic_set_write_protect(esp_flash_t *chip, bool write esp_err_t spi_flash_chip_generic_get_write_protect(esp_flash_t *chip, bool *out_write_protect); #define ESP_FLASH_CHIP_GENERIC_NO_TIMEOUT -1 +/** + * @brief Send commands to read one of the reg of the chip + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param reg_id Type of the register to read + * @param out_reg Output of the register value + * @return esp_err_t Error code passed from the ``read_status`` function of host driver. + */ +esp_err_t spi_flash_chip_generic_read_reg(esp_flash_t* chip, spi_flash_register_t reg_id, uint32_t* out_reg); + /** * @brief Read flash status via the RDSR command and wait for bit 0 (write in * progress bit) to be cleared. @@ -362,13 +372,14 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t * transactions. Also prepare the command to be sent in read functions. * * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param addr_32bit Whether 32 bit commands will be used (Currently only W25Q256 is supported) * * @return * - ESP_OK if success * - ESP_ERR_FLASH_NOT_INITIALISED if chip not initialized properly * - or other error passed from the ``configure_host_mode`` function of host driver */ -esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip); +esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, bool addr_32bit); /// Default timeout configuration used by most chips const flash_chip_op_timeout_t spi_flash_chip_generic_timeout; \ No newline at end of file diff --git a/components/spi_flash/include/spi_flash_chip_winbond.h b/components/spi_flash/include/spi_flash_chip_winbond.h new file mode 100644 index 0000000000..23dc19c259 --- /dev/null +++ b/components/spi_flash/include/spi_flash_chip_winbond.h @@ -0,0 +1,27 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "esp_flash.h" +#include "spi_flash_chip_driver.h" + + +/** + * Winbond SPI flash chip_drv, uses all the above functions for its operations. In + * default autodetection, this is used as a catchall if a more specific chip_drv + * is not found. + */ +extern const spi_flash_chip_t esp_flash_chip_winbond; diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index e9e86718ea..6691173e54 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -5,5 +5,6 @@ entries: spi_flash_chip_generic (noflash) spi_flash_chip_issi (noflash) spi_flash_chip_mxic (noflash) - spi_flash_chip_gd(noflash) + spi_flash_chip_gd (noflash) + spi_flash_chip_winbond (noflash) memspi_host_driver (noflash) diff --git a/components/spi_flash/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c index 9286b10fb1..2ac80d69fe 100644 --- a/components/spi_flash/memspi_host_driver.c +++ b/components/spi_flash/memspi_host_driver.c @@ -135,8 +135,10 @@ void memspi_host_erase_chip(spi_flash_host_inst_t *host) host->driver->common_command(host, &t); } +// Only support 24bit address void memspi_host_erase_sector(spi_flash_host_inst_t *host, uint32_t start_address) { + assert(start_address < 0x1000000); spi_flash_trans_t t = { .command = CMD_SECTOR_ERASE, .address_bitlen = 24, @@ -145,8 +147,10 @@ void memspi_host_erase_sector(spi_flash_host_inst_t *host, uint32_t start_addres host->driver->common_command(host, &t); } +// Only support 24bit address void memspi_host_erase_block(spi_flash_host_inst_t *host, uint32_t start_address) { + assert(start_address < 0x1000000); spi_flash_trans_t t = { .command = CMD_LARGE_BLOCK_ERASE, .address_bitlen = 24, @@ -155,8 +159,10 @@ void memspi_host_erase_block(spi_flash_host_inst_t *host, uint32_t start_address host->driver->common_command(host, &t); } +// Only support 24bit address void memspi_host_program_page(spi_flash_host_inst_t *host, const void *buffer, uint32_t address, uint32_t length) { + assert(address + length <= 0x1000000); spi_flash_trans_t t = { .command = CMD_PROGRAM_PAGE, .address_bitlen = 24, diff --git a/components/spi_flash/private_include/spi_flash_defs.h b/components/spi_flash/private_include/spi_flash_defs.h index e37b9780cb..3d07eb1353 100644 --- a/components/spi_flash/private_include/spi_flash_defs.h +++ b/components/spi_flash/private_include/spi_flash_defs.h @@ -27,25 +27,34 @@ #define CMD_RDSR 0x05 #define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ -#define CMD_FASTRD_QIO 0xEB -#define CMD_FASTRD_QUAD 0x6B -#define CMD_FASTRD_DIO 0xBB -#define CMD_FASTRD_DUAL 0x3B -#define CMD_FASTRD 0x0B -#define CMD_READ 0x03 /* Speed limited */ +#define CMD_FASTRD_QIO 0xEB +#define CMD_FASTRD_QIO_4B 0xEC +#define CMD_FASTRD_QUAD 0x6B +#define CMD_FASTRD_QUAD_4B 0x6C +#define CMD_FASTRD_DIO 0xBB +#define CMD_FASTRD_DIO_4B 0xBC +#define CMD_FASTRD_DUAL 0x3B +#define CMD_FASTRD_DUAL_4B 0x3C +#define CMD_FASTRD 0x0B +#define CMD_FASTRD_4B 0x0C +#define CMD_READ 0x03 /* Speed limited */ +#define CMD_READ_4B 0x13 /* Speed limited */ -#define CMD_CHIP_ERASE 0xC7 -#define CMD_SECTOR_ERASE 0x20 -#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */ -#define CMD_PROGRAM_PAGE 0x02 +#define CMD_CHIP_ERASE 0xC7 +#define CMD_SECTOR_ERASE 0x20 +#define CMD_SECTOR_ERASE_4B 0x21 +#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */ +#define CMD_LARGE_BLOCK_ERASE_4B 0xDC /* 64KB block erase command */ +#define CMD_PROGRAM_PAGE 0x02 +#define CMD_PROGRAM_PAGE_4B 0x12 #define CMD_RST_EN 0x66 #define CMD_RST_DEV 0x99 -#define SPI_FLASH_DIO_ADDR_BITLEN (24+4) -#define SPI_FLASH_DIO_DUMMY_BITLEN 2 -#define SPI_FLASH_QIO_ADDR_BITLEN (24+8) -#define SPI_FLASH_QIO_DUMMY_BITLEN 4 +#define SPI_FLASH_DIO_ADDR_BITLEN 24 +#define SPI_FLASH_DIO_DUMMY_BITLEN 4 +#define SPI_FLASH_QIO_ADDR_BITLEN 24 +#define SPI_FLASH_QIO_DUMMY_BITLEN 6 #define SPI_FLASH_QOUT_ADDR_BITLEN 24 #define SPI_FLASH_QOUT_DUMMY_BITLEN 8 #define SPI_FLASH_DOUT_ADDR_BITLEN 24 diff --git a/components/spi_flash/spi_flash_chip_drivers.c b/components/spi_flash/spi_flash_chip_drivers.c index 6e06d963e0..ab5ad1b653 100644 --- a/components/spi_flash/spi_flash_chip_drivers.c +++ b/components/spi_flash/spi_flash_chip_drivers.c @@ -18,6 +18,7 @@ #include "spi_flash_chip_issi.h" #include "spi_flash_chip_mxic.h" #include "spi_flash_chip_gd.h" +#include "spi_flash_chip_winbond.h" #include "sdkconfig.h" /* @@ -38,6 +39,9 @@ static const spi_flash_chip_t *default_registered_chips[] = { #endif #ifdef CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP &esp_flash_chip_mxic, +#endif +#ifdef CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP + &esp_flash_chip_winbond, #endif // Default chip drivers that will accept all chip ID. // FM, Winbond and XMC chips are supposed to be supported by this chip driver. diff --git a/components/spi_flash/spi_flash_chip_gd.c b/components/spi_flash/spi_flash_chip_gd.c index e6c4539c1b..6f710817be 100644 --- a/components/spi_flash/spi_flash_chip_gd.c +++ b/components/spi_flash/spi_flash_chip_gd.c @@ -91,7 +91,6 @@ const spi_flash_chip_t esp_flash_chip_gd = { .get_chip_write_protect = spi_flash_chip_generic_get_write_protect, .set_chip_write_protect = spi_flash_chip_generic_set_write_protect, - // TODO support protected regions on ISSI flash .num_protectable_regions = 0, .protectable_regions = NULL, .get_protected_regions = NULL, @@ -106,4 +105,6 @@ const spi_flash_chip_t esp_flash_chip_gd = { .wait_idle = spi_flash_chip_generic_wait_idle, .set_io_mode = spi_flash_chip_gd_set_io_mode, .get_io_mode = spi_flash_chip_gd_get_io_mode, + + .read_reg = spi_flash_chip_generic_read_reg, }; diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index 160b96852d..48120573c9 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -191,7 +191,7 @@ esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t uint8_t temp_buffer[64]; //spiflash hal max length of read no longer than 64byte // Configure the host, and return - err = spi_flash_chip_generic_config_host_io_mode(chip); + err = spi_flash_chip_generic_config_host_io_mode(chip, false); if (err == ESP_ERR_NOT_SUPPORTED) { ESP_LOGE(TAG, "configure host io mode failed - unsupported"); @@ -313,6 +313,11 @@ esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ return ESP_OK; } +esp_err_t spi_flash_chip_generic_read_reg(esp_flash_t* chip, spi_flash_register_t reg_id, uint32_t* out_reg) +{ + return chip->host->driver->read_status(chip->host, (uint8_t*)out_reg); +} + esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_us) { bool timeout_en = (timeout_us != ESP_FLASH_CHIP_GENERIC_NO_TIMEOUT); @@ -330,10 +335,13 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u return err; } - err = chip->host->driver->read_status(chip->host, &status); + uint32_t read; + err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &read); if (err != ESP_OK) { return err; } + status = read; + if ((status & SR_WIP) == 0) { break; // Write in progress is complete } @@ -349,51 +357,62 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u return (timeout_us > 0) ? ESP_OK : ESP_ERR_TIMEOUT; } -esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip) +esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, bool addr_32bit) { uint32_t dummy_cyclelen_base; uint32_t addr_bitlen; uint32_t read_command; + bool conf_required = false; + esp_flash_io_mode_t read_mode = chip->read_mode; - switch (chip->read_mode) { + switch (read_mode & 0xFFFF) { case SPI_FLASH_QIO: //for QIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that. addr_bitlen = SPI_FLASH_QIO_ADDR_BITLEN; dummy_cyclelen_base = rom_flash_chip_dummy->qio_dummy_bitlen; - read_command = CMD_FASTRD_QIO; + read_command = (addr_32bit? CMD_FASTRD_QIO_4B: CMD_FASTRD_QIO); + conf_required = true; break; case SPI_FLASH_QOUT: addr_bitlen = SPI_FLASH_QOUT_ADDR_BITLEN; dummy_cyclelen_base = rom_flash_chip_dummy->qout_dummy_bitlen; - read_command = CMD_FASTRD_QUAD; + read_command = (addr_32bit? CMD_FASTRD_QUAD_4B: CMD_FASTRD_QUAD); break; case SPI_FLASH_DIO: //for DIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that. addr_bitlen = SPI_FLASH_DIO_ADDR_BITLEN; dummy_cyclelen_base = rom_flash_chip_dummy->dio_dummy_bitlen; - read_command = CMD_FASTRD_DIO; + read_command = (addr_32bit? CMD_FASTRD_DIO_4B: CMD_FASTRD_DIO); + conf_required = true; break; case SPI_FLASH_DOUT: addr_bitlen = SPI_FLASH_DOUT_ADDR_BITLEN; dummy_cyclelen_base = rom_flash_chip_dummy->dout_dummy_bitlen; - read_command = CMD_FASTRD_DUAL; + read_command = (addr_32bit? CMD_FASTRD_DUAL_4B: CMD_FASTRD_DUAL); break; case SPI_FLASH_FASTRD: addr_bitlen = SPI_FLASH_FASTRD_ADDR_BITLEN; dummy_cyclelen_base = rom_flash_chip_dummy->fastrd_dummy_bitlen; - read_command = CMD_FASTRD; + read_command = (addr_32bit? CMD_FASTRD_4B: CMD_FASTRD); break; case SPI_FLASH_SLOWRD: addr_bitlen = SPI_FLASH_SLOWRD_ADDR_BITLEN; dummy_cyclelen_base = rom_flash_chip_dummy->slowrd_dummy_bitlen; - read_command = CMD_READ; + read_command = (addr_32bit? CMD_READ_4B: CMD_READ); break; default: return ESP_ERR_FLASH_NOT_INITIALISED; } + //For W25Q256 chip, the only difference between 4-Byte address command and 3-Byte version is the command value and the address bit length. + if (addr_32bit) { + addr_bitlen += 8; + } - return chip->host->driver->configure_host_io_mode(chip->host, read_command, addr_bitlen, dummy_cyclelen_base, - chip->read_mode); + if (conf_required) { + read_mode |= SPI_FLASH_CONFIG_CONF_BITS; + } + + return chip->host->driver->configure_host_io_mode(chip->host, read_command, addr_bitlen, dummy_cyclelen_base, read_mode); } esp_err_t spi_flash_chip_generic_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode) @@ -455,6 +474,8 @@ const spi_flash_chip_t esp_flash_chip_generic = { .wait_idle = spi_flash_chip_generic_wait_idle, .set_io_mode = spi_flash_chip_generic_set_io_mode, .get_io_mode = spi_flash_chip_generic_get_io_mode, + + .read_reg = spi_flash_chip_generic_read_reg, }; /******************************************************************************* @@ -586,4 +607,4 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t chip->chip_drv->set_chip_write_protect(chip, true); } return ret; -} +} \ No newline at end of file diff --git a/components/spi_flash/spi_flash_chip_issi.c b/components/spi_flash/spi_flash_chip_issi.c index e57d227686..7759e61438 100644 --- a/components/spi_flash/spi_flash_chip_issi.c +++ b/components/spi_flash/spi_flash_chip_issi.c @@ -77,7 +77,6 @@ const spi_flash_chip_t esp_flash_chip_issi = { .get_chip_write_protect = spi_flash_chip_generic_get_write_protect, .set_chip_write_protect = spi_flash_chip_generic_set_write_protect, - // TODO support protected regions on ISSI flash .num_protectable_regions = 0, .protectable_regions = NULL, .get_protected_regions = NULL, @@ -92,4 +91,6 @@ const spi_flash_chip_t esp_flash_chip_issi = { .wait_idle = spi_flash_chip_generic_wait_idle, .set_io_mode = spi_flash_chip_issi_set_io_mode, .get_io_mode = spi_flash_chip_issi_get_io_mode, + + .read_reg = spi_flash_chip_generic_read_reg, }; diff --git a/components/spi_flash/spi_flash_chip_mxic.c b/components/spi_flash/spi_flash_chip_mxic.c index 36c27b9162..8159269e36 100644 --- a/components/spi_flash/spi_flash_chip_mxic.c +++ b/components/spi_flash/spi_flash_chip_mxic.c @@ -35,6 +35,7 @@ esp_err_t spi_flash_chip_issi_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t // Use the same implementation as ISSI chips #define spi_flash_chip_mxic_set_io_mode spi_flash_chip_issi_set_io_mode #define spi_flash_chip_mxic_get_io_mode spi_flash_chip_issi_get_io_mode +#define spi_flash_chip_mxic_read_reg spi_flash_chip_generic_read_reg static const char chip_name[] = "mxic"; @@ -55,7 +56,6 @@ const spi_flash_chip_t esp_flash_chip_mxic = { .get_chip_write_protect = spi_flash_chip_generic_get_write_protect, .set_chip_write_protect = spi_flash_chip_generic_set_write_protect, - // TODO support protected regions on MXIC flash .num_protectable_regions = 0, .protectable_regions = NULL, .get_protected_regions = NULL, @@ -70,4 +70,6 @@ const spi_flash_chip_t esp_flash_chip_mxic = { .wait_idle = spi_flash_chip_generic_wait_idle, .set_io_mode = spi_flash_chip_mxic_set_io_mode, .get_io_mode = spi_flash_chip_mxic_get_io_mode, + + .read_reg = spi_flash_chip_mxic_read_reg, }; diff --git a/components/spi_flash/spi_flash_chip_winbond.c b/components/spi_flash/spi_flash_chip_winbond.c new file mode 100644 index 0000000000..d1740bec2d --- /dev/null +++ b/components/spi_flash/spi_flash_chip_winbond.c @@ -0,0 +1,214 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include // For MIN/MAX +#include "esp_log.h" +#include "spi_flash_chip_generic.h" +#include "spi_flash_defs.h" + + +#define REGION_32BIT(start, len) ((start) + (len) > (1<<24)) +#define ADDR_32BIT(addr) (addr >= (1<<24)) + + +static const char TAG[] = "chip_wb"; + +/* Driver for Winbond flash chip */ +static esp_err_t spi_flash_command_winbond_program_4B(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); +static esp_err_t spi_flash_command_winbond_erase_sector_4B(esp_flash_t *chip, uint32_t start_address); +static esp_err_t spi_flash_command_erase_block_4B(esp_flash_t *chip, uint32_t start_address); + +esp_err_t spi_flash_chip_winbond_probe(esp_flash_t *chip, uint32_t flash_id) +{ + /* Check manufacturer and product IDs match our desired masks */ + const uint8_t MFG_ID = 0xEF; + if (flash_id >> 16 != MFG_ID) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +esp_err_t spi_flash_chip_winbond_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err = ESP_OK; + const uint32_t page_size = chip->chip_drv->page_size; + uint32_t align_address; + uint8_t temp_buffer[64]; //spiflash hal max length of read no longer than 64byte + + // Configure the host, and return + err = spi_flash_chip_generic_config_host_io_mode(chip, REGION_32BIT(address, length)); + + if (err == ESP_ERR_NOT_SUPPORTED) { + ESP_LOGE(TAG, "configure host io mode failed - unsupported"); + return err; + } + + while (err == ESP_OK && length > 0) { + memset(temp_buffer, 0xFF, sizeof(temp_buffer)); + uint32_t read_len = chip->host->driver->read_data_slicer(chip->host, address, length, &align_address, page_size); + uint32_t left_off = address - align_address; + uint32_t data_len = MIN(align_address + read_len, address + length) - address; + err = chip->host->driver->read(chip->host, temp_buffer, align_address, read_len); + + memcpy(buffer, temp_buffer + left_off, data_len); + + address += data_len; + buffer = (void *)((intptr_t)buffer + data_len); + length = length - data_len; + } + + return err; +} + +esp_err_t spi_flash_chip_winbond_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err; + + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + + if (err == ESP_OK) { + // Perform the actual Page Program command + err = spi_flash_command_winbond_program_4B(chip, buffer, address, length); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->page_program_timeout); + } + return err; +} + +esp_err_t spi_flash_chip_winbond_erase_sector(esp_flash_t *chip, uint32_t start_address) +{ + esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + } + + if (err == ESP_OK) { + err = spi_flash_command_winbond_erase_sector_4B(chip, start_address); + if (err != ESP_OK) { + return err; + } + //to save time, flush cache here + if (chip->host->driver->flush_cache) { + err = chip->host->driver->flush_cache(chip->host, start_address, chip->chip_drv->sector_size); + if (err != ESP_OK) { + return err; + } + } + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->sector_erase_timeout); + } + return err; +} + +esp_err_t spi_flash_chip_winbond_erase_block(esp_flash_t *chip, uint32_t start_address) +{ + esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + } + + if (err == ESP_OK) { + err = spi_flash_command_erase_block_4B(chip, start_address); + if (err != ESP_OK) { + return err; + } + //to save time, flush cache here + if (chip->host->driver->flush_cache) { + err = chip->host->driver->flush_cache(chip->host, start_address, chip->chip_drv->block_erase_size); + if (err != ESP_OK) { + return err; + } + } + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->block_erase_timeout); + } + return err; +} + +static const char chip_name[] = "winbond"; + +// The issi chip can use the functions for generic chips except from set read mode and probe, +// So we only replace these two functions. +const spi_flash_chip_t esp_flash_chip_winbond = { + .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, + .probe = spi_flash_chip_winbond_probe, + .reset = spi_flash_chip_generic_reset, + .detect_size = spi_flash_chip_generic_detect_size, + .erase_chip = spi_flash_chip_generic_erase_chip, + .erase_sector = spi_flash_chip_winbond_erase_sector, + .erase_block = spi_flash_chip_winbond_erase_block, + .sector_size = 4 * 1024, + .block_erase_size = 64 * 1024, + + .get_chip_write_protect = spi_flash_chip_generic_get_write_protect, + .set_chip_write_protect = spi_flash_chip_generic_set_write_protect, + + .num_protectable_regions = 0, + .protectable_regions = NULL, + .get_protected_regions = NULL, + .set_protected_regions = NULL, + + .read = spi_flash_chip_winbond_read, + .write = spi_flash_chip_generic_write, + .program_page = spi_flash_chip_winbond_page_program, + .page_size = 256, + .write_encrypted = spi_flash_chip_generic_write_encrypted, + + .wait_idle = spi_flash_chip_generic_wait_idle, + .set_io_mode = spi_flash_chip_generic_set_io_mode, + .get_io_mode = spi_flash_chip_generic_get_io_mode, + + .read_reg = spi_flash_chip_generic_read_reg, +}; + + +static esp_err_t spi_flash_command_winbond_program_4B(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + bool addr_4b = ADDR_32BIT(address); + spi_flash_trans_t t = { + .command = (addr_4b? CMD_PROGRAM_PAGE_4B: CMD_PROGRAM_PAGE), + .address_bitlen = (addr_4b? 32: 24), + .address = address, + .mosi_len = length, + .mosi_data = buffer, + }; + return chip->host->driver->common_command(chip->host, &t); +} + +esp_err_t spi_flash_command_winbond_erase_sector_4B(esp_flash_t *chip, uint32_t start_address) +{ + bool addr_4b = ADDR_32BIT(start_address); + spi_flash_trans_t t = { + .command = (addr_4b? CMD_SECTOR_ERASE_4B: CMD_SECTOR_ERASE), + .address_bitlen = (addr_4b? 32: 24), + .address = start_address, + }; + return chip->host->driver->common_command(chip->host, &t); +} + +esp_err_t spi_flash_command_erase_block_4B(esp_flash_t *chip, uint32_t start_address) +{ + bool addr_4b = ADDR_32BIT(start_address); + spi_flash_trans_t t = { + .command = (addr_4b? CMD_LARGE_BLOCK_ERASE_4B: CMD_LARGE_BLOCK_ERASE), + .address_bitlen = (addr_4b? 32: 24), + .address = start_address, + }; + return chip->host->driver->common_command(chip->host, &t); +} \ No newline at end of file diff --git a/tools/ci/check_public_headers_exceptions.txt b/tools/ci/check_public_headers_exceptions.txt index bb7e4d8443..61cb5b5742 100644 --- a/tools/ci/check_public_headers_exceptions.txt +++ b/tools/ci/check_public_headers_exceptions.txt @@ -41,6 +41,7 @@ components/esp_wifi/esp32/include/phy_init_data.h components/spi_flash/include/spi_flash_chip_issi.h components/spi_flash/include/spi_flash_chip_mxic.h components/spi_flash/include/spi_flash_chip_gd.h +components/spi_flash/include/spi_flash_chip_winbond.h components/spi_flash/include/memspi_host_driver.h components/spi_flash/include/spi_flash_chip_driver.h components/spi_flash/include/spi_flash_chip_generic.h