Merge branch 'feature/esp32s2beta_spi_flash_driver' into 'master'

Use new SPI flash driver abstraction layers

See merge request espressif/esp-idf!5504
This commit is contained in:
Angus Gratton 2019-11-28 09:20:01 +08:00
commit b8af7dd7cb
34 changed files with 1394 additions and 281 deletions

View file

@ -275,43 +275,23 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
ESP_LOGD(SPI_TAG, "SPI%d use iomux pins.", host+1);
if (bus_config->mosi_io_num >= 0) {
gpio_iomux_in(bus_config->mosi_io_num, spi_periph_signal[host].spid_in);
#if CONFIG_IDF_TARGET_ESP32
gpio_iomux_out(bus_config->mosi_io_num, spi_periph_signal[host].func, false);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
gpio_iomux_out(bus_config->mosi_io_num, spi_periph_signal[host].func, false);
#endif
}
if (bus_config->miso_io_num >= 0) {
gpio_iomux_in(bus_config->miso_io_num, spi_periph_signal[host].spiq_in);
#if CONFIG_IDF_TARGET_ESP32
gpio_iomux_out(bus_config->miso_io_num, spi_periph_signal[host].func, false);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
gpio_iomux_out(bus_config->miso_io_num, spi_periph_signal[host].func, false);
#endif
}
if (bus_config->quadwp_io_num >= 0) {
gpio_iomux_in(bus_config->quadwp_io_num, spi_periph_signal[host].spiwp_in);
#if CONFIG_IDF_TARGET_ESP32
gpio_iomux_out(bus_config->quadwp_io_num, spi_periph_signal[host].func, false);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
gpio_iomux_out(bus_config->quadwp_io_num, spi_periph_signal[host].func, false);
#endif
}
if (bus_config->quadhd_io_num >= 0) {
gpio_iomux_in(bus_config->quadhd_io_num, spi_periph_signal[host].spihd_in);
#if CONFIG_IDF_TARGET_ESP32
gpio_iomux_out(bus_config->quadhd_io_num, spi_periph_signal[host].func, false);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
gpio_iomux_out(bus_config->quadhd_io_num, spi_periph_signal[host].func, false);
#endif
}
if (bus_config->sclk_io_num >= 0) {
gpio_iomux_in(bus_config->sclk_io_num, spi_periph_signal[host].spiclk_in);
#if CONFIG_IDF_TARGET_ESP32
gpio_iomux_out(bus_config->sclk_io_num, spi_periph_signal[host].func, false);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
gpio_iomux_out(bus_config->sclk_io_num, spi_periph_signal[host].func, false);
#endif
}
temp_flag |= SPICOMMON_BUSFLAG_IOMUX_PINS;
} else {

View file

@ -46,6 +46,7 @@
#include "esp_heap_caps_init.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_flash_internal.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_spi_flash.h"
@ -318,6 +319,11 @@ void start_cpu0_default(void)
spi_flash_init();
/* init default OS-aware flash access critical section */
spi_flash_guard_set(&g_flash_guard_default_ops);
esp_flash_app_init();
esp_err_t flash_ret = esp_flash_init_default_chip();
assert(flash_ret == ESP_OK);
#ifdef CONFIG_PM_ENABLE
esp_pm_impl_init();
#ifdef CONFIG_PM_DFS_INIT_AUTO

View file

@ -33,14 +33,16 @@ list(APPEND srcs
"src/hal/gpio_hal.c"
"src/hal/uart_hal.c"
"src/hal/uart_hal_iram.c"
"src/hal/spi_flash_hal.c"
"src/hal/spi_flash_hal_iram.c"
)
# TODO: SPI Flash HAL for ESP32S2Beta also
if(IDF_TARGET STREQUAL "esp32")
list(APPEND srcs "src/hal/spi_flash_hal.c"
"src/hal/spi_flash_hal_iram.c"
"src/hal/mcpwm_hal.c"
)
list(APPEND srcs "src/hal/mcpwm_hal.c")
endif()
if(IDF_TARGET STREQUAL "esp32s2beta")
list(APPEND srcs "src/hal/spi_flash_hal_gpspi.c")
endif()
idf_component_register(SRCS "${srcs}"

View file

@ -153,6 +153,27 @@ static inline void spi_flash_ll_write_word(spi_dev_t *dev, uint32_t word)
dev->data_buf[0] = word;
}
/**
* Set the data to be written in the data buffer.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer holding the data
* @param length Length of data in bytes.
*/
static inline void spi_flash_ll_set_buffer_data(spi_dev_t *dev, const void *buffer, uint32_t length)
{
// Load data registers, word at a time
int num_words = (length + 3) >> 2;
for (int i = 0; i < num_words; i++) {
uint32_t word = 0;
uint32_t word_len = MIN(length, sizeof(word));
memcpy(&word, buffer, word_len);
dev->data_buf[i] = word;
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
}
}
/**
* Program a page of the flash chip. Call ``spi_flash_ll_set_address`` before
* this to set the address to program.
@ -164,18 +185,7 @@ static inline void spi_flash_ll_write_word(spi_dev_t *dev, uint32_t word)
static inline void spi_flash_ll_program_page(spi_dev_t *dev, const void *buffer, uint32_t length)
{
dev->user.usr_dummy = 0;
// Load data registers, word at a time
int num_words = (length + 3) / 4;
for (int i = 0; i < num_words; i++) {
uint32_t word = 0;
uint32_t word_len = MIN(length, sizeof(word));
memcpy(&word, buffer, word_len);
dev->data_buf[i] = word;
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
}
spi_flash_ll_set_buffer_data(dev, buffer, length);
dev->cmd.flash_pp = 1;
}

View file

@ -60,3 +60,4 @@
//#define SOC_SPI_SLAVE_SUPPORT_SEG_TRANS
//#define SOC_SPI_SUPPORT_CD_SIG
#define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(SPI_HOST) true

View file

@ -0,0 +1,319 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The Lowlevel layer for SPI Flash
#pragma once
#include <stdlib.h>
#include "soc/spi_periph.h"
#include "hal/spi_types.h"
#include "hal/spi_flash_types.h"
#include <sys/param.h> // For MIN/MAX
#include <stdbool.h>
#include <string.h>
#define gpspi_flash_ll_get_hw(host_id) (((host_id)==SPI2_HOST ? &GPSPI2 \
: ((host_id)==SPI3_HOST ? &GPSPI3 \
: ((host_id)==SPI4_HOST ? &GPSPI4 \
: ({abort();(spi_dev_t*)0;})))))
typedef typeof(GPSPI2.clock) gpspi_flash_ll_clock_reg_t;
//Supported clock register values
#define GPSPI_FLASH_LL_CLKREG_VAL_5MHZ ((gpspi_flash_ll_clock_reg_t){.val=0x0000F1CF}) ///< Clock set to 5 MHz
#define GPSPI_FLASH_LL_CLKREG_VAL_10MHZ ((gpspi_flash_ll_clock_reg_t){.val=0x000070C7}) ///< Clock set to 10 MHz
#define GPSPI_FLASH_LL_CLKREG_VAL_20MHZ ((gpspi_flash_ll_clock_reg_t){.val=0x00003043}) ///< Clock set to 20 MHz
#define GPSPI_FLASH_LL_CLKREG_VAL_26MHZ ((gpspi_flash_ll_clock_reg_t){.val=0x00002002}) ///< Clock set to 26 MHz
#define GPSPI_FLASH_LL_CLKREG_VAL_40MHZ ((gpspi_flash_ll_clock_reg_t){.val=0x00001001}) ///< Clock set to 40 MHz
#define GPSPI_FLASH_LL_CLKREG_VAL_80MHZ ((gpspi_flash_ll_clock_reg_t){.val=0x80000000}) ///< Clock set to 80 MHz
/*------------------------------------------------------------------------------
* Control
*----------------------------------------------------------------------------*/
/**
* Reset peripheral registers before configuration and starting control
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_reset(spi_dev_t *dev)
{
dev->user.val = 0;
dev->ctrl.val = 0;
}
/**
* Check whether the previous operation is done.
*
* @param dev Beginning address of the peripheral registers.
*
* @return true if last command is done, otherwise false.
*/
static inline bool gpspi_flash_ll_cmd_is_done(const spi_dev_t *dev)
{
return (dev->cmd.val == 0);
}
/**
* Get the read data from the buffer after ``gpspi_flash_ll_read`` is done.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer to hold the output data
* @param read_len Length to get out of the buffer
*/
static inline void gpspi_flash_ll_get_buffer_data(spi_dev_t *dev, void *buffer, uint32_t read_len)
{
if (((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) {
// If everything is word-aligned, do a faster memcpy
memcpy(buffer, (void *)dev->data_buf, read_len);
} else {
// Otherwise, slow(er) path copies word by word
int copy_len = read_len;
for (int i = 0; i < (read_len + 3) / 4; i++) {
int word_len = MIN(sizeof(uint32_t), copy_len);
uint32_t word = dev->data_buf[i];
memcpy(buffer, &word, word_len);
buffer = (void *)((intptr_t)buffer + word_len);
copy_len -= word_len;
}
}
}
/**
* Write a word to the data buffer.
*
* @param dev Beginning address of the peripheral registers.
* @param word Data to write at address 0.
*/
static inline void gpspi_flash_ll_write_word(spi_dev_t *dev, uint32_t word)
{
dev->data_buf[0] = word;
}
/**
* Set the data to be written in the data buffer.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer holding the data
* @param length Length of data in bytes.
*/
static inline void gpspi_flash_ll_set_buffer_data(spi_dev_t *dev, const void *buffer, uint32_t length)
{
// Load data registers, word at a time
int num_words = (length + 3) / 4;
for (int i = 0; i < num_words; i++) {
uint32_t word = 0;
uint32_t word_len = MIN(length, sizeof(word));
memcpy(&word, buffer, word_len);
dev->data_buf[i] = word;
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
}
}
/**
* Trigger a user defined transaction. All phases, including command, address, dummy, and the data phases,
* should be configured before this is called.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
{
dev->cmd.usr = 1;
}
/**
* Check whether the host is idle to perform new commands.
*
* @param dev Beginning address of the peripheral registers.
*
* @return true if the host is idle, otherwise false
*/
static inline bool gpspi_flash_ll_host_idle(const spi_dev_t *dev)
{
return dev->fsm.st != 0;
}
/**
* Set phases for user-defined transaction to read
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_read_phase(spi_dev_t *dev)
{
typeof (dev->user) user = {
.usr_command = 1,
.usr_mosi = 0,
.usr_miso = 1,
.usr_addr = 1,
};
dev->user = user;
}
/*------------------------------------------------------------------------------
* Configs
*----------------------------------------------------------------------------*/
/**
* Select which pin to use for the flash
*
* @param dev Beginning address of the peripheral registers.
* @param pin Pin ID to use, 0-2. Set to other values to disable all the CS pins.
*/
static inline void gpspi_flash_ll_set_cs_pin(spi_dev_t *dev, int pin)
{
dev->misc.cs0_dis = (pin == 0) ? 0 : 1;
dev->misc.cs1_dis = (pin == 1) ? 0 : 1;
}
/**
* Set the read io mode.
*
* @param dev Beginning address of the peripheral registers.
* @param read_mode I/O mode to use in the following transactions.
*/
static inline void gpspi_flash_ll_set_read_mode(spi_dev_t *dev, esp_flash_io_mode_t read_mode)
{
typeof (dev->ctrl) ctrl = dev->ctrl;
typeof (dev->user) user = dev->user;
ctrl.val &= ~(SPI_FCMD_QUAD_M | SPI_FADDR_QUAD_M | SPI_FREAD_QUAD_M | SPI_FCMD_DUAL_M | SPI_FADDR_DUAL_M | SPI_FREAD_DUAL_M);
user.val &= ~(SPI_FWRITE_QUAD_M | SPI_FWRITE_DUAL_M);
ctrl.val |= SPI_FAST_RD_MODE_M;
switch (read_mode) {
case SPI_FLASH_FASTRD:
//the default option
break;
case SPI_FLASH_QIO:
ctrl.fread_quad = 1;
ctrl.faddr_quad = 1;
user.fwrite_quad = 1;
break;
case SPI_FLASH_QOUT:
ctrl.fread_quad = 1;
user.fwrite_quad = 1;
break;
case SPI_FLASH_DIO:
ctrl.fread_dual = 1;
ctrl.faddr_dual = 1;
user.fwrite_dual = 1;
break;
case SPI_FLASH_DOUT:
ctrl.fread_dual = 1;
user.fwrite_dual = 1;
break;
case SPI_FLASH_SLOWRD:
ctrl.fast_rd_mode = 0;
break;
default:
abort();
}
dev->ctrl = ctrl;
dev->user = user;
}
/**
* Set clock frequency to work at.
*
* @param dev Beginning address of the peripheral registers.
* @param clock_val pointer to the clock value to set
*/
static inline void gpspi_flash_ll_set_clock(spi_dev_t *dev, gpspi_flash_ll_clock_reg_t *clock_val)
{
dev->clock = *clock_val;
}
/**
* Set the input length, in bits.
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of input, in bits.
*/
static inline void gpspi_flash_ll_set_miso_bitlen(spi_dev_t *dev, uint32_t bitlen)
{
dev->user.usr_miso = bitlen > 0;
dev->miso_dlen.usr_miso_bit_len = bitlen ? (bitlen - 1) : 0;
}
/**
* Set the output length, in bits (not including command, address and dummy
* phases)
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of output, in bits.
*/
static inline void gpspi_flash_ll_set_mosi_bitlen(spi_dev_t *dev, uint32_t bitlen)
{
dev->user.usr_mosi = bitlen > 0;
dev->mosi_dlen.usr_mosi_bit_len = bitlen ? (bitlen - 1) : 0;
}
/**
* Set the command with fixed length (8 bits).
*
* @param dev Beginning address of the peripheral registers.
* @param command Command to send
*/
static inline void gpspi_flash_ll_set_command8(spi_dev_t *dev, uint8_t command)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (8 - 1),
};
dev->user2 = user2;
}
/**
* Set the address length to send, in bits. Should be called before commands that requires the address e.g. erase sector, read, write...
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of the address, in bits
*/
static inline void gpspi_flash_ll_set_addr_bitlen(spi_dev_t *dev, uint32_t bitlen)
{
dev->user1.usr_addr_bitlen = (bitlen - 1);
dev->user.usr_addr = bitlen ? 1 : 0;
}
/**
* Set the address to send. 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 gpspi_flash_ll_set_address(spi_dev_t *dev, uint32_t addr)
{
dev->addr = addr;
}
/**
* Set the length of dummy cycles.
*
* @param dev Beginning address of the peripheral registers.
* @param dummy_n Cycles of dummy phases
*/
static inline void gpspi_flash_ll_set_dummy(spi_dev_t *dev, uint32_t dummy_n)
{
dev->user.usr_dummy = dummy_n ? 1 : 0;
dev->user1.usr_dummy_cyclelen = dummy_n - 1;
}

View file

@ -0,0 +1,85 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The Lowlevel layer for SPI Flash
#pragma once
#include "gpspi_flash_ll.h"
#include "spimem_flash_ll.h"
// For esp32s2beta, spimem is equivalent to traditional spi peripherals found
// in esp32. Let the spi flash clock reg definitions reflect this.
#define SPI_FLASH_LL_CLKREG_VAL_5MHZ {.spimem=SPIMEM_FLASH_LL_CLKREG_VAL_5MHZ}
#define SPI_FLASH_LL_CLKREG_VAL_10MHZ {.spimem=SPIMEM_FLASH_LL_CLKREG_VAL_10MHZ}
#define SPI_FLASH_LL_CLKREG_VAL_20MHZ {.spimem=SPIMEM_FLASH_LL_CLKREG_VAL_20MHZ}
#define SPI_FLASH_LL_CLKREG_VAL_26MHZ {.spimem=SPIMEM_FLASH_LL_CLKREG_VAL_26MHZ}
#define SPI_FLASH_LL_CLKREG_VAL_40MHZ {.spimem=SPIMEM_FLASH_LL_CLKREG_VAL_40MHZ}
#define SPI_FLASH_LL_CLKREG_VAL_80MHZ {.spimem=SPIMEM_FLASH_LL_CLKREG_VAL_80MHZ}
#define spi_flash_ll_get_hw(host_id) (((host_id)<=SPI1_HOST ? (spi_dev_t*) spimem_flash_ll_get_hw(host_id) \
: gpspi_flash_ll_get_hw(host_id)))
typedef union {
gpspi_flash_ll_clock_reg_t gpspi;
spimem_flash_ll_clock_reg_t spimem;
} 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)
#define spi_flash_ll_set_buffer_data(dev, buffer, len) gpspi_flash_ll_set_buffer_data((spi_dev_t*)dev, buffer, len)
#define spi_flash_ll_user_start(dev) gpspi_flash_ll_user_start((spi_dev_t*)dev)
#define spi_flash_ll_host_idle(dev) gpspi_flash_ll_host_idle((spi_dev_t*)dev)
#define spi_flash_ll_read_phase(dev) gpspi_flash_ll_read_phase((spi_dev_t*)dev)
#define spi_flash_ll_set_cs_pin(dev, pin) gpspi_flash_ll_set_cs_pin((spi_dev_t*)dev, pin)
#define spi_flash_ll_set_read_mode(dev, read_mode) gpspi_flash_ll_set_read_mode((spi_dev_t*)dev, read_mode)
#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_addr_bitlen(dev, bitlen) gpspi_flash_ll_set_addr_bitlen((spi_dev_t*)dev, bitlen)
#define spi_flash_ll_set_address(dev, addr) gpspi_flash_ll_set_address((spi_dev_t*)dev, addr)
#define spi_flash_ll_set_dummy(dev, dummy) gpspi_flash_ll_set_dummy((spi_dev_t*)dev, dummy)
#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)
#define spi_flash_ll_erase_sector(dev) spimem_flash_ll_erase_sector((spi_mem_dev_t*)dev)
#define spi_flash_ll_erase_block(dev) spimem_flash_ll_erase_block((spi_mem_dev_t*)dev)
#define spi_flash_ll_set_write_protect(dev, wp) spimem_flash_ll_set_write_protect((spi_mem_dev_t*)dev, wp)
#define spi_flash_ll_get_buffer_data(dev, buffer, read_len) spimem_flash_ll_get_buffer_data((spi_mem_dev_t*)dev, buffer, read_len)
#define spi_flash_ll_set_buffer_data(dev, buffer, len) spimem_flash_ll_set_buffer_data((spi_mem_dev_t*)dev, buffer, len)
#define spi_flash_ll_program_page(dev, buffer, len) spimem_flash_ll_program_page((spi_mem_dev_t*)dev, buffer, len)
#define spi_flash_ll_user_start(dev) spimem_flash_ll_user_start((spi_mem_dev_t*)dev)
#define spi_flash_ll_host_idle(dev) spimem_flash_ll_host_idle((spi_mem_dev_t*)dev)
#define spi_flash_ll_read_phase(dev) spimem_flash_ll_read_phase((spi_mem_dev_t*)dev)
#define spi_flash_ll_set_cs_pin(dev, pin) spimem_flash_ll_set_cs_pin((spi_mem_dev_t*)dev, pin)
#define spi_flash_ll_set_read_mode(dev, read_mode) spimem_flash_ll_set_read_mode((spi_mem_dev_t*)dev, read_mode)
#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_addr_bitlen(dev, bitlen) spimem_flash_ll_set_addr_bitlen((spi_mem_dev_t*)dev, bitlen)
#define spi_flash_ll_set_address(dev, addr) spimem_flash_ll_set_address((spi_mem_dev_t*)dev, addr)
#define spi_flash_ll_set_dummy(dev, dummy) spimem_flash_ll_set_dummy((spi_mem_dev_t*)dev, dummy)
#endif

