#include "flash_qio_mode.h"
#include "esp_log.h"
#include "rom/spi_flash.h"
+#include "rom/efuse.h"
#include "soc/spi_struct.h"
+#include "soc/efuse_reg.h"
#include "sdkconfig.h"
/* SPI flash controller */
#define CMD_RDSR 0x05
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
+
+#define ESP32_D2WD_WP_GPIO 7 /* ESP32-D2WD has this GPIO wired to WP pin of flash */
+
static const char *TAG = "qio_mode";
typedef unsigned (*read_status_fn_t)();
const static qio_info_t chip_data[] = {
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
{ "MXIC", 0xC2, 0x2000, 0xFF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 },
- { "ISSI", 0x9D, 0x4000, 0xFF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 },
+ { "ISSI", 0x9D, 0x4000, 0xCF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 }, /* IDs 0x40xx, 0x70xx */
{ "WinBond", 0xEF, 0x4000, 0xFF00, read_status_16b_rdsr_rdsr2, write_status_16b_wrsr, 9 },
/* Final entry is default entry, if no other IDs have matched.
*/
static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
+/* dummy_len_plus values defined in ROM for SPI flash configuration */
+extern uint8_t g_rom_spiflash_dummy_len_plus[];
+
void bootloader_enable_qio_mode(void)
{
uint32_t raw_flash_id;
uint8_t status_qio_bit)
{
uint32_t status;
+ const uint32_t spiconfig = ets_efuse_get_spiconfig();
+
+ if (spiconfig != EFUSE_SPICONFIG_SPI_DEFAULTS && spiconfig != EFUSE_SPICONFIG_HSPI_DEFAULTS) {
+ // spiconfig specifies a custom efuse pin configuration. This config defines all pins -except- WP.
+ //
+ // For now, in this situation we only support Quad I/O mode for ESP32-D2WD where WP pin is known.
+ uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_RESERVE);
+ uint32_t pkg_ver = chip_ver & 0x7;
+ const uint32_t PKG_VER_ESP32_D2WD = 2; // TODO: use chip detection API once available
+ if (pkg_ver != PKG_VER_ESP32_D2WD) {
+ ESP_LOGE(TAG, "Quad I/O is only supported for standard pin numbers or ESP32-D2WD. Falling back to Dual I/O.");
+ return;
+ }
+ }
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
#else
mode = ESP_ROM_SPIFLASH_QIO_MODE;
#endif
+
esp_rom_spiflash_config_readmode(mode);
+ esp_rom_spiflash_select_qio_pins(ESP32_D2WD_WP_GPIO, spiconfig);
}
static unsigned read_status_8b_rdsr()
SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
SPIFLASH.data_buf[0] = mosi_data;
+ if (g_rom_spiflash_dummy_len_plus[1]) {
+ /* When flash pins are mapped via GPIO matrix, need a dummy cycle before reading via MISO */
+ if (miso_len > 0) {
+ SPIFLASH.user.usr_dummy = 1;
+ SPIFLASH.user1.usr_dummy_cyclelen = g_rom_spiflash_dummy_len_plus[1] - 1;
+ } else {
+ SPIFLASH.user.usr_dummy = 0;
+ SPIFLASH.user1.usr_dummy_cyclelen = 0;
+ }
+ }
+
SPIFLASH.cmd.usr = 1;
while(SPIFLASH.cmd.usr != 0)
{ }
uint32_t ets_efuse_get_8M_clock(void);
/**
- * @brief Read spi pad configuration, show gpio number of flash pad, includes 5 pads.
+ * @brief Read spi flash pin configuration from Efuse
*
- * @param null
- *
- * @return uint32_t: 0, invalid, flash pad decided by strapping
- * else, bit[5:0] spiclk, bit[11:6] spiq, bit[17:12] spid, bit[23:18] spics0, bit[29:24] spihd
+ * @return
+ * - 0 for default SPI pins.
+ * - 1 for default HSPI pins.
+ * - Other values define a custom pin configuration mask. Pins are encoded as per the EFUSE_SPICONFIG_RET_SPICLK,
+ * EFUSE_SPICONFIG_RET_SPIQ, EFUSE_SPICONFIG_RET_SPID, EFUSE_SPICONFIG_RET_SPICS0, EFUSE_SPICONFIG_RET_SPIHD macros.
+ * WP pin (for quad I/O modes) is not saved in efuse and not returned by this function.
*/
uint32_t ets_efuse_get_spiconfig(void);
+#define EFUSE_SPICONFIG_SPI_DEFAULTS 0
+#define EFUSE_SPICONFIG_HSPI_DEFAULTS 1
+
#define EFUSE_SPICONFIG_RET_SPICLK_MASK 0x3f
#define EFUSE_SPICONFIG_RET_SPICLK_SHIFT 0
#define EFUSE_SPICONFIG_RET_SPICLK(ret) (((ret) >> EFUSE_SPICONFIG_RET_SPICLK_SHIFT) & EFUSE_SPICONFIG_RET_SPICLK_MASK)
esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *spi);
+/** @brief Enable Quad I/O pin functions
+ *
+ * @note Please do not call this function in SDK.
+ *
+ * Sets the HD & WP pin functions for Quad I/O modes, based on the
+ * efuse SPI pin configuration.
+ *
+ * @param wp_gpio_num - Number of the WP pin to reconfigure for quad I/O.
+ *
+ * @param spiconfig - Pin configuration, as returned from ets_efuse_get_spiconfig().
+ * - If this parameter is 0, default SPI pins are used and wp_gpio_num parameter is ignored.
+ * - If this parameter is 1, default HSPI pins are used and wp_gpio_num parameter is ignored.
+ * - For other values, this parameter encodes the HD pin number and also the CLK pin number. CLK pin selection is used
+ * to determine if HSPI or SPI peripheral will be used (use HSPI if CLK pin is the HSPI clock pin, otherwise use SPI).
+ * Both HD & WP pins are configured via GPIO matrix to map to the selected peripheral.
+ */
+void esp_rom_spiflash_select_qio_pins(uint8_t wp_gpio_num, uint32_t spiconfig);
+
/** @brief Global esp_rom_spiflash_chip_t structure used by ROM functions
*
*/