blob: 3d2b92e00aaf7a660e591499ef515b9f9f966341 [file] [log] [blame]
/****************************************************************************
* arch/risc-v/src/common/espressif/esp_spiflash.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <string.h>
#include <sys/types.h>
#include <inttypes.h>
#include <errno.h>
#include "riscv_internal.h"
#include "riscv/rv_utils.h"
#include <nuttx/arch.h>
#include <nuttx/init.h>
#include "esp_spiflash.h"
#include "esp_attr.h"
#include "memspi_host_driver.h"
#include "spi_flash_defs.h"
#include "hal/spimem_flash_ll.h"
#include "hal/spi_flash_ll.h"
#include "esp_rom_spiflash.h"
#include "esp_irq.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_ESPRESSIF_SPI_FLASH_USE_ROM_CODE
/* SPI buffer size */
# define SPI_BUFFER_WORDS (16)
# define SPI_BUFFER_BYTES (SPI_BUFFER_WORDS * 4)
/* SPI flash hardware definition */
# define FLASH_SECTOR_SIZE (4096)
/* SPI flash SR1 bits */
# define FLASH_SR1_BUSY ESP_ROM_SPIFLASH_BUSY_FLAG
# define FLASH_SR1_WREN ESP_ROM_SPIFLASH_WRENABLE_FLAG
/* SPI flash operation */
# define FLASH_CMD_WRDI CMD_WRDI
# define FLASH_CMD_WREN CMD_WREN
# define FLASH_CMD_RDSR CMD_RDSR
#ifdef CONFIG_ESPRESSIF_SPI_FLASH_USE_32BIT_ADDRESS
# define ADDR_BITS(addr) (((addr) & 0xff000000) ? 32 : 24)
# define READ_CMD(addr) (ADDR_BITS(addr) == 32 ? \
CMD_FASTRD_4B : CMD_FASTRD)
# define WRITE_CMD(addr) (ADDR_BITS(addr) == 32 ? CMD_PROGRAM_PAGE_4B : \
CMD_PROGRAM_PAGE)
# define ERASE_CMD(addr) (ADDR_BITS(addr) == 32 ? CMD_SECTOR_ERASE_4B : \
CMD_SECTOR_ERASE)
# define READ_DUMMY(addr) (8)
#else
# define ADDR_BITS(addr) (24)
# define READ_CMD(addr) CMD_FASTRD
# define WRITE_CMD(addr) CMD_PROGRAM_PAGE
# define ERASE_CMD(addr) CMD_SECTOR_ERASE
# define READ_DUMMY(addr) (8)
#endif
# define SEND_CMD8_TO_FLASH(cmd) \
esp_spi_trans((cmd), 8, \
0, 0, \
NULL, 0, \
NULL, 0, \
0)
# define READ_SR1_FROM_FLASH(cmd, status) \
esp_spi_trans((cmd), 8, \
0, 0, \
NULL, 0, \
(status), 1, \
0)
# define ERASE_FLASH_SECTOR(addr) \
esp_spi_trans(ERASE_CMD(addr), 8, \
(addr), ADDR_BITS(addr), \
NULL, 0, \
NULL, 0, \
0)
# define WRITE_DATA_TO_FLASH(addr, buffer, size) \
esp_spi_trans(WRITE_CMD(addr), 8, \
(addr), ADDR_BITS(addr), \
buffer, size, \
NULL, 0, \
0)
# define READ_DATA_FROM_FLASH(addr, buffer, size) \
esp_spi_trans(READ_CMD(addr), 8, \
(addr), ADDR_BITS(addr), \
NULL, 0, \
buffer, size, \
READ_DUMMY(addr))
/****************************************************************************
* Private Types
****************************************************************************/
spi_mem_dev_t *dev = spimem_flash_ll_get_hw(SPI1_HOST);
#endif /* CONFIG_ESPRESSIF_SPI_FLASH_USE_ROM_CODE */
/****************************************************************************
* Private Functions Declaration
****************************************************************************/
static void spiflash_start(void);
static void spiflash_end(void);
#ifndef CONFIG_ESPRESSIF_SPI_FLASH_USE_ROM_CODE
extern bool spi_flash_check_and_flush_cache(size_t start_addr,
size_t length);
#endif /* CONFIG_ESPRESSIF_SPI_FLASH_USE_ROM_CODE */
/****************************************************************************
* Private Data
****************************************************************************/
static struct spiflash_guard_funcs g_spi_flash_guard_funcs =
{
.start = spiflash_start,
.end = spiflash_end,
.op_lock = NULL,
.op_unlock = NULL,
.address_is_safe = NULL,
.yield = NULL,
};
static mutex_t s_flash_op_mutex;
static uint32_t s_flash_op_cache_state[CONFIG_ESPRESSIF_NUM_CPUS];
static volatile bool s_sched_suspended[CONFIG_ESPRESSIF_NUM_CPUS];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: spiflash_start
*
* Description:
* Prepare for an SPIFLASH operation.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static IRAM_ATTR void spiflash_start(void)
{
extern uint32_t cache_suspend_icache(void);
int cpu;
irqstate_t flags;
uint32_t regval;
nxmutex_lock(&s_flash_op_mutex);
flags = enter_critical_section();
cpu = up_cpu_index();
s_sched_suspended[cpu] = true;
esp_intr_noniram_disable();
s_flash_op_cache_state[cpu] = cache_suspend_icache() << 16;
leave_critical_section(flags);
}
/****************************************************************************
* Name: spiflash_end
*
* Description:
* Undo all the steps of opstart.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static IRAM_ATTR void spiflash_end(void)
{
extern void cache_resume_icache(uint32_t);
extern void cache_invalidate_icache_all(void);
int cpu;
irqstate_t flags;
flags = enter_critical_section();
cpu = up_cpu_index();
cache_invalidate_icache_all();
cache_resume_icache(s_flash_op_cache_state[cpu] >> 16);
esp_intr_noniram_enable();
s_sched_suspended[cpu] = false;
leave_critical_section(flags);
nxmutex_unlock(&s_flash_op_mutex);
}
/****************************************************************************
* Name: esp_spi_trans
*
* Description:
* Transmit given command, address and data.
*
* Input Parameters:
* command - command value
* command_bits - command bits
* address - address value
* address_bits - address bits
* tx_buffer - write buffer
* tx_bytes - write buffer size
* rx_buffer - read buffer
* rx_bytes - read buffer size
* dummy_bits - dummy bits
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
#ifndef CONFIG_ESPRESSIF_SPI_FLASH_USE_ROM_CODE
static IRAM_ATTR void esp_spi_trans(uint32_t command,
uint32_t command_bits,
uint32_t address,
uint32_t address_bits,
uint32_t *tx_buffer,
uint32_t tx_bytes,
uint32_t *rx_buffer,
uint32_t rx_bytes,
uint32_t dummy_bits)
{
/* Initiliaze SPI user register */
spi_flash_ll_reset(dev);
while (!spi_flash_ll_host_idle(dev));
/* Set command bits and value, and command is always needed */
spi_flash_ll_set_command(dev, command, command_bits);
/* Set address bits and value */
if (address_bits)
{
spi_flash_ll_set_addr_bitlen(dev, address_bits);
spi_flash_ll_set_address(dev, address);
}
/* Set dummy */
if (dummy_bits)
{
spi_flash_ll_set_dummy(dev, dummy_bits);
}
/* Set TX data */
if (tx_bytes)
{
spi_flash_ll_set_mosi_bitlen(dev, tx_bytes * 8);
spi_flash_ll_set_buffer_data(dev, tx_buffer, tx_bytes);
}
/* Set RX data */
if (rx_bytes)
{
spi_flash_ll_set_miso_bitlen(dev, rx_bytes * 8);
}
/* Set I/O mode */
spi_flash_ll_set_read_mode(dev, SPI_FLASH_FASTRD);
/* Set clock and delay */
spimem_flash_ll_suspend_cmd_setup(dev, 0);
/* Start transmision */
spi_flash_ll_user_start(dev);
/* Wait until transmission is done */
while (!spi_flash_ll_cmd_is_done(dev));
/* Get read data */
if (rx_bytes)
{
spi_flash_ll_get_buffer_data(dev, rx_buffer, rx_bytes);
}
}
/****************************************************************************
* Name: wait_flash_idle
*
* Description:
* Wait until flash enters idle state
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static IRAM_ATTR void wait_flash_idle(void)
{
uint32_t status;
do
{
READ_SR1_FROM_FLASH(FLASH_CMD_RDSR, &status);
if ((status & FLASH_SR1_BUSY) == 0)
{
break;
}
}
while (1);
}
/****************************************************************************
* Name: enable_flash_write
*
* Description:
* Enable Flash write mode
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static IRAM_ATTR void enable_flash_write(void)
{
uint32_t status;
do
{
SEND_CMD8_TO_FLASH(FLASH_CMD_WREN);
READ_SR1_FROM_FLASH(FLASH_CMD_RDSR, &status);
if ((status & FLASH_SR1_WREN) != 0)
{
break;
}
}
while (1);
}
/****************************************************************************
* Name: disable_flash_write
*
* Description:
* Disable Flash write mode
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
****************************************************************************/
static IRAM_ATTR void disable_flash_write(void)
{
uint32_t status;
do
{
SEND_CMD8_TO_FLASH(FLASH_CMD_WRDI);
READ_SR1_FROM_FLASH(FLASH_CMD_RDSR, &status);
if ((status & FLASH_SR1_WREN) == 0)
{
break;
}
}
while (1);
}
/****************************************************************************
* Name: spi_flash_read
*
* Description:
* Read data from Flash.
*
* Parameters:
* address - source address of the data in Flash.
* buffer - pointer to the destination buffer
* length - length of data
*
* Returned Values:
* Zero (OK) is returned or a negative error.
*
****************************************************************************/
IRAM_ATTR int spi_flash_read(uint32_t address, void *buffer, uint32_t length)
{
int ret = OK;
uint8_t *rx_buf = (uint8_t *)buffer;
uint32_t rx_bytes = length;
uint32_t rx_addr = address;
spiflash_start();
for (uint32_t i = 0; i < length; i += SPI_BUFFER_BYTES)
{
uint32_t spi_buffer[SPI_BUFFER_WORDS];
uint32_t n = MIN(rx_bytes, SPI_BUFFER_BYTES);
READ_DATA_FROM_FLASH(rx_addr, spi_buffer, n);
memcpy(rx_buf, spi_buffer, n);
rx_bytes -= n;
rx_buf += n;
rx_addr += n;
}
spiflash_end();
return ret;
}
/****************************************************************************
* Name: spi_flash_erase_sector
*
* Description:
* Erase the Flash sector.
*
* Parameters:
* sector - Sector number, the count starts at sector 0, 4KB per sector.
*
* Returned Values: esp_err_t
* Zero (OK) is returned or a negative error.
*
****************************************************************************/
IRAM_ATTR int spi_flash_erase_sector(uint32_t sector)
{
int ret = OK;
uint32_t addr = sector * FLASH_SECTOR_SIZE;
spiflash_start();
wait_flash_idle();
enable_flash_write();
ERASE_FLASH_SECTOR(addr);
wait_flash_idle();
disable_flash_write();
spiflash_end();
return ret;
}
/****************************************************************************
* Name: spi_flash_erase_range
*
* Description:
* Erase a range of flash sectors
*
* Parameters:
* start_address - Address where erase operation has to start.
* Must be 4kB-aligned
* size - Size of erased range, in bytes. Must be divisible by
* 4kB.
*
* Returned Values:
* Zero (OK) is returned or a negative error.
*
****************************************************************************/
IRAM_ATTR int spi_flash_erase_range(uint32_t start_address, uint32_t size)
{
int ret = OK;
uint32_t addr = start_address;
spiflash_start();
for (uint32_t i = 0; i < size; i += FLASH_SECTOR_SIZE)
{
wait_flash_idle();
enable_flash_write();
ERASE_FLASH_SECTOR(addr);
addr += FLASH_SECTOR_SIZE;
}
wait_flash_idle();
disable_flash_write();
spi_flash_check_and_flush_cache(start_address, size);
spiflash_end();
return ret;
}
/****************************************************************************
* Name: spi_flash_write
*
* Description:
* Write data to Flash.
*
* Parameters:
* dest_addr - Destination address in Flash.
* buffer - Pointer to the source buffer.
* size - Length of data, in bytes.
*
* Returned Values:
* Zero (OK) is returned or a negative error.
*
****************************************************************************/
IRAM_ATTR int spi_flash_write(uint32_t dest_addr,
const void *buffer,
uint32_t size)
{
int ret = OK;
const uint8_t *tx_buf = (const uint8_t *)buffer;
uint32_t tx_bytes = size;
uint32_t tx_addr = dest_addr;
spiflash_start();
for (int i = 0; i < size; i += SPI_BUFFER_BYTES)
{
uint32_t spi_buffer[SPI_BUFFER_WORDS];
uint32_t n = MIN(tx_bytes, SPI_BUFFER_BYTES);
memcpy(spi_buffer, tx_buf, n);
wait_flash_idle();
enable_flash_write();
WRITE_DATA_TO_FLASH(tx_addr, spi_buffer, n);
tx_bytes -= n;
tx_buf += n;
tx_addr += n;
}
wait_flash_idle();
disable_flash_write();
spi_flash_check_and_flush_cache(dest_addr, size);
spiflash_end();
return ret;
}
#endif /* CONFIG_ESPRESSIF_SPI_FLASH_USE_ROM_CODE */
/****************************************************************************
* Name: esp_spiflash_init
*
* Description:
* Initialize ESP SPI flash driver.
*
* Input Parameters:
* None.
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_spiflash_init(void)
{
extern void spi_flash_guard_set(const struct spiflash_guard_funcs *);
nxmutex_init(&s_flash_op_mutex);
spi_flash_guard_set(&g_spi_flash_guard_funcs);
return OK;
}