View file

@ -0,0 +1,354 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The Lowlevel layer for SPI Flash
#pragma once
#include <stdlib.h>
#include <sys/param.h> // For MIN/MAX
#include <stdbool.h>
#include <string.h>
#include "soc/spi_periph.h"
#include "hal/spi_types.h"
#include "hal/spi_flash_types.h"
#define spimem_flash_ll_get_hw(host_id) (((host_id)==SPI1_HOST ? &SPIMEM1 : NULL ))
typedef typeof(SPIMEM1.clock) spimem_flash_ll_clock_reg_t;
//Supported clock register values
#define SPIMEM_FLASH_LL_CLKREG_VAL_5MHZ ((spimem_flash_ll_clock_reg_t){.val=0x000F070F}) ///< Clock set to 5 MHz
#define SPIMEM_FLASH_LL_CLKREG_VAL_10MHZ ((spimem_flash_ll_clock_reg_t){.val=0x00070307}) ///< Clock set to 10 MHz
#define SPIMEM_FLASH_LL_CLKREG_VAL_20MHZ ((spimem_flash_ll_clock_reg_t){.val=0x00030103}) ///< Clock set to 20 MHz
#define SPIMEM_FLASH_LL_CLKREG_VAL_26MHZ ((spimem_flash_ll_clock_reg_t){.val=0x00020002}) ///< Clock set to 26 MHz
#define SPIMEM_FLASH_LL_CLKREG_VAL_40MHZ ((spimem_flash_ll_clock_reg_t){.val=0x00010001}) ///< Clock set to 40 MHz
#define SPIMEM_FLASH_LL_CLKREG_VAL_80MHZ ((spimem_flash_ll_clock_reg_t){.val=0x80000000}) ///< Clock set to 80 MHz
/*------------------------------------------------------------------------------
* Control
*----------------------------------------------------------------------------*/
/**
* Reset peripheral registers before configuration and starting control
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_reset(spi_mem_dev_t *dev)
{
dev->user.val = 0;
dev->ctrl.val = 0;
}
/**
* Check whether the previous operation is done.
*
* @param dev Beginning address of the peripheral registers.
*
* @return true if last command is done, otherwise false.
*/
static inline bool spimem_flash_ll_cmd_is_done(const spi_mem_dev_t *dev)
{
return (dev->cmd.val == 0);
}
/**
* Erase the flash chip.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_erase_chip(spi_mem_dev_t *dev)
{
dev->cmd.flash_ce = 1;
}
/**
* Erase the sector, the address should be set by spimem_flash_ll_set_address.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_erase_sector(spi_mem_dev_t *dev)
{
dev->ctrl.val = 0;
dev->cmd.flash_se = 1;
}
/**
* Erase the block, the address should be set by spimem_flash_ll_set_address.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev)
{
dev->cmd.flash_be = 1;
}
/**
* Enable/disable write protection for the flash chip.
*
* @param dev Beginning address of the peripheral registers.
* @param wp true to enable the protection, false to disable (write enable).
*/
static inline void spimem_flash_ll_set_write_protect(spi_mem_dev_t *dev, bool wp)
{
if (wp) {
dev->cmd.flash_wrdi = 1;
} else {
dev->cmd.flash_wren = 1;
}
}
/**
* Get the read data from the buffer after ``spimem_flash_ll_read`` is done.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer to hold the output data
* @param read_len Length to get out of the buffer
*/
static inline void spimem_flash_ll_get_buffer_data(spi_mem_dev_t *dev, void *buffer, uint32_t read_len)
{
if (((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) {
// If everything is word-aligned, do a faster memcpy
memcpy(buffer, (void *)dev->data_buf, read_len);
} else {
// Otherwise, slow(er) path copies word by word
int copy_len = read_len;
for (int i = 0; i < (read_len + 3) / 4; i++) {
int word_len = MIN(sizeof(uint32_t), copy_len);
uint32_t word = dev->data_buf[i];
memcpy(buffer, &word, word_len);
buffer = (void *)((intptr_t)buffer + word_len);
copy_len -= word_len;
}
}
}
/**
* Set the data to be written in the data buffer.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer holding the data
* @param length Length of data in bytes.
*/
static inline void spimem_flash_ll_set_buffer_data(spi_mem_dev_t *dev, const void *buffer, uint32_t length)
{
// Load data registers, word at a time
int num_words = (length + 3) / 4;
for (int i = 0; i < num_words; i++) {
uint32_t word = 0;
uint32_t word_len = MIN(length, sizeof(word));
memcpy(&word, buffer, word_len);
dev->data_buf[i] = word;
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
}
}
/**
* Program a page of the flash chip. Call ``spimem_flash_ll_set_address`` before
* this to set the address to program.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer holding the data to program
* @param length Length to program.
*/
static inline void spimem_flash_ll_program_page(spi_mem_dev_t *dev, const void *buffer, uint32_t length)
{
dev->user.usr_dummy = 0;
spimem_flash_ll_set_buffer_data(dev, buffer, length);
dev->cmd.flash_pp = 1;
}
/**
* Trigger a user defined transaction. All phases, including command, address, dummy, and the data phases,
* should be configured before this is called.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
{
dev->cmd.usr = 1;
}
/**
* Check whether the host is idle to perform new commands.
*
* @param dev Beginning address of the peripheral registers.
*
* @return true if the host is idle, otherwise false
*/
static inline bool spimem_flash_ll_host_idle(const spi_mem_dev_t *dev)
{
return dev->fsm.st != 0;
}
/**
* Set phases for user-defined transaction to read
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_read_phase(spi_mem_dev_t *dev)
{
typeof (dev->user) user = {
.usr_command = 1,
.usr_mosi = 0,
.usr_miso = 1,
.usr_addr = 1,
};
dev->user = user;
}
/*------------------------------------------------------------------------------
* Configs
*----------------------------------------------------------------------------*/
/**
* Select which pin to use for the flash
*
* @param dev Beginning address of the peripheral registers.
* @param pin Pin ID to use, 0-2. Set to other values to disable all the CS pins.
*/
static inline void spimem_flash_ll_set_cs_pin(spi_mem_dev_t *dev, int pin)
{
dev->misc.cs0_dis = (pin == 0) ? 0 : 1;
dev->misc.cs1_dis = (pin == 1) ? 0 : 1;
}
/**
* Set the read io mode.
*
* @param dev Beginning address of the peripheral registers.
* @param read_mode I/O mode to use in the following transactions.
*/
static inline void spimem_flash_ll_set_read_mode(spi_mem_dev_t *dev, esp_flash_io_mode_t read_mode)
{
typeof (dev->ctrl) ctrl = dev->ctrl;
ctrl.val &= ~(SPI_MEM_FREAD_QIO_M | SPI_MEM_FREAD_QUAD_M | SPI_MEM_FREAD_DIO_M | SPI_MEM_FREAD_DUAL_M);
ctrl.val |= SPI_MEM_FASTRD_MODE_M;
switch (read_mode) {
case SPI_FLASH_FASTRD:
//the default option
break;
case SPI_FLASH_QIO:
ctrl.fread_qio = 1;
break;
case SPI_FLASH_QOUT:
ctrl.fread_quad = 1;
break;
case SPI_FLASH_DIO:
ctrl.fread_dio = 1;
break;
case SPI_FLASH_DOUT:
ctrl.fread_dual = 1;
break;
case SPI_FLASH_SLOWRD:
ctrl.fastrd_mode = 0;
break;
default:
abort();
}
dev->ctrl = ctrl;
}
/**
* Set clock frequency to work at.
*
* @param dev Beginning address of the peripheral registers.
* @param clock_val pointer to the clock value to set
*/
static inline void spimem_flash_ll_set_clock(spi_mem_dev_t *dev, spimem_flash_ll_clock_reg_t *clock_val)
{
dev->clock = *clock_val;
}
/**
* Set the input length, in bits.
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of input, in bits.
*/
static inline void spimem_flash_ll_set_miso_bitlen(spi_mem_dev_t *dev, uint32_t bitlen)
{
dev->user.usr_miso = bitlen > 0;
dev->miso_dlen.usr_miso_bit_len = bitlen ? (bitlen - 1) : 0;
}
/**
* Set the output length, in bits (not including command, address and dummy
* phases)
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of output, in bits.
*/
static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t bitlen)
{
dev->user.usr_mosi = bitlen > 0;
dev->mosi_dlen.usr_mosi_bit_len = bitlen ? (bitlen - 1) : 0;
}
/**
* Set the command with fixed length (8 bits).
*
* @param dev Beginning address of the peripheral registers.
* @param command Command to send
*/
static inline void spimem_flash_ll_set_command8(spi_mem_dev_t *dev, uint8_t command)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (8 - 1),
};
dev->user2 = user2;
}
/**
* Set the address length to send, in bits. Should be called before commands that requires the address e.g. erase sector, read, write...
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of the address, in bits
*/
static inline void spimem_flash_ll_set_addr_bitlen(spi_mem_dev_t *dev, uint32_t bitlen)
{
dev->user1.usr_addr_bitlen = (bitlen - 1);
dev->user.usr_addr = bitlen ? 1 : 0;
}
/**
* Set the address to send. 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_address(spi_mem_dev_t *dev, uint32_t addr)
{
dev->addr = addr;
}
/**
* Set the length of dummy cycles.
*
* @param dev Beginning address of the peripheral registers.
* @param dummy_n Cycles of dummy phases
*/
static inline void spimem_flash_ll_set_dummy(spi_mem_dev_t *dev, uint32_t dummy_n)
{
dev->user.usr_dummy = dummy_n ? 1 : 0;
dev->user1.usr_dummy_cyclelen = dummy_n - 1;
}

