2147 lines
65 KiB
C
2147 lines
65 KiB
C
/**
|
|
* @file sdrv_xspi.c
|
|
* @brief sdrv xspi driver
|
|
*
|
|
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <debug.h>
|
|
#include <param.h>
|
|
#include <reg.h>
|
|
#include <sdrv_spi_nor.h>
|
|
#include "sdrv_xspi.h"
|
|
#include <armv7-r/cache.h>
|
|
#include <irq.h>
|
|
#include <common.h>
|
|
#include "udelay/udelay.h"
|
|
|
|
/* default use direct read mode */
|
|
#ifndef XSPI_USE_DIRECT_MODE
|
|
#define XSPI_USE_DIRECT_MODE (1)
|
|
#endif
|
|
|
|
|
|
#define XSPI_POLLING_INTERVAL_FACTOR 80
|
|
#define SPI_NOR_XFER_COMPLETE (0x1u)
|
|
|
|
volatile int g_xspi_debug = 1;
|
|
|
|
static uint32_t s_lockstep_err = 0;
|
|
|
|
extern void sdrv_xspi_rx_config(struct xspi_pdata *xspi, uint32_t mode);
|
|
|
|
static uint32_t g_enable_event;
|
|
|
|
static inline uint8_t _fls(unsigned int x)
|
|
{
|
|
uint8_t left = 0, right = 31, mid = 0;
|
|
|
|
while (left < right) {
|
|
mid = (left + right) >> 1;
|
|
|
|
if ((0xffffffff - ((1u << (mid + 1)) - 1)) & x) {
|
|
left = mid + 1;
|
|
}
|
|
else if (x & (1u << mid)) {
|
|
return mid;
|
|
}
|
|
else {
|
|
right = mid - 1;
|
|
}
|
|
}
|
|
|
|
return left;
|
|
}
|
|
|
|
static inline uint8_t my_log2_uint(uint64_t val)
|
|
{
|
|
uint8_t ret = 0;
|
|
|
|
if (val & (val - 1)) {
|
|
ssdk_printf(SSDK_ERR, "val is not 2^n\r\n");
|
|
}
|
|
|
|
while (val > 1ull) {
|
|
val >>= 1ull;
|
|
ret++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sdrv_xspi_wait_for_bit_times(addr_t reg, const uint32_t mask,
|
|
bool clear,
|
|
uint32_t times)
|
|
{
|
|
uint32_t val;
|
|
uint32_t count = 0;
|
|
|
|
while (count < times) {
|
|
val = readl(reg);
|
|
|
|
if (clear) {
|
|
val = ~val;
|
|
}
|
|
|
|
val &= mask;
|
|
|
|
if (val == mask) {
|
|
return 0;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline uint32_t sdrv_xspi_get_rd_buf_level(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_INDIRECT_RX_BUF);
|
|
reg >>= XSPI_INDIRECT_RX_BUF_ENTRY_LSB;
|
|
return reg & XSPI_INDIRECT_RX_BUF_ENTRY_MASK;
|
|
}
|
|
|
|
static inline uint32_t sdrv_xspi_get_wr_buf_level(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_INDIRECT_TX_BUF);
|
|
reg >>= XSPI_INDIRECT_TX_BUF_ENTRY_LSB;
|
|
return reg & XSPI_INDIRECT_TX_BUF_ENTRY_MASK;
|
|
}
|
|
|
|
void sdrv_xspi_reset_flash(struct xspi_pdata *xspi)
|
|
{
|
|
/* rst enable */
|
|
RMWREG32(xspi->apb_base + XSPI_IO_CTRL, 0, 1, 1);
|
|
|
|
/* rst start */
|
|
RMWREG32(xspi->apb_base + XSPI_IO_CTRL, 1, 1, 0);
|
|
udelay(1000u);
|
|
|
|
/* rst end */
|
|
RMWREG32(xspi->apb_base + XSPI_IO_CTRL, 1, 1, 1);
|
|
udelay(1000u);
|
|
|
|
/* rst disable */
|
|
RMWREG32(xspi->apb_base + XSPI_IO_CTRL, 0, 1, 0);
|
|
xspi_readl(xspi->apb_base + XSPI_IO_CTRL);
|
|
}
|
|
|
|
static void sdrv_xspi_delay_set(struct spi_nor *nor)
|
|
{
|
|
uint32_t reg, tslch, tshsl, tchsh;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
tslch = XSPI_NS_2_TICKS(xspi->ref_clk_hz, 10);
|
|
tshsl = XSPI_NS_2_TICKS(xspi->ref_clk_hz, 30);
|
|
tchsh = XSPI_NS_2_TICKS(xspi->ref_clk_hz, 10);
|
|
tchsh = tchsh >= 3 ? tchsh : 3;
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_CS_CTRL);
|
|
reg &= ~(XSPI_CS_CTRL_TIME_DELAY_MASK << XSPI_CS_CTRL_TSLCH_LSB);
|
|
reg |= (tslch & XSPI_CS_CTRL_TIME_DELAY_MASK) << XSPI_CS_CTRL_TSLCH_LSB;
|
|
|
|
reg &= ~(XSPI_CS_CTRL_TIME_SHSL_DELAY_MASK << XSPI_CS_CTRL_TSHSL_LSB);
|
|
reg |= (tshsl & XSPI_CS_CTRL_TIME_SHSL_DELAY_MASK) << XSPI_CS_CTRL_TSHSL_LSB;
|
|
|
|
reg &= ~(XSPI_CS_CTRL_TIME_DELAY_MASK << XSPI_CS_CTRL_TCHSH_LSB);
|
|
reg |= (tchsh & XSPI_CS_CTRL_TIME_DELAY_MASK) << XSPI_CS_CTRL_TCHSH_LSB;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_CS_CTRL);
|
|
}
|
|
|
|
__UNUSED static void sdrv_xspi_timeout_set(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t timeout = XSPI_MS_2_TICKS(xspi->ref_clk_hz, 10);
|
|
RMWREG32(xspi->apb_base + XSPI_AXI_CTRL, 0, 24, timeout);
|
|
}
|
|
|
|
static uint32_t sdrv_xspi_idle_status(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg;
|
|
reg = readl(xspi->apb_base + XSPI_MODE_CTRL);
|
|
return reg & XSPI_MODE_CTRL_IDLE;
|
|
}
|
|
|
|
static int sdrv_xspi_wait_idle(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t idle_latency_cycles = 4;
|
|
uint32_t idle_count = 0;
|
|
uint32_t polling_cycles = 0;
|
|
|
|
while (1) {
|
|
if (sdrv_xspi_idle_status(xspi) != 0u) {
|
|
idle_count++;
|
|
}
|
|
else {
|
|
idle_count = 0;
|
|
}
|
|
|
|
/* When IDLE bit asserted, need wait 4 cycyles of ref_clk. */
|
|
if (idle_count >= idle_latency_cycles) {
|
|
return 0;
|
|
}
|
|
else if (polling_cycles > XSPI_IDLE_TIMEOUT_CYCLES) {
|
|
return -1;
|
|
}
|
|
|
|
polling_cycles++;
|
|
}
|
|
}
|
|
|
|
void sdrv_xspi_xip_enable(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg;
|
|
uint32_t read_dummy;
|
|
|
|
/* Enable xip mode */
|
|
reg = xspi_readl(xspi->apb_base + XSPI_MODE_CTRL);
|
|
reg |= XSPI_MODE_CTRL_XIP;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_MODE_CTRL);
|
|
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_DIRECT_M_CODE);
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_DIRECT_RD_PH_CTRL);
|
|
/* Set read mode phase 1 byte */
|
|
reg &= ~(XSPI_DIRECT_MODE_SIZE_MASK << XSPI_DIRECT_MODE_SIZE_LSB);
|
|
/* Xip mode bit sent by 8 lines */
|
|
reg |= 0x3u << XSPI_DIRECT_MODE_LINE_LSB;
|
|
/* Enable read mode phase */
|
|
reg |= XSPI_DIRECT_MODE_EN;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_DIRECT_RD_PH_CTRL);
|
|
|
|
/* Read dummy cycle - 1 */
|
|
reg = xspi_readl(xspi->apb_base + XSPI_DIRECT_D_CYC);
|
|
read_dummy = reg & XSPI_DIRECT_D_CYC_RD_MASK;
|
|
reg &= ~(XSPI_DIRECT_D_CYC_RD_MASK);
|
|
reg |= read_dummy - 1;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_DIRECT_D_CYC);
|
|
}
|
|
|
|
void sdrv_xspi_xip_enter(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* Disable direct read cmd phase */
|
|
reg = xspi_readl(xspi->apb_base + XSPI_DIRECT_RD_PH_CTRL);
|
|
reg &= ~XSPI_DIRECT_CMD_EN;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_DIRECT_RD_PH_CTRL);
|
|
}
|
|
|
|
#if CONFIG_XSPI_ENABLE_DMA
|
|
void sdrv_xspi_dma_enable(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg;
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_INDIRECT_WR_DMA);
|
|
reg |= XSPI_INDIRECT_DMA_SINGLE_EN | XSPI_INDIRECT_DMA_REQ_EN;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_INDIRECT_WR_DMA);
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_INDIRECT_RD_DMA);
|
|
reg |= XSPI_INDIRECT_DMA_SINGLE_EN | XSPI_INDIRECT_DMA_REQ_EN;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_INDIRECT_RD_DMA);
|
|
}
|
|
|
|
void sdrv_xspi_dma_disable(struct xspi_pdata *xspi)
|
|
{
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_INDIRECT_WR_DMA);
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_INDIRECT_RD_DMA);
|
|
}
|
|
#endif
|
|
|
|
void sdrv_xspi_rx_config(struct xspi_pdata *xspi, uint32_t mode)
|
|
{
|
|
uint32_t reg;
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_SCLK_CTRL);
|
|
reg &= ~(XSPI_SCLK_CTRL_RX_SEL_MASK << XSPI_SCLK_CTRL_RX_SEL_LSB);
|
|
reg |= mode << XSPI_SCLK_CTRL_RX_SEL_LSB;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_SCLK_CTRL);
|
|
}
|
|
|
|
#ifdef XSPI_DLL_USE_OVERRIDE_MODE
|
|
void sdrv_xspi_dll_enable(struct xspi_pdata *xspi, int dll_num, int value,
|
|
bool enable)
|
|
{
|
|
uint32_t reg = 0u;
|
|
uint32_t addr;
|
|
|
|
addr = dll_num ? XSPI_DLL1_CTRL : XSPI_DLL_CTRL;
|
|
|
|
if (enable) {
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
reg |= value << 9;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
reg |= BIT(8);
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
}
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_MODE_CTRL, 31, 1, 1);
|
|
udelay(1);
|
|
RMWREG32(xspi->apb_base + XSPI_MODE_CTRL, 31, 1, 0);
|
|
sdrv_xspi_wait_idle(xspi);
|
|
|
|
/* set dll_en */
|
|
RMWREG32(xspi->apb_base + XSPI_SCLK_CTRL, 1, 1, enable ? 1 : 0);
|
|
xspi_readl(xspi->apb_base + XSPI_SCLK_CTRL);
|
|
}
|
|
#else
|
|
void sdrv_xspi_dll_enable(struct xspi_pdata *xspi, int dll_num, int value,
|
|
bool enable)
|
|
{
|
|
uint32_t reg = 0u;
|
|
int ret = 0;
|
|
uint32_t addr;
|
|
|
|
addr = dll_num ? XSPI_DLL1_CTRL : XSPI_DLL_CTRL;
|
|
|
|
if (enable) {
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
reg |= value << 1;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
reg |= BIT(0);
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
|
|
ret = sdrv_xspi_wait_for_bit_times(xspi->apb_base + addr + 4, BIT(16), false,
|
|
100000);
|
|
|
|
if (ret) {
|
|
ssdk_printf(SSDK_ERR, "dll enable err\r\n");
|
|
}
|
|
|
|
#if CONFIG_E3L
|
|
if (IS_P0)
|
|
#endif
|
|
{
|
|
/* workround: XSPI DLL slave chain update should be gated during flash read operation.
|
|
otherwise, there will be DQS (read strobe) glitch and read data will be wrong. */
|
|
RMWREG32(xspi->apb_base + addr, 31, 1, 1);
|
|
}
|
|
}
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_MODE_CTRL, 31, 1, 1);
|
|
udelay(1);
|
|
RMWREG32(xspi->apb_base + XSPI_MODE_CTRL, 31, 1, 0);
|
|
sdrv_xspi_wait_idle(xspi);
|
|
|
|
/* set dll_en */
|
|
RMWREG32(xspi->apb_base + XSPI_SCLK_CTRL, 1, 1, enable ? 1 : 0);
|
|
reg = xspi_readl(xspi->apb_base + addr);
|
|
}
|
|
#endif
|
|
|
|
static void sdrv_xspi_prefetch_config(struct xspi_pdata *xspi, uint32_t channel,
|
|
uint16_t gid_mask, uint16_t gid_match,
|
|
uint8_t block_num, uint8_t group_num)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* Entry size is 8 blocks */
|
|
reg = block_num;
|
|
reg |= group_num << 8;
|
|
/* Disable cache for hyperram */
|
|
reg |= 1u << 16;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_RBUF_CTRL + 0x10 * channel);
|
|
sdrv_xspi_writel(gid_mask, xspi->apb_base + XSPI_GID_MASK + 0x10 * channel);
|
|
sdrv_xspi_writel(gid_match, xspi->apb_base + XSPI_GID_MATCH + 0x10 * channel);
|
|
}
|
|
|
|
void sdrv_xspi_prefetch_flush(struct xspi_pdata *xspi, uint32_t channel)
|
|
{
|
|
RMWREG32(xspi->apb_base + XSPI_RBUF_CTRL + 0x10 * channel , 17, 1, 1);
|
|
RMWREG32(xspi->apb_base + XSPI_RBUF_CTRL + 0x10 * channel , 17, 1, 0);
|
|
}
|
|
|
|
static void sdrv_xspi_prefetch_clear(struct xspi_pdata *xspi, uint32_t channel)
|
|
{
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_RBUF_CTRL + 0x10 * channel);
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_GID_MASK + 0x10 * channel);
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_GID_MATCH + 0x10 * channel);
|
|
}
|
|
|
|
static void sdrv_xspi_prefetch_disable(struct xspi_pdata *xspi)
|
|
{
|
|
sdrv_xspi_prefetch_clear(xspi, 0u);
|
|
sdrv_xspi_prefetch_clear(xspi, 1u);
|
|
sdrv_xspi_prefetch_clear(xspi, 2u);
|
|
sdrv_xspi_prefetch_clear(xspi, 3u);
|
|
sdrv_xspi_prefetch_clear(xspi, 4u);
|
|
sdrv_xspi_prefetch_clear(xspi, 5u);
|
|
sdrv_xspi_prefetch_clear(xspi, 6u);
|
|
sdrv_xspi_prefetch_clear(xspi, 7u);
|
|
sdrv_xspi_writel(0, xspi->apb_base + XSPI_GROUP_MERGE);
|
|
}
|
|
|
|
static void sdrv_xspi_prefetch_enable(struct xspi_pdata *xspi)
|
|
{
|
|
RMWREG32(xspi->apb_base + XSPI_AXI_CTRL, 25, 1, 1);
|
|
|
|
sdrv_xspi_prefetch_config(xspi, 0u, 0x0000u, 0x000u, 2u, 4u);
|
|
sdrv_xspi_prefetch_config(xspi, 1u, 0xff00u, 0x000u, 2u, 4u);
|
|
sdrv_xspi_prefetch_config(xspi, 2u, 0xff00u, 0x100u, 2u, 4u);
|
|
sdrv_xspi_prefetch_config(xspi, 3u, 0xff00u, 0x200u, 2u, 4u);
|
|
sdrv_xspi_prefetch_config(xspi, 4u, 0xff00u, 0x300u, 2u, 4u);
|
|
sdrv_xspi_prefetch_config(xspi, 5u, 0xff00u, 0x400u, 2u, 4u);
|
|
sdrv_xspi_prefetch_config(xspi, 6u, 0xff00u, 0x500u, 8u, 1u);
|
|
sdrv_xspi_prefetch_config(xspi, 7u, 0xff00u, 0x800u, 8u, 1u);
|
|
/* merge group 0-1 for sf core */
|
|
sdrv_xspi_writel(1u, xspi->apb_base + XSPI_GROUP_MERGE);
|
|
}
|
|
|
|
void *sdrv_xspi_init(struct xspi_pdata *xspi)
|
|
{
|
|
if (sdrv_xspi_wait_idle(xspi)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Use rx mode by pop data by deafult */
|
|
sdrv_xspi_writel(XSPI_MODE_CTRL_RX_MODE, xspi->apb_base + XSPI_MODE_CTRL);
|
|
|
|
/* Enable prefetch by default */
|
|
sdrv_xspi_prefetch_enable(xspi);
|
|
|
|
/* Set AXI access timeout threshold */
|
|
sdrv_xspi_timeout_set(xspi);
|
|
|
|
#ifdef XSPI_INDIRECT_AXI_EN
|
|
/* indirect wr/rd enable */
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 10, 2, 0x3);
|
|
xspi_readl(xspi->apb_base + XSPI_MISC);
|
|
#endif
|
|
|
|
/* Set capture enable select to 7 clk period */
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 12, 3, 0x5);
|
|
|
|
sdrv_xspi_reset_flash(xspi);
|
|
|
|
/* rx_mode use internal sclk by default */
|
|
sdrv_xspi_rx_config(xspi, 0x0);
|
|
|
|
sdrv_xspi_dll_enable(xspi, 0, 7u, true);
|
|
sdrv_xspi_dll_enable(xspi, 1, 4u, true);
|
|
|
|
return (void *)xspi;
|
|
}
|
|
|
|
static inline void sdrv_fifo_readsl(uint32_t *addr, uint32_t *data,
|
|
uint32_t len)
|
|
{
|
|
while (len-- != 0u) {
|
|
*data++ = readl(addr);
|
|
}
|
|
}
|
|
|
|
static inline void sdrv_fifo_writesl(uint32_t *addr, uint32_t const *data,
|
|
uint32_t len)
|
|
{
|
|
while (len-- != 0u) {
|
|
writel(*data, (uint32_t)addr);
|
|
data++;
|
|
}
|
|
}
|
|
|
|
static void sdrv_xspi_size_set(struct spi_nor *nor)
|
|
{
|
|
uint32_t size = 0;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_DEV_SIZE);
|
|
|
|
size = my_log2_uint(nor->info.size) & 0x1Fu;
|
|
|
|
if ((size != 0) && (size != ((reg >> (nor->cs * 8)) & 0xff))) {
|
|
RMWREG32(xspi->apb_base + XSPI_DEV_SIZE, nor->cs * 8, 5, size);
|
|
reg = xspi_readl(xspi->apb_base + XSPI_DEV_SIZE);
|
|
ssdk_printf(SSDK_CRIT, "xspi dev size reg = 0x%x\r\n", reg);
|
|
}
|
|
}
|
|
|
|
void sdrv_xspi_proto_setup(struct spi_nor *nor, struct spi_nor_cmd *cmd,
|
|
enum xspi_access_mode access_mode,
|
|
enum spi_nor_ops ops)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
uint32_t ph_ctrl = 0;
|
|
uint16_t cmd_code = 0;
|
|
uint8_t dummy = 0;
|
|
|
|
if (!nor->hyperbus_mode) {
|
|
ph_ctrl |= XSPI_C_EN | XSPI_C_SIZE(0u);
|
|
}
|
|
|
|
if (nor->octal_dtr_en || nor->hyperbus_mode) {
|
|
ph_ctrl |= XSPI_C_RATE(1u) | XSPI_A_RATE(1u) | XSPI_D_RATE(1u) |
|
|
XSPI_C_SIZE(1u) | XSPI_A_SIZE(3u);
|
|
}
|
|
|
|
if (nor->xfer_info_bottom.size > 0) {
|
|
ph_ctrl |= XSPI_D_EN;
|
|
}
|
|
|
|
if (cmd) {
|
|
dummy = cmd->dummy;
|
|
ph_ctrl |= XSPI_C_LINE(cmd->inst_type) | XSPI_A_LINE(cmd->inst_type) |
|
|
XSPI_D_LINE(cmd->inst_type);
|
|
cmd_code = cmd->opcode;
|
|
|
|
if (cmd->addr_bytes) {
|
|
ph_ctrl |= XSPI_A_EN | XSPI_A_SIZE(cmd->addr_bytes - 1);
|
|
}
|
|
}
|
|
else {
|
|
if (nor->addr_width) {
|
|
ph_ctrl |= XSPI_A_SIZE(nor->addr_width - 1);
|
|
}
|
|
|
|
if (SPI_NOR_OPS_WRITE == ops) {
|
|
dummy = (SPI_HYPERRAM == nor->mem_type) ? nor->info.write_dummy : 0;
|
|
ph_ctrl |= XSPI_C_LINE(SNOR_INST_LANS(nor->info.write_proto)) |
|
|
XSPI_A_LINE(SNOR_ADDR_LANS(nor->info.write_proto)) |
|
|
XSPI_D_LINE(SNOR_DATA_LANS(nor->info.write_proto));
|
|
cmd_code = (nor->info.write_proto >> SNOR_OPCODE_PROTO_LSB) & 0xff;
|
|
}
|
|
else if (SPI_NOR_OPS_READ == ops) {
|
|
dummy = nor->info.read_dummy;
|
|
ph_ctrl |= XSPI_C_LINE(SNOR_INST_LANS(nor->info.read_proto)) |
|
|
XSPI_A_LINE(SNOR_ADDR_LANS(nor->info.read_proto)) |
|
|
XSPI_D_LINE(SNOR_DATA_LANS(nor->info.read_proto));
|
|
cmd_code = (nor->info.read_proto >> SNOR_OPCODE_PROTO_LSB) & 0xff;
|
|
}
|
|
}
|
|
|
|
if (SPI_HYPERRAM == nor->mem_type) {
|
|
/* For hyperram, taishan xspi's dummy is device spec's dummy + 1 */
|
|
dummy = dummy + 1u;
|
|
} else if (nor->hyperbus_mode && dummy > 0u) {
|
|
/* For hyperflash, taishan xspi's dummy is device spec's dummy - 1 */
|
|
dummy = dummy - 1u;
|
|
}
|
|
|
|
if (nor->octal_dtr_en || nor->hyperbus_mode) {
|
|
cmd_code |= cmd_code << 8u;
|
|
}
|
|
|
|
if (0u == dummy) {
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 8u, 1u, 1);
|
|
}
|
|
else {
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 8u, 1u, 0);
|
|
}
|
|
|
|
if (XSPI_DIRECT_ACCESS_MODE == access_mode) {
|
|
if (SPI_NOR_OPS_WRITE == ops) {
|
|
sdrv_xspi_writel(ph_ctrl, xspi->apb_base + XSPI_DIRECT_WR_PH_CTRL);
|
|
RMWREG32(xspi->apb_base + XSPI_DIRECT_C_CODE, 16, 16, cmd_code);
|
|
xspi_readl(xspi->apb_base + XSPI_DIRECT_C_CODE);
|
|
RMWREG32(xspi->apb_base + XSPI_DIRECT_D_CYC, 8, 5, dummy);
|
|
xspi_readl(xspi->apb_base + XSPI_DIRECT_D_CYC);
|
|
|
|
/* Hyperbus mode not need write enable */
|
|
if (!nor->hyperbus_mode) {
|
|
sdrv_xspi_writel(0x06u << 16 | 1, xspi->apb_base + XSPI_DIRECT_WREN);
|
|
}
|
|
}
|
|
else if (SPI_NOR_OPS_READ == ops) {
|
|
sdrv_xspi_writel(ph_ctrl, xspi->apb_base + XSPI_DIRECT_RD_PH_CTRL);
|
|
RMWREG32(xspi->apb_base + XSPI_DIRECT_C_CODE, 0, 16, cmd_code);
|
|
xspi_readl(xspi->apb_base + XSPI_DIRECT_C_CODE);
|
|
RMWREG32(xspi->apb_base + XSPI_DIRECT_D_CYC, 0, 5, dummy);
|
|
xspi_readl(xspi->apb_base + XSPI_DIRECT_D_CYC);
|
|
}
|
|
else {
|
|
ssdk_printf(SSDK_ERR, "%s: set direct mode err. %d\r\n", __FUNCTION__,
|
|
ops);
|
|
}
|
|
}
|
|
else {
|
|
if (!cmd) {
|
|
ph_ctrl |= (nor->addr_width > 0 ? XSPI_A_EN : 0);
|
|
}
|
|
|
|
if (SPI_NOR_OPS_WRITE == ops) {
|
|
sdrv_xspi_writel(ph_ctrl, xspi->apb_base + XSPI_INDIRECT_WR_PH_CTRL);
|
|
sdrv_xspi_writel(cmd_code, xspi->apb_base + XSPI_INDIRECT_WR_CMD_CODE);
|
|
sdrv_xspi_writel(dummy, xspi->apb_base + XSPI_INDIRECT_WR_CYC);
|
|
}
|
|
else if (SPI_NOR_OPS_READ == ops) {
|
|
sdrv_xspi_writel(ph_ctrl, xspi->apb_base + XSPI_INDIRECT_RD_PH_CTRL);
|
|
sdrv_xspi_writel(cmd_code, xspi->apb_base + XSPI_INDIRECT_RD_CMD_CODE);
|
|
sdrv_xspi_writel(dummy, xspi->apb_base + XSPI_INDIRECT_RD_CYC);
|
|
}
|
|
else {
|
|
ssdk_printf(SSDK_ERR, "%s: set indirect mode err. %d\r\n",
|
|
__FUNCTION__, ops);
|
|
}
|
|
}
|
|
}
|
|
|
|
__UNUSED static void sdrv_xspi_cs_set(struct spi_nor *nor)
|
|
{
|
|
uint32_t reg;
|
|
uint8_t cs;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if (nor->cs == 0u) {
|
|
cs = XSPI_CS_CTRL_HW_SEL_DEV1;
|
|
}
|
|
else {
|
|
cs = XSPI_CS_CTRL_HW_SEL_DEV2;
|
|
}
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_CS_CTRL);
|
|
reg &= ~(XSPI_CS_CTRL_HW_SEL_MASK << XSPI_CS_CTRL_HW_SEL_LSB);
|
|
|
|
reg |= cs << XSPI_CS_CTRL_HW_SEL_LSB;
|
|
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_CS_CTRL);
|
|
}
|
|
|
|
static void sdrv_xspi_polling_set(struct spi_nor *nor)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
sdrv_xspi_writel(1u, xspi->apb_base + XSPI_INDIRECT_P_VALUE);
|
|
sdrv_xspi_writel(0xfffffffeu, xspi->apb_base + XSPI_INDIRECT_P_MASK);
|
|
sdrv_xspi_writel(XSPI_US_2_TICKS((uint32_t)xspi->ref_clk_hz / 80, 1),
|
|
xspi->apb_base + XSPI_INDIRECT_P_TIME);
|
|
}
|
|
|
|
void sdrv_xspi_dqs_enable(struct xspi_pdata *xspi, bool enable)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* For aviod dqs bug */
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 9u, 1u, 1);
|
|
|
|
reg = xspi_readl(xspi->apb_base + XSPI_SCLK_CTRL);
|
|
reg &= ~(XSPI_SCLK_CTRL_RX_SEL_MASK << XSPI_SCLK_CTRL_RX_SEL_LSB);
|
|
|
|
if (enable) {
|
|
/* dqs mode */
|
|
reg |= 2u << XSPI_SCLK_CTRL_RX_SEL_LSB;
|
|
}
|
|
else {
|
|
reg |= 0u << XSPI_SCLK_CTRL_RX_SEL_LSB;
|
|
}
|
|
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_SCLK_CTRL);
|
|
}
|
|
|
|
static inline void sdrv_xspi_hyperbus_en(struct spi_nor *nor)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
/* Set rx enable mode to half-dummy or rdata phase 0 */
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 9u, 1u, 1);
|
|
xspi_readl(xspi->apb_base + XSPI_MISC);
|
|
|
|
/* Set bytes swap */
|
|
RMWREG32(xspi->apb_base + XSPI_MODE_CTRL, 12u, 1u, 1);
|
|
|
|
/* Set profile2.0 enable */
|
|
RMWREG32(xspi->apb_base + XSPI_HYPERBUS_CTRL, 3u, 1u, 1);
|
|
|
|
/* Set address type divided to 2 */
|
|
RMWREG32(xspi->apb_base + XSPI_HYPERBUS_CTRL, 2u, 1u, 0);
|
|
|
|
/*Set address space type memory */
|
|
RMWREG32(xspi->apb_base + XSPI_HYPERBUS_CTRL, 1u, 1u, 0);
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_HYPERBUS_CTRL, 0u, 1u, nor->hyperbus_mode);
|
|
}
|
|
|
|
static void sdrv_xspi_hyperbus_direct_setup(struct spi_nor *nor)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
/* Config direct read pre-transaction to 0 */
|
|
sdrv_xspi_writel(0, xspi->apb_base + XSPI_DIRECT_RD_PTC);
|
|
|
|
/* Config direct write pre-transaction to 0 */
|
|
sdrv_xspi_writel(0, xspi->apb_base + XSPI_DIRECT_WR_PTC);
|
|
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_DIRECT_ACCESS_MODE, SPI_NOR_OPS_READ);
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_DIRECT_ACCESS_MODE, SPI_NOR_OPS_WRITE);
|
|
}
|
|
|
|
void sdrv_xspi_hyperram_enable(struct spi_nor *nor, uint8_t cs,
|
|
uint8_t read_dummy, uint8_t write_dummy)
|
|
{
|
|
uint32_t dummy;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_DEV_SIZE, 16u + cs, 1u, 0x1u);
|
|
|
|
/* Config direct read pre-transaction to 0 */
|
|
sdrv_xspi_writel(0, xspi->apb_base + XSPI_DIRECT_RD_PTC);
|
|
|
|
/* Config direct write pre-transaction to 0 */
|
|
sdrv_xspi_writel(0, xspi->apb_base + XSPI_DIRECT_WR_PTC);
|
|
|
|
dummy = ((read_dummy + 1) & XSPI_DIRECT_D_CYC_RD_MASK) <<
|
|
XSPI_DIRECT_D_CYC_RD_LSB;
|
|
dummy |= ((write_dummy + 1) & XSPI_DIRECT_D_CYC_WR_MASK) <<
|
|
XSPI_DIRECT_D_CYC_WR_LSB;
|
|
sdrv_xspi_writel(dummy, xspi->apb_base + XSPI_DIRECT_D_CYC);
|
|
|
|
sdrv_xspi_prefetch_disable(xspi);
|
|
}
|
|
|
|
void sdrv_xspi_nor_setup(struct spi_nor *nor, enum spi_nor_ops ops)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if (sdrv_xspi_wait_idle(xspi)) {
|
|
ssdk_printf(SSDK_ERR, "%s: wait idle err.", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
if (nor->cs != xspi->current_cs && nor->info.name != 0) {
|
|
/* cs select by hardware, based on access address */
|
|
// sdrv_xspi_cs_set(nor);
|
|
sdrv_xspi_delay_set(nor);
|
|
|
|
if (nor->hyperbus_mode) {
|
|
sdrv_xspi_hyperbus_en(nor);
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_READ);
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_WRITE);
|
|
sdrv_xspi_hyperbus_direct_setup(nor);
|
|
}
|
|
else {
|
|
sdrv_xspi_polling_set(nor);
|
|
}
|
|
|
|
xspi->current_cs = nor->cs;
|
|
}
|
|
|
|
if (nor->info.name != 0) {
|
|
sdrv_xspi_size_set(nor);
|
|
}
|
|
|
|
if (!nor->host->xip_mode && nor->dqs_en != xspi->dqs_en) {
|
|
sdrv_xspi_dll_enable(xspi, 0, 7u, nor->dqs_en);
|
|
sdrv_xspi_dll_enable(xspi, 1, 7u, nor->dqs_en);
|
|
sdrv_xspi_dqs_enable(xspi, nor->dqs_en);
|
|
xspi->dqs_en = nor->dqs_en;
|
|
}
|
|
|
|
if (!nor->host->xip_mode && !nor->hyperbus_mode) {
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_DIRECT_ACCESS_MODE, SPI_NOR_OPS_READ);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void sdrv_xspi_setup_xfer(struct spi_nor *nor, enum flash_opt opt,
|
|
flash_addr_t addr, uint8_t *buf, flash_size_t size)
|
|
|
|
{
|
|
nor->xfer_info_bottom.opt_type = opt;
|
|
nor->xfer_info_bottom.addr = addr;
|
|
nor->xfer_info_bottom.buf = buf;
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_PENDING;
|
|
nor->xfer_info_bottom.size = size;
|
|
}
|
|
|
|
uint32_t sdrv_xspi_read_fifo(struct xspi_pdata *xspi, uint8_t *buf,
|
|
flash_size_t size)
|
|
{
|
|
uint32_t last_word;
|
|
uint32_t aligned_size;
|
|
uint32_t bytes_need_read = 0;
|
|
|
|
#ifdef XSPI_INDIRECT_AXI_EN
|
|
uint32_t addr = xspi_readl(xspi->apb_base + XSPI_INDIRECT_RD_ADDR);
|
|
addr_t fifo_address = xspi->direct_base + ROUNDUP(addr, 4u);
|
|
#else
|
|
addr_t fifo_address = xspi->apb_base + XSPI_INDIRECT_RDATA;
|
|
#endif
|
|
|
|
bytes_need_read =
|
|
sdrv_xspi_get_rd_buf_level(xspi) * (uint32_t)xspi->fifo_width;
|
|
|
|
bytes_need_read = MIN(bytes_need_read, size);
|
|
|
|
if (bytes_need_read) {
|
|
aligned_size = bytes_need_read / 4u * 4u;
|
|
|
|
if (aligned_size == 16) {
|
|
ssdk_printf(SSDK_INFO, "DBG: aligned size %d\r\n", aligned_size);
|
|
}
|
|
|
|
sdrv_fifo_readsl((uint32_t *)fifo_address, (void *)buf, aligned_size / 4u);
|
|
|
|
if (bytes_need_read % 4) {
|
|
last_word = readl(fifo_address);
|
|
memcpy(buf + aligned_size, &last_word, bytes_need_read % 4);
|
|
}
|
|
|
|
return bytes_need_read;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t sdrv_xspi_write_fifo(struct xspi_pdata *xspi, const uint8_t *buf,
|
|
flash_size_t size)
|
|
{
|
|
uint32_t sram_level;
|
|
uint32_t write_bytes = 0;
|
|
uint32_t sram_size = (uint32_t)xspi->fifo_depth * xspi->fifo_width;
|
|
|
|
#ifdef XSPI_INDIRECT_AXI_EN
|
|
uint32_t addr = xspi_readl(xspi->apb_base + XSPI_INDIRECT_WR_ADDR);
|
|
addr_t fifo_address = xspi->direct_base + ROUNDUP(addr, 4u);
|
|
#else
|
|
addr_t fifo_address = xspi->apb_base + XSPI_INDIRECT_WDATA;
|
|
#endif
|
|
|
|
write_bytes = MIN(size, sram_size);
|
|
|
|
sram_level = sdrv_xspi_get_wr_buf_level(xspi);
|
|
|
|
if (0u == sram_level) {
|
|
sdrv_fifo_writesl((uint32_t *)fifo_address, (void *)buf,
|
|
DIV_ROUND_UP(write_bytes, 4u));
|
|
return write_bytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sdrv_xspi_indirect_trigger(const struct xspi_pdata *xspi, uint32_t addr,
|
|
uint32_t size, int flag)
|
|
{
|
|
if (flag == INDIRECT_READ_FLAG) {
|
|
/* Set indirect read start address */
|
|
sdrv_xspi_writel(addr, xspi->apb_base + XSPI_INDIRECT_RD_ADDR);
|
|
/* Set indirect read bytes number */
|
|
sdrv_xspi_writel(size, xspi->apb_base + XSPI_INDIRECT_RD_SIZE);
|
|
/* Start the indirect read */
|
|
writel(1u, xspi->apb_base + XSPI_INDIRECT_RD_CTRL);
|
|
}
|
|
else {
|
|
/* Set indirect write start address */
|
|
sdrv_xspi_writel(addr, xspi->apb_base + XSPI_INDIRECT_WR_ADDR);
|
|
/* Set indirect write bytes number */
|
|
sdrv_xspi_writel(size, xspi->apb_base + XSPI_INDIRECT_WR_SIZE);
|
|
/* Start the indirect write */
|
|
writel(1u, xspi->apb_base + XSPI_INDIRECT_WR_CTRL);
|
|
}
|
|
}
|
|
|
|
int sdrv_xspi_tx_complete(struct xspi_pdata *xspi)
|
|
{
|
|
int ret = 0;
|
|
uint32_t reg;
|
|
|
|
ret =
|
|
sdrv_xspi_wait_for_bit_times(xspi->apb_base + XSPI_INT_ST_FUC,
|
|
XSPI_INT_ST_FUC_INDIRECT_WR_DONE, false, 100u);
|
|
|
|
if (ret != 0) {
|
|
ssdk_printf(SSDK_ERR, "Indirect write not complete, ret %d.\r\n", ret);
|
|
}
|
|
else {
|
|
reg = xspi_readl(xspi->apb_base + XSPI_INT_ST_FUC);
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_INT_ST_FUC);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sdrv_xspi_rx_complete(struct xspi_pdata *xspi)
|
|
{
|
|
int ret = 0;
|
|
uint32_t reg;
|
|
|
|
ret =
|
|
sdrv_xspi_wait_for_bit_times(xspi->apb_base + XSPI_INT_ST_FUC,
|
|
XSPI_INT_ST_FUC_INDIRECT_RD_DONE, false, 10000u);
|
|
|
|
if (ret != 0) {
|
|
ssdk_printf(SSDK_ERR, "Indirect read not complete, ret %d.\r\n", ret);
|
|
}
|
|
else {
|
|
reg = xspi_readl(xspi->apb_base + XSPI_INT_ST_FUC);
|
|
sdrv_xspi_writel(reg, xspi->apb_base + XSPI_INT_ST_FUC);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sdrv_xspi_rx_polling(struct spi_nor *nor)
|
|
{
|
|
int ret = 0;
|
|
uint32_t rx_size;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if ((nor->xfer_info_bottom.buf != NULL) &&
|
|
(nor->xfer_info_bottom.size > 0u)) {
|
|
rx_size = sdrv_xspi_read_fifo(xspi, nor->xfer_info_bottom.buf,
|
|
nor->xfer_info_bottom.size);
|
|
nor->xfer_info_bottom.buf += rx_size;
|
|
nor->xfer_info_bottom.size -= rx_size;
|
|
|
|
if (nor->xfer_mode == SPI_NOR_XFER_POLLING_MODE) {
|
|
if (nor->xfer_info_bottom.size == 0u) {
|
|
ret = sdrv_xspi_rx_complete(xspi);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ret = sdrv_xspi_rx_complete(xspi);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sdrv_xspi_tx_polling(struct spi_nor *nor)
|
|
{
|
|
int ret = 0;
|
|
uint32_t tx_size;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if ((nor->xfer_info_bottom.buf != NULL) &&
|
|
(nor->xfer_info_bottom.size > 0u)) {
|
|
tx_size = sdrv_xspi_write_fifo(xspi, nor->xfer_info_bottom.buf,
|
|
nor->xfer_info_bottom.size);
|
|
nor->xfer_info_bottom.buf += tx_size;
|
|
nor->xfer_info_bottom.size -= tx_size;
|
|
|
|
if (nor->xfer_mode == SPI_NOR_XFER_POLLING_MODE) {
|
|
if (nor->xfer_info_bottom.size == 0u) {
|
|
ret = sdrv_xspi_tx_complete(xspi);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ret = sdrv_xspi_tx_complete(xspi);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void sdrv_xspi_int_unmask(struct xspi_pdata *xspi, addr_t addr, uint32_t mask)
|
|
{
|
|
uint32_t reg;
|
|
reg = xspi_readl(xspi->apb_base + addr);
|
|
reg |= mask;
|
|
sdrv_xspi_writel(reg, xspi->apb_base + addr);
|
|
}
|
|
|
|
int sdrv_xspi_rfd_en(struct spi_nor *nor, uint8_t mask)
|
|
{
|
|
uint32_t reg;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
uint8_t region_mask = mask & 0xF;
|
|
|
|
nor->xfer_info_bottom.size = 4; // enable the data phase
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_READ);
|
|
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
if (region_mask & (1u << i)) {
|
|
/* Key derivation trigger */
|
|
RMWREG32(xspi->apb_base + XSPI_IMG_RFD, 8 + i, 1, 0x01);
|
|
|
|
/* Polling key ready */
|
|
while (!(readl(xspi->apb_base + XSPI_IMG_RFD) & BIT(16 + i)))
|
|
;
|
|
}
|
|
}
|
|
|
|
/* Enable decryption */
|
|
RMWREG32(xspi->apb_base + XSPI_IMG_RFD, 28, 4, region_mask);
|
|
nor->xfer_info_bottom.size = 0;
|
|
|
|
/* Flush prefetch */
|
|
reg = xspi_readl(xspi->apb_base + XSPI_GROUP_MERGE);
|
|
if (reg) {
|
|
for (uint8_t channel = 0; channel < 8; channel++) {
|
|
sdrv_xspi_prefetch_flush(xspi, channel);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static flash_size_t sdrv_xspi_read_dev1_size(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_DEV_SIZE);
|
|
return 0x1u << (reg & XSPI_DEV_SIZE_SIZE_MASK);
|
|
}
|
|
|
|
static bool sdrv_xspi_read_dqs_en(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_SCLK_CTRL);
|
|
if ((reg >> XSPI_SCLK_CTRL_RX_SEL_LSB) == 2) {
|
|
xspi->dqs_en = true;
|
|
return true;
|
|
} else {
|
|
xspi->dqs_en = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static uint32_t sdrv_xspi_read_direct_dummy_cycle(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_DIRECT_D_CYC);
|
|
return reg & XSPI_DIRECT_D_CYC_RD_MASK;
|
|
}
|
|
|
|
static uint32_t sdrv_xspi_read_indirect_dummy_cycle(struct xspi_pdata *xspi)
|
|
{
|
|
uint32_t reg = xspi_readl(xspi->apb_base + XSPI_INDIRECT_RD_CYC);
|
|
return reg & XSPI_INDIRECT_RD_CYC_MASK;
|
|
}
|
|
|
|
static uint32_t sdrv_spi_nor_get_proto(struct spi_nor *nor, enum spi_nor_rw_para ops) {
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
ph_ctrl_t ph_data = { 0 };
|
|
uint32_t opcode;
|
|
uint32_t res = 0;
|
|
|
|
switch (ops) {
|
|
case SPI_NOR_DIRECT_READ:
|
|
ph_data.data = xspi_readl(xspi->apb_base + XSPI_DIRECT_RD_PH_CTRL);
|
|
opcode = (xspi_readl(xspi->apb_base + XSPI_DIRECT_C_CODE)) & XSPI_DIRECT_C_CODE_MASK;
|
|
break;
|
|
case SPI_NOR_INDIRECT_READ:
|
|
ph_data.data = xspi_readl(xspi->apb_base + XSPI_INDIRECT_RD_PH_CTRL);
|
|
opcode = (xspi_readl(xspi->apb_base + XSPI_INDIRECT_RD_CMD_CODE)) & XSPI_INDIRECT_RD_CMD_CODE_MASK;
|
|
break;
|
|
case SPI_NOR_DIRECT_WRITE:
|
|
ph_data.data = xspi_readl(xspi->apb_base + XSPI_DIRECT_WR_PH_CTRL);
|
|
opcode = (xspi_readl(xspi->apb_base + XSPI_DIRECT_C_CODE)) & XSPI_DIRECT_C_CODE_MASK;
|
|
break;
|
|
case SPI_NOR_INDIRECT_WRITE:
|
|
ph_data.data = xspi_readl(xspi->apb_base + XSPI_INDIRECT_WR_PH_CTRL);
|
|
opcode = (xspi_readl(xspi->apb_base + XSPI_INDIRECT_WR_CMD_CODE)) & XSPI_INDIRECT_WR_CMD_CODE_MASK;
|
|
if (ph_data.ph_ctrl.a_size) {
|
|
nor->addr_width = ph_data.ph_ctrl.a_size + 1;
|
|
}
|
|
break;
|
|
default:
|
|
ssdk_printf(SSDK_CRIT, "This ops %x not support\r\n", ops);
|
|
break;
|
|
}
|
|
ssdk_printf(SSDK_CRIT, "opcode: 0x%x, %d, %d, %d \r\n", opcode,
|
|
ph_data.ph_ctrl.c_line, ph_data.ph_ctrl.a_line, ph_data.ph_ctrl.d_line);
|
|
res |= opcode << SNOR_OPCODE_PROTO_LSB;
|
|
res |= ph_data.ph_ctrl.c_line << SNOR_INST_LANS_PROTO_LSB;
|
|
res |= ph_data.ph_ctrl.a_line << SNOR_ADDR_LANS_PROTO_LSB;
|
|
res |= ph_data.ph_ctrl.d_line;
|
|
|
|
if (ph_data.ph_ctrl.c_rate && ph_data.ph_ctrl.a_rate && ph_data.ph_ctrl.d_rate) {
|
|
res |= SNOR_DTR_PROTO;
|
|
}
|
|
ssdk_printf(SSDK_CRIT, "Proto Res = %x\r\n", res);
|
|
return res;
|
|
}
|
|
|
|
static void sdrv_spi_nor_read_dummy_para(struct spi_nor *nor) {
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
nor->info.read_dummy = sdrv_xspi_read_direct_dummy_cycle(xspi);
|
|
nor->info.status_dummy = sdrv_xspi_read_indirect_dummy_cycle(xspi);
|
|
ssdk_printf(SSDK_CRIT, "read_dummy = 0x%x, status_dummy = 0x%x \r\n",
|
|
nor->info.read_dummy, nor->info.status_dummy);
|
|
}
|
|
|
|
static flash_size_t sdrv_spi_nor_read_dev_size_para(struct spi_nor *nor) {
|
|
flash_size_t flash_size;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
flash_size = sdrv_xspi_read_dev1_size(xspi);
|
|
ssdk_printf(SSDK_CRIT, "flash_size = 0x%llx \r\n", flash_size);
|
|
|
|
return flash_size;
|
|
}
|
|
|
|
static void sdrv_xspi_xip_proto_setup(struct spi_nor *nor) {
|
|
nor->reg_proto = sdrv_spi_nor_get_proto(nor, SPI_NOR_INDIRECT_READ);
|
|
nor->info.write_proto = sdrv_spi_nor_get_proto(nor, SPI_NOR_INDIRECT_WRITE);
|
|
nor->info.read_proto = sdrv_spi_nor_get_proto(nor, SPI_NOR_DIRECT_READ);
|
|
nor->info.size = sdrv_spi_nor_read_dev_size_para(nor);
|
|
nor->dqs_en = sdrv_xspi_read_dqs_en(nor->host->priv_data);
|
|
if (SNOR_PROTO_DTR(nor->info.read_proto) == SNOR_PROTO_8_8_8_DTR &&
|
|
SNOR_PROTO_DTR(nor->info.write_proto) == SNOR_PROTO_8_8_8_DTR) {
|
|
nor->octal_dtr_en = true;
|
|
}
|
|
if (nor->info.size > 0x1000000) {
|
|
nor->addr_width = 4;
|
|
}
|
|
sdrv_spi_nor_read_dummy_para(nor);
|
|
}
|
|
|
|
/**
|
|
* @brief xspi polling mode function
|
|
* @param[in] arg spi_nor context
|
|
*/
|
|
static void sdrv_xspi_polling_handler(void *arg)
|
|
{
|
|
int ret = 0;
|
|
struct spi_nor *nor = arg;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if (nor->xfer_info_bottom.opt_result == FLASH_OPT_PENDING) {
|
|
if (nor->xfer_info_bottom.opt_type == FLASH_OPT_READ) {
|
|
ret = sdrv_xspi_rx_polling(nor);
|
|
}
|
|
else if (nor->xfer_info_bottom.opt_type == FLASH_OPT_WRITE) {
|
|
ret = sdrv_xspi_tx_polling(nor);
|
|
}
|
|
}
|
|
|
|
nor->xfer_info_bottom.opt_result = ret ? FLASH_OPT_FAILED : FLASH_OPT_PENDING;
|
|
ssdk_printf(SSDK_DEBUG, "xfer_type = %d, xfer size = %lld, xfer result = %d\r\n",
|
|
nor->xfer_info_bottom.opt_type == FLASH_OPT_READ, nor->xfer_info_bottom.size,
|
|
nor->xfer_info_bottom.opt_result);
|
|
|
|
if ((nor->xfer_info_bottom.size == 0 || nor->xfer_info_bottom.buf == NULL) &&
|
|
nor->xfer_info_bottom.opt_result == FLASH_OPT_PENDING) {
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_COMPLETE;
|
|
|
|
if ((nor->xfer_mode == SPI_NOR_XFER_POLLING_MODE)
|
|
|| (nor->xfer_info_bottom.opt_type == FLASH_OPT_READ)) {
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_INT_EN_FUC);
|
|
|
|
if (nor->async_mode) {
|
|
if (nor->event_handler)
|
|
nor->event_handler(nor->xfer_info_bottom.opt_type,
|
|
nor->xfer_info_bottom.opt_result);
|
|
}
|
|
else {
|
|
/* Set xfer complete flag when read opt */
|
|
g_enable_event = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi wait mode function, xfer polling mode or xfer irq mode
|
|
* @param[in] nor spi_nor ptr
|
|
*/
|
|
static void sdrv_xspi_wait_xfer_done(struct spi_nor *nor)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
uint32_t try_count = 0;
|
|
|
|
if (nor->xfer_mode != SPI_NOR_XFER_POLLING_MODE) {
|
|
|
|
while ((!g_enable_event) && (try_count++ < 1000000U)) {
|
|
udelay(1u); // wait 1s for indirect write(<=256B) or read(xxB)
|
|
}
|
|
if (try_count >= 1000000U) {
|
|
ssdk_printf(SSDK_NOTICE, "xspi wait %d us, exit\r\n", try_count);
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_FAILED;
|
|
}
|
|
g_enable_event = 0;
|
|
s_lockstep_err = xspi_readl(xspi->apb_base + XSPI_LSP_ERR_ST);
|
|
|
|
if (s_lockstep_err != 0) {
|
|
writel(s_lockstep_err, xspi->apb_base + XSPI_LSP_ERR_ST);
|
|
ssdk_printf(SSDK_NOTICE, "xspi lsp error status = 0x%x\r\n", s_lockstep_err);
|
|
}
|
|
}
|
|
else {
|
|
while (1) {
|
|
sdrv_xspi_polling_handler(nor);
|
|
|
|
if (g_enable_event == 1) {
|
|
g_enable_event = 0;
|
|
s_lockstep_err = xspi_readl(xspi->apb_base + XSPI_LSP_ERR_ST);
|
|
|
|
if (s_lockstep_err != 0) {
|
|
writel(s_lockstep_err, xspi->apb_base + XSPI_LSP_ERR_ST);
|
|
ssdk_printf(SSDK_NOTICE, "xspi lsp error status = 0x%x\r\n", s_lockstep_err);
|
|
}
|
|
|
|
break;
|
|
} else if (try_count++ >= 10000000U) {
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_FAILED;
|
|
ssdk_printf(SSDK_NOTICE, "xspi polling %d times, exit\r\n", try_count);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi nor parallel mode data prepare
|
|
* @param[in] raw raw data
|
|
* @param[in] dst switch dst data form raw
|
|
* @param[in] len data length
|
|
*/
|
|
static inline void sdrv_para_data_prep(const uint8_t *raw, uint8_t *dst,
|
|
uint32_t len)
|
|
{
|
|
for (int i = 0; i < len / 2; i++) {
|
|
*(dst + 2 * i) = *(raw + i);
|
|
*(dst + 2 * i + 1) = *(raw + i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi nor parallel mode check command read data
|
|
* @param[in] raw raw data
|
|
* @param[in] dst switch dst data form raw
|
|
* @param[in] len data length
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static inline int sdrv_para_data_parse(const uint8_t *raw, uint8_t *dst,
|
|
uint32_t len)
|
|
{
|
|
for (int i = 0; i < len / 2; i++) {
|
|
if (*(raw + 2 * i) != *(raw + 2 * i + 1)) {
|
|
ssdk_printf(SSDK_ERR, "xspi parallel read register data error!\r\n");
|
|
return -1;
|
|
}
|
|
|
|
*(dst + i) = *(raw + 2 * i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi nor read register command
|
|
* @param[in] nor spi nor ptr
|
|
* @param[in] cmd spi nor cmd
|
|
* @param[in] addr flash read address
|
|
* @param[out] buf read data buf
|
|
* @param[in] len read data length
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_command_read(struct spi_nor *nor, struct spi_nor_cmd *cmd,
|
|
flash_addr_t addr, uint8_t *buf, flash_size_t len)
|
|
{
|
|
uint32_t irq_trig_level;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
uint8_t *ptr = buf;
|
|
uint8_t local_buf[32] = {0};
|
|
flash_size_t read_length = len;
|
|
flash_addr_t read_addr = addr + nor->offset_address;
|
|
|
|
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
|
|
ptr = local_buf;
|
|
read_length = MAX(4u, len * 2);
|
|
read_addr = 2u * addr;
|
|
}
|
|
|
|
sdrv_xspi_setup_xfer(nor, FLASH_OPT_READ, read_addr, ptr, read_length);
|
|
|
|
sdrv_xspi_proto_setup(nor, cmd, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_READ);
|
|
|
|
irq_trig_level = MIN(xspi->fifo_depth / 2, DIV_ROUND_UP(read_length, 4u));
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_INDIRECT_RD_DMA,
|
|
XSPI_INDIRECT_DMA_TRIG_WML_LSB, 8u, irq_trig_level);
|
|
|
|
sdrv_xspi_int_unmask(xspi, XSPI_INT_EN_FUC, XSPI_INT_ST_FUC_RX_FRE_FULL |
|
|
XSPI_INT_ST_FUC_INDIRECT_RD_DONE);
|
|
|
|
sdrv_xspi_indirect_trigger(xspi, read_addr, read_length, INDIRECT_READ_FLAG);
|
|
|
|
sdrv_xspi_wait_xfer_done(nor);
|
|
if (FLASH_OPT_FAILED == nor->xfer_info_bottom.opt_result) {
|
|
return -1;
|
|
}
|
|
|
|
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
|
|
return sdrv_para_data_parse(local_buf, buf, read_length);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi nor write register command
|
|
* @param[in] nor spi nor ptr
|
|
* @param[in] cmd spi nor cmd
|
|
* @param[in] addr flash write address
|
|
* @param[in] buf write data buf
|
|
* @param[in] len write data length
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_command_write(struct spi_nor *nor, struct spi_nor_cmd *cmd,
|
|
flash_addr_t addr, const uint8_t *buf,
|
|
flash_size_t len)
|
|
{
|
|
uint32_t tx_size;
|
|
uint32_t irq_trig_level;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
uint8_t *ptr = (uint8_t *)buf;
|
|
uint8_t local_buf[32] = {0};
|
|
flash_size_t write_length = len;
|
|
flash_addr_t write_addr = addr + nor->offset_address;
|
|
|
|
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
|
|
if (len != 0u) {
|
|
write_length = len * 2u;
|
|
sdrv_para_data_prep(buf, local_buf, write_length);
|
|
ptr = local_buf;
|
|
}
|
|
|
|
write_addr = addr * 2u;
|
|
}
|
|
|
|
sdrv_xspi_setup_xfer(nor, FLASH_OPT_WRITE, write_addr, ptr, write_length);
|
|
|
|
sdrv_xspi_proto_setup(nor, cmd, XSPI_INDIRECT_ACCESS_MODE, SPI_NOR_OPS_WRITE);
|
|
|
|
if ((nor->xfer_info_bottom.buf != NULL) &&
|
|
(nor->xfer_info_bottom.size > 0u)) {
|
|
tx_size = sdrv_xspi_write_fifo(xspi, nor->xfer_info_bottom.buf,
|
|
nor->xfer_info_bottom.size);
|
|
nor->xfer_info_bottom.buf += tx_size;
|
|
nor->xfer_info_bottom.size -= tx_size;
|
|
}
|
|
|
|
sdrv_xspi_indirect_trigger(xspi, write_addr, write_length, INDIRECT_WRITE_FLAG);
|
|
|
|
sdrv_xspi_int_unmask(xspi, XSPI_INT_EN_FUC, XSPI_INT_ST_FUC_INDIRECT_WR_DONE);
|
|
|
|
if (buf != NULL) {
|
|
irq_trig_level = MIN(xspi->fifo_depth, DIV_ROUND_UP(write_length, 4u));
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_INDIRECT_WR_DMA,
|
|
XSPI_INDIRECT_DMA_TRIG_WML_LSB, 8u, irq_trig_level);
|
|
}
|
|
|
|
sdrv_xspi_wait_xfer_done(nor);
|
|
if (FLASH_OPT_FAILED == nor->xfer_info_bottom.opt_result) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi irq handler function
|
|
* @param[in] irq irq num
|
|
* @param[in] arg spi nor host context
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
__UNUSED static int sdrv_xspi_irq_handler(uint32_t irq, void *arg)
|
|
{
|
|
struct spi_nor_host *host = arg;
|
|
struct xspi_pdata *xspi = host->priv_data;
|
|
struct spi_nor *nor = host->dev;
|
|
uint32_t status;
|
|
|
|
status = xspi_readl(xspi->apb_base + XSPI_INT_ST_ERR);
|
|
writel(status, xspi->apb_base + XSPI_INT_ST_ERR);
|
|
|
|
status = xspi_readl(xspi->apb_base + XSPI_INT_ST_FUC);
|
|
ssdk_printf(SSDK_DEBUG, "xspi irq status = 0x%x\r\n", status);
|
|
|
|
if ((status & (XSPI_INT_ST_FUC_IND_COMP)) != 0u) {
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_COMPLETE;
|
|
|
|
if (nor->async_mode) {
|
|
if (nor->event_handler)
|
|
nor->event_handler(nor->xfer_info_bottom.opt_type,
|
|
nor->xfer_info_bottom.opt_result);
|
|
}
|
|
else {
|
|
g_enable_event = 1;
|
|
}
|
|
|
|
sdrv_xspi_writel(0u, xspi->apb_base + XSPI_INT_EN_FUC);
|
|
}
|
|
else if (status &
|
|
(XSPI_INT_ST_FUC_FIFO_LEVEL_TRIG | XSPI_INT_ST_FUC_INDIRECT_RD_DONE)) {
|
|
sdrv_xspi_polling_handler((void *)nor);
|
|
}
|
|
|
|
writel(status, xspi->apb_base + XSPI_INT_ST_FUC);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi r/w prepare setup
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] ops r/w ops flags
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_lock(struct spi_nor *nor, enum spi_nor_ops ops)
|
|
{
|
|
__UNUSED struct xspi_pdata *xspi = nor->host->priv_data;
|
|
sdrv_xspi_indr_pre_trans_clean(nor);
|
|
|
|
if (NULL == nor->host->dev) {
|
|
nor->host->dev = nor;
|
|
sdrv_xspi_nor_setup(nor, ops);
|
|
|
|
#if CONFIG_IRQ
|
|
|
|
if (nor->xfer_mode != SPI_NOR_XFER_POLLING_MODE) {
|
|
ssdk_printf(SSDK_INFO, "irq enable!\r\n");
|
|
irq_enable(xspi->irq);
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi r/w unprepare setup
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] ops r/w ops flags
|
|
*/
|
|
static void sdrv_xspi_unlock(struct spi_nor *nor, enum spi_nor_ops ops)
|
|
{
|
|
__UNUSED struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
#if CONFIG_IRQ
|
|
|
|
if (nor->xfer_mode != SPI_NOR_XFER_POLLING_MODE) {
|
|
ssdk_printf(SSDK_INFO, "irq disable!\r\n");
|
|
irq_disable(xspi->irq);
|
|
}
|
|
|
|
#endif
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_MISC, 8u, 1u, 0);
|
|
|
|
nor->host->dev = NULL;
|
|
}
|
|
|
|
static inline void sdrv_readsl(uint32_t *addr, uint32_t *data, uint32_t len)
|
|
{
|
|
while (len-- != 0u) {
|
|
*data++ = readl(addr++);
|
|
}
|
|
}
|
|
|
|
static void sdrv_xspi_cache_flush(struct spi_nor *nor, flash_addr_t addr,
|
|
flash_size_t length)
|
|
{
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
if (nor->host->xip_mode) {
|
|
return; // xip mode can not flush cache
|
|
}
|
|
|
|
arch_invalidate_cache_range((addr_t)xspi->direct_base + addr + nor->offset_address,
|
|
ROUNDUP(length, CONFIG_ARCH_CACHE_LINE));
|
|
sdrv_xspi_prefetch_flush(xspi, 0);
|
|
sdrv_xspi_prefetch_flush(xspi, 1);
|
|
}
|
|
|
|
/**
|
|
* @brief xspi read flash data
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] addr flash address
|
|
* @param[out] buf read flash data to dest address
|
|
* @param[in] size read flash size
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_read(struct spi_nor *nor, flash_addr_t addr, uint8_t *buf,
|
|
flash_size_t size)
|
|
{
|
|
uint32_t irq_trig_level;
|
|
uint32_t mask = 0u;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
flash_size_t limit_size = size;
|
|
flash_size_t read_size = 0;
|
|
flash_addr_t current_addr = addr + nor->offset_address;
|
|
flash_size_t size_done = 0;
|
|
uint32_t rel_data;
|
|
|
|
if (!IS_ALIGNED(addr, 4) || !IS_ALIGNED(buf, 4)) {
|
|
return SPI_NOR_ADDRESS_INVALID;
|
|
}
|
|
|
|
if (addr < xspi->direct_range_size && XSPI_USE_DIRECT_MODE
|
|
&& !nor->async_mode) {
|
|
/* direct access */
|
|
arch_invalidate_cache_range((addr_t)xspi->direct_base + current_addr, ROUNDUP(size,
|
|
32));
|
|
// TODO: for hyperflash not support 1byte access, need a new memcpy
|
|
// memcpy(buf, (uint8_t *)xspi->direct_base + addr, size);
|
|
sdrv_readsl((uint32_t *)(xspi->direct_base + (uint32_t)current_addr), (uint32_t *)buf,
|
|
size / 4);
|
|
|
|
if (0 != (size % 4)) {
|
|
sdrv_readsl((uint32_t *)(xspi->direct_base + (uint32_t)current_addr + (uint32_t)size / 4 * 4), &rel_data, 1);
|
|
memcpy(buf + (uint32_t)size / 4 * 4, &rel_data, size % 4);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else {
|
|
if (!IS_ALIGNED(size, 4)) {
|
|
return SPI_NOR_LENGTH_INVALID;
|
|
}
|
|
|
|
#if CONFIG_E3L
|
|
if (IS_P0)
|
|
#endif
|
|
{
|
|
if ((nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE)) {
|
|
limit_size = xspi->fifo_depth * xspi->fifo_width;
|
|
}
|
|
}
|
|
|
|
while (size_done < size) {
|
|
if ((size - size_done) > limit_size) {
|
|
read_size = limit_size;
|
|
} else {
|
|
read_size = size - size_done;
|
|
}
|
|
sdrv_xspi_setup_xfer(nor, FLASH_OPT_READ, current_addr, buf + size_done, read_size);
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_READ);
|
|
|
|
if (xspi->fifo_depth & (xspi->fifo_depth - 1)) {
|
|
irq_trig_level = MIN(xspi->fifo_depth, (size / 4u));
|
|
} else {
|
|
irq_trig_level = MIN(xspi->fifo_depth / 2u, (size / 4u));
|
|
}
|
|
irq_trig_level = 1u << _fls(irq_trig_level);
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_INDIRECT_RD_DMA,
|
|
XSPI_INDIRECT_DMA_TRIG_WML_LSB, 8u, irq_trig_level);
|
|
#if CONFIG_XSPI_ENABLE_DMA
|
|
if (nor->xfer_mode == SPI_NOR_XFER_DMA_MODE) {
|
|
arch_invalidate_cache_range((addr_t)buf + size_done, ROUNDUP(read_size, 32));
|
|
sdrv_xspi_dma_enable(xspi);
|
|
ssdk_printf(SSDK_DEBUG, "indirect read length %lld, dma trig level %d\r\n", read_size,
|
|
irq_trig_level);
|
|
if(nor->dma_xfer_config){
|
|
nor->dma_xfer_config(nor, current_addr, buf + size_done, read_size, true, false);
|
|
}
|
|
mask |= XSPI_INT_ST_FUC_INDIRECT_DMA_RD_DONE;
|
|
}
|
|
else {
|
|
mask |= XSPI_INT_ST_FUC_RX_FRE_FULL | XSPI_INT_ST_FUC_INDIRECT_RD_DONE;
|
|
}
|
|
|
|
#else
|
|
mask |= XSPI_INT_ST_FUC_RX_FRE_FULL | XSPI_INT_ST_FUC_INDIRECT_RD_DONE;
|
|
#endif
|
|
|
|
sdrv_xspi_int_unmask(xspi, XSPI_INT_EN_FUC, mask);
|
|
sdrv_xspi_indirect_trigger(xspi, current_addr, read_size, INDIRECT_READ_FLAG);
|
|
|
|
if (!nor->async_mode) {
|
|
sdrv_xspi_wait_xfer_done(nor);
|
|
#if CONFIG_XSPI_ENABLE_DMA
|
|
|
|
if (nor->xfer_mode == SPI_NOR_XFER_DMA_MODE) {
|
|
sdrv_xspi_dma_disable(xspi);
|
|
/* stop channel transfer */
|
|
if (nor->dma_xfer_stop) {
|
|
nor->dma_xfer_stop();
|
|
} else if (nor->dma_stop) {
|
|
nor->dma_stop(nor);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (FLASH_OPT_FAILED == nor->xfer_info_bottom.opt_result) {
|
|
break;
|
|
} else {
|
|
current_addr += read_size;
|
|
size_done += read_size;
|
|
}
|
|
}
|
|
return nor->xfer_info_bottom.opt_result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi write flash data
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] addr flash address
|
|
* @param[in] buf write flash data to dest address
|
|
* @param[in] size write flash size
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_write(struct spi_nor *nor, flash_addr_t addr,
|
|
const uint8_t *buf, flash_size_t size)
|
|
{
|
|
int ret;
|
|
uint32_t irq_trig_level;
|
|
uint32_t mask = 0u;
|
|
uint32_t addr_tmp = addr + nor->offset_address;
|
|
uint32_t buf_index = 0;
|
|
uint32_t size_tmp = size;
|
|
uint32_t write_size = 0;
|
|
bool hyperram_mode = false;
|
|
|
|
hyperram_mode = nor->mem_type == SPI_HYPERRAM ? true : false;
|
|
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if (!IS_ALIGNED(addr, 4) || !IS_ALIGNED(buf, 4)) {
|
|
return SPI_NOR_ADDRESS_INVALID;
|
|
} else if (!IS_ALIGNED(size, 4)) {
|
|
return SPI_NOR_LENGTH_INVALID;
|
|
}
|
|
|
|
if (addr < xspi->direct_range_size && XSPI_USE_DIRECT_MODE && hyperram_mode
|
|
&& !nor->async_mode) {
|
|
/* direct access */
|
|
memcpy((uint8_t *)xspi->direct_base + addr_tmp, buf, size);
|
|
arch_clean_invalidate_cache_range((addr_t)xspi->direct_base + addr_tmp,
|
|
ROUNDUP(size, 32));
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_COMPLETE;
|
|
}
|
|
else {
|
|
sdrv_xspi_cache_flush(nor, addr, size);
|
|
#if CONFIG_XSPI_ENABLE_DMA
|
|
if (nor->xfer_mode == SPI_NOR_XFER_DMA_MODE) {
|
|
arch_clean_cache_range((addr_t)buf, ROUNDUP(size, 32));
|
|
}
|
|
#endif
|
|
|
|
while (size_tmp > 0) {
|
|
write_size = MIN((nor->info.page_size - addr_tmp % nor->info.page_size),
|
|
size_tmp);
|
|
sdrv_spi_nor_write_enable(nor, true);
|
|
|
|
sdrv_xspi_setup_xfer(nor, FLASH_OPT_WRITE, addr_tmp, (uint8_t *)buf + buf_index,
|
|
write_size);
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_WRITE);
|
|
|
|
if (xspi->fifo_depth & (xspi->fifo_depth - 1)) {
|
|
irq_trig_level = MIN(xspi->fifo_depth, (size / 4u));
|
|
} else {
|
|
irq_trig_level = MIN(xspi->fifo_depth / 2u, (size / 4u));
|
|
}
|
|
irq_trig_level = 1u << _fls(irq_trig_level);
|
|
|
|
RMWREG32(xspi->apb_base + XSPI_INDIRECT_WR_DMA,
|
|
XSPI_INDIRECT_DMA_TRIG_WML_LSB, 8u, irq_trig_level);
|
|
#if CONFIG_XSPI_ENABLE_DMA
|
|
if (nor->xfer_mode == SPI_NOR_XFER_DMA_MODE) {
|
|
sdrv_xspi_dma_enable(xspi);
|
|
if(nor->dma_xfer_config){
|
|
nor->dma_xfer_config(nor, addr_tmp, (uint8_t *)buf + buf_index, write_size, false, irq_trig_level);
|
|
}
|
|
|
|
mask |= XSPI_INT_ST_FUC_INDIRECT_WR_DONE;
|
|
}
|
|
else {
|
|
mask |= XSPI_INT_ST_FUC_TX_FRE_EMPTY | XSPI_INT_ST_FUC_INDIRECT_WR_DONE;
|
|
}
|
|
#else
|
|
mask |= XSPI_INT_ST_FUC_TX_FRE_EMPTY | XSPI_INT_ST_FUC_INDIRECT_WR_DONE;
|
|
#endif
|
|
sdrv_xspi_indirect_trigger(xspi, addr_tmp, write_size, INDIRECT_WRITE_FLAG);
|
|
sdrv_xspi_int_unmask(xspi, XSPI_INT_EN_FUC, mask);
|
|
|
|
if (!nor->async_mode) {
|
|
sdrv_xspi_wait_xfer_done(nor);
|
|
#if CONFIG_XSPI_ENABLE_DMA
|
|
if (nor->xfer_mode == SPI_NOR_XFER_DMA_MODE) {
|
|
|
|
sdrv_xspi_dma_disable(xspi);
|
|
/* stop channel transfer */
|
|
if(nor->dma_xfer_stop){
|
|
nor->dma_xfer_stop();
|
|
} else if (nor->dma_stop) {
|
|
nor->dma_stop(nor);
|
|
}
|
|
}
|
|
#endif
|
|
/* wait for flash idle */
|
|
do {
|
|
ret = sdrv_spi_nor_wait_idle(nor);
|
|
|
|
if (ret) {
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_FAILED;
|
|
}
|
|
}
|
|
while (s_lockstep_err != 0u);
|
|
}
|
|
|
|
if (FLASH_OPT_FAILED == nor->xfer_info_bottom.opt_result) {
|
|
break;
|
|
}
|
|
addr_tmp += write_size;
|
|
size_tmp -= write_size;
|
|
buf_index += write_size;
|
|
}
|
|
|
|
return nor->xfer_info_bottom.opt_result;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi erase flash data
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] offset erase flash address
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
int sdrv_xspi_erase(struct spi_nor *nor, flash_addr_t offset)
|
|
{
|
|
int ret;
|
|
flash_addr_t erase_addr = offset;
|
|
|
|
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
|
|
erase_addr = offset / 2u;
|
|
}
|
|
|
|
sdrv_xspi_cache_flush(nor, erase_addr, nor->info.sector_size);
|
|
|
|
struct spi_nor_cmd write_enable_cmd = {
|
|
.opcode = 0x6u,
|
|
.addr_bytes = 0,
|
|
/* the write enable cmd inst_width need aline with erase cmd */
|
|
.inst_type = SNOR_INST_LANS(nor->reg_proto),
|
|
};
|
|
|
|
ret = sdrv_xspi_command_write(nor, &write_enable_cmd, 0, NULL, 0);
|
|
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
struct spi_nor_cmd erase_cmd = {
|
|
.opcode = (uint8_t)(nor->info.erase_proto >> SNOR_OPCODE_PROTO_LSB),
|
|
.addr_bytes = (uint8_t)nor->addr_width,
|
|
/* the write enable cmd inst_width need aline with erase cmd */
|
|
.inst_type = SNOR_INST_LANS(nor->reg_proto),
|
|
};
|
|
|
|
ret = sdrv_xspi_command_write(nor, &erase_cmd, erase_addr, NULL, 0);
|
|
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
/* wait for flash idle */
|
|
do {
|
|
ret = sdrv_spi_nor_wait_idle(nor);
|
|
|
|
if (ret) {
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_FAILED;
|
|
}
|
|
}
|
|
while (s_lockstep_err != 0u);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sdrv_xspi_complex_erase(struct spi_nor *nor, struct spi_nor_erase_cmd *cmd)
|
|
{
|
|
int ret;
|
|
flash_addr_t erase_addr = cmd->addr;
|
|
|
|
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
|
|
erase_addr = cmd->addr / 2u;
|
|
}
|
|
|
|
sdrv_xspi_cache_flush(nor, erase_addr, cmd->map->erase_size);
|
|
|
|
struct spi_nor_cmd write_enable_cmd = {
|
|
.opcode = 0x6u,
|
|
.addr_bytes = 0,
|
|
/* the write enable cmd inst_width need aline with erase cmd */
|
|
.inst_type = SNOR_INST_LANS(nor->reg_proto),
|
|
};
|
|
|
|
ret = sdrv_xspi_command_write(nor, &write_enable_cmd, 0, NULL, 0);
|
|
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
struct spi_nor_cmd erase_cmd = {
|
|
.opcode = (uint8_t)(cmd->map->erase_proto >> SNOR_OPCODE_PROTO_LSB),
|
|
.addr_bytes = (uint8_t)nor->addr_width,
|
|
/* the write enable cmd inst_width need aline with erase cmd */
|
|
.inst_type = SNOR_INST_LANS(nor->reg_proto),
|
|
};
|
|
|
|
ret = sdrv_xspi_command_write(nor, &erase_cmd, erase_addr, NULL, 0);
|
|
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
/* wait for flash idle */
|
|
do {
|
|
ret = sdrv_spi_nor_wait_idle(nor);
|
|
|
|
if (ret) {
|
|
nor->xfer_info_bottom.opt_result = FLASH_OPT_FAILED;
|
|
}
|
|
}
|
|
while (s_lockstep_err != 0u);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi nor training flash and verify the correctness of the data
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] addr training flash address
|
|
* @param[in] buf read buf address
|
|
* @param[in] pattern training pattern data
|
|
* @param[in] size training data size
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_do_training(struct spi_nor *nor, flash_addr_t addr,
|
|
uint8_t *buf, const uint8_t *pattern,
|
|
flash_size_t size)
|
|
{
|
|
int ret = 0;
|
|
uint32_t rx_delay;
|
|
uint32_t rx_delay_start = 0;
|
|
uint32_t training_steps;
|
|
bool train_ok = false;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
#ifdef XSPI_DLL_USE_OVERRIDE_MODE
|
|
training_steps = 0x7fu;
|
|
#else
|
|
training_steps = 0x1fu;
|
|
#endif
|
|
|
|
sdrv_xspi_prefetch_disable(xspi);
|
|
|
|
for (rx_delay = 0u; rx_delay <= training_steps; rx_delay++) {
|
|
sdrv_xspi_dll_enable(xspi, 0u, rx_delay, true);
|
|
sdrv_xspi_dll_enable(xspi, 1u, rx_delay, true);
|
|
sdrv_spi_nor_read(nor, addr, buf, size);
|
|
|
|
s_lockstep_err = xspi_readl(xspi->apb_base + XSPI_LSP_ERR_ST);
|
|
|
|
if (s_lockstep_err != 0) {
|
|
writel(s_lockstep_err, xspi->apb_base + XSPI_LSP_ERR_ST);
|
|
ssdk_printf(SSDK_NOTICE, "xspi lsp error status = 0x%x\r\n", s_lockstep_err);
|
|
}
|
|
|
|
if (memcmp(buf, pattern, (size_t)size) == 0) {
|
|
if (train_ok == false) {
|
|
train_ok = true;
|
|
rx_delay_start = rx_delay;
|
|
}
|
|
|
|
ssdk_printf(SSDK_INFO, "xspi phy training succeful, rx_delay = %d \r\n",
|
|
rx_delay);
|
|
}
|
|
else {
|
|
if (train_ok && (rx_delay > (rx_delay_start + 10u)))
|
|
break;
|
|
else
|
|
train_ok = false;
|
|
}
|
|
}
|
|
|
|
if (train_ok) {
|
|
#if (CONFIG_E3 || CONFIG_D3)
|
|
if (IS_1P0) {
|
|
/* For xspi dll first delay sell bigger than others, not use the training window's centre */
|
|
rx_delay = (rx_delay_start + rx_delay) / 2u - 4u; // for 1.0
|
|
} else
|
|
#endif /* CONFIG_E3 || CONFIG_D3 */
|
|
{
|
|
rx_delay = (rx_delay_start + rx_delay) / 2u;
|
|
}
|
|
sdrv_xspi_dll_enable(xspi, 0u, rx_delay, true);
|
|
sdrv_xspi_dll_enable(xspi, 1u, rx_delay, true);
|
|
ret = -1;
|
|
if (0 != sdrv_spi_nor_read(nor, addr, buf, size)) {
|
|
ssdk_printf(SSDK_INFO, "training read failed\r\n");
|
|
} else if (memcmp(buf, pattern, (size_t)size) != 0) {
|
|
ssdk_printf(SSDK_INFO, "training read back check failed!\r\n");
|
|
if (0 != sdrv_spi_nor_read(nor, addr, buf, size)) {
|
|
ssdk_printf(SSDK_INFO, "training read failed\r\n");
|
|
} else if (memcmp(buf, pattern, (size_t)size) != 0) {
|
|
ssdk_printf(SSDK_INFO, "training read back check(after try again)!\r\n");
|
|
} else {
|
|
ssdk_printf(SSDK_INFO, "xspi phy training pass(after try again)!\r\n");
|
|
ret = 0;
|
|
}
|
|
} else {
|
|
ssdk_printf(SSDK_INFO, "xspi phy training pass!\r\n");
|
|
ret = 0;
|
|
}
|
|
}
|
|
else {
|
|
sdrv_xspi_dll_enable(xspi, 0u, 0u, false);
|
|
sdrv_xspi_dll_enable(xspi, 1u, 0u, false);
|
|
ssdk_printf(SSDK_ERR, "xspi phy training failed, disable dll!\r\n");
|
|
ret = -1;
|
|
}
|
|
|
|
if (SPI_HYPERRAM != nor->mem_type) {
|
|
sdrv_xspi_prefetch_enable(xspi);
|
|
}
|
|
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_WRITE);
|
|
sdrv_xspi_proto_setup(nor, NULL, XSPI_INDIRECT_ACCESS_MODE,
|
|
SPI_NOR_OPS_READ);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void sdrv_spi_nor_drv_main_function(struct spi_nor *nor)
|
|
{
|
|
if (nor->xfer_mode == SPI_NOR_XFER_POLLING_MODE) {
|
|
sdrv_xspi_polling_handler(nor);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi nor host provide callback function
|
|
*/
|
|
struct spi_nor_host_ops xspi_host_ops = {
|
|
.reg_read = sdrv_xspi_command_read,
|
|
.reg_write = sdrv_xspi_command_write,
|
|
.read = sdrv_xspi_read,
|
|
.write = sdrv_xspi_write,
|
|
.erase = sdrv_xspi_erase,
|
|
.complex_erase = sdrv_xspi_complex_erase,
|
|
.training = sdrv_xspi_do_training,
|
|
.prepare = sdrv_xspi_lock,
|
|
.unprepare = sdrv_xspi_unlock,
|
|
.cache_flush = sdrv_xspi_cache_flush,
|
|
.enable_rfd = sdrv_xspi_rfd_en,
|
|
.xip_proto_setup = sdrv_xspi_xip_proto_setup,
|
|
};
|
|
|
|
/**
|
|
* @brief xspi hyper command read
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] addr flash register addr
|
|
* @param[out] buf 2byte data
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_hyper_read16(struct spi_nor *nor, flash_addr_t addr,
|
|
uint16_t *buf)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct spi_nor_cmd read_cmd = {
|
|
.opcode = 0,
|
|
.dummy = nor->info.read_dummy,
|
|
.addr_bytes = nor->addr_width,
|
|
.inst_type = SNOR_INST_LANS(nor->reg_proto),
|
|
};
|
|
|
|
ret = sdrv_xspi_command_read(nor, &read_cmd, addr * 2u, (uint8_t *)buf, 2u);
|
|
|
|
if (ret)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi hyper command write
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] addr flash register addr
|
|
* @param[in] buf 2byte data
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
static int sdrv_xspi_hyper_write16(struct spi_nor *nor, flash_addr_t addr,
|
|
uint16_t *buf)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct spi_nor_cmd write_cmd = {
|
|
.opcode = 0,
|
|
.dummy = 0,
|
|
.addr_bytes = nor->addr_width,
|
|
.inst_type = SNOR_INST_LANS(nor->reg_proto),
|
|
};
|
|
|
|
ret = sdrv_xspi_command_write(nor, &write_cmd, addr * 2u, (uint8_t *)buf, 2u);
|
|
|
|
if (ret)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief xspi hyper setup before transaction
|
|
* @param[in] nor spi_nor ptr
|
|
* @param[in] is_read read or write mode
|
|
* @param[in] is_direct direct or indirect mode
|
|
* @param[in] num hyperbus_ca trans number
|
|
* @param[in] ca_array hyperbus_ca ptr
|
|
*/
|
|
static void sdrv_xspi_hyper_set_pre_transaction(struct spi_nor *nor,
|
|
bool is_read,
|
|
bool is_direct, uint32_t num, struct hyperbus_ca **ca_array)
|
|
{
|
|
uint32_t addr;
|
|
uint16_t data;
|
|
addr_t ptc_addr;
|
|
addr_t pta_base_addr;
|
|
addr_t ptd_base_addr;
|
|
struct hyperbus_ca *ca_ptr = *ca_array;
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
|
|
if (nor->mem_type == SPI_HYPERBUS_MCP) {
|
|
for (int i = 0u; i < num; i++) {
|
|
addr = ca_ptr->addr;
|
|
data = ca_ptr->data;
|
|
sdrv_xspi_hyper_write16(nor, addr, &data);
|
|
ca_ptr += 1;
|
|
}
|
|
}
|
|
else {
|
|
if (is_read) {
|
|
if (is_direct) {
|
|
ptc_addr = xspi->apb_base + XSPI_DIRECT_RD_PTC;
|
|
pta_base_addr = xspi->apb_base + XSPI_DIRECT_RD_PTA;
|
|
ptd_base_addr = xspi->apb_base + XSPI_DIRECT_RD_PTD;
|
|
}
|
|
else {
|
|
ptc_addr = xspi->apb_base + XSPI_INDR_RD_PTC;
|
|
pta_base_addr = xspi->apb_base + XSPI_INDR_RD_PTA;
|
|
ptd_base_addr = xspi->apb_base + XSPI_INDR_RD_PTD;
|
|
}
|
|
}
|
|
else {
|
|
if (is_direct) {
|
|
ptc_addr = xspi->apb_base + XSPI_DIRECT_WR_PTC;
|
|
pta_base_addr = xspi->apb_base + XSPI_DIRECT_WR_PTA;
|
|
ptd_base_addr = xspi->apb_base + XSPI_DIRECT_WR_PTD;
|
|
}
|
|
else {
|
|
ptc_addr = xspi->apb_base + XSPI_INDR_WR_PTC;
|
|
pta_base_addr = xspi->apb_base + XSPI_INDR_WR_PTA;
|
|
ptd_base_addr = xspi->apb_base + XSPI_INDR_WR_PTD;
|
|
}
|
|
}
|
|
|
|
for (int i = 0u; i < num; i++) {
|
|
addr = ca_ptr->addr;
|
|
data = ca_ptr->data;
|
|
sdrv_xspi_writel(addr, pta_base_addr);
|
|
sdrv_xspi_writel(data, ptd_base_addr);
|
|
|
|
pta_base_addr += 0x20;
|
|
ptd_base_addr += 0x20;
|
|
ca_ptr += 1;
|
|
}
|
|
|
|
/* set pre-tansaction num */
|
|
sdrv_xspi_writel(num << 8u, ptc_addr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xspi hyperbus ops provide callback function
|
|
*/
|
|
struct hyperbus_host_ops xspi_hyperbus_ops = {
|
|
.read16 = sdrv_xspi_hyper_read16,
|
|
.write16 = sdrv_xspi_hyper_write16,
|
|
.read = sdrv_xspi_read,
|
|
.write = sdrv_xspi_write,
|
|
.hyperram_en = sdrv_xspi_hyperram_enable,
|
|
.set_pre_transaction = sdrv_xspi_hyper_set_pre_transaction,
|
|
.cache_flush = sdrv_xspi_cache_flush,
|
|
};
|
|
|
|
/**
|
|
* @brief xspi host init
|
|
*
|
|
* @param[in] host spi nor host
|
|
* @param[in] xspi xspi private info
|
|
* @param[in] config xspi config info
|
|
* @return int
|
|
* @retval 0: success
|
|
* @retval other: failed
|
|
*/
|
|
int sdrv_xspi_host_init(struct spi_nor_host *host, struct xspi_pdata *xspi,
|
|
struct xspi_config *config)
|
|
{
|
|
if (!host || !xspi || !config) {
|
|
ssdk_printf(SSDK_EMERG, "%s: point is null\r\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
memset(host, 0, sizeof(struct spi_nor_host));
|
|
memset(xspi, 0, sizeof(struct xspi_pdata));
|
|
|
|
xspi->id = config->id;
|
|
xspi->apb_base = config->apb_base;
|
|
xspi->direct_range_size = 0x8000000;
|
|
xspi->direct_base = config->direct_base;
|
|
xspi->irq = config->irq;
|
|
xspi->ref_clk_hz = config->ref_clk;
|
|
/* initialize the current cs for no one */
|
|
xspi->current_cs = 0xff;
|
|
#if CONFIG_E3L
|
|
xspi->fifo_depth = 48;
|
|
#else
|
|
xspi->fifo_depth = 16;
|
|
#endif
|
|
xspi->fifo_width = 4u;
|
|
|
|
g_enable_event = 0;
|
|
host->ref_clk_hz = config->ref_clk;
|
|
host->priv_data = xspi;
|
|
host->ops = &xspi_host_ops;
|
|
host->hyper_ops = &xspi_hyperbus_ops;
|
|
host->xip_mode = config->xip_mode;
|
|
|
|
#if CONFIG_IRQ
|
|
|
|
if (xspi->irq) {
|
|
ssdk_printf(SSDK_INFO, "xspi irq num = %d\r\n", xspi->irq);
|
|
irq_attach(xspi->irq, sdrv_xspi_irq_handler, (void *)host);
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!host->xip_mode) { // xip mode no need to do initialization
|
|
sdrv_xspi_init(xspi);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sdrv_spi_nor_drv_deinit(struct spi_nor *nor)
|
|
{
|
|
#ifdef SPI_NOR_DMA_ENABLE
|
|
// TODO
|
|
#endif
|
|
struct xspi_pdata *xspi = nor->host->priv_data;
|
|
sdrv_xspi_reset_flash(xspi);
|
|
return;
|
|
}
|