Files
E3/e3_176_ref/middleware/flash/fls.c
2025-10-21 19:40:27 +08:00

461 lines
14 KiB
C

/**
* @file fls.c
*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*
* Description:
*
* Revision History:
* -----------------
*/
#include <armv7-r/barriers.h>
#include <armv7-r/cache.h>
#include <clock_cfg.h>
#include <clock_ip.h>
#include <debug.h>
#include <irq.h>
#include <irq_num.h>
#include <param.h>
#include <pinmux_cfg.h>
#include <regs_base.h>
#include <reset_cfg.h>
#include <scr_hw.h>
#include <sdrv_ckgen.h>
#include <sdrv_rstgen.h>
#include <sdrv_scr.h>
#include <sdrv_spi_nor.h>
#include <sdrv_xspi.h>
#include <stdlib.h>
#include <string.h>
#include <types.h>
#include <udelay/udelay.h>
#include <reset_ip.h>
#if CONFIG_XSPI_ENABLE_DMA
#include "sdrv_dma.h"
#endif
#include "fls.h"
extern sdrv_scr_t g_scr_ctrl;
#define XSPI_SWITCH_DEVICE_MAX_NUM (2u)
typedef struct {
Fls_Controller_ID_t id;
struct spi_nor_host spi_nor_host;
struct xspi_pdata xspi;
struct spi_nor flash[XSPI_SWITCH_DEVICE_MAX_NUM];
uint8_t flash_num;
#if CONFIG_XSPI_ENABLE_DMA
sdrv_dma_t g_dma_instance;
sdrv_dma_channel_t g_channel;
volatile uint8_t dma_xspi_done_flag;
uint8_t dma_xspi_init_dmac_flag;
#endif
bool is_locksetp_mode;
bool is_parallel_mode;
bool is_xip_mode;
uint8_t flag;
} Fls_Context_t;
#if defined(APB_XSPI2PORTA_BASE) && defined(APB_XSPI2PORTB_BASE)
#define XSPI_RESOURE_NUM (2u)
#else
#define XSPI_RESOURE_NUM (1u)
#endif
#define XSPI_PORT_MAX_NUM (XSPI_RESOURE_NUM * 2)
static Fls_Context_t fls_context[XSPI_PORT_MAX_NUM];
static sdrv_ckgen_node_t *clk_node_tab[FLS_CONTROLLER_ID_MAX] = {
[FLS_XSPI1_PORTA] = CLK_NODE(g_ckgen_ip_xspi1a),
[FLS_XSPI1_PORTB] = CLK_NODE(g_ckgen_ip_xspi1b),
#if (XSPI_RESOURE_NUM >= 2)
[FLS_XSPI2_PORTA] = CLK_NODE(g_ckgen_ip_xspi2a),
[FLS_XSPI2_PORTB] = CLK_NODE(g_ckgen_ip_xspi2b),
#endif
};
static struct xspi_config host_config[FLS_CONTROLLER_ID_MAX] = {
[FLS_XSPI1_PORTA] = {
.id = FLS_XSPI1_PORTA,
.irq = XSPI1_IRQ0_INTR_NUM,
.apb_base = APB_XSPI1PORTA_BASE,
.direct_base = XSPI1_XSPI1PORTA_BASE,
},
[FLS_XSPI1_PORTB] = {
.id = FLS_XSPI1_PORTB,
.irq = XSPI1_IRQ1_INTR_NUM,
.apb_base = APB_XSPI1PORTB_BASE,
.direct_base = XSPI1_XSPI1PORTB_BASE,
},
#if (XSPI_RESOURE_NUM >= 2)
[FLS_XSPI2_PORTA] = {
.id = FLS_XSPI2_PORTA,
.irq = XSPI2_IRQ0_INTR_NUM,
.apb_base = APB_XSPI2PORTA_BASE,
.direct_base = XSPI2_XSPI2PORTA_BASE,
},
[FLS_XSPI2_PORTB] = {
.id = FLS_XSPI2_PORTB,
.irq = XSPI2_IRQ1_INTR_NUM,
.apb_base = APB_XSPI2PORTB_BASE,
.direct_base = XSPI2_XSPI2PORTB_BASE,
},
#endif
};
#ifndef SCR_SF_XSPI1_SRC_CFG_LOCKSTEP_MODE_N
#define SCR_SF_XSPI1_SRC_CFG_LOCKSTEP_MODE_N SCR_SF_XSPI1_LOCKSTEP_DISABLE
#endif
static void sdrv_xspi_lockstep_enable(bool flag, uint8_t id)
{
scr_signal_t signal;
sdrv_rstgen_sig_t *xspi_porta, *xspi_portb;
switch (id) {
case FLS_XSPI1_PORTA:
signal = (scr_signal_t)SCR_SF_XSPI1_SRC_CFG_LOCKSTEP_MODE_N;
xspi_porta = &rstsig_xspi1a;
xspi_portb = &rstsig_xspi1b;
break;
#if (XSPI_RESOURE_NUM >= 2)
case FLS_XSPI2_PORTA:
signal = (scr_signal_t)SCR_SF_XSPI2_SRC_CFG_LOCKSTEP_MODE_N;
xspi_porta = &rstsig_xspi2a;
xspi_portb = &rstsig_xspi2b;
break;
#endif
default:
ssdk_printf(SSDK_ERR, "xspi %d unsupport lockstep mode", id);
return;
}
sdrv_rstgen_assert(xspi_portb);
sdrv_rstgen_assert(xspi_porta);
DSB;
udelay(10);
scr_set(&g_scr_ctrl, &signal, !flag);
udelay(10);
DSB;
sdrv_rstgen_deassert(xspi_porta);
sdrv_rstgen_deassert(xspi_portb);
}
static void sdrv_xspi_parallel_enable(bool flag, uint8_t id)
{
scr_signal_t signal;
sdrv_rstgen_sig_t *xspi_porta, * xspi_portb;
switch (id) {
case FLS_XSPI1_PORTA:
signal = (scr_signal_t)SCR_SF_XSPI1_SRC_CFG_PARALLEL_MODE;
xspi_porta = &rstsig_xspi1a;
xspi_portb = &rstsig_xspi1b;
break;
#if (XSPI_RESOURE_NUM >= 2)
case FLS_XSPI2_PORTA:
signal = (scr_signal_t)SCR_SF_XSPI2_SRC_CFG_PARALLEL_MODE;
xspi_porta = &rstsig_xspi2a;
xspi_portb = &rstsig_xspi2b;
break;
#endif
default:
ssdk_printf(SSDK_ERR, "xspi %d unsupport parallel mode", id);
return;
}
sdrv_rstgen_assert(xspi_portb);
sdrv_rstgen_assert(xspi_porta);
DSB;
udelay(10);
scr_set(&g_scr_ctrl, &signal, flag);
udelay(10);
DSB;
sdrv_rstgen_deassert(xspi_porta);
sdrv_rstgen_deassert(xspi_portb);
}
#if CONFIG_XSPI_ENABLE_DMA
static void sdrv_dma_transfer_every_mad_done(uint32_t status, uint32_t param,
void *context) {
Fls_Context_t *pCtx = context;
ssdk_printf(SSDK_INFO, " xspi set dma transfer done\r\n");
if (status == SDRV_DMA_COMPLETED) {
pCtx->dma_xspi_done_flag = 1;
}
}
static void sdrv_xspi_dma_config(struct spi_nor *nor, flash_addr_t addr, uint8_t *buf,
uint32_t len, bool is_read_flag, uint32_t burst_len) {
uint32_t burst_width;
struct xspi_pdata *xspi = nor->host->priv_data;
sdrv_dma_channel_config_t xspi_dma_cfg;
Fls_Context_t *pCtx = &fls_context[nor->id];
burst_width = SDRV_DMA_BUSWIDTH_4_BYTES;
ssdk_printf(SSDK_INFO, "burst lens is %d\n", burst_len);
if (pCtx->dma_xspi_init_dmac_flag == 0) {
ssdk_printf(SSDK_INFO, "dma init controller\r\n");
/* 1.init dma controller (only one time) */
sdrv_dma_init_dmac(APB_DMA_SF0_BASE);
/* 2.create dma instance */
sdrv_dma_create_instance(&pCtx->g_dma_instance, APB_DMA_SF0_BASE);
pCtx->dma_xspi_init_dmac_flag = 1;
}
/* 3.get default config */
sdrv_dma_init_channel_config(&xspi_dma_cfg, &pCtx->g_dma_instance);
/* 4.modify config param */
xspi_dma_cfg.channel_id = SDRV_DMA_CHANNEL_1; /* select channel */
xspi_dma_cfg.xfer_mode =
SDRV_DMA_TRANSFER_MODE_SINGLE; /* single or continuous or linklist */
xspi_dma_cfg.buffer_mode = SDRV_DMA_SINGLE_BUFFER; /* buffer mode */
xspi_dma_cfg.loop_mode =
SDRV_DMA_LOOP_MODE_2; /* mem2mem(MODE_0) mem2dev/dev2mem(MODE_1,MODE_2) */
xspi_dma_cfg.interrupt_type = SDRV_DMA_LAST_MAD_DONE; /* set interrupt type */
xspi_dma_cfg.trig_mode = SDRV_DMA_TRIGGER_BY_HARDWARE; /* by hardware trig */
if (is_read_flag) {
ssdk_printf(SSDK_INFO, "dev2mem\r\n");
xspi_dma_cfg.xfer_type = SDRV_DMA_DIR_DEV2MEM;
xspi_dma_cfg.src_addr = (addr_t)xspi->apb_base + XSPI_INDIRECT_RDATA;
xspi_dma_cfg.dst_addr = (addr_t)buf;
xspi_dma_cfg.src_inc =
SDRV_DMA_ADDR_NO_INC; /* source address increase or not */
xspi_dma_cfg.dst_inc =
SDRV_DMA_ADDR_INC; /* destination address increase or not */
xspi_dma_cfg.src_port_sel =
SDRV_DMA_PORT_AHB32; /* periph -> SDRV_DMA_PORT_AHB32 */
}
else {
ssdk_printf(SSDK_INFO, "mem2dev\r\n");
xspi_dma_cfg.xfer_type = SDRV_DMA_DIR_MEM2DEV;
xspi_dma_cfg.dst_addr = (addr_t)xspi->apb_base + XSPI_INDIRECT_WDATA;
xspi_dma_cfg.src_addr = (addr_t)buf;
xspi_dma_cfg.src_inc = SDRV_DMA_ADDR_INC; /* source address increase or not */
xspi_dma_cfg.dst_inc =
SDRV_DMA_ADDR_NO_INC; /* destination address increase or not */
xspi_dma_cfg.dst_port_sel =
SDRV_DMA_PORT_AHB32; /* periph -> SDRV_DMA_PORT_AHB32 */
}
xspi_dma_cfg.xfer_bytes = len;
xspi_dma_cfg.src_width = (sdrv_dma_bus_width_e)burst_width;
xspi_dma_cfg.dst_width = (sdrv_dma_bus_width_e)burst_width;
xspi_dma_cfg.src_burst_len = burst_len;
xspi_dma_cfg.dst_burst_len = burst_len;
/* 5.init channel config */
sdrv_dma_init_channel(&pCtx->g_channel, &xspi_dma_cfg);
/* 6.set interrupt callback */
pCtx->g_channel.irq_callback = sdrv_dma_transfer_every_mad_done;
pCtx->g_channel.irq_context = (void *)pCtx;
/* 7.start channel transfer */
pCtx->dma_xspi_done_flag = 0;
sdrv_dma_start_channel_xfer(&pCtx->g_channel);
}
static void sdrv_xspi_dma_stop(struct spi_nor *nor) {
Fls_Context_t *pCtx = &fls_context[nor->id];
sdrv_dma_stop_channel_xfer(&pCtx->g_channel);
return;
}
static void spi_nor_register_dma_handler(struct spi_nor *flash_handle) {
flash_handle->dma_xfer_config = sdrv_xspi_dma_config;
flash_handle->dma_stop = sdrv_xspi_dma_stop;
return;
}
#endif
struct spi_nor_host* sdrv_norflash_init(struct spi_nor_config *config,
uint8_t num, bool xip_en)
{
bool locksetp_mode = false;
bool parallel_mode = false;
Fls_Context_t *pCtx = NULL;
if (config->id >= XSPI_PORT_MAX_NUM) {
ssdk_printf(SSDK_CRIT, "config id(%d) Cross the line(%d)!\r\n",
config->id, XSPI_PORT_MAX_NUM);
return NULL;
}
pCtx = &fls_context[config->id];
/* config dev spi nor dev single mode */
switch (config->dev_mode) {
case SPI_NOR_DEV_LOCKSTEP_MODE:
locksetp_mode = true;
break;
case SPI_NOR_DEV_PARALLEL_MODE:
parallel_mode = true;
break;
default:
break;
}
if (pCtx->flag) { // host has been initialized
if ((locksetp_mode != pCtx->is_locksetp_mode) ||
(parallel_mode != pCtx->is_parallel_mode) ||
(xip_en != pCtx->is_xip_mode)) {
ssdk_printf(SSDK_CRIT, "This controller(%d) has been initialized to (lockstep:%d parallel%d xip:%d) mode!\r\n",
config->id, pCtx->is_locksetp_mode, pCtx->is_parallel_mode, pCtx->is_xip_mode);
return NULL;
} else if ((pCtx->flash_num + num) > XSPI_SWITCH_DEVICE_MAX_NUM) {
ssdk_printf(SSDK_CRIT, "This controller(%d) unsupport %d devices be connected at the same time!\r\n",
config->id, pCtx->flash_num + num);
return NULL;
}
} else {
sdrv_ckgen_node_t *clk = clk_node_tab[config->id];
struct xspi_config host_cfg;
memcpy(&host_cfg, &host_config[config->id], sizeof(struct xspi_config));
if (!xip_en) {
/* close locksetp/parallel mode */
sdrv_xspi_lockstep_enable(locksetp_mode, config->id);
sdrv_xspi_parallel_enable(parallel_mode, config->id);
}
/* initializes xspi host */
host_cfg.ref_clk = sdrv_ckgen_get_rate(clk);
host_cfg.xip_mode = xip_en;
if (config->xfer_mode == SPI_NOR_XFER_POLLING_MODE) {
host_cfg.irq = 0;
}
sdrv_xspi_host_init(&(pCtx->spi_nor_host), &(pCtx->xspi), &host_cfg);
pCtx->spi_nor_host.clk = clk;
sdrv_ckgen_set_rate(clk, config->baudrate);
pCtx->is_locksetp_mode = locksetp_mode;
pCtx->is_parallel_mode = parallel_mode;
pCtx->is_xip_mode = xip_en;
pCtx->flag = 1;
pCtx->id = (Fls_Controller_ID_t)config->id;
ssdk_printf(SSDK_CRIT, "xspi(%d) init (lockstep:%d parallel%d) sucessfully! clock rate is %u!\r\n",
pCtx->id, pCtx->is_locksetp_mode, pCtx->is_parallel_mode, sdrv_ckgen_get_rate(clk));
}
for (uint8_t i = 0; i < num; i++) {
config->cs = pCtx->flash_num;
/* initializes spi nor device */
if (sdrv_spi_nor_init(&(pCtx->flash[pCtx->flash_num]), &(pCtx->spi_nor_host), config)) {
ssdk_printf(SSDK_CRIT, "spinor(id:%d cs:%d) init failed!\r\n", pCtx->id, pCtx->flash_num);
return NULL;
}
#if CONFIG_XSPI_ENABLE_DMA
spi_nor_register_dma_handler(&(pCtx->flash[pCtx->flash_num]));
#endif
ssdk_printf(SSDK_CRIT, "spinor(id:%d cs:%d) size = 0x%llx, sector_size = 0x%x \r\n",
pCtx->id, pCtx->flash_num,
pCtx->flash[pCtx->flash_num].info.size,
pCtx->flash[pCtx->flash_num].info.sector_size);
pCtx->flash_num++;
}
return &pCtx->spi_nor_host;
}
void sdrv_norflash_deinit(struct spi_nor_host* host) {
uint32_t j;
Fls_Context_t *pCtx = &fls_context[host->id];
if (!pCtx->flag) {
return;
}
for (j = 0; j < pCtx->flash_num; j++) {
struct spi_nor *flash = &pCtx->flash[j];
/* soft reset flash */
sdrv_spi_nor_deinit(flash);
/* soft reset xspi host */
sdrv_spi_nor_drv_deinit(flash);
}
memset(pCtx, 0, sizeof(Fls_Context_t));
}
void sdrv_norflash_deinit_all(void)
{
uint32_t i;
for (i = 0; i < FLS_CONTROLLER_ID_MAX; i++) {
sdrv_norflash_deinit(&(fls_context[i].spi_nor_host));
}
return;
}
struct spi_nor* get_flashhandler_by_id(Fls_Controller_ID_t port_id, uint8_t cs_id) {
if (port_id >= FLS_CONTROLLER_ID_MAX) {
ssdk_printf(SSDK_CRIT, "xspi port id %s cross!\r\n", port_id);
return NULL;
} else if (!fls_context[port_id].flag) {
ssdk_printf(SSDK_CRIT, "xspi host %d uninitialized id\r\n", port_id);
return NULL;
} else if (cs_id >= fls_context[port_id].flash_num) {
ssdk_printf(SSDK_CRIT, "xspi host %d cs %d uninitialized id\r\n",
port_id, cs_id);
return NULL;
}
return &(fls_context[port_id].flash[cs_id]);
}
int sdrv_norflash_enable_rfd(struct spi_nor_host* host, uint8_t mask) {
if (!host || !host->nor_tab[0]) {
return -1;
}
return sdrv_spi_nor_enable_rfd(host->nor_tab[0], mask);
}
int get_norflash_direct_base(struct spi_nor_host* host, addr_t *direct_base) {
if (!host || !direct_base) {
return -1;
}
*direct_base = ((struct xspi_pdata*)(host->priv_data))->direct_base;
return 0;
}
int sdrv_norflash_swap_cs(struct spi_nor_host* host, bool is_reverse) {
uint32_t reg;
uint8_t cs_order = (is_reverse) ? 0x89 : 0x98;
if (!host) {
return -1;
}
reg = readl(((struct xspi_pdata*)(host->priv_data))->apb_base + 0x44);
reg = (reg & (~0xffu)) | (cs_order);
writel(reg, ((struct xspi_pdata*)(host->priv_data))->apb_base + 0x44);
return 0;
}
int sdrv_norflash_get_flash_info(struct spi_nor_host* host, uint8_t cs_id,
Fls_Flash_Info_t* info) {
if (!host || !info) {
return -1;
} else if (!host->nor_tab[cs_id]) {
return -2;
}
info->octal_dtr_en = host->nor_tab[cs_id]->octal_dtr_en;
info->cinst_type = (host->nor_tab[cs_id]->info.read_proto >> 8) & 0xf;
return 0;
}