461 lines
14 KiB
C
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;
|
|
}
|