drivers: flash: add Andes qspi-nor driver
Add flash driver for Andes qspi. Signed-off-by: Wei-Tai Lee <wtlee@andestech.com>
This commit is contained in:
parent
3548bf26d8
commit
b69aea9f89
|
@ -50,6 +50,7 @@ zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_SMARTBOND flash_smartbond.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_FLASH_CAD_QSPI_NOR flash_cadence_qspi_nor.c flash_cadence_qspi_nor_ll.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_XMC4XXX soc_flash_xmc4xxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_FLASH_RPI_PICO flash_rpi_pico.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_FLASH_ANDES_QSPI flash_andes_qspi.c)
|
||||
|
||||
if(CONFIG_FLASH_MCUX_FLEXSPI_XIP)
|
||||
dt_chosen(chosen_flash PROPERTY "zephyr,flash")
|
||||
|
|
|
@ -156,4 +156,6 @@ source "drivers/flash/Kconfig.numaker"
|
|||
|
||||
source "drivers/flash/Kconfig.nxp_s32"
|
||||
|
||||
source "drivers/flash/Kconfig.andes"
|
||||
|
||||
endif # FLASH
|
||||
|
|
57
drivers/flash/Kconfig.andes
Normal file
57
drivers/flash/Kconfig.andes
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Kconfig Andes QSPI-NOR configuration options
|
||||
#
|
||||
# Copyright (c) 2023 Andes Technology Corporation.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig FLASH_ANDES_QSPI
|
||||
bool "Andes FLASH driver"
|
||||
default y
|
||||
depends on DT_HAS_ANDESTECH_QSPI_NOR_ENABLED
|
||||
select FLASH_HAS_PAGE_LAYOUT
|
||||
select FLASH_JESD216
|
||||
select FLASH_HAS_DRIVER_ENABLED
|
||||
depends on !SPI_NOR
|
||||
help
|
||||
Enable driver for Andes QSPI
|
||||
|
||||
if FLASH_ANDES_QSPI
|
||||
|
||||
choice FLASH_ANDES_QSPI_SFDP
|
||||
prompt "Source for Serial Flash Discoverable Parameters"
|
||||
default FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
|
||||
config FLASH_ANDES_QSPI_SFDP_DEVICETREE
|
||||
bool "Basic Flash Parameters from devicetree node"
|
||||
help
|
||||
The JESD216 Basic Flash Parameters table must be provided in the
|
||||
sfdp-bfp property in devicetree. The size and jedec-id properties are
|
||||
also required.
|
||||
|
||||
config FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
bool "Read flash parameters at runtime"
|
||||
help
|
||||
Read all flash device characteristics from the device at runtime.
|
||||
This option is the most flexible as it should provide functionality
|
||||
for all supported JESD216-compatible devices.
|
||||
|
||||
endchoice
|
||||
|
||||
config FLASH_ANDES_QSPI_INIT_PRIORITY
|
||||
int
|
||||
default 80
|
||||
help
|
||||
Device driver initialization priority.
|
||||
|
||||
config FLASH_ANDES_QSPI_LAYOUT_PAGE_SIZE
|
||||
int "Page size to use for FLASH_LAYOUT feature"
|
||||
default 65536
|
||||
help
|
||||
When CONFIG_FLASH_PAGE_LAYOUT is used this driver will support
|
||||
that API. By default the page size corresponds to the block
|
||||
size (65536). Other options include the 32K-byte erase size
|
||||
(32768), the sector size (4096), or any non-zero multiple of the
|
||||
sector size.
|
||||
|
||||
endif
|
965
drivers/flash/flash_andes_qspi.c
Normal file
965
drivers/flash/flash_andes_qspi.c
Normal file
|
@ -0,0 +1,965 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Andes Technology Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT andestech_qspi_nor
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "flash_andes_qspi.h"
|
||||
#include "spi_nor.h"
|
||||
#include "jesd216.h"
|
||||
#include "flash_priv.h"
|
||||
|
||||
LOG_MODULE_REGISTER(flash_andes, CONFIG_FLASH_LOG_LEVEL);
|
||||
|
||||
/* Indicates that an access command includes bytes for the address.
|
||||
* If not provided the opcode is not followed by address bytes.
|
||||
*/
|
||||
#define ANDES_ACCESS_ADDRESSED BIT(0)
|
||||
|
||||
/* Indicates that an access command is performing a write. If not
|
||||
* provided access is a read.
|
||||
*/
|
||||
#define ANDES_ACCESS_WRITE BIT(7)
|
||||
|
||||
#define flash_andes_qspi_cmd_read(dev, opcode, dest, length) \
|
||||
flash_andes_qspi_access(dev, opcode, 0, 0, dest, length)
|
||||
#define flash_andes_qspi_cmd_addr_read(dev, opcode, addr, dest, length) \
|
||||
flash_andes_qspi_access(dev, opcode, ANDES_ACCESS_ADDRESSED, addr, \
|
||||
dest, length)
|
||||
#define flash_andes_qspi_cmd_write(dev, opcode) \
|
||||
flash_andes_qspi_access(dev, opcode, ANDES_ACCESS_WRITE, 0, NULL, 0)
|
||||
#define flash_andes_qspi_cmd_addr_write(dev, opcode, addr, src, length) \
|
||||
flash_andes_qspi_access(dev, opcode, \
|
||||
ANDES_ACCESS_WRITE | ANDES_ACCESS_ADDRESSED, \
|
||||
addr, (void *)src, length)
|
||||
|
||||
|
||||
typedef void (*flash_andes_qspi_config_func_t)(void);
|
||||
struct flash_andes_qspi_config {
|
||||
flash_andes_qspi_config_func_t cfg_func;
|
||||
uint32_t base;
|
||||
uint32_t irq_num;
|
||||
struct flash_parameters parameters;
|
||||
bool xip;
|
||||
#if defined(CONFIG_FLASH_ANDES_QSPI_SFDP_DEVICETREE)
|
||||
uint8_t jedec_id[SPI_NOR_MAX_ID_LEN];
|
||||
uint32_t flash_size;
|
||||
uint8_t bfp_len;
|
||||
const struct jesd216_bfp *bfp;
|
||||
#ifdef CONFIG_FLASH_PAGE_LAYOUT
|
||||
struct flash_pages_layout layout;
|
||||
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
||||
#endif /* CONFIG_FLASH_ANDES_QSPI_SFDP_DEVICETREE */
|
||||
};
|
||||
|
||||
struct flash_andes_qspi_data {
|
||||
struct k_sem sem;
|
||||
struct k_sem device_sync_sem;
|
||||
uint32_t tx_fifo_size;
|
||||
uint32_t rx_fifo_size;
|
||||
uint8_t *tx_buf;
|
||||
uint8_t *rx_buf;
|
||||
uint32_t tx_len;
|
||||
uint32_t rx_len;
|
||||
uint32_t tx_ptr; /* write pointer */
|
||||
uint32_t rx_ptr; /* read pointer */
|
||||
struct jesd216_erase_type erase_types[JESD216_NUM_ERASE_TYPES];
|
||||
uint16_t page_size;
|
||||
#ifdef CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
uint32_t flash_size;
|
||||
#ifdef CONFIG_FLASH_PAGE_LAYOUT
|
||||
struct flash_pages_layout layout;
|
||||
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
||||
#endif
|
||||
};
|
||||
|
||||
static int flash_andes_qspi_write_protection_set(const struct device *dev,
|
||||
bool write_protect);
|
||||
|
||||
/* Get pointer to array of supported erase types. */
|
||||
static inline const struct jesd216_erase_type *
|
||||
dev_erase_types(const struct device *dev)
|
||||
{
|
||||
const struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
|
||||
return dev_data->erase_types;
|
||||
}
|
||||
|
||||
/* Get the size of the flash device. */
|
||||
static inline uint32_t dev_flash_size(const struct device *dev)
|
||||
{
|
||||
#ifdef CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
const struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
|
||||
return dev_data->flash_size;
|
||||
#else /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
|
||||
return config->flash_size;
|
||||
#endif /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
}
|
||||
|
||||
/* Get the flash device page size. */
|
||||
static inline uint16_t dev_page_size(const struct device *dev)
|
||||
{
|
||||
const struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
|
||||
return dev_data->page_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Send an SPI command
|
||||
*
|
||||
* @param dev Device struct
|
||||
* @param opcode The command to send
|
||||
* @param access flags that determine how the command is constructed.
|
||||
* @param addr The address to send
|
||||
* @param data The buffer to store or read the value
|
||||
* @param length The size of the buffer
|
||||
* @return 0 on success
|
||||
*/
|
||||
static int flash_andes_qspi_access(const struct device *const dev,
|
||||
uint8_t opcode, uint8_t access, off_t addr,
|
||||
void *data, size_t length)
|
||||
{
|
||||
struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
uint32_t base = config->base;
|
||||
|
||||
bool is_addressed = (access & ANDES_ACCESS_ADDRESSED) != 0U;
|
||||
bool is_write = (access & ANDES_ACCESS_WRITE) != 0U;
|
||||
int ret = 0;
|
||||
|
||||
uint32_t tctrl, int_msk;
|
||||
|
||||
/* Command phase enable */
|
||||
tctrl = TCTRL_CMD_EN_MSK;
|
||||
if (is_addressed) {
|
||||
/* Enable and set ADDR len */
|
||||
sys_write32((sys_read32(QSPI_TFMAT(base)) |
|
||||
(0x2 << TFMAT_ADDR_LEN_OFFSET)), QSPI_TFMAT(base));
|
||||
sys_write32(addr, QSPI_ADDR(base));
|
||||
/* Address phase enable */
|
||||
tctrl |= TCTRL_ADDR_EN_MSK;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
if ((opcode == FLASH_ANDES_CMD_4PP) ||
|
||||
(opcode == FLASH_ANDES_CMD_4READ)) {
|
||||
goto exit;
|
||||
}
|
||||
tctrl |= TRNS_MODE_NONE_DATA;
|
||||
int_msk = IEN_END_MSK;
|
||||
} else if (is_write) {
|
||||
dev_data->tx_ptr = 0;
|
||||
dev_data->tx_buf = (uint8_t *)data;
|
||||
dev_data->tx_len = length;
|
||||
|
||||
tctrl |= (TRNS_MODE_WRITE_ONLY |
|
||||
((length - 1) << TCTRL_WR_TCNT_OFFSET));
|
||||
int_msk = IEN_TX_FIFO_MSK | IEN_END_MSK;
|
||||
} else {
|
||||
dev_data->rx_ptr = 0;
|
||||
dev_data->rx_buf = (uint8_t *)data;
|
||||
|
||||
tctrl |= (TRNS_MODE_READ_ONLY |
|
||||
((length - 1) << TCTRL_RD_TCNT_OFFSET));
|
||||
int_msk = IEN_RX_FIFO_MSK | IEN_END_MSK;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case FLASH_ANDES_CMD_4PP:
|
||||
tctrl = ((tctrl & ~TCTRL_TRNS_MODE_MSK) |
|
||||
DUAL_IO_MODE |
|
||||
TCTRL_ADDR_FMT_MSK |
|
||||
TCTRL_ADDR_EN_MSK |
|
||||
TRNS_MODE_WRITE_ONLY);
|
||||
break;
|
||||
case FLASH_ANDES_CMD_4READ:
|
||||
tctrl = ((tctrl & ~TCTRL_TRNS_MODE_MSK) |
|
||||
DUAL_IO_MODE |
|
||||
TCTRL_ADDR_FMT_MSK |
|
||||
TCTRL_ADDR_EN_MSK |
|
||||
TRNS_MODE_DUMMY_READ |
|
||||
DUMMY_CNT_3);
|
||||
break;
|
||||
case JESD216_CMD_READ_SFDP:
|
||||
tctrl = ((tctrl & ~TCTRL_TRNS_MODE_MSK) |
|
||||
TCTRL_ADDR_EN_MSK |
|
||||
TRNS_MODE_DUMMY_READ);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sys_write32(tctrl, QSPI_TCTRL(base));
|
||||
/* Enable TX/RX FIFO interrupts */
|
||||
sys_write32(int_msk, QSPI_INTEN(base));
|
||||
/* write CMD register to send command*/
|
||||
sys_write32(opcode, QSPI_CMD(base));
|
||||
k_sem_take(&dev_data->device_sync_sem, K_FOREVER);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Everything necessary to acquire owning access to the device. */
|
||||
static void acquire_device(const struct device *dev)
|
||||
{
|
||||
struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
|
||||
k_sem_take(&dev_data->sem, K_FOREVER);
|
||||
}
|
||||
|
||||
/* Everything necessary to release access to the device. */
|
||||
static void release_device(const struct device *dev)
|
||||
{
|
||||
struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
|
||||
k_sem_give(&dev_data->sem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait until the flash is ready
|
||||
*
|
||||
* @param dev The device structure
|
||||
* @return 0 on success, negative errno code otherwise
|
||||
*/
|
||||
static int flash_andes_qspi_wait_until_ready(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
uint8_t reg;
|
||||
|
||||
do {
|
||||
ret = flash_andes_qspi_cmd_read(dev,
|
||||
FLASH_ANDES_CMD_RDSR, ®, 1);
|
||||
} while (!ret && (reg & FLASH_ANDES_WIP_BIT));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME) || \
|
||||
defined(CONFIG_FLASH_JESD216_API)
|
||||
/*
|
||||
* @brief Read content from the SFDP hierarchy
|
||||
*
|
||||
* @note The device must be externally acquired before invoking this
|
||||
* function.
|
||||
*
|
||||
* @param dev Device struct
|
||||
* @param addr The address to send
|
||||
* @param data The buffer to store or read the value
|
||||
* @param length The size of the buffer
|
||||
* @return 0 on success, negative errno code otherwise
|
||||
*/
|
||||
static int read_sfdp(const struct device *const dev,
|
||||
off_t addr, void *data, size_t length)
|
||||
{
|
||||
/* READ_SFDP requires a 24-bit address followed by a single
|
||||
* byte for a wait state. This is effected by using 32-bit
|
||||
* address by shifting the 24-bit address up 8 bits.
|
||||
*/
|
||||
return flash_andes_qspi_access(dev, JESD216_CMD_READ_SFDP,
|
||||
ANDES_ACCESS_ADDRESSED,
|
||||
addr, data, length);
|
||||
}
|
||||
#endif /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
|
||||
/**
|
||||
* @brief Write the status register.
|
||||
*
|
||||
* @note The device must be externally acquired before invoking this
|
||||
* function.
|
||||
*
|
||||
* @param dev Device struct
|
||||
* @param sr The new value of the status register
|
||||
*
|
||||
* @return 0 on success or a negative error code.
|
||||
*/
|
||||
static int flash_andes_qspi_wrsr(const struct device *dev,
|
||||
uint8_t sr)
|
||||
{
|
||||
int ret = flash_andes_qspi_cmd_write(dev, FLASH_ANDES_CMD_WREN);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = flash_andes_qspi_access(dev, FLASH_ANDES_CMD_WRSR,
|
||||
ANDES_ACCESS_WRITE, 0, &sr,
|
||||
sizeof(sr));
|
||||
flash_andes_qspi_wait_until_ready(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flash_andes_qspi_read(const struct device *dev,
|
||||
off_t addr, void *dest, size_t size)
|
||||
{
|
||||
const size_t flash_size = dev_flash_size(dev);
|
||||
int ret;
|
||||
|
||||
/* should be between 0 and flash size */
|
||||
if ((addr < 0 || addr >= flash_size || ((flash_size - addr) < size))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
acquire_device(dev);
|
||||
|
||||
ret = flash_andes_qspi_cmd_addr_read(dev,
|
||||
FLASH_ANDES_CMD_4READ, addr, dest, size);
|
||||
|
||||
release_device(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flash_andes_qspi_write(const struct device *dev, off_t addr,
|
||||
const void *src, size_t size)
|
||||
{
|
||||
const size_t flash_size = dev_flash_size(dev);
|
||||
const uint16_t page_size = dev_page_size(dev);
|
||||
size_t to_write = size;
|
||||
int ret = 0;
|
||||
|
||||
/* should be between 0 and flash size */
|
||||
if ((addr < 0 || addr >= flash_size || ((flash_size - addr) < size))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
acquire_device(dev);
|
||||
|
||||
ret = flash_andes_qspi_write_protection_set(dev, false);
|
||||
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
/* Get the adequate size to send*/
|
||||
to_write = MIN(page_size - (addr % page_size), size);
|
||||
|
||||
ret = flash_andes_qspi_cmd_addr_write(dev,
|
||||
FLASH_ANDES_CMD_4PP, addr, src, to_write);
|
||||
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
size -= to_write;
|
||||
src = (const uint8_t *)src + to_write;
|
||||
addr += to_write;
|
||||
|
||||
flash_andes_qspi_wait_until_ready(dev);
|
||||
} while (size > 0);
|
||||
|
||||
|
||||
int ret2 = flash_andes_qspi_write_protection_set(dev, true);
|
||||
|
||||
if (!ret) {
|
||||
ret = ret2;
|
||||
}
|
||||
|
||||
out:
|
||||
release_device(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flash_andes_qspi_erase(const struct device *dev,
|
||||
off_t addr, size_t size)
|
||||
{
|
||||
const size_t flash_size = dev_flash_size(dev);
|
||||
int ret = 0;
|
||||
|
||||
/* erase area must be subregion of device */
|
||||
if ((addr < 0 || addr >= flash_size || ((flash_size - addr) < size))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* address must be sector-aligned */
|
||||
if (!SPI_NOR_IS_SECTOR_ALIGNED(addr)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* size must be a multiple of sectors */
|
||||
if ((size % SPI_NOR_SECTOR_SIZE) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
acquire_device(dev);
|
||||
|
||||
ret = flash_andes_qspi_write_protection_set(dev, false);
|
||||
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (size == flash_size) {
|
||||
/* chip erase */
|
||||
flash_andes_qspi_cmd_write(dev, FLASH_ANDES_CMD_CE);
|
||||
size -= flash_size;
|
||||
flash_andes_qspi_wait_until_ready(dev);
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
const struct jesd216_erase_type *erase_types =
|
||||
dev_erase_types(dev);
|
||||
const struct jesd216_erase_type *bet = NULL;
|
||||
|
||||
for (uint8_t ei = 0; ei < JESD216_NUM_ERASE_TYPES; ++ei) {
|
||||
const struct jesd216_erase_type *etp =
|
||||
&erase_types[ei];
|
||||
|
||||
if ((etp->exp != 0) &&
|
||||
SPI_NOR_IS_ALIGNED(addr, etp->exp) &&
|
||||
SPI_NOR_IS_ALIGNED(size, etp->exp) &&
|
||||
((bet == NULL) || (etp->exp > bet->exp))) {
|
||||
bet = etp;
|
||||
}
|
||||
}
|
||||
|
||||
if (bet != NULL) {
|
||||
flash_andes_qspi_cmd_addr_write(dev, bet->cmd,
|
||||
addr, NULL, 0);
|
||||
addr += BIT(bet->exp);
|
||||
size -= BIT(bet->exp);
|
||||
} else {
|
||||
LOG_DBG("Can't erase %zu at 0x%lx",
|
||||
size, (long)addr);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
flash_andes_qspi_wait_until_ready(dev);
|
||||
}
|
||||
|
||||
int ret2 = flash_andes_qspi_write_protection_set(dev, true);
|
||||
|
||||
if (!ret) {
|
||||
ret = ret2;
|
||||
}
|
||||
|
||||
out:
|
||||
release_device(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int flash_andes_qspi_write_protection_set(const struct device *dev,
|
||||
bool write_protect)
|
||||
{
|
||||
return flash_andes_qspi_cmd_write(dev, (write_protect) ?
|
||||
FLASH_ANDES_CMD_WRDI : FLASH_ANDES_CMD_WREN);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FLASH_JESD216_API)
|
||||
|
||||
static int flash_andes_qspi_sfdp_read(const struct device *dev, off_t addr,
|
||||
void *dest, size_t size)
|
||||
{
|
||||
acquire_device(dev);
|
||||
|
||||
int ret = read_sfdp(dev, addr, dest, size);
|
||||
|
||||
release_device(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FLASH_JESD216_API */
|
||||
|
||||
static int flash_andes_qspi_read_jedec_id(const struct device *dev,
|
||||
uint8_t *id)
|
||||
{
|
||||
if (id == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
acquire_device(dev);
|
||||
|
||||
int ret = flash_andes_qspi_cmd_read(dev, FLASH_ANDES_CMD_RDID, id, 3);
|
||||
|
||||
release_device(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_nor_process_bfp(const struct device *dev,
|
||||
const struct jesd216_param_header *php,
|
||||
const struct jesd216_bfp *bfp)
|
||||
{
|
||||
struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
struct jesd216_erase_type *etp = dev_data->erase_types;
|
||||
const size_t flash_size = jesd216_bfp_density(bfp) / 8U;
|
||||
|
||||
LOG_DBG("%s: %u MiBy flash", dev->name, (uint32_t)(flash_size >> 20));
|
||||
|
||||
/* Copy over the erase types, preserving their order. (The
|
||||
* Sector Map Parameter table references them by index.)
|
||||
*/
|
||||
memset(dev_data->erase_types, 0, sizeof(dev_data->erase_types));
|
||||
for (uint8_t ti = 1; ti <= ARRAY_SIZE(dev_data->erase_types); ++ti) {
|
||||
if (jesd216_bfp_erase(bfp, ti, etp) == 0) {
|
||||
LOG_DBG("Erase %u with %02x",
|
||||
(uint32_t)BIT(etp->exp), etp->cmd);
|
||||
}
|
||||
++etp;
|
||||
}
|
||||
|
||||
dev_data->page_size = jesd216_bfp_page_size(php, bfp);
|
||||
#ifdef CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
dev_data->flash_size = flash_size;
|
||||
#else /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
if (flash_size != dev_flash_size(dev)) {
|
||||
LOG_ERR("BFP flash size mismatch with devicetree");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_process_sfdp(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined(CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME)
|
||||
|
||||
const uint8_t decl_nph = 2;
|
||||
union {
|
||||
/* We only process BFP so use one parameter block */
|
||||
uint8_t raw[JESD216_SFDP_SIZE(decl_nph)];
|
||||
struct jesd216_sfdp_header sfdp;
|
||||
} u;
|
||||
const struct jesd216_sfdp_header *hp = &u.sfdp;
|
||||
|
||||
ret = read_sfdp(dev, 0, u.raw, sizeof(u.raw));
|
||||
if (ret != 0) {
|
||||
LOG_ERR("SFDP read failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t magic = jesd216_sfdp_magic(hp);
|
||||
|
||||
if (magic != JESD216_SFDP_MAGIC) {
|
||||
LOG_ERR("SFDP magic %08x invalid", magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_DBG("%s: SFDP v %u.%u AP %x with %u PH", dev->name,
|
||||
hp->rev_major, hp->rev_minor, hp->access, 1 + hp->nph);
|
||||
|
||||
const struct jesd216_param_header *php = hp->phdr;
|
||||
const struct jesd216_param_header *phpe =
|
||||
php + MIN(decl_nph, 1 + hp->nph);
|
||||
|
||||
while (php != phpe) {
|
||||
uint16_t id = jesd216_param_id(php);
|
||||
|
||||
LOG_DBG("PH%zu: %04x rev %u.%u: %u DW @ %x",
|
||||
(php - hp->phdr), id, php->rev_major, php->rev_minor,
|
||||
php->len_dw, jesd216_param_addr(php));
|
||||
|
||||
if (id == JESD216_SFDP_PARAM_ID_BFP) {
|
||||
union {
|
||||
uint32_t dw[MIN(php->len_dw, 20)];
|
||||
struct jesd216_bfp bfp;
|
||||
} u;
|
||||
const struct jesd216_bfp *bfp = &u.bfp;
|
||||
|
||||
ret = read_sfdp(dev,
|
||||
jesd216_param_addr(php), u.dw, sizeof(u.dw));
|
||||
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = spi_nor_process_bfp(dev, php, bfp);
|
||||
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++php;
|
||||
}
|
||||
#elif defined(CONFIG_FLASH_ANDES_QSPI_SFDP_DEVICETREE)
|
||||
/* For devicetree we need to synthesize a parameter header and
|
||||
* process the stored BFP data as if we had read it.
|
||||
*/
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
struct jesd216_param_header bfp_hdr = {
|
||||
.len_dw = config->bfp_len,
|
||||
};
|
||||
|
||||
ret = spi_nor_process_bfp(dev, &bfp_hdr, cfg->bfp);
|
||||
#else
|
||||
#error Unhandled SFDP choice
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
static int setup_pages_layout(const struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#if defined(CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME)
|
||||
|
||||
struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
const size_t flash_size = dev_flash_size(dev);
|
||||
const uint32_t layout_page_size =
|
||||
CONFIG_FLASH_ANDES_QSPI_LAYOUT_PAGE_SIZE;
|
||||
uint8_t exponent = 0;
|
||||
|
||||
/* Find the smallest erase size. */
|
||||
for (size_t i = 0; i < ARRAY_SIZE(dev_data->erase_types); ++i) {
|
||||
const struct jesd216_erase_type *etp =
|
||||
&dev_data->erase_types[i];
|
||||
|
||||
if ((etp->cmd != 0) &&
|
||||
((exponent == 0) || (etp->exp < exponent))) {
|
||||
exponent = etp->exp;
|
||||
}
|
||||
}
|
||||
|
||||
if (exponent == 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uint32_t erase_size = BIT(exponent);
|
||||
|
||||
/* Error if layout page size is not a multiple of smallest
|
||||
* erase size.
|
||||
*/
|
||||
if ((layout_page_size % erase_size) != 0) {
|
||||
LOG_ERR("layout page %u not compatible with erase size %u",
|
||||
layout_page_size, erase_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Warn but accept layout page sizes that leave inaccessible
|
||||
* space.
|
||||
*/
|
||||
if ((flash_size % layout_page_size) != 0) {
|
||||
LOG_WRN("layout page %u wastes space with device size %zu",
|
||||
layout_page_size, flash_size);
|
||||
}
|
||||
|
||||
dev_data->layout.pages_size = layout_page_size;
|
||||
dev_data->layout.pages_count = flash_size / layout_page_size;
|
||||
LOG_DBG("layout %zu x %zu By pages", dev_data->layout.pages_count,
|
||||
dev_data->layout.pages_size);
|
||||
|
||||
#elif defined(CONFIG_FLASH_ANDES_QSPI_SFDP_DEVICETREE)
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
const struct flash_pages_layout *layout = &config->layout;
|
||||
const size_t flash_size = dev_flash_size(dev);
|
||||
size_t layout_size = layout->pages_size * layout->pages_count;
|
||||
|
||||
if (!SPI_NOR_IS_SECTOR_ALIGNED(layout->pages_size)) {
|
||||
LOG_ERR("ANDES_QSPI_FLASH_LAYOUT_PAGE_SIZE must be "
|
||||
"multiple of 4096");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (flash_size != layout_size) {
|
||||
LOG_ERR("device size %zu mismatch %zu * %zu By pages",
|
||||
flash_size, layout->pages_count, layout->pages_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
#else /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
#error Unhandled SFDP choice
|
||||
#endif /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
||||
|
||||
static int qspi_andes_configure(const struct device *dev)
|
||||
{
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
uint32_t base = config->base;
|
||||
|
||||
/* Setting the divisor value to 0xff indicates the SCLK
|
||||
* frequency should be the same as the spi_clock frequency.
|
||||
*/
|
||||
sys_set_bits(QSPI_TIMIN(base), TIMIN_SCLK_DIV_MSK);
|
||||
|
||||
/* Set Master mode */
|
||||
sys_clear_bits(QSPI_TFMAT(base), TFMAT_SLVMODE_MSK);
|
||||
|
||||
/* Disable data merge mode */
|
||||
sys_clear_bits(QSPI_TFMAT(base), TFMAT_DATA_MERGE_MSK);
|
||||
|
||||
/* Set data length */
|
||||
sys_clear_bits(QSPI_TFMAT(base), TFMAT_DATA_LEN_MSK);
|
||||
sys_set_bits(QSPI_TFMAT(base), (7 << TFMAT_DATA_LEN_OFFSET));
|
||||
|
||||
/* Set TX/RX FIFO threshold */
|
||||
sys_clear_bits(QSPI_CTRL(base), CTRL_TX_THRES_MSK);
|
||||
sys_clear_bits(QSPI_CTRL(base), CTRL_RX_THRES_MSK);
|
||||
|
||||
sys_set_bits(QSPI_CTRL(base), TX_FIFO_THRESHOLD);
|
||||
sys_set_bits(QSPI_CTRL(base), RX_FIFO_THRESHOLD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qspi_andes_irq_handler(const struct device *dev)
|
||||
{
|
||||
struct flash_andes_qspi_data *data = dev->data;
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
uint32_t base = config->base;
|
||||
|
||||
uint32_t i, intr_status, spi_status;
|
||||
uint32_t rx_data, cur_tx_fifo_num, cur_rx_fifo_num;
|
||||
uint32_t tx_num = 0, tx_data = 0;
|
||||
|
||||
intr_status = sys_read32(QSPI_INTST(base));
|
||||
|
||||
if ((intr_status & INTST_TX_FIFO_INT_MSK) &&
|
||||
!(intr_status & INTST_END_INT_MSK)) {
|
||||
|
||||
spi_status = sys_read32(QSPI_STAT(base));
|
||||
cur_tx_fifo_num = GET_TX_NUM(base);
|
||||
|
||||
tx_num = data->tx_fifo_size - cur_tx_fifo_num;
|
||||
|
||||
if (tx_num > data->tx_len) {
|
||||
tx_num = data->tx_len;
|
||||
}
|
||||
|
||||
for (i = tx_num; i > 0; i--) {
|
||||
tx_data = data->tx_buf[data->tx_ptr];
|
||||
sys_write32(tx_data, QSPI_DATA(base));
|
||||
data->tx_ptr++;
|
||||
if (data->tx_ptr == data->tx_len) {
|
||||
sys_clear_bits(QSPI_INTEN(base), IEN_TX_FIFO_MSK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sys_write32(INTST_TX_FIFO_INT_MSK, QSPI_INTST(base));
|
||||
}
|
||||
|
||||
if (intr_status & INTST_RX_FIFO_INT_MSK) {
|
||||
cur_rx_fifo_num = GET_RX_NUM(base);
|
||||
|
||||
for (i = cur_rx_fifo_num; i > 0; i--) {
|
||||
rx_data = sys_read32(QSPI_DATA(base));
|
||||
data->rx_buf[data->rx_ptr] = rx_data;
|
||||
data->rx_ptr++;
|
||||
if (data->rx_ptr == data->rx_len) {
|
||||
sys_clear_bits(QSPI_INTEN(base), IEN_RX_FIFO_MSK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sys_write32(INTST_RX_FIFO_INT_MSK, QSPI_INTST(base));
|
||||
}
|
||||
|
||||
if (intr_status & INTST_END_INT_MSK) {
|
||||
|
||||
/* Clear end interrupt */
|
||||
sys_write32(INTST_END_INT_MSK, QSPI_INTST(base));
|
||||
|
||||
/* Disable all SPI interrupts */
|
||||
sys_write32(0, QSPI_INTEN(base));
|
||||
|
||||
k_sem_give(&data->device_sync_sem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize and configure the flash
|
||||
*
|
||||
* @param name The flash name
|
||||
* @return 0 on success, negative errno code otherwise
|
||||
*/
|
||||
static int flash_andes_qspi_init(const struct device *dev)
|
||||
{
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
uint32_t base = config->base;
|
||||
|
||||
uint8_t ret, reg = (0x1UL << 6);
|
||||
uint8_t jedec_id[SPI_NOR_MAX_ID_LEN];
|
||||
|
||||
/* we should not configure the device we are running on */
|
||||
if (config->xip) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_sem_init(&dev_data->sem, 1, 1);
|
||||
k_sem_init(&dev_data->device_sync_sem, 0, 1);
|
||||
|
||||
/* Get the TX/RX FIFO size of this device */
|
||||
dev_data->tx_fifo_size = TX_FIFO_SIZE(base);
|
||||
dev_data->rx_fifo_size = RX_FIFO_SIZE(base);
|
||||
|
||||
config->cfg_func();
|
||||
irq_enable(config->irq_num);
|
||||
|
||||
qspi_andes_configure(dev);
|
||||
|
||||
ret = flash_andes_qspi_read_jedec_id(dev, jedec_id);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("JEDEC ID read failed: %d", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
|
||||
if (memcmp(jedec_id, cfg->jedec_id, sizeof(jedec_id)) != 0) {
|
||||
LOG_ERR("Device id %02x %02x %02x does not match config"
|
||||
"%02x %02x %02x", jedec_id[0], jedec_id[1], jedec_id[2],
|
||||
cfg->jedec_id[0], cfg->jedec_id[1], cfg->jedec_id[2]);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = spi_nor_process_sfdp(dev);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("SFDP read failed: %d", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
ret = setup_pages_layout(dev);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("layout setup failed: %d", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
||||
|
||||
/* Set status register QE bit. */
|
||||
flash_andes_qspi_wrsr(dev, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
static void flash_andes_qspi_pages_layout(const struct device *dev,
|
||||
const struct flash_pages_layout **layout,
|
||||
size_t *layout_size)
|
||||
{
|
||||
#ifdef CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME
|
||||
const struct flash_andes_qspi_data *dev_data = dev->data;
|
||||
|
||||
*layout = &dev_data->layout;
|
||||
#else /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
|
||||
*layout = &config->layout;
|
||||
#endif /* CONFIG_FLASH_ANDES_QSPI_SFDP_RUNTIME */
|
||||
*layout_size = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static const struct flash_parameters *
|
||||
flash_andes_qspi_get_parameters(const struct device *dev)
|
||||
{
|
||||
const struct flash_andes_qspi_config *config = dev->config;
|
||||
|
||||
return &config->parameters;
|
||||
}
|
||||
|
||||
static const struct flash_driver_api flash_andes_qspi_api = {
|
||||
.read = flash_andes_qspi_read,
|
||||
.write = flash_andes_qspi_write,
|
||||
.erase = flash_andes_qspi_erase,
|
||||
.get_parameters = flash_andes_qspi_get_parameters,
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
.page_layout = flash_andes_qspi_pages_layout,
|
||||
#endif
|
||||
#if defined(CONFIG_FLASH_JESD216_API)
|
||||
.sfdp_read = flash_andes_qspi_sfdp_read,
|
||||
.read_jedec_id = flash_andes_qspi_read_jedec_id,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if (CONFIG_XIP)
|
||||
#define QSPI_ROM_CFG_XIP(node_id) DT_SAME_NODE(node_id, DT_CHOSEN(zephyr_flash))
|
||||
#else
|
||||
#define QSPI_ROM_CFG_XIP(node_id) false
|
||||
#endif
|
||||
|
||||
#define LAYOUT_PAGES_PROP(n) \
|
||||
IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, \
|
||||
(.layout = { \
|
||||
.pages_count = ((DT_INST_PROP(n, size) / 8) / \
|
||||
CONFIG_FLASH_ANDES_QSPI_LAYOUT_PAGE_SIZE), \
|
||||
.pages_size = \
|
||||
CONFIG_FLASH_ANDES_QSPI_LAYOUT_PAGE_SIZE, \
|
||||
}, \
|
||||
))
|
||||
|
||||
#define ANDES_QSPI_SFDP_DEVICETREE_CONFIG(n) \
|
||||
IF_ENABLED(CONFIG_FLASH_ANDES_QSPI_SFDP_DEVICETREE, \
|
||||
( \
|
||||
static const __aligned(4) uint8_t bfp_data_##n[] = \
|
||||
DT_INST_PROP(n, sfdp_bfp); \
|
||||
))
|
||||
|
||||
#define ANDES_QSPI_SFDP_DEVICETREE_PROP(n) \
|
||||
IF_ENABLED(CONFIG_FLASH_ANDES_QSPI_SFDP_DEVICETREE, \
|
||||
(.jedec_id = DT_INST_PROP(n, jedec_id), \
|
||||
.flash_size = DT_INST_PROP(n, size) / 8, \
|
||||
.bfp_len = sizeof(bfp_data_##n) / 4, \
|
||||
.bfp = (const struct jesd216_bfp *)bfp_data_##n, \
|
||||
LAYOUT_PAGES_PROP(n) \
|
||||
))
|
||||
|
||||
#define FLASH_ANDES_QSPI_INIT(n) \
|
||||
static struct flash_andes_qspi_data flash_andes_qspi_data_##n; \
|
||||
ANDES_QSPI_SFDP_DEVICETREE_CONFIG(n) \
|
||||
\
|
||||
static void flash_andes_qspi_configure_##n(void); \
|
||||
static const struct flash_andes_qspi_config \
|
||||
flash_andes_qspi_config_##n = { \
|
||||
.cfg_func = flash_andes_qspi_configure_##n, \
|
||||
.base = DT_REG_ADDR(DT_INST_BUS(n)), \
|
||||
.irq_num = DT_IRQN(DT_INST_BUS(n)), \
|
||||
.parameters = { \
|
||||
.write_block_size = 1, \
|
||||
.erase_value = 0xff \
|
||||
}, \
|
||||
.xip = QSPI_ROM_CFG_XIP(DT_DRV_INST(n)), \
|
||||
ANDES_QSPI_SFDP_DEVICETREE_PROP(n) \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, \
|
||||
&flash_andes_qspi_init, \
|
||||
NULL, \
|
||||
&flash_andes_qspi_data_##n, \
|
||||
&flash_andes_qspi_config_##n, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_FLASH_ANDES_QSPI_INIT_PRIORITY, \
|
||||
&flash_andes_qspi_api); \
|
||||
\
|
||||
static void flash_andes_qspi_configure_##n(void) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_IRQN(DT_INST_BUS(n)), \
|
||||
DT_IRQ(DT_INST_BUS(n), priority), \
|
||||
qspi_andes_irq_handler, \
|
||||
DEVICE_DT_INST_GET(n), \
|
||||
0); \
|
||||
} \
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(FLASH_ANDES_QSPI_INIT)
|
121
drivers/flash/flash_andes_qspi.h
Normal file
121
drivers/flash/flash_andes_qspi.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Andes Technology Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* Flash opcodes */
|
||||
#define FLASH_ANDES_CMD_WRSR 0x01 /* Write status register */
|
||||
#define FLASH_ANDES_CMD_RDSR 0x05 /* Read status register */
|
||||
#define FLASH_ANDES_CMD_READ 0x03 /* Read data */
|
||||
#define FLASH_ANDES_CMD_4READ 0xEB /* Quad mode Read data*/
|
||||
#define FLASH_ANDES_CMD_WREN 0x06 /* Write enable */
|
||||
#define FLASH_ANDES_CMD_WRDI 0x04 /* Write disable */
|
||||
#define FLASH_ANDES_CMD_PP 0x02 /* Page program */
|
||||
#define FLASH_ANDES_CMD_4PP 0x38 /* Quad mode page program*/
|
||||
#define FLASH_ANDES_CMD_SE 0x20 /* Sector erase */
|
||||
#define FLASH_ANDES_CMD_BE_32K 0x52 /* Block erase 32KB */
|
||||
#define FLASH_ANDES_CMD_BE 0xD8 /* Block erase */
|
||||
#define FLASH_ANDES_CMD_CE 0xC7 /* Chip erase */
|
||||
#define FLASH_ANDES_CMD_RDID 0x9F /* Read JEDEC ID */
|
||||
#define FLASH_ANDES_CMD_ULBPR 0x98 /* Global Block Protection Unlock */
|
||||
#define FLASH_ANDES_CMD_DPD 0xB9 /* Deep Power Down */
|
||||
#define FLASH_ANDES_CMD_RDPD 0xAB /* Release from Deep Power Down */
|
||||
|
||||
/* Status register bits */
|
||||
#define FLASH_ANDES_WIP_BIT BIT(0) /* Write in progress */
|
||||
#define FLASH_ANDES_WEL_BIT BIT(1) /* Write enable latch */
|
||||
#define FLASH_ANDES_QE_BIT BIT(6)
|
||||
|
||||
#define QSPI_TFMAT(base) (base + 0x10)
|
||||
#define QSPI_TCTRL(base) (base + 0x20)
|
||||
#define QSPI_CMD(base) (base + 0x24)
|
||||
#define QSPI_ADDR(base) (base + 0x28)
|
||||
#define QSPI_DATA(base) (base + 0x2c)
|
||||
#define QSPI_CTRL(base) (base + 0x30)
|
||||
#define QSPI_STAT(base) (base + 0x34)
|
||||
#define QSPI_INTEN(base) (base + 0x38)
|
||||
#define QSPI_INTST(base) (base + 0x3c)
|
||||
#define QSPI_TIMIN(base) (base + 0x40)
|
||||
#define QSPI_CONFIG(base) (base + 0x7c)
|
||||
|
||||
/* Field mask of SPI transfer format register */
|
||||
#define TFMAT_DATA_LEN_OFFSET (8)
|
||||
#define TFMAT_ADDR_LEN_OFFSET (16)
|
||||
|
||||
#define TFMAT_SLVMODE_MSK BIT(2)
|
||||
#define TFMAT_DATA_MERGE_MSK BIT(7)
|
||||
#define TFMAT_DATA_LEN_MSK GENMASK(12, 8)
|
||||
|
||||
/* Field mask of SPI transfer control register */
|
||||
#define TCTRL_RD_TCNT_OFFSET (0)
|
||||
#define TCTRL_DUMMY_CNT_OFFSET (9)
|
||||
#define TCTRL_WR_TCNT_OFFSET (12)
|
||||
#define TCTRL_DUAL_MODE_OFFSET (22)
|
||||
#define TCTRL_TRNS_MODE_OFFSET (24)
|
||||
|
||||
#define TCTRL_TRNS_MODE_MSK GENMASK(27, 24)
|
||||
#define TCTRL_ADDR_FMT_MSK BIT(28)
|
||||
#define TCTRL_ADDR_EN_MSK BIT(29)
|
||||
#define TCTRL_CMD_EN_MSK BIT(30)
|
||||
|
||||
/* Transfer mode */
|
||||
#define TRNS_MODE_WRITE_READ (0 << TCTRL_TRNS_MODE_OFFSET)
|
||||
#define TRNS_MODE_WRITE_ONLY (1 << TCTRL_TRNS_MODE_OFFSET)
|
||||
#define TRNS_MODE_READ_ONLY (2 << TCTRL_TRNS_MODE_OFFSET)
|
||||
#define TRNS_MODE_NONE_DATA (7 << TCTRL_TRNS_MODE_OFFSET)
|
||||
#define TRNS_MODE_DUMMY_READ (9 << TCTRL_TRNS_MODE_OFFSET)
|
||||
|
||||
/* Dual/Qual mode */
|
||||
#define DUAL_IO_MODE (2 << TCTRL_DUAL_MODE_OFFSET)
|
||||
|
||||
/* Dummy count */
|
||||
/* In Qual mode, dummy count 3 implies 6 dummy cycles */
|
||||
#define DUMMY_CNT_3 (0x2 << TCTRL_DUMMY_CNT_OFFSET)
|
||||
|
||||
/* Field mask of SPI interrupt enable register */
|
||||
#define IEN_RX_FIFO_MSK BIT(2)
|
||||
#define IEN_TX_FIFO_MSK BIT(3)
|
||||
#define IEN_END_MSK BIT(4)
|
||||
|
||||
/* Field mask of SPI interrupt status register */
|
||||
#define INTST_RX_FIFO_INT_MSK BIT(2)
|
||||
#define INTST_TX_FIFO_INT_MSK BIT(3)
|
||||
#define INTST_END_INT_MSK BIT(4)
|
||||
|
||||
/* Field mask of SPI config register */
|
||||
#define CFG_RX_FIFO_SIZE_MSK GENMASK(3, 0)
|
||||
#define CFG_TX_FIFO_SIZE_MSK GENMASK(7, 4)
|
||||
|
||||
/* Field mask of SPI status register */
|
||||
#define STAT_RX_NUM_MSK GENMASK(13, 8)
|
||||
#define STAT_TX_NUM_MSK GENMASK(21, 16)
|
||||
|
||||
/* Field mask of SPI control register */
|
||||
#define CTRL_RX_THRES_OFFSET (8)
|
||||
#define CTRL_TX_THRES_OFFSET (16)
|
||||
|
||||
#define CTRL_RX_THRES_MSK GENMASK(15, 8)
|
||||
#define CTRL_TX_THRES_MSK GENMASK(23, 16)
|
||||
|
||||
/* Field mask of SPI status register */
|
||||
#define TIMIN_SCLK_DIV_MSK GENMASK(7, 0)
|
||||
|
||||
#define TX_FIFO_THRESHOLD (1 << CTRL_TX_THRES_OFFSET)
|
||||
#define RX_FIFO_THRESHOLD (1 << CTRL_RX_THRES_OFFSET)
|
||||
#define MAX_TRANSFER_CNT (512)
|
||||
|
||||
#define TX_FIFO_SIZE_SETTING(base) \
|
||||
(sys_read32(QSPI_CONFIG(base)) & CFG_TX_FIFO_SIZE_MSK)
|
||||
#define TX_FIFO_SIZE(base) \
|
||||
(2 << (TX_FIFO_SIZE_SETTING(base) >> 4))
|
||||
|
||||
#define RX_FIFO_SIZE_SETTING(base) \
|
||||
(sys_read32(QSPI_CONFIG(base)) & CFG_RX_FIFO_SIZE_MSK)
|
||||
#define RX_FIFO_SIZE(base) \
|
||||
(2 << (RX_FIFO_SIZE_SETTING(base) >> 0))
|
||||
|
||||
#define TX_NUM_STAT(base) (sys_read32(QSPI_STAT(base)) & STAT_TX_NUM_MSK)
|
||||
#define RX_NUM_STAT(base) (sys_read32(QSPI_STAT(base)) & STAT_RX_NUM_MSK)
|
||||
#define GET_TX_NUM(base) (TX_NUM_STAT(base) >> 16)
|
||||
#define GET_RX_NUM(base) (RX_NUM_STAT(base) >> 8)
|
Loading…
Reference in a new issue