View file

@ -176,7 +176,7 @@
#define TIMER_CLK_FREQ (80000000>>4) //80MHz divided by 16
#define SPI_CLK_DIV 4
#define TICKS_PER_US_ROM 40 // CPU is 80MHz
#define GPIO_MATRIX_DELAY_NS 25
#define GPIO_MATRIX_DELAY_NS 15
//}}
/* Overall memory map */

View file

@ -26,7 +26,7 @@
#define SPI_IOMUX_PIN_NUM_WP 28
//TODO: add the next slot
#define FSPI_FUNC_NUM 0
#define FSPI_FUNC_NUM 2
#define FSPI_IOMUX_PIN_NUM_HD 9
#define FSPI_IOMUX_PIN_NUM_CS 10
#define FSPI_IOMUX_PIN_NUM_MOSI 11
@ -43,3 +43,7 @@
#define SOC_SPI_SUPPORT_DDRCLK 1
#define SOC_SPI_SLAVE_SUPPORT_SEG_TRANS 1
#define SOC_SPI_SUPPORT_CD_SIG 1
// Peripheral supports DIO, DOUT, QIO, or QOUT
#define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(spi_dev) (!((void*)spi_dev == (void*)&GPSPI3 \
|| (void*)spi_dev == (void*)&GPSPI4))

