Fix warning when no cache handling enabled. Signed-off-by: Swift Tian <swift.tian@ambiq.com>
1050 lines
31 KiB
C
1050 lines
31 KiB
C
/*
|
|
* Copyright (c) 2025, Ambiq Micro Inc. <www.ambiq.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT mspi_is25xx0xx
|
|
/*
|
|
* This driver supports the non-standard 1s-1/8s-8s operation
|
|
* as well as basic 1s-1s-1s operation.
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/mspi.h>
|
|
#include <zephyr/cache.h>
|
|
#include <zephyr/drivers/flash.h>
|
|
#include "spi_nor.h"
|
|
|
|
#if CONFIG_SOC_FAMILY_AMBIQ
|
|
|
|
#include "mspi_ambiq.h"
|
|
typedef struct mspi_ambiq_timing_cfg mspi_timing_cfg;
|
|
typedef enum mspi_ambiq_timing_param mspi_timing_param;
|
|
|
|
#else
|
|
|
|
typedef struct mspi_timing_cfg mspi_timing_cfg;
|
|
typedef enum mspi_timing_param mspi_timing_param;
|
|
|
|
#endif
|
|
|
|
LOG_MODULE_REGISTER(flash_mspi_is25xX0xx, CONFIG_FLASH_LOG_LEVEL);
|
|
|
|
#define NOR_WRITE_SIZE 1
|
|
#define NOR_ERASE_VALUE 0xff
|
|
|
|
#define IS25XX0XX_VENDOR_ID 0x9D
|
|
|
|
#define IS25XX0XX_DC_DEFAULT 0x1F
|
|
|
|
#define IS25XX0XX_32KSECTOR_SIZE 0x8000
|
|
#define IS25XX0XX_BLOCK_SIZE 0x20000
|
|
|
|
#define IS25XX0XX_WRITE_VOL_REG_CMD 0x81
|
|
|
|
enum is25xX0xx_io_mode {
|
|
IS25XX0XX_IO_MODE_EXTENDED_SPI = 0xFF,
|
|
IS25XX0XX_IO_MODE_EXTENDED_SPI_NONDQS = 0xDF,
|
|
};
|
|
|
|
struct flash_mspi_is25xX0xx_config {
|
|
uint8_t port;
|
|
uint32_t mem_size;
|
|
struct flash_parameters flash_param;
|
|
struct flash_pages_layout page_layout;
|
|
|
|
const struct device *bus;
|
|
struct mspi_dev_id dev_id;
|
|
struct mspi_dev_cfg serial_cfg;
|
|
struct mspi_dev_cfg tar_dev_cfg;
|
|
|
|
MSPI_XIP_CFG_STRUCT_DECLARE(tar_xip_cfg)
|
|
MSPI_XIP_BASE_ADDR_DECLARE(xip_base_addr)
|
|
MSPI_SCRAMBLE_CFG_STRUCT_DECLARE(tar_scramble_cfg)
|
|
MSPI_TIMING_CFG_STRUCT_DECLARE(tar_timing_cfg)
|
|
MSPI_TIMING_PARAM_DECLARE(timing_cfg_mask)
|
|
|
|
bool sw_multi_periph;
|
|
|
|
struct gpio_dt_spec reset_gpio;
|
|
uint32_t reset_pulse_us;
|
|
uint32_t reset_recovery_us;
|
|
};
|
|
|
|
struct flash_mspi_is25xX0xx_data {
|
|
struct mspi_dev_cfg dev_cfg;
|
|
struct mspi_xip_cfg xip_cfg;
|
|
struct mspi_scramble_cfg scramble_cfg;
|
|
mspi_timing_cfg timing_cfg;
|
|
struct mspi_xfer trans;
|
|
struct mspi_xfer_packet packet;
|
|
|
|
struct k_sem lock;
|
|
uint8_t id[20];
|
|
};
|
|
|
|
static int is25xX0xx_get_dummy_clk(uint8_t rxdummy, uint8_t *dummy_clk)
|
|
{
|
|
if (rxdummy == 0 || rxdummy > 30) {
|
|
return 1;
|
|
}
|
|
*dummy_clk = rxdummy;
|
|
return 0;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_enter_command_mode(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
if (cfg->serial_cfg.io_mode == data->dev_cfg.io_mode) {
|
|
return 0;
|
|
}
|
|
|
|
ret = mspi_dev_config(cfg->bus, &cfg->dev_id,
|
|
MSPI_DEVICE_CONFIG_ALL, &cfg->serial_cfg);
|
|
if (ret) {
|
|
LOG_ERR("Failed to enter command mode/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_exit_command_mode(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
if (cfg->serial_cfg.io_mode == data->dev_cfg.io_mode) {
|
|
return 0;
|
|
}
|
|
|
|
ret = mspi_dev_config(cfg->bus, &cfg->dev_id,
|
|
MSPI_DEVICE_CONFIG_ALL, &data->dev_cfg);
|
|
if (ret) {
|
|
LOG_ERR("Failed to exit command mode/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_command_write(const struct device *flash, uint8_t cmd,
|
|
uint32_t addr, uint16_t addr_len, uint32_t tx_dummy,
|
|
uint8_t *wdata, uint32_t length)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
data->packet.dir = MSPI_TX;
|
|
data->packet.cmd = cmd;
|
|
data->packet.address = addr;
|
|
data->packet.data_buf = wdata;
|
|
data->packet.num_bytes = length;
|
|
|
|
data->trans.async = false;
|
|
data->trans.xfer_mode = MSPI_PIO;
|
|
data->trans.tx_dummy = tx_dummy;
|
|
data->trans.rx_dummy = data->dev_cfg.rx_dummy;
|
|
data->trans.cmd_length = 1;
|
|
data->trans.addr_length = addr_len;
|
|
data->trans.hold_ce = false;
|
|
data->trans.packets = &data->packet;
|
|
data->trans.num_packet = 1;
|
|
data->trans.timeout = 10;
|
|
|
|
ret = mspi_transceive(cfg->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->trans);
|
|
if (ret) {
|
|
LOG_ERR("MSPI write transaction failed with code: %d/%u", ret, __LINE__);
|
|
return -EIO;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_command_read(const struct device *flash, uint8_t cmd,
|
|
uint32_t addr, uint16_t addr_len, uint32_t rx_dummy,
|
|
uint8_t *rdata, uint32_t length)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
data->packet.dir = MSPI_RX;
|
|
data->packet.cmd = cmd;
|
|
data->packet.address = addr;
|
|
data->packet.data_buf = rdata;
|
|
data->packet.num_bytes = length;
|
|
|
|
data->trans.async = false;
|
|
data->trans.xfer_mode = MSPI_PIO;
|
|
data->trans.rx_dummy = rx_dummy;
|
|
data->trans.tx_dummy = data->dev_cfg.tx_dummy;
|
|
data->trans.cmd_length = 1;
|
|
data->trans.addr_length = addr_len;
|
|
data->trans.hold_ce = false;
|
|
data->trans.packets = &data->packet;
|
|
data->trans.num_packet = 1;
|
|
data->trans.timeout = 10;
|
|
|
|
ret = mspi_transceive(cfg->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->trans);
|
|
if (ret) {
|
|
LOG_ERR("MSPI read transaction failed with code: %d/%u", ret, __LINE__);
|
|
return -EIO;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void acquire(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
|
|
k_sem_take(&data->lock, K_FOREVER);
|
|
|
|
if (cfg->sw_multi_periph) {
|
|
while (mspi_dev_config(cfg->bus, &cfg->dev_id,
|
|
MSPI_DEVICE_CONFIG_ALL, &data->dev_cfg)) {
|
|
;
|
|
}
|
|
} else {
|
|
while (mspi_dev_config(cfg->bus, &cfg->dev_id,
|
|
MSPI_DEVICE_CONFIG_NONE, NULL)) {
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void release(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
|
|
while (mspi_get_channel_status(cfg->bus, cfg->port)) {
|
|
;
|
|
}
|
|
|
|
k_sem_give(&data->lock);
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_write_enable(const struct device *flash)
|
|
{
|
|
int ret;
|
|
|
|
LOG_DBG("Enabling write");
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_WREN, 0, 0, 0, NULL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_write_disable(const struct device *flash)
|
|
{
|
|
int ret;
|
|
|
|
LOG_DBG("Disabling write");
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_WRDI, 0, 0, 0, NULL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_is_ready(const struct device *flash)
|
|
{
|
|
uint32_t flag_stat = 0;
|
|
uint32_t rx_dummy = 0;
|
|
uint32_t timeout = 400; /* max tSSE */
|
|
int ret;
|
|
|
|
do {
|
|
LOG_DBG("Reading flag status register");
|
|
ret = flash_mspi_is25xX0xx_command_read(flash, 0x70, 0, 0, rx_dummy,
|
|
(uint8_t *)&flag_stat, 1);
|
|
if (ret) {
|
|
LOG_ERR("Could not read flag status");
|
|
return ret;
|
|
}
|
|
|
|
LOG_DBG("flag status: 0x%x", flag_stat);
|
|
if (flag_stat & BIT(7)) {
|
|
LOG_DBG("Device is ready");
|
|
break;
|
|
}
|
|
|
|
k_sleep(K_MSEC(1));
|
|
timeout--;
|
|
} while (timeout);
|
|
|
|
if (timeout == 0) {
|
|
LOG_ERR("Operation timed out");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_reset(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
int ret;
|
|
|
|
LOG_DBG("RESETTING");
|
|
|
|
if (cfg->reset_gpio.port) {
|
|
if (!gpio_is_ready_dt(&cfg->reset_gpio)) {
|
|
LOG_ERR("Device %s is not ready",
|
|
cfg->reset_gpio.port->name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = gpio_pin_configure_dt(&cfg->reset_gpio,
|
|
GPIO_OUTPUT_ACTIVE);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to activate RESET: %d", ret);
|
|
return -EIO;
|
|
}
|
|
|
|
if (cfg->reset_pulse_us != 0) {
|
|
k_busy_wait(cfg->reset_pulse_us);
|
|
}
|
|
|
|
ret = gpio_pin_set_dt(&cfg->reset_gpio, 0);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to deactivate RESET: %d", ret);
|
|
return -EIO;
|
|
}
|
|
|
|
if (cfg->reset_recovery_us != 0) {
|
|
k_busy_wait(cfg->reset_recovery_us);
|
|
}
|
|
} else {
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_RESET_EN,
|
|
0, 0, 0, NULL, 0);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_RESET_MEM,
|
|
0, 0, 0, NULL, 0);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_is_ready(flash);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_get_vendor_id(const struct device *flash, uint8_t *vendor_id)
|
|
{
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
if (vendor_id == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("Reading id");
|
|
/* serial mode */
|
|
ret = flash_mspi_is25xX0xx_command_read(flash, SPI_NOR_CMD_RDID, 0, 0, 0,
|
|
(uint8_t *)data->id, 20);
|
|
*vendor_id = data->id[0];
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_erase_sector(const struct device *flash, off_t addr)
|
|
{
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
LOG_DBG("Erasing sector at 0x%08zx", (ssize_t)addr);
|
|
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_SE, addr,
|
|
data->dev_cfg.addr_length, 0, NULL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_erase_32k_sector(const struct device *flash, off_t addr)
|
|
{
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
LOG_DBG("Erasing sector at 0x%08zx", (ssize_t)addr);
|
|
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_BE_32K, addr,
|
|
data->dev_cfg.addr_length, 0, NULL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_erase_block(const struct device *flash, off_t addr)
|
|
{
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
LOG_DBG("Erasing block at 0x%08zx", (ssize_t)addr);
|
|
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_BE, addr,
|
|
data->dev_cfg.addr_length, 0, NULL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_erase_chip(const struct device *flash)
|
|
{
|
|
int ret;
|
|
|
|
LOG_DBG("Erasing chip");
|
|
|
|
ret = flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_CE, 0, 0, 0, NULL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_page_program(const struct device *flash, off_t offset,
|
|
void *wdata, size_t len)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
data->packet.dir = MSPI_TX;
|
|
data->packet.cmd = data->dev_cfg.write_cmd;
|
|
data->packet.address = offset;
|
|
data->packet.data_buf = wdata;
|
|
data->packet.num_bytes = len;
|
|
|
|
data->trans.async = false;
|
|
data->trans.xfer_mode = MSPI_DMA;
|
|
data->trans.tx_dummy = data->dev_cfg.tx_dummy;
|
|
data->trans.rx_dummy = data->dev_cfg.rx_dummy;
|
|
data->trans.cmd_length = data->dev_cfg.cmd_length;
|
|
data->trans.addr_length = data->dev_cfg.addr_length;
|
|
data->trans.hold_ce = false;
|
|
data->trans.priority = MSPI_XFER_PRIORITY_MEDIUM;
|
|
data->trans.packets = &data->packet;
|
|
data->trans.num_packet = 1;
|
|
data->trans.timeout = CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE;
|
|
|
|
LOG_DBG("Page programming %d bytes to 0x%08zx", len, (ssize_t)offset);
|
|
|
|
ret = mspi_transceive(cfg->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->trans);
|
|
if (ret) {
|
|
LOG_ERR("MSPI write transaction failed with code: %d/%u", ret, __LINE__);
|
|
return -EIO;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_busy_wait(const struct device *flash, unsigned int timeout)
|
|
{
|
|
uint32_t status = 0;
|
|
uint32_t flag_stat = 0;
|
|
uint32_t rx_dummy = 0;
|
|
int ret;
|
|
|
|
do {
|
|
LOG_DBG("Reading status register");
|
|
ret = flash_mspi_is25xX0xx_command_read(flash, SPI_NOR_CMD_RDSR, 0, 0, rx_dummy,
|
|
(uint8_t *)&status, 1);
|
|
if (ret) {
|
|
LOG_ERR("Could not read status");
|
|
return ret;
|
|
}
|
|
ret = flash_mspi_is25xX0xx_command_read(flash, 0x70, 0, 0, rx_dummy,
|
|
(uint8_t *)&flag_stat, 1);
|
|
if (ret) {
|
|
LOG_ERR("Could not read flag status");
|
|
return ret;
|
|
}
|
|
LOG_DBG("status: 0x%x, flag status: 0x%x", status, flag_stat);
|
|
|
|
if (flag_stat & BIT(1)) {
|
|
LOG_ERR("Access denied");
|
|
return -EACCES;
|
|
} else if (flag_stat & BIT(4)) {
|
|
LOG_ERR("Program operation failed");
|
|
return -EIO;
|
|
} else if (flag_stat & BIT(5)) {
|
|
LOG_ERR("Erase operation failed");
|
|
return -EIO;
|
|
}
|
|
|
|
k_sleep(K_MSEC(1));
|
|
timeout--;
|
|
} while ((status & SPI_NOR_WIP_BIT) && timeout);
|
|
|
|
if (timeout == 0) {
|
|
LOG_ERR("Operation timed out");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_read(const struct device *flash, off_t offset, void *rdata,
|
|
size_t len)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
acquire(flash);
|
|
|
|
#if CONFIG_FLASH_MSPI_XIP_READ
|
|
if (cfg->tar_xip_cfg.enable) {
|
|
uint32_t xip_addr = cfg->xip_base_addr + cfg->tar_xip_cfg.address_offset + offset;
|
|
|
|
memcpy(rdata, (void *)xip_addr, len);
|
|
} else {
|
|
#endif /* CONFIG_FLASH_MSPI_XIP_READ */
|
|
|
|
#if CONFIG_FLASH_MSPI_HANDLE_CACHE
|
|
if (!buf_in_nocache((uintptr_t)rdata, len)) {
|
|
if (len > CONFIG_FLASH_MSPI_RANGE_HANDLE_CACHE_SIZE) {
|
|
sys_cache_data_flush_all();
|
|
} else {
|
|
sys_cache_data_flush_range(rdata, len);
|
|
}
|
|
}
|
|
#endif /* CONFIG_FLASH_MSPI_HANDLE_CACHE */
|
|
|
|
data->packet.dir = MSPI_RX;
|
|
data->packet.cmd = data->dev_cfg.read_cmd;
|
|
data->packet.address = offset;
|
|
data->packet.data_buf = rdata;
|
|
data->packet.num_bytes = len;
|
|
|
|
data->trans.async = false;
|
|
data->trans.xfer_mode = MSPI_DMA;
|
|
data->trans.tx_dummy = data->dev_cfg.tx_dummy;
|
|
data->trans.rx_dummy = data->dev_cfg.rx_dummy;
|
|
data->trans.cmd_length = data->dev_cfg.cmd_length;
|
|
data->trans.addr_length = data->dev_cfg.addr_length;
|
|
data->trans.hold_ce = false;
|
|
data->trans.priority = MSPI_XFER_PRIORITY_MEDIUM;
|
|
data->trans.packets = &data->packet;
|
|
data->trans.num_packet = 1;
|
|
data->trans.timeout = CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE;
|
|
|
|
LOG_DBG("Read %d bytes from 0x%08zx", len, (ssize_t)offset);
|
|
|
|
ret = mspi_transceive(cfg->bus, &cfg->dev_id,
|
|
(const struct mspi_xfer *)&data->trans);
|
|
if (ret) {
|
|
LOG_ERR("MSPI read transaction failed with code: %d/%u", ret, __LINE__);
|
|
return -EIO;
|
|
}
|
|
|
|
#if CONFIG_FLASH_MSPI_HANDLE_CACHE
|
|
if (!buf_in_nocache((uintptr_t)rdata, len)) {
|
|
if (len > CONFIG_FLASH_MSPI_RANGE_HANDLE_CACHE_SIZE) {
|
|
sys_cache_data_flush_and_invd_all();
|
|
} else {
|
|
sys_cache_data_invd_range(rdata, len);
|
|
}
|
|
}
|
|
#endif /* CONFIG_FLASH_MSPI_HANDLE_CACHE */
|
|
#if CONFIG_FLASH_MSPI_XIP_READ
|
|
}
|
|
#endif /* CONFIG_FLASH_MSPI_XIP_READ */
|
|
|
|
release(flash);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_write(const struct device *flash, off_t offset, const void *wdata,
|
|
size_t len)
|
|
{
|
|
int ret;
|
|
uint8_t *src = (uint8_t *)wdata;
|
|
int i;
|
|
#if CONFIG_FLASH_MSPI_HANDLE_CACHE && CONFIG_FLASH_MSPI_XIP_READ
|
|
off_t addr = offset;
|
|
size_t size = len;
|
|
#endif
|
|
|
|
acquire(flash);
|
|
|
|
#if CONFIG_FLASH_MSPI_HANDLE_CACHE
|
|
if (!buf_in_nocache((uintptr_t)src, len)) {
|
|
if (len > CONFIG_FLASH_MSPI_RANGE_HANDLE_CACHE_SIZE) {
|
|
sys_cache_data_flush_all();
|
|
} else {
|
|
sys_cache_data_flush_range(src, len);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while (len) {
|
|
/* If the offset isn't a multiple of the NOR page size, we first need
|
|
* to write the remaining part that fits, otherwise the write could
|
|
* be wrapped around within the same page
|
|
*/
|
|
i = MIN(SPI_NOR_PAGE_SIZE - (offset % SPI_NOR_PAGE_SIZE), len);
|
|
|
|
ret = flash_mspi_is25xX0xx_enter_command_mode(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_write_enable(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_exit_command_mode(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_page_program(flash, offset, src, i);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_enter_command_mode(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_busy_wait(flash, 3);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_exit_command_mode(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
src += i;
|
|
offset += i;
|
|
len -= i;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_write_disable(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
#if CONFIG_FLASH_MSPI_HANDLE_CACHE && CONFIG_FLASH_MSPI_XIP_READ
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
|
|
if (cfg->tar_xip_cfg.enable) {
|
|
uint32_t xip_addr = cfg->xip_base_addr + cfg->tar_xip_cfg.address_offset + addr;
|
|
|
|
if (!buf_in_nocache((uintptr_t)xip_addr, size)) {
|
|
if (size > CONFIG_FLASH_MSPI_RANGE_HANDLE_CACHE_SIZE) {
|
|
sys_cache_data_flush_and_invd_all();
|
|
} else {
|
|
sys_cache_data_invd_range((void *)xip_addr, size);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
release(flash);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_mspi_is25xX0xx_erase(const struct device *flash, off_t offset, size_t size)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
int ret = 0;
|
|
const size_t num_sectors = size / SPI_NOR_SECTOR_SIZE;
|
|
const size_t num_32k_sectors = size / IS25XX0XX_32KSECTOR_SIZE;
|
|
const size_t num_blocks = size / IS25XX0XX_BLOCK_SIZE;
|
|
int i;
|
|
|
|
acquire(flash);
|
|
|
|
if (offset % SPI_NOR_SECTOR_SIZE) {
|
|
LOG_ERR("Invalid offset");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (size % SPI_NOR_SECTOR_SIZE) {
|
|
LOG_ERR("Invalid size");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_enter_command_mode(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if ((offset == 0) && (size == cfg->mem_size)) {
|
|
ret = flash_mspi_is25xX0xx_write_enable(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_erase_chip(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_busy_wait(flash, 45000);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
} else if ((0 == (offset % IS25XX0XX_BLOCK_SIZE)) &&
|
|
(0 == (size % IS25XX0XX_BLOCK_SIZE))) {
|
|
for (i = 0; i < num_blocks; i++) {
|
|
ret = flash_mspi_is25xX0xx_write_enable(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_erase_block(flash, offset);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_busy_wait(flash, 1000);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
offset += IS25XX0XX_BLOCK_SIZE;
|
|
}
|
|
} else if ((0 == (offset % IS25XX0XX_32KSECTOR_SIZE)) &&
|
|
(0 == (size % IS25XX0XX_32KSECTOR_SIZE))) {
|
|
for (i = 0; i < num_32k_sectors; i++) {
|
|
ret = flash_mspi_is25xX0xx_write_enable(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_erase_32k_sector(flash, offset);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_busy_wait(flash, 1000);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
offset += IS25XX0XX_32KSECTOR_SIZE;
|
|
}
|
|
} else {
|
|
for (i = 0; i < num_sectors; i++) {
|
|
ret = flash_mspi_is25xX0xx_write_enable(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_erase_sector(flash, offset);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_busy_wait(flash, 400);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
offset += SPI_NOR_SECTOR_SIZE;
|
|
}
|
|
}
|
|
|
|
ret = flash_mspi_is25xX0xx_exit_command_mode(flash);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
release(flash);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct flash_parameters *
|
|
flash_mspi_is25xX0xx_get_parameters(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
|
|
return &cfg->flash_param;
|
|
}
|
|
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
static void flash_mspi_is25xX0xx_pages_layout(const struct device *flash,
|
|
const struct flash_pages_layout **layout,
|
|
size_t *layout_size)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
|
|
*layout = &cfg->page_layout;
|
|
*layout_size = 1;
|
|
}
|
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
|
|
|
static int flash_mspi_is25xX0xx_init(const struct device *flash)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
uint8_t vendor_id;
|
|
uint8_t reg_dummy;
|
|
uint8_t reg_io_mode;
|
|
|
|
if (!device_is_ready(cfg->bus)) {
|
|
LOG_ERR("Controller device is not ready.");
|
|
return -ENODEV;
|
|
}
|
|
|
|
switch (cfg->tar_dev_cfg.io_mode) {
|
|
case MSPI_IO_MODE_SINGLE:
|
|
case MSPI_IO_MODE_OCTAL_1_1_8:
|
|
case MSPI_IO_MODE_OCTAL_1_8_8:
|
|
reg_io_mode = IS25XX0XX_IO_MODE_EXTENDED_SPI;
|
|
break;
|
|
default:
|
|
LOG_ERR("bus mode %d not supported/%u", cfg->tar_dev_cfg.io_mode, __LINE__);
|
|
return -EIO;
|
|
}
|
|
|
|
if (mspi_dev_config(cfg->bus, &cfg->dev_id, MSPI_DEVICE_CONFIG_ALL, &cfg->serial_cfg)) {
|
|
LOG_ERR("Failed to config mspi controller/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
data->dev_cfg = cfg->serial_cfg;
|
|
|
|
if (flash_mspi_is25xX0xx_reset(flash)) {
|
|
LOG_ERR("Could not reset Flash/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
|
|
if (flash_mspi_is25xX0xx_get_vendor_id(flash, &vendor_id)) {
|
|
LOG_ERR("Could not read vendor id/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
LOG_DBG("Vendor id: 0x%0x", vendor_id);
|
|
if (vendor_id != IS25XX0XX_VENDOR_ID) {
|
|
LOG_WRN("Vendor ID does not match expected value of 0x%0x/%u",
|
|
IS25XX0XX_VENDOR_ID, __LINE__);
|
|
}
|
|
|
|
if (is25xX0xx_get_dummy_clk(cfg->tar_dev_cfg.rx_dummy, ®_dummy)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (flash_mspi_is25xX0xx_write_enable(flash)) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (flash_mspi_is25xX0xx_command_write(flash, IS25XX0XX_WRITE_VOL_REG_CMD,
|
|
0x1, 1, 0, ®_dummy, 1)) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (!cfg->tar_dev_cfg.dqs_enable) {
|
|
reg_io_mode = IS25XX0XX_IO_MODE_EXTENDED_SPI_NONDQS;
|
|
if (flash_mspi_is25xX0xx_write_enable(flash)) {
|
|
return -EIO;
|
|
}
|
|
if (flash_mspi_is25xX0xx_command_write(flash, IS25XX0XX_WRITE_VOL_REG_CMD,
|
|
0x0, 1, 0, ®_io_mode, 1)) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if (cfg->tar_dev_cfg.addr_length == 4) {
|
|
LOG_DBG("Enter 4 byte address mode");
|
|
if (flash_mspi_is25xX0xx_write_enable(flash)) {
|
|
return -EIO;
|
|
}
|
|
if (flash_mspi_is25xX0xx_command_write(flash, SPI_NOR_CMD_4BA,
|
|
0, 0, 0, NULL, 0)) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if (mspi_dev_config(cfg->bus, &cfg->dev_id,
|
|
MSPI_DEVICE_CONFIG_ALL, &cfg->tar_dev_cfg)) {
|
|
LOG_ERR("Failed to config mspi controller/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
data->dev_cfg = cfg->tar_dev_cfg;
|
|
|
|
#if CONFIG_MSPI_TIMING
|
|
if (mspi_timing_config(cfg->bus, &cfg->dev_id, cfg->timing_cfg_mask,
|
|
(void *)&cfg->tar_timing_cfg)) {
|
|
LOG_ERR("Failed to config mspi timing/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
data->timing_cfg = cfg->tar_timing_cfg;
|
|
#endif
|
|
|
|
#if CONFIG_MSPI_XIP
|
|
if (cfg->tar_xip_cfg.enable) {
|
|
if (mspi_xip_config(cfg->bus, &cfg->dev_id, &cfg->tar_xip_cfg)) {
|
|
LOG_ERR("Failed to enable XIP/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
data->xip_cfg = cfg->tar_xip_cfg;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_MSPI_SCRAMBLE
|
|
if (cfg->tar_scramble_cfg.enable) {
|
|
if (mspi_scramble_config(cfg->bus, &cfg->dev_id, &cfg->tar_scramble_cfg)) {
|
|
LOG_ERR("Failed to enable scrambling/%u", __LINE__);
|
|
return -EIO;
|
|
}
|
|
data->scramble_cfg = cfg->tar_scramble_cfg;
|
|
}
|
|
#endif
|
|
|
|
release(flash);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_FLASH_JESD216_API)
|
|
static int flash_mspi_is25xX0xx_read_sfdp(const struct device *flash, off_t addr, void *rdata,
|
|
size_t size)
|
|
{
|
|
const struct flash_mspi_is25xX0xx_config *cfg = flash->config;
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
int ret;
|
|
|
|
acquire(flash);
|
|
|
|
data->packet.dir = MSPI_RX;
|
|
data->packet.cmd = 0x5A;
|
|
data->packet.address = addr;
|
|
data->packet.data_buf = rdata;
|
|
data->packet.num_bytes = size;
|
|
|
|
data->trans.async = false;
|
|
data->trans.xfer_mode = MSPI_DMA;
|
|
data->trans.rx_dummy = 8;
|
|
data->trans.cmd_length = 1;
|
|
data->trans.addr_length = 3;
|
|
data->trans.hold_ce = false;
|
|
data->trans.priority = MSPI_XFER_PRIORITY_MEDIUM;
|
|
data->trans.packets = &data->packet;
|
|
data->trans.num_packet = 1;
|
|
data->trans.timeout = CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE;
|
|
|
|
LOG_DBG("Read %d bytes from 0x%08zx", size, (ssize_t)addr);
|
|
|
|
ret = mspi_transceive(cfg->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->trans);
|
|
|
|
if (ret) {
|
|
LOG_ERR("MSPI read transaction failed with code: %d/%u", ret, __LINE__);
|
|
return -EIO;
|
|
}
|
|
|
|
release(flash);
|
|
return 0;
|
|
}
|
|
static int flash_mspi_is25xX0xx_read_jedec_id(const struct device *flash, uint8_t *id)
|
|
{
|
|
struct flash_mspi_is25xX0xx_data *data = flash->data;
|
|
|
|
id = &data->id;
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_FLASH_JESD216_API */
|
|
|
|
static DEVICE_API(flash, flash_mspi_is25xX0xx_api) = {
|
|
.erase = flash_mspi_is25xX0xx_erase,
|
|
.write = flash_mspi_is25xX0xx_write,
|
|
.read = flash_mspi_is25xX0xx_read,
|
|
.get_parameters = flash_mspi_is25xX0xx_get_parameters,
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
.page_layout = flash_mspi_is25xX0xx_pages_layout,
|
|
#endif
|
|
#if defined(CONFIG_FLASH_JESD216_API)
|
|
.sfdp_read = flash_mspi_is25xX0xx_read_sfdp,
|
|
.read_jedec_id = flash_mspi_is25xX0xx_read_jedec_id,
|
|
#endif
|
|
};
|
|
|
|
#define MSPI_DEVICE_CONFIG_SERIAL(n) \
|
|
{ \
|
|
.ce_num = DT_INST_PROP(n, mspi_hardware_ce_num), \
|
|
.freq = 12000000, \
|
|
.io_mode = MSPI_IO_MODE_SINGLE, \
|
|
.data_rate = MSPI_DATA_RATE_SINGLE, \
|
|
.cpp = MSPI_CPP_MODE_0, \
|
|
.endian = MSPI_XFER_LITTLE_ENDIAN, \
|
|
.ce_polarity = MSPI_CE_ACTIVE_LOW, \
|
|
.dqs_enable = false, \
|
|
.rx_dummy = 8, \
|
|
.tx_dummy = 0, \
|
|
.read_cmd = SPI_NOR_CMD_READ_FAST, \
|
|
.write_cmd = SPI_NOR_CMD_PP, \
|
|
.cmd_length = 1, \
|
|
.addr_length = 3, \
|
|
.mem_boundary = 0, \
|
|
.time_to_break = 0, \
|
|
}
|
|
|
|
#define MSPI_TIMING_CONFIG(n) \
|
|
COND_CODE_1(CONFIG_SOC_FAMILY_AMBIQ, \
|
|
(MSPI_AMBIQ_TIMING_CONFIG(n)), ({}))
|
|
|
|
#define MSPI_TIMING_CONFIG_MASK(n) \
|
|
COND_CODE_1(CONFIG_SOC_FAMILY_AMBIQ, \
|
|
(MSPI_AMBIQ_TIMING_CONFIG_MASK(n)), (MSPI_TIMING_PARAM_DUMMY))
|
|
|
|
#define MSPI_PORT(n) \
|
|
COND_CODE_1(CONFIG_SOC_FAMILY_AMBIQ, \
|
|
(MSPI_AMBIQ_PORT(n)), (0))
|
|
|
|
#define FLASH_MSPI_IS25XX0XX(n) \
|
|
static const struct flash_mspi_is25xX0xx_config flash_mspi_is25xX0xx_config_##n = { \
|
|
.mem_size = DT_INST_PROP(n, size) / 8, \
|
|
.port = MSPI_PORT(n), \
|
|
.flash_param = \
|
|
{ \
|
|
.write_block_size = NOR_WRITE_SIZE, \
|
|
.erase_value = NOR_ERASE_VALUE, \
|
|
}, \
|
|
.page_layout = \
|
|
{ \
|
|
.pages_count = DT_INST_PROP(n, size) / 8 / SPI_NOR_PAGE_SIZE, \
|
|
.pages_size = SPI_NOR_PAGE_SIZE, \
|
|
}, \
|
|
.bus = DEVICE_DT_GET(DT_INST_BUS(n)), \
|
|
.dev_id = MSPI_DEVICE_ID_DT_INST(n), \
|
|
.serial_cfg = MSPI_DEVICE_CONFIG_SERIAL(n), \
|
|
.tar_dev_cfg = MSPI_DEVICE_CONFIG_DT_INST(n), \
|
|
MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_XIP, \
|
|
tar_xip_cfg, MSPI_XIP_CONFIG_DT_INST(n)) \
|
|
MSPI_XIP_BASE_ADDR_INIT(xip_base_addr, DT_INST_BUS(n)) \
|
|
MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_SCRAMBLE, \
|
|
tar_scramble_cfg, MSPI_SCRAMBLE_CONFIG_DT_INST(n)) \
|
|
MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_TIMING, \
|
|
tar_timing_cfg, MSPI_TIMING_CONFIG(n)) \
|
|
MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_TIMING, \
|
|
timing_cfg_mask, MSPI_TIMING_CONFIG_MASK(n)) \
|
|
.sw_multi_periph = DT_PROP(DT_INST_BUS(n), software_multiperipheral), \
|
|
.reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \
|
|
.reset_pulse_us = DT_INST_PROP_OR(n, t_reset_pulse, 0), \
|
|
.reset_recovery_us = DT_INST_PROP_OR(n, t_reset_recovery, 0), \
|
|
}; \
|
|
static struct flash_mspi_is25xX0xx_data flash_mspi_is25xX0xx_data_##n = { \
|
|
.lock = Z_SEM_INITIALIZER(flash_mspi_is25xX0xx_data_##n.lock, 0, 1), \
|
|
}; \
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
flash_mspi_is25xX0xx_init, \
|
|
NULL, \
|
|
&flash_mspi_is25xX0xx_data_##n, \
|
|
&flash_mspi_is25xX0xx_config_##n, \
|
|
POST_KERNEL, \
|
|
CONFIG_FLASH_INIT_PRIORITY, \
|
|
&flash_mspi_is25xX0xx_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(FLASH_MSPI_IS25XX0XX)
|