View file

@ -204,17 +204,7 @@ void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *driver);
*
* @return True if the buffer can be used to send data, otherwise false.
*/
static inline bool spi_flash_hal_supports_direct_write(spi_flash_host_driver_t *driver, const void *p)
{
#ifdef ESP_PLATFORM
bool direct_write = ( ((spi_flash_memspi_data_t *)driver->driver_data)->spi != &SPI1
|| esp_ptr_in_dram(p) );
#else
//If it is not on real chips, there is no limitation that the data has to be in DRAM.
bool direct_write = true;
#endif
return direct_write;
}
bool spi_flash_hal_supports_direct_write(spi_flash_host_driver_t *driver, const void *p);
/**
* Check whether the given buffer can be used as the read buffer directly. If 'chip' is connected to the main SPI bus, we can only read directly from
@ -225,15 +215,4 @@ static inline bool spi_flash_hal_supports_direct_write(spi_flash_host_driver_t *
*
* @return True if the buffer can be used to receive data, otherwise false.
*/
static inline bool spi_flash_hal_supports_direct_read(spi_flash_host_driver_t *driver, const void *p)
{
#ifdef ESP_PLATFORM
//currently the driver doesn't support to read through DMA, no word-aligned requirements
bool direct_read = ( ((spi_flash_memspi_data_t *)driver->driver_data)->spi != &SPI1
|| esp_ptr_in_dram(p) );
#else
//If it is not on real chips, there is no limitation that the data has to be in DRAM.
bool direct_read = true;
#endif
return direct_read;
}
bool spi_flash_hal_supports_direct_read(spi_flash_host_driver_t *driver, const void *p);

View file

@ -24,10 +24,12 @@ extern "C" {
/** Definition of a common transaction. Also holds the return value. */
typedef struct {
uint8_t command; ///< Command to send, always 8bits
uint8_t mosi_len; ///< Output data length, in bits
uint8_t miso_len; ///< Input data length, in bits
uint32_t mosi_data; ///< Output data to slave
uint32_t miso_data[2]; ///< [out] Input data from slave, little endian
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
} spi_flash_trans_t;
/**

View file

@ -25,6 +25,7 @@
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32S2BETA
#include "soc/spi_mem_struct.h"
#include "soc/spi_mem_reg.h"
#endif
#ifdef __cplusplus

View file

@ -19,4 +19,5 @@ entries:
spi_flash_hal_iram (noflash)
ledc_hal_iram (noflash_text)
i2c_hal_iram (noflash)
spi_flash_hal_gpspi (noflash)
lldesc (noflash_text)

View file

@ -36,12 +36,23 @@ static const spi_flash_hal_clock_config_t spi_flash_clk_cfg_reg[ESP_FLASH_SPEED_
{80e6, SPI_FLASH_LL_CLKREG_VAL_80MHZ},
};
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
static const spi_flash_hal_clock_config_t spi_flash_gpspi_clk_cfg_reg[ESP_FLASH_SPEED_MAX] = {
{5e6, {.gpspi=GPSPI_FLASH_LL_CLKREG_VAL_5MHZ}},
{10e6, {.gpspi=GPSPI_FLASH_LL_CLKREG_VAL_10MHZ}},
{20e6, {.gpspi=GPSPI_FLASH_LL_CLKREG_VAL_20MHZ}},
{26e6, {.gpspi=GPSPI_FLASH_LL_CLKREG_VAL_26MHZ}},
{40e6, {.gpspi=GPSPI_FLASH_LL_CLKREG_VAL_40MHZ}},
{80e6, {.gpspi=GPSPI_FLASH_LL_CLKREG_VAL_80MHZ}},
};
#endif
static inline int get_dummy_n(bool gpio_is_used, int input_delay_ns, int eff_clk)
{
const int apbclk_kHz = APB_CLK_FREQ / 1000;
//calculate how many apb clocks a period has
const int apbclk_n = APB_CLK_FREQ / eff_clk;
const int gpio_delay_ns = gpio_is_used ? (APB_CYCLE_NS * 2) : 0;
const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0;
//calculate how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
int apb_period_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
@ -57,13 +68,38 @@ esp_err_t spi_flash_hal_init(spi_flash_memspi_data_t *data_out, const spi_flash_
if (!esp_ptr_internal(data_out)) {
return ESP_ERR_INVALID_ARG;
}
spi_flash_hal_clock_config_t clock_cfg = spi_flash_clk_cfg_reg[cfg->speed];
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
if (cfg->host_id > SPI_HOST) {
clock_cfg = spi_flash_gpspi_clk_cfg_reg[cfg->speed];
}
#endif
*data_out = (spi_flash_memspi_data_t) {
.spi = spi_flash_ll_get_hw(cfg->host_id),
.cs_num = cfg->cs_num,
.extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, spi_flash_clk_cfg_reg[cfg->speed].freq),
.clock_conf = spi_flash_clk_cfg_reg[cfg->speed].clock_reg_val,
.extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, clock_cfg.freq),
.clock_conf = clock_cfg.clock_reg_val,
};
ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy);
return ESP_OK;
}
bool spi_flash_hal_supports_direct_write(spi_flash_host_driver_t *host, const void *p)
{
bool direct_write = ( ((spi_flash_memspi_data_t *)host->driver_data)->spi != spi_flash_ll_get_hw(SPI_HOST)
|| esp_ptr_in_dram(p) );
return direct_write;
}
bool spi_flash_hal_supports_direct_read(spi_flash_host_driver_t *host, const void *p)
{
//currently the host doesn't support to read through dma, no word-aligned requirements
bool direct_read = ( ((spi_flash_memspi_data_t *)host->driver_data)->spi != spi_flash_ll_get_hw(SPI_HOST)
|| esp_ptr_in_dram(p) );
return direct_read;
}

View file

@ -0,0 +1,102 @@
// Copyright 2015-2019 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 <stdlib.h>
#include "hal/spi_flash_hal.h"
#include "string.h"
#include "hal/hal_defs.h"
#include "sdkconfig.h"
#define ADDRESS_MASK_24BIT 0xFFFFFF
#define COMPUTE_DUMMY_CYCLELEN(host, base) ((base) + ((spi_flash_memspi_data_t *)(host)->driver_data)->extra_dummy)
static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *host)
{
return ((spi_flash_memspi_data_t *)host->driver_data)->spi;
}
void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *host)
{
while (!spi_flash_ll_cmd_is_done(get_spi_dev(host))) {
//nop
}
}
esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *host)
{
spi_flash_memspi_data_t *drv_data = (spi_flash_memspi_data_t *)host->driver_data;
spi_dev_t *dev = get_spi_dev(host);
spi_flash_ll_reset(dev);
spi_flash_ll_set_cs_pin(dev, drv_data->cs_num);
spi_flash_ll_set_clock(dev, &drv_data->clock_conf);
return ESP_OK;
}
esp_err_t spi_flash_hal_configure_host_io_mode(
spi_flash_host_driver_t *host,
uint32_t command,
uint32_t addr_bitlen,
int dummy_cyclelen_base,
esp_flash_io_mode_t io_mode)
{
spi_dev_t *dev = get_spi_dev(host);
if (!SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(dev) && io_mode > SPI_FLASH_FASTRD) {
return ESP_ERR_NOT_SUPPORTED;
}
spi_flash_ll_set_command8(dev, command);
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));
//disable all data phases, enable them later if needed
spi_flash_ll_set_miso_bitlen(dev, 0);
spi_flash_ll_set_mosi_bitlen(dev, 0);
spi_flash_ll_set_read_mode(dev, io_mode);
return ESP_OK;
}
esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *host, spi_flash_trans_t *trans)
{
host->configure_host_io_mode(host, trans->command, trans->address_bitlen, 0, SPI_FLASH_FASTRD);
spi_dev_t *dev = get_spi_dev(host);
//disable dummy if no input phase
if (trans->miso_len == 0) {
spi_flash_ll_set_dummy(dev, 0);
}
spi_flash_ll_set_address(dev, (trans->address & ADDRESS_MASK_24BIT) << 8);
spi_flash_ll_set_mosi_bitlen(dev, trans->mosi_len * 8);
spi_flash_ll_set_buffer_data(dev, trans->mosi_data, trans->mosi_len);
spi_flash_ll_set_miso_bitlen(dev, trans->miso_len * 8);
spi_flash_ll_user_start(dev);
host->poll_cmd_done(host);
spi_flash_ll_get_buffer_data(dev, trans->miso_data, trans->miso_len);
return ESP_OK;
}
esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *host, void *buffer, uint32_t address, uint32_t read_len)
{
spi_dev_t *dev = get_spi_dev(host);
spi_flash_ll_set_address(dev, address << 8);
spi_flash_ll_set_miso_bitlen(dev, read_len * 8);
spi_flash_ll_user_start(dev);
host->poll_cmd_done(host);
spi_flash_ll_get_buffer_data(dev, buffer, read_len);
return ESP_OK;
}

View file

@ -0,0 +1,39 @@
// Copyright 2015-2019 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.
#define GPSPI_BUILD
#define spi_flash_hal_common_command spi_flash_hal_gpspi_common_command
#define spi_flash_hal_poll_cmd_done spi_flash_hal_gpspi_poll_cmd_done
#define spi_flash_hal_device_config spi_flash_hal_gpspi_device_config
#define spi_flash_hal_configure_host_io_mode spi_flash_hal_gpspi_configure_host_io_mode
#define spi_flash_hal_read spi_flash_hal_gpspi_read
#include "spi_flash_hal_common.inc"
bool spi_flash_hal_gpspi_supports_direct_write(spi_flash_host_driver_t *host, const void *p)
{
return true;
}
bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_driver_t *host, const void *p)
{
return true;
}
bool spi_flash_hal_gpspi_host_idle(spi_flash_host_driver_t *chip_drv)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
return spi_flash_ll_host_idle(dev);
}

View file

@ -12,87 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include "hal/spi_flash_hal.h"
#include "string.h"
#include "hal/hal_defs.h"
#define ADDRESS_MASK_24BIT 0xFFFFFF
static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *host)
{
return ((spi_flash_memspi_data_t *)host->driver_data)->spi;
}
void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *host)
{
while (!spi_flash_ll_cmd_is_done(get_spi_dev(host))) {
//nop
}
}
esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *host)
{
spi_flash_memspi_data_t *drv_data = (spi_flash_memspi_data_t *)host->driver_data;
spi_dev_t *dev = get_spi_dev(host);
spi_flash_ll_reset(dev);
spi_flash_ll_set_cs_pin(dev, drv_data->cs_num);
spi_flash_ll_set_clock(dev, &drv_data->clock_conf);
/*
* workaround for the ROM: the ROM, as well as the OpenOCD, don't know the
* clock registers and the dummy are modified this help the ROM to read and
* write correctly according to the new dummy len.
*/
if (dev == &SPI1) {
//0 for cache, 1 for SPI1
extern uint8_t g_rom_spiflash_dummy_len_plus[];
g_rom_spiflash_dummy_len_plus[1] = drv_data->extra_dummy;
}
return ESP_OK;
}
esp_err_t spi_flash_hal_configure_host_io_mode(
spi_flash_host_driver_t *host,
uint32_t command,
uint32_t addr_bitlen,
int dummy_cyclelen_base,
esp_flash_io_mode_t io_mode)
{
// Add dummy cycles to compensate for latency of GPIO matrix and external delay, if necessary...
int dummy_cyclelen = dummy_cyclelen_base + ((spi_flash_memspi_data_t *)host->driver_data)->extra_dummy;
spi_dev_t *dev = get_spi_dev(host);
spi_flash_ll_set_command8(dev, command);
spi_flash_ll_set_addr_bitlen(dev, addr_bitlen);
spi_flash_ll_set_dummy(dev, dummy_cyclelen);
//disable all data phases, enable them later if needed
spi_flash_ll_set_miso_bitlen(dev, 0);
spi_flash_ll_set_mosi_bitlen(dev, 0);
spi_flash_ll_set_read_mode(dev, io_mode);
return ESP_OK;
}
esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *host, spi_flash_trans_t *trans)
{
host->configure_host_io_mode(host, trans->command, 0, 0, SPI_FLASH_FASTRD);
spi_dev_t *dev = get_spi_dev(host);
//disable dummy if no input phase
if (trans->miso_len == 0) {
spi_flash_ll_set_dummy(dev, 0);
}
spi_flash_ll_set_miso_bitlen(dev, trans->miso_len);
spi_flash_ll_set_mosi_bitlen(dev, trans->mosi_len);
spi_flash_ll_write_word(dev, trans->mosi_data);
spi_flash_ll_user_start(dev);
host->poll_cmd_done(host);
spi_flash_ll_get_buffer_data(dev, trans->miso_data, 8);
return ESP_OK;
}
#include "spi_flash_hal_common.inc"
void spi_flash_hal_erase_chip(spi_flash_host_driver_t *host)
{
@ -128,33 +48,6 @@ void spi_flash_hal_program_page(spi_flash_host_driver_t *host, const void *buffe
host->poll_cmd_done(host);
}
esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *host, void *buffer, uint32_t address, uint32_t read_len)
{
spi_dev_t *dev = get_spi_dev(host);
//the command is already set by ``spi_flash_hal_configure_host_io_mode`` before.
spi_flash_ll_set_address(dev, address << 8);
spi_flash_ll_set_miso_bitlen(dev, read_len * 8);
spi_flash_ll_user_start(dev);
host->poll_cmd_done(host);
spi_flash_ll_get_buffer_data(dev, buffer, read_len);
return ESP_OK;
}
bool spi_flash_hal_host_idle(spi_flash_host_driver_t *host)
{
spi_dev_t *dev = get_spi_dev(host);
bool idle = spi_flash_ll_host_idle(dev);
// Not clear if this is necessary, or only necessary if
// chip->spi == SPI1. But probably doesn't hurt...
if (dev == &SPI1) {
idle &= spi_flash_ll_host_idle(&SPI0);
}
return idle;
}
esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *host, bool wp)
{
spi_dev_t *dev = get_spi_dev(host);
@ -162,3 +55,21 @@ esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *host, bool wp
host->poll_cmd_done(host);
return ESP_OK;
}
bool spi_flash_hal_host_idle(spi_flash_host_driver_t *chip_drv)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
bool idle = spi_flash_ll_host_idle(dev);
// Not clear if this is necessary, or only necessary if
// chip->spi == SPI1. But probably doesn't hurt...
if ((void*) dev == spi_flash_ll_get_hw(SPI_HOST)) {
#if CONFIG_IDF_TARGET_ESP32
idle &= spi_flash_ll_host_idle(&SPI0);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
idle &= spi_flash_ll_host_idle(&SPIMEM0);
#endif
}
return idle;
}

View file

@ -1,4 +1,5 @@
set(priv_requires bootloader_support soc)
if(BOOTLOADER_BUILD)
if (CONFIG_IDF_TARGET_ESP32)
# ESP32 Bootloader needs SPIUnlock from this file, but doesn't
@ -33,18 +34,15 @@ else()
"spi_flash_chip_drivers.c"
"spi_flash_chip_generic.c"
"spi_flash_chip_issi.c"
"spi_flash_chip_gd.c")
if (NOT CONFIG_IDF_TARGET_ESP32S2BETA)
# TODO: workaround until ESP32-S2 supports new API, can be always included
list(APPEND srcs
"spi_flash_chip_gd.c"
"memspi_host_driver.c")
list(APPEND cache_srcs
"esp_flash_api.c"
"esp_flash_spi_init.c"
"spi_flash_os_func_app.c"
"spi_flash_os_func_noos.c")
endif()
list(APPEND srcs ${cache_srcs})
set(priv_requires bootloader_support app_update soc)
endif()

View file

@ -77,15 +77,6 @@ menu "SPI Flash driver"
bool "Allowed"
endchoice
# Force the Legacy implementation to be used on ESP32S2Beta
#
# TODO esp32s2beta: Remove once SPI Flash HAL available on S2 Beta
config SPI_FLASH_FORCE_LEGACY_ESP32S2BETA
bool
default y
depends on IDF_TARGET_ESP32S2BETA
select SPI_FLASH_USE_LEGACY_IMPL
config SPI_FLASH_USE_LEGACY_IMPL
bool "Use the legacy implementation before IDF v4.0"
default n

View file

@ -16,12 +16,12 @@
#include <sys/param.h>
#include "esp_spi_flash.h"
#include "esp_spi_flash_chip.h"
#include "cache_utils.h"
#include "soc/system_reg.h"
#include "soc/soc_memory_layout.h"
#include "esp32s2beta/rom/spi_flash.h"
#include "esp32s2beta/rom/cache.h"
#include "hal/spi_flash_hal.h"
#include "esp_flash.h"
esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size)
{
@ -48,7 +48,7 @@ esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_a
return ESP_ROM_SPIFLASH_RESULT_OK;
}
else { // Already in internal memory
rc = spi_flash_unlock();
rc = esp_rom_spiflash_unlock();
if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
return rc;
}
@ -113,7 +113,7 @@ esp_err_t spi_flash_enable_wrap(uint32_t wrap_size)
}
}
void spi_flash_disable_wrap()
void spi_flash_disable_wrap(void)
{
spi_flash_wrap_set(FLASH_WRAP_MODE_DISABLE);
}

View file

@ -70,17 +70,15 @@ static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_f
int cs_io_num = config->cs_io_num;
int spics_in = spi_periph_signal[config->host_id].spics_in;
int spics_out = spi_periph_signal[config->host_id].spics_out[config->cs_id];
int spics_func = spi_periph_signal[config->host_id].func;
uint32_t iomux_reg = GPIO_PIN_MUX_REG[cs_io_num];
//To avoid the panic caused by flash data line conflicts during cs line
//initialization, disable the cache temporarily
chip->os_func->start(chip->os_func_data);
if (use_iomux) {
GPIO.func_in_sel_cfg[spics_in].sig_in_sel = 0;
PIN_INPUT_ENABLE(iomux_reg);
GPIO.func_out_sel_cfg[spics_out].oen_sel = 0;
GPIO.func_out_sel_cfg[spics_out].oen_inv_sel = false;
PIN_FUNC_SELECT(iomux_reg, 1);
gpio_iomux_in(cs_io_num, spics_in);
gpio_iomux_out(cs_io_num, spics_func, false);
} else {
PIN_INPUT_ENABLE(iomux_reg);
if (cs_io_num < 32) {

View file

@ -99,3 +99,44 @@ esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *o
* @return always ESP_OK.
*/
esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);
/**
* Erase contents of entire chip.
*
* @param driver The driver context.
*/
void memspi_host_erase_chip(spi_flash_host_driver_t *driver);
/**
* Erase a sector starting from a given address.
*
* @param driver The driver context.
* @param start_address Starting address of the sector.
*/
void memspi_host_erase_sector(spi_flash_host_driver_t *driver, uint32_t start_address);
/**
* Erase a block starting from a given address.
*
* @param driver The driver context.
* @param start_address Starting address of the block.
*/
void memspi_host_erase_block(spi_flash_host_driver_t *driver, uint32_t start_address);
/**
* Program a page with contents of a buffer.
*
* @param driver The driver context.
* @param buffer Buffer which contains the data to be flashed.
* @param address Starting address of where to flash the data.
* @param length The number of bytes to flash.
*/
void memspi_host_program_page(spi_flash_host_driver_t *driver, const void *buffer, uint32_t address, uint32_t length);
/**
* Set ability to write to chip.
*
* @param driver The driver context.
* @param wp Enable or disable write protect (true - enable, false - disable).
*/
esp_err_t memspi_host_set_write_protect(spi_flash_host_driver_t *driver, bool wp);

View file

@ -22,9 +22,56 @@
static const char TAG[] = "memspi";
static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
extern void spi_flash_hal_gpspi_poll_cmd_done(spi_flash_host_driver_t *driver);
extern esp_err_t spi_flash_hal_gpspi_device_config(spi_flash_host_driver_t *driver);
esp_err_t spi_flash_hal_gpspi_configure_host_io_mode(
spi_flash_host_driver_t *host,
uint32_t command,
uint32_t addr_bitlen,
int dummy_cyclelen_base,
esp_flash_io_mode_t io_mode);
extern esp_err_t spi_flash_hal_gpspi_common_command(spi_flash_host_driver_t *chip_drv, spi_flash_trans_t *trans);
extern esp_err_t spi_flash_hal_gpspi_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len);
extern bool spi_flash_hal_gpspi_host_idle(spi_flash_host_driver_t *chip_drv);
extern bool spi_flash_hal_gpspi_supports_direct_write(spi_flash_host_driver_t *driver, const void *p);
extern bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_driver_t *driver, const void *p);
/** Default configuration for GPSPI */
static const spi_flash_host_driver_t esp_flash_gpspi_host = {
.dev_config = spi_flash_hal_gpspi_device_config,
.common_command = spi_flash_hal_gpspi_common_command,
.read_id = memspi_host_read_id_hs,
.erase_chip = memspi_host_erase_chip,
.erase_sector = memspi_host_erase_sector,
.erase_block = memspi_host_erase_block,
.read_status = memspi_host_read_status_hs,
.set_write_protect = memspi_host_set_write_protect,
.supports_direct_write = spi_flash_hal_gpspi_supports_direct_write,
.supports_direct_read = spi_flash_hal_gpspi_supports_direct_read,
.program_page = memspi_host_program_page,
.max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES,
.read = spi_flash_hal_gpspi_read,
.max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES,
.host_idle = spi_flash_hal_gpspi_host_idle,
.configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode,
.poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done,
.flush_cache = NULL,
};
#endif
esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg)
{
#ifdef CONFIG_IDF_TARGET_ESP32
memcpy(host, &esp_flash_default_host, sizeof(spi_flash_host_driver_t));
#elif CONFIG_IDF_TARGET_ESP32S2BETA
if (cfg->host_id == SPI_HOST)
memcpy(host, &esp_flash_default_host, sizeof(spi_flash_host_driver_t));
else {
memcpy(host, &esp_flash_gpspi_host, sizeof(spi_flash_host_driver_t));
}
#endif
esp_err_t err = spi_flash_hal_init(data, cfg);
if (err != ESP_OK) {
return err;
@ -32,7 +79,7 @@ esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_d
host->driver_data = data;
//some functions are not required if not SPI1
if (data->spi != &SPI1) {
if ((void*)data->spi != (void*)spi_flash_ll_get_hw(SPI_HOST)) {
host->flush_cache = NULL;
}
return ESP_OK;
@ -40,21 +87,20 @@ esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_d
esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *host, uint32_t *id)
{
//NOTE: we do have a read id function, however it doesn't work in high freq
uint32_t id_buf = 0;
spi_flash_trans_t t = {
.command = CMD_RDID,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = 24
.miso_len = 3,
.miso_data = ((uint8_t*) &id_buf),
};
host->common_command(host, &t);
uint32_t raw_flash_id = t.miso_data[0];
uint32_t raw_flash_id = id_buf;
ESP_EARLY_LOGV(TAG, "raw_chip_id: %X\n", raw_flash_id);
if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) {
ESP_EARLY_LOGE(TAG, "no response\n");
return ESP_ERR_FLASH_NO_RESPONSE;
}
// Byte swap the flash id as it's usually written the other way around
uint8_t mfg_id = raw_flash_id & 0xFF;
uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00);
@ -66,24 +112,84 @@ esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *host, uint32_t *id)
esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr)
{
//NOTE: we do have a read id function, however it doesn't work in high freq
uint32_t stat_buf = 0;
spi_flash_trans_t t = {
.command = CMD_RDSR,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = 8
.miso_data = ((uint8_t*) &stat_buf),
.miso_len = 1
};
esp_err_t err = driver->common_command(driver, &t);
if (err != ESP_OK) {
return err;
}
*out_sr = t.miso_data[0];
*out_sr = stat_buf;
return ESP_OK;
}
esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size)
{
if (((memspi_host_data_t*)(driver->driver_data))->spi == &SPI1) {
if ((void*)((memspi_host_data_t*)(driver->driver_data))->spi == (void*) spi_flash_ll_get_hw(SPI_HOST)) {
spi_flash_check_and_flush_cache(addr, size);
}
return ESP_OK;
}
void memspi_host_erase_chip(spi_flash_host_driver_t *chip_drv)
{
spi_flash_trans_t t = { 0 };
t.command = CMD_CHIP_ERASE;
chip_drv->common_command(chip_drv, &t);
}
void memspi_host_erase_sector(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
{
spi_flash_trans_t t = {
.command = CMD_SECTOR_ERASE,
.address_bitlen = 24,
.address = start_address
};
chip_drv->common_command(chip_drv, &t);
}
void memspi_host_erase_block(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
{
spi_flash_trans_t t = {
.command = CMD_LARGE_BLOCK_ERASE,
.address_bitlen = 24,
.address = start_address,
};
chip_drv->common_command(chip_drv, &t);
}
void memspi_host_program_page(spi_flash_host_driver_t *chip_drv, const void *buffer, uint32_t address, uint32_t length)
{
spi_flash_trans_t t = {
.command = CMD_PROGRAM_PAGE,
.address_bitlen = 24,
.address = address,
.mosi_len = length,
.mosi_data = buffer
};
chip_drv->common_command(chip_drv, &t);
}
esp_err_t memspi_host_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len)
{
spi_flash_trans_t t = {
.address_bitlen = 24,
.address = address,
.miso_len = read_len,
.miso_data = buffer
};
chip_drv->common_command(chip_drv, &t);
return ESP_OK;
}
esp_err_t memspi_host_set_write_protect(spi_flash_host_driver_t *chip_drv, bool wp)
{
spi_flash_trans_t t = {
.command = wp ? CMD_WRDI : CMD_WREN
};
chip_drv->common_command(chip_drv, &t);
return ESP_OK;
}

View file

@ -37,8 +37,7 @@
#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_RST_EN 0x66
#define CMD_RST_DEV 0x99

View file

@ -140,7 +140,12 @@ esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t
{
esp_err_t err = ESP_OK;
// Configure the host, and return
spi_flash_chip_generic_config_host_io_mode(chip);
err = spi_flash_chip_generic_config_host_io_mode(chip);
if (err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGE(TAG, "configure host io mode failed - unsupported");
return err;
}
while (err == ESP_OK && length > 0) {
uint32_t read_len = MIN(length, chip->host->max_read_bytes);
@ -386,14 +391,14 @@ const spi_flash_chip_t esp_flash_chip_generic = {
static esp_err_t spi_flash_common_read_qe_sr(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_sr_bitwidth, uint32_t *sr)
{
uint32_t sr_buf = 0;
spi_flash_trans_t t = {
.command = qe_rdsr_command,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = qe_sr_bitwidth,
.miso_data = (uint8_t*) &sr_buf,
.miso_len = qe_sr_bitwidth / 8,
};
esp_err_t ret = chip->host->common_command(chip->host, &t);
*sr = t.miso_data[0];
*sr = sr_buf;
return ret;
}
@ -401,8 +406,8 @@ static esp_err_t spi_flash_common_write_qe_sr(esp_flash_t *chip, uint8_t qe_wrsr
{
spi_flash_trans_t t = {
.command = qe_wrsr_command,
.mosi_data = qe,
.mosi_len = qe_sr_bitwidth,
.mosi_data = ((uint8_t*) &qe),
.mosi_len = qe_sr_bitwidth / 8,
.miso_len = 0,
};
return chip->host->common_command(chip->host, &t);

View file

@ -17,6 +17,7 @@
#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops``
#include "esp_flash.h"
#include "esp_flash_partitions.h"
#include "hal/spi_types.h"
#ifdef CONFIG_IDF_TARGET_ESP32
@ -99,23 +100,29 @@ static IRAM_ATTR esp_err_t main_flash_region_protected(void* arg, size_t start_a
}
static DRAM_ATTR spi1_app_func_arg_t spi1_arg = {
.host_id = 0, //for SPI1,
.host_id = SPI1_HOST, //for SPI1,
.no_protect = true,
};
static DRAM_ATTR spi1_app_func_arg_t main_flash_arg = {
.host_id = 0, //for SPI1,
.host_id = SPI1_HOST, //for SPI1,
.no_protect = false,
};
static app_func_arg_t spi2_arg = {
.host_id = 1, //for SPI2,
.host_id = SPI2_HOST, //for SPI2,
};
static app_func_arg_t spi3_arg = {
.host_id = 2, //for SPI3,
.host_id = SPI3_HOST, //for SPI3,
};
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
static app_func_arg_t spi4_arg = {
.host_id = SPI4_HOST, //for SPI4,
};
#endif
//for SPI1, we have to disable the cache and interrupts before using the SPI bus
const DRAM_ATTR esp_flash_os_functions_t esp_flash_spi1_default_os_functions = {
.start = spi1_start,
@ -132,14 +139,22 @@ const esp_flash_os_functions_t esp_flash_spi23_default_os_functions = {
esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id)
{
if (host_id == 0) {
if (host_id == SPI1_HOST) {
//SPI1
chip->os_func = &esp_flash_spi1_default_os_functions;
chip->os_func_data = &spi1_arg;
} else if (host_id == 1 || host_id == 2) {
//SPI2,3
} else if (host_id == SPI2_HOST || host_id == SPI3_HOST
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
|| host_id == SPI4_HOST
#endif
) {
//SPI2,3,4
chip->os_func = &esp_flash_spi23_default_os_functions;
chip->os_func_data = (host_id == 1) ? &spi2_arg : &spi3_arg;
#if CONFIG_IDF_TARGET_ESP32
chip->os_func_data = (host_id == SPI2_HOST) ? &spi2_arg : &spi3_arg;
#elif CONFIG_IDF_TARGET_ESP32S2BETA
chip->os_func_data = (host_id == SPI2_HOST) ? &spi2_arg : ((host_id == SPI3_HOST) ? &spi3_arg : &spi4_arg);
#endif
} else {
return ESP_ERR_INVALID_ARG;
}

View file

@ -17,28 +17,51 @@
#include "esp_flash.h"
#include "esp_attr.h"
#ifdef CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/ets_sys.h"
#include "esp32/rom/cache.h"
#else
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#include "esp32s2beta/rom/ets_sys.h"
#include "esp32s2beta/rom/cache.h"
#endif
#include "esp_attr.h"
#if CONFIG_IDF_TARGET_ESP32S2BETA
typedef struct {
uint32_t icache_autoload;
uint32_t dcache_autoload;
} spi_noos_arg_t;
static DRAM_ATTR spi_noos_arg_t spi_arg = { 0 };
#endif
static IRAM_ATTR esp_err_t start(void *arg)
{
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Disable(0);
Cache_Read_Disable(1);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
spi_noos_arg_t *spi_arg = arg;
spi_arg->icache_autoload = Cache_Suspend_ICache();
spi_arg->dcache_autoload = Cache_Suspend_DCache();
#endif
return ESP_OK;
}
static IRAM_ATTR esp_err_t end(void *arg)
{
#if CONFIG_IDF_TARGET_ESP32
Cache_Flush(0);
Cache_Flush(1);
Cache_Read_Enable(0);
Cache_Read_Enable(1);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
spi_noos_arg_t *spi_arg = arg;
Cache_Invalidate_ICache_All();
Cache_Resume_ICache(spi_arg->icache_autoload);
Cache_Resume_DCache(spi_arg->dcache_autoload);
#endif
return ESP_OK;
}
@ -58,5 +81,10 @@ const DRAM_ATTR esp_flash_os_functions_t esp_flash_noos_functions = {
esp_err_t IRAM_ATTR esp_flash_app_disable_os_functions(esp_flash_t* chip)
{
chip->os_func = &esp_flash_noos_functions;
#if CONFIG_IDF_TARGET_ESP32S2BETA
chip->os_func_data = &spi_arg;
#endif
return ESP_OK;
}

View file

@ -1,15 +1,9 @@
set(src_dirs ".")
set(exclude_srcs)
if(IDF_TARGET STREQUAL "esp32")
list(APPEND src_dirs "esp32")
if(CONFIG_SPI_FLASH_USE_LEGACY_IMPL)
set(exclude_srcs "esp32/test_esp_flash.c" "esp32/test_partition_ext.c")
endif()
set(exclude_srcs "test_esp_flash.c" "test_partition_ext.c")
endif()
idf_component_register(SRC_DIRS ${src_dirs}
EXCLUDE_SRCS ${exclude_srcs}
idf_component_register(SRC_DIRS "."
EXCLUDE_SRCS "${exclude_srcs}"
INCLUDE_DIRS "."
REQUIRES unity test_utils spi_flash bootloader_support app_update)

View file

@ -18,6 +18,8 @@
#include "soc/io_mux_reg.h"
#include "sdkconfig.h"
#include "hal/spi_flash_hal.h"
#define FUNC_SPI 1
static uint8_t sector_buf[4096];
@ -28,17 +30,43 @@ static uint8_t sector_buf[4096];
#define EXTRA_SPI1_CLK_IO 17 //the pin which is usually used by the PSRAM clk
#if CONFIG_IDF_TARGET_ESP32
#define HSPI_PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI
#define HSPI_PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO
#define HSPI_PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK
#define HSPI_PIN_NUM_HD HSPI_IOMUX_PIN_NUM_HD
#define HSPI_PIN_NUM_WP HSPI_IOMUX_PIN_NUM_WP
#define HSPI_PIN_NUM_CS HSPI_IOMUX_PIN_NUM_CS
#define VSPI_PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI
#define VSPI_PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO
#define VSPI_PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK
#define VSPI_PIN_NUM_HD VSPI_IOMUX_PIN_NUM_HD
#define VSPI_PIN_NUM_WP VSPI_IOMUX_PIN_NUM_WP
#define VSPI_PIN_NUM_CS VSPI_IOMUX_PIN_NUM_CS
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#define FSPI_PIN_NUM_MOSI FSPI_IOMUX_PIN_NUM_MOSI
#define FSPI_PIN_NUM_MISO FSPI_IOMUX_PIN_NUM_MISO
#define FSPI_PIN_NUM_CLK FSPI_IOMUX_PIN_NUM_CLK
#define FSPI_PIN_NUM_HD FSPI_IOMUX_PIN_NUM_HD
#define FSPI_PIN_NUM_WP FSPI_IOMUX_PIN_NUM_WP
#define FSPI_PIN_NUM_CS FSPI_IOMUX_PIN_NUM_CS
// Just use the same pins for HSPI and VSPI
#define HSPI_PIN_NUM_MOSI FSPI_PIN_NUM_MOSI
#define HSPI_PIN_NUM_MISO FSPI_PIN_NUM_MISO
#define HSPI_PIN_NUM_CLK FSPI_PIN_NUM_CLK
#define HSPI_PIN_NUM_HD FSPI_PIN_NUM_HD
#define HSPI_PIN_NUM_WP FSPI_PIN_NUM_WP
#define HSPI_PIN_NUM_CS FSPI_PIN_NUM_CS
#define VSPI_PIN_NUM_MOSI FSPI_PIN_NUM_MOSI
#define VSPI_PIN_NUM_MISO FSPI_PIN_NUM_MISO
#define VSPI_PIN_NUM_CLK FSPI_PIN_NUM_CLK
#define VSPI_PIN_NUM_HD FSPI_PIN_NUM_HD
#define VSPI_PIN_NUM_WP FSPI_PIN_NUM_WP
#define VSPI_PIN_NUM_CS FSPI_PIN_NUM_CS
#endif
#define ALL_TEST_NUM (sizeof(config_list)/sizeof(flashtest_config_t))
typedef void (*flash_test_func_t)(esp_flash_t* chip);
@ -50,7 +78,7 @@ typedef void (*flash_test_func_t)(esp_flash_t* chip);
#define FLASH_TEST_CASE_3(STR, FUNCT_TO_RUN)
#else
#define FLASH_TEST_CASE_3(STR, FUNC_TO_RUN) \
TEST_CASE(STR", 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]") {flash_test_func(FUNC_TO_RUN, ALL_TEST_NUM);}
TEST_CASE_ESP32(STR", 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]") {flash_test_func(FUNC_TO_RUN, ALL_TEST_NUM);}
#endif
//currently all the configs are the same with esp_flash_spi_device_config_t, no more information required
@ -58,38 +86,68 @@ typedef esp_flash_spi_device_config_t flashtest_config_t;
static const char TAG[] = "test_esp_flash";
#define FLASHTEST_CONFIG_COMMON \
/* 0 always reserved for main flash */ \
{ \
/* no need to init */ \
.host_id = -1, \
}, \
{ \
.io_mode = TEST_SPI_READ_MODE,\
.speed = TEST_SPI_SPEED, \
.host_id = SPI_HOST, \
.cs_id = 1, \
/* the pin which is usually used by the PSRAM */ \
.cs_io_num = 16, \
.input_delay_ns = 0, \
}
#if CONFIG_IDF_TARGET_ESP32
flashtest_config_t config_list[] = {
// 0 always reserved for main flash
{
.host_id = -1, // no need to init
},
{
.io_mode = TEST_SPI_READ_MODE,
.speed = TEST_SPI_SPEED,
.host_id = SPI_HOST,
.cs_id = 1,
.cs_io_num = 16, //the pin which is usually used by the PSRAM
.input_delay_ns = 0,
},
/* current runner doesn't have a flash on HSPI
{
.io_mode = TEST_SPI_READ_MODE,
.speed = TEST_SPI_SPEED,
.host = HSPI_HOST,
.cs_id = 0,
.cs_io_num = HSPI_IOMUX_PIN_NUM_CS,
.input_delay_ns = 20,
},
*/
FLASHTEST_CONFIG_COMMON,
/* current runner doesn't have a flash on HSPI */
// {
// .io_mode = TEST_SPI_READ_MODE,
// .speed = TEST_SPI_SPEED,
// .host_id = HSPI_HOST,
// .cs_id = 0,
// // uses GPIO matrix on esp32s2beta regardles if FORCE_GPIO_MATRIX
// .cs_io_num = HSPI_PIN_NUM_CS,
// .input_delay_ns = 20,
// },
{
.io_mode = TEST_SPI_READ_MODE,
.speed = TEST_SPI_SPEED,
.host_id = VSPI_HOST,
.cs_id = 0,
.cs_io_num = VSPI_IOMUX_PIN_NUM_CS,
.cs_io_num = VSPI_PIN_NUM_CS,
.input_delay_ns = 0,
},
};
#elif CONFIG_IDF_TARGET_ESP32S2BETA
flashtest_config_t config_list[] = {
FLASHTEST_CONFIG_COMMON,
/* No runners for esp32s2beta for these config yet */
// {
// .io_mode = TEST_SPI_READ_MODE,
// .speed = TEST_SPI_SPEED,
// .host_id = FSPI_HOST,
// .cs_id = 0,
// .cs_io_num = FSPI_PIN_NUM_CS,
// .input_delay_ns = 0,
// },
// /* current runner doesn't have a flash on HSPI */
// {
// .io_mode = TEST_SPI_READ_MODE,
// .speed = TEST_SPI_SPEED,
// .host_id = HSPI_HOST,
// .cs_id = 0,
// // uses GPIO matrix on esp32s2beta regardles if FORCE_GPIO_MATRIX
// .cs_io_num = HSPI_PIN_NUM_CS,
// .input_delay_ns = 20,
// },
};
#endif
static void setup_bus(spi_host_device_t host_id)
{
@ -98,10 +156,27 @@ static void setup_bus(spi_host_device_t host_id)
//no need to initialize the bus, however the CLK may need one more output if it's on the usual place of PSRAM
#ifdef EXTRA_SPI1_CLK_IO
gpio_matrix_out(EXTRA_SPI1_CLK_IO, SPICLK_OUT_IDX, 0, 0);
#endif
#if CONFIG_IDF_TARGET_ESP32S2BETA
} else if (host_id == FSPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI%d (FSPI) CS0...\n", host_id + 1);
spi_bus_config_t fspi_bus_cfg = {
.mosi_io_num = FSPI_PIN_NUM_MOSI,
.miso_io_num = FSPI_PIN_NUM_MISO,
.sclk_io_num = FSPI_PIN_NUM_CLK,
.quadhd_io_num = FSPI_PIN_NUM_HD,
.quadwp_io_num = FSPI_PIN_NUM_WP,
.max_transfer_sz = 64,
};
#ifdef FORCE_GPIO_MATRIX
fspi_bus_cfg.quadhd_io_num = 5;
#endif
esp_err_t ret = spi_bus_initialize(host_id, &fspi_bus_cfg, 0);
TEST_ESP_OK(ret);
#endif
//currently the SPI bus for main flash chip is initialized through GPIO matrix
} else if (host_id == HSPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI2 (HSPI) CS0...\n");
ESP_LOGI(TAG, "setup flash on SPI%d (HSPI) CS0...\n", host_id + 1);
spi_bus_config_t hspi_bus_cfg = {
.mosi_io_num = HSPI_PIN_NUM_MOSI,
.miso_io_num = HSPI_PIN_NUM_MISO,
@ -110,13 +185,26 @@ static void setup_bus(spi_host_device_t host_id)
.quadwp_io_num = HSPI_PIN_NUM_WP,
.max_transfer_sz = 64,
};
#ifdef CONFIG_IDF_TARGET_ESP32
#ifdef FORCE_GPIO_MATRIX
hspi_bus_cfg.quadhd_io_num = 23;
#endif
#endif
esp_err_t ret = spi_bus_initialize(host_id, &hspi_bus_cfg, 0);
TEST_ESP_OK(ret);
} else if (host_id == VSPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI3 (VSPI) CS0...\n");
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
// HSPI have no multiline mode, use GPIO to pull those pins up
gpio_set_direction(HSPI_PIN_NUM_HD, GPIO_MODE_OUTPUT);
gpio_set_level(HSPI_PIN_NUM_HD, 1);
gpio_set_direction(HSPI_PIN_NUM_WP, GPIO_MODE_OUTPUT);
gpio_set_level(HSPI_PIN_NUM_WP, 1);
#endif
}
#if CONFIG_IDF_TARGET_ESP32
else if (host_id == VSPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI%d (VSPI) CS0...\n", host_id + 1);
spi_bus_config_t vspi_bus_cfg = {
.mosi_io_num = VSPI_PIN_NUM_MOSI,
.miso_io_num = VSPI_PIN_NUM_MISO,
@ -130,14 +218,20 @@ static void setup_bus(spi_host_device_t host_id)
#endif
esp_err_t ret = spi_bus_initialize(host_id, &vspi_bus_cfg, 0);
TEST_ESP_OK(ret);
} else {
}
#endif // CONFIG_IDF_TARGET_ESP32
else {
ESP_LOGE(TAG, "invalid bus");
}
}
static void release_bus(int host_id)
{
#if CONFIG_IDF_TARGET_ESP32
if (host_id == HSPI_HOST || host_id == VSPI_HOST) {
#elif CONFIG_IDF_TARGET_ESP32S2BETA
if (host_id == FSPI_HOST || host_id == HSPI_HOST || host_id == VSPI_HOST) {
#endif
spi_bus_free(host_id);
}
}
@ -479,7 +573,6 @@ void test_permutations(flashtest_config_t* config)
read_and_check(chip, part, source_buf, length);
teardown_test_chip(chip, cfg->host_id);
if (config->host_id != -1) {
esp_flash_speed_t speed = ESP_FLASH_SPEED_MIN;
while (speed != ESP_FLASH_SPEED_MAX) {
@ -487,10 +580,17 @@ void test_permutations(flashtest_config_t* config)
//the io mode will switch frequently.
esp_flash_io_mode_t io_mode = SPI_FLASH_READ_MODE_MIN;
while (io_mode != SPI_FLASH_READ_MODE_MAX) {
ESP_LOGI(TAG, "test flash io mode: %d, speed: %d", io_mode, speed);
cfg->io_mode = io_mode;
cfg->speed = speed;
setup_new_chip(cfg, &chip);
if (io_mode > SPI_FLASH_FASTRD
&& !SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(((spi_flash_memspi_data_t *)chip->host->driver_data)->spi)) {
continue;
}
ESP_LOGI(TAG, "test flash io mode: %d, speed: %d", io_mode, speed);
read_and_check(chip, part, source_buf, length);
teardown_test_chip(chip, cfg->host_id);
io_mode++;
@ -512,7 +612,7 @@ TEST_CASE("SPI flash test reading with all speed/mode permutations", "[esp_flash
}
#ifndef CONFIG_ESP32_SPIRAM_SUPPORT
TEST_CASE("SPI flash test reading with all speed/mode permutations, 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]")
TEST_CASE_ESP32("SPI flash test reading with all speed/mode permutations, 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]")
{
for (int i = 0; i < ALL_TEST_NUM; i++) {
test_permutations(&config_list[i]);

View file

@ -3,7 +3,7 @@
#include "unity.h"
TEST_CASE("Basic handling of a partition in external flash", "[partition]")
TEST_CASE_ESP32("Basic handling of a partition in external flash", "[partition]")
{
esp_flash_t flash = {
.size = 1 * 1024 * 1024,

View file

@ -80,5 +80,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
CONFIG_LWIP_IRAM_OPTIMIZATION=y
CONFIG_ESP32_WIFI_RX_IRAM_OPT=n
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y

View file

@ -460,7 +460,7 @@ UT_034:
UT_035:
extends: .unit_test_template
parallel: 16
parallel: 38
tags:
- ESP32S2BETA_IDF
- UT_T1_1

View file

@ -0,0 +1,4 @@
TEST_COMPONENTS=spi_flash
CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_SPI_FLASH_USE_LEGACY_IMPL=y
CONFIG_IDF_TARGET="esp32s2beta"