blob: 7f9d3bc9c9e73a93b1c7062326be301bb1d6b75f [file] [log] [blame]
#include "Errors.h"
#include "misc.h"
#include "timer.h"
#include "PlatformConfig.h"
#include "platform_interrupts.h"
#include "xllp_dmac.h"
#include "PMUA.h"
#ifdef NZAS
#include "APB_SPARE.h"
#endif
#include "qspi_host.h"
#include "spi_nand.h"
#define PMUA_QSPI_CLK_RES_CTRL (PMUA_BASE + 0x0060)
#if UPDATER
#undef QSPI_USE_INTR
#else
#define QSPI_USE_INTR
#endif
static struct qspi_host qspi_host;
static XLLP_DMAC_DESCRIPTOR_T dma_desc __attribute__ ((aligned (16)));
static XLLP_DMAC_DESCRIPTOR_T *tx_desc = &dma_desc;
/* qspi write with check*/
static inline void qspi_writel_check(struct qspi_host *host, uint32_t val,
int reg, uint32_t mask)
{
uint32_t tmp;
(void)host;
_writel(val, reg);
val &= ~mask;
do {
tmp = _readl(reg);
tmp &= ~mask;
if (tmp == val)
break;
} while (1);
}
/* qspi write check with auto-clear field */
static inline void qspi_writel_clear(struct qspi_host *host, uint32_t val,
int reg, uint32_t mask)
{
uint32_t tmp;
(void)host;
_writel(val, reg);
val &= ~mask;
do {
tmp = _readl(reg);
tmp &= ~mask;
if (!(tmp & val))
break;
} while (1);
}
/*
* Error Flag may happen for write access to RBCT/SFAR register,
* need retry for these two register
*/
static void qspi_write_rbct(struct qspi_host *host, uint32_t val)
{
uint32_t fr;
do {
qspi_writel_check(host, val, QSPI0_RBCT, QSPI_RBCT_RESV);
fr = _readl(QSPI0_FR);
if (!(fr & QSPI_FR_IPIEF))
break;
fr &= QSPI_FR_IPIEF;
_writel(fr, QSPI0_FR);
} while (1);
}
static void qspi_write_sfar(struct qspi_host *host, uint32_t val)
{
uint32_t fr;
do {
qspi_writel_check(host, val, QSPI0_SFAR, 0x0);
fr = _readl(QSPI0_FR);
if (!(fr & QSPI_FR_IPIEF))
break;
fr &= QSPI_FR_IPIEF;
_writel(fr, QSPI0_FR);
} while (1);
}
static void qspi_enter_mode(struct qspi_host *host, uint32_t mode)
{
uint32_t mcr;
mcr = _readl(QSPI0_MCR);
if (mode == QSPI_NORMAL_MODE)
mcr &= ~QSPI_MCR_MDIS;
else if (mode == QSPI_DISABLE_MODE)
mcr |= QSPI_MCR_MDIS;
qspi_writel_check(host, mcr, QSPI0_MCR, QSPI_MCR_RESV);
}
void qspi_init_ahb(int lut, int page_size)
{
#if 0
//Top index of BUFs
_writel(0x20, QSPI0_BUF0IND);
_writel(0x40, QSPI0_BUF1IND);
_writel(0x60, QSPI0_BUF2IND);
//128Byte, master ID?
_writel(0x00001001, QSPI0_BUF0CR);
_writel(0x1006, QSPI0_BUF1CR);
_writel(0x1003, QSPI0_BUF2CR);
_writel(0x80001002, QSPI0_BUF3CR);
#else
uint32_t buf_cfg;
int data_size;
int i = 0;
do {
i++;
data_size = page_size/(1 << i);
if (data_size <= QSPI_AHB_BUFF_MAX_SIZE)
break;
} while(1);
buf_cfg = QSPI_BUF3CR_ALLMST |
(data_size / 8) << QSPI_BUF3CR_ADATSZ_SHIFT;
/*
* Config the ahb buffer
* Disable BUF0~BUF1, use BUF3 for all masters
*/
#if LAPW
_writel((512/8 - 1 )*8, QSPI0_BUF0IND);
_writel(512, QSPI0_BUF1IND);
_writel(512, QSPI0_BUF2IND);
/* AHB Master port */
_writel(buf_cfg, QSPI0_BUF0CR); // other masters
_writel(0xe, QSPI0_BUF1CR);
_writel(0xe, QSPI0_BUF2CR);
_writel(0xe, QSPI0_BUF3CR);
#else
_writel(0, QSPI0_BUF0IND);
_writel(0, QSPI0_BUF1IND);
_writel(0, QSPI0_BUF2IND);
/* AHB Master port */
_writel(0xe, QSPI0_BUF0CR);
_writel(0xe, QSPI0_BUF1CR);
#endif
_writel(0xe, QSPI0_BUF2CR);
_writel(buf_cfg, QSPI0_BUF3CR); // other masters
#endif
_writel(lut << QSPI_BFGENCR_SEQID_SHIFT, QSPI0_BFGENCR);
}
static void qspi_lock_lut(void)
{
uint32_t lckcr;
lckcr = _readl(QSPI0_LCKCR);
if (lckcr & QSPI_LCKCR_LOCK)
return;
_writel(0x5af05af0, QSPI0_LUTKEY);
_writel(QSPI_LCKCR_LOCK, QSPI0_LCKCR);
}
static void qspi_unlock_lut(void)
{
uint32_t lckcr;
lckcr = _readl(QSPI0_LCKCR);
if (lckcr & QSPI_LCKCR_UNLOCK)
return;
_writel(LUT_KEY_VALUE, QSPI0_LUTKEY);
_writel(QSPI_LCKCR_UNLOCK, QSPI0_LCKCR);
}
static void qspi_config_lookup_tbl(struct spi_flash_chip *chip,
struct spi_flash_cmd_cfg *cmd_cfg,
uint8_t mode, int seq_id)
{
uint32_t lut_value;
uint16_t lut_entry[8];
uint8_t pins[] = {0, QSPI_PAD_1X, QSPI_PAD_2X, 0, QSPI_PAD_4X};
uint8_t seq = 0, i;
uint8_t mode_instr, dummy_cycles, addr_bytes;
int lut_addr;
/* Set Lookup table entry: CMD, ADDR, MODE, DUMMY, DATA, etc */
if (chip->qpi_enabled)
lut_entry[seq++] =
(cmd_cfg->cmd_dtr ?
QSPI_INSTR_CMD_DDR : QSPI_INSTR_CMD) << 10 |
QSPI_PAD_4X << 8 | cmd_cfg->opcode;
else
lut_entry[seq++] =
(cmd_cfg->cmd_dtr ?
QSPI_INSTR_CMD_DDR : QSPI_INSTR_CMD) << 10 |
QSPI_PAD_1X << 8 | cmd_cfg->opcode;
if (cmd_cfg->addr_bytes) {
if (chip->en_addr_4byte && cmd_cfg->addr_bytes == 3)
addr_bytes = 4;
else
addr_bytes = cmd_cfg->addr_bytes;
lut_entry[seq++] =
(cmd_cfg->addr_dtr ?
QSPI_INSTR_ADDR_DDR : QSPI_INSTR_ADDR) << 10 |
pins[cmd_cfg->addr_pins] << 8 | addr_bytes*8;
if (addr_bytes == 1)
lut_entry[seq - 1] |= 0x4000;
}
if (cmd_cfg->mode_bits) {
if (cmd_cfg->mode_bits == 2) {
mode_instr = (cmd_cfg->mode_dtr ?
QSPI_INSTR_MODE2_DDR : QSPI_INSTR_MODE2);
mode &= 0x3;
} else if (cmd_cfg->mode_bits == 4) {
mode_instr = (cmd_cfg->mode_dtr ?
QSPI_INSTR_MODE4_DDR : QSPI_INSTR_MODE4);
mode &= 0xf;
} else {
mode_instr = (cmd_cfg->mode_dtr ?
QSPI_INSTR_MODE_DDR : QSPI_INSTR_MODE);
}
lut_entry[seq++] = mode_instr << 10 |
pins[cmd_cfg->mode_pins] << 8 |
mode;
}
if (cmd_cfg->dummy_pins) {
if (chip->qpi_enabled)
dummy_cycles = chip->qpi_dummy;
else
dummy_cycles = cmd_cfg->dummy_cycles;
lut_entry[seq++] = QSPI_INSTR_DUMMY << 10 |
pins[cmd_cfg->dummy_pins] << 8 |
dummy_cycles;
}
if (cmd_cfg->data_pins) {
if (cmd_cfg->type == CMD_W_RX_DATA) {
lut_entry[seq++] =
(cmd_cfg->data_dtr ?
QSPI_INSTR_READ_DDR : QSPI_INSTR_READ) << 10 |
pins[cmd_cfg->data_pins] << 8;
/* Add JMP_ON_CS for read */
lut_entry[seq++] = QSPI_INSTR_JMP_ON_CS << 10;
} else if (cmd_cfg->type == CMD_W_TX_DATA) {
lut_entry[seq++] =
(cmd_cfg->data_dtr ?
QSPI_INSTR_WRITE_DDR : QSPI_INSTR_WRITE) << 10 |
pins[cmd_cfg->data_pins] << 8;
} else {
obm_printf("wrong cmd type %d\r\n", cmd_cfg->opcode);
return;
}
}
/* Add stop at the end */
lut_entry[seq++] = QSPI_INSTR_STOP << 10;
/*
* lut read back value may be different from write,
* so add extra read to make sure write take effect
*/
for (i = 0; i < seq/2; i++) {
lut_addr = QSPI0_LUT0 + seq_id*0x10 + i*0x4;
lut_value = lut_entry[i*2] | (lut_entry[i*2 + 1] << 16);
_writel(lut_value, lut_addr);
lut_value = _readl(lut_addr);
}
if (seq % 2) {
lut_addr = QSPI0_LUT0 + seq_id*0x10 + (seq/2)*0x4;
lut_value =lut_entry[seq - 1];
_writel(lut_value, lut_addr);
lut_value = _readl(lut_addr);
}
// for (i = 0; i < seq; i++)
// obm_printf("seq=%d, lut_entry[%d]=0x%x\n\r", seq, i, lut_entry[i]);
return;
}
static int qspi_update_shared_lut(struct spi_flash_chip *chip,
struct spi_flash_cmd *cmd)
{
struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg;
uint32_t seq_id = QSPI_LUT_SEQID1;
qspi_config_lookup_tbl(chip, cmd_cfg, cmd->mode, seq_id);
return seq_id;
}
/*
* Reserved for future optimization
* Pre-init some lookup tables for special commands to accelerate
*/
int qspi_preinit_lookup_tbl(struct spi_flash_chip *chip)
{
struct spi_flash_cmd_cfg *cmd_cfg = chip->table;
int lut_map = 0;
for (; cmd_cfg->opcode != 0x0; cmd_cfg++) {
if (cmd_cfg->seq_id != -1) {
if (lut_map & 1 << cmd_cfg->seq_id) {
obm_printf("err: LUT %d already used\r\n",
cmd_cfg->seq_id);
return -1;
}
qspi_config_lookup_tbl(chip, cmd_cfg, 0xff,
cmd_cfg->seq_id);
lut_map |= 1 << cmd_cfg->seq_id;
}
}
chip->host->lut_map = lut_map;
obm_printf("Fixed LUT bit-map: 0x%x\r\n", lut_map);
return 0;
}
int qspi_enable_xip(struct spi_flash_chip *chip,
struct spi_flash_cmd_cfg *cmd_cfg)
{
struct qspi_host *host = chip->host;
qspi_config_lookup_tbl(chip, cmd_cfg, 0xff, QSPI_LUT_SEQID0);
qspi_enter_mode(host, QSPI_DISABLE_MODE);
qspi_init_ahb(QSPI_LUT_SEQID0, chip->page_size);
qspi_enter_mode(host, QSPI_NORMAL_MODE);
if (host->use_xip) {
host->xip_read = 1;
obm_printf("XIP enabled\r\n");
} else {
host->xip_read = 0;
obm_printf("IPS enabled\r\n");
}
return 0;
}
static void qspi_invalid_ahb(struct qspi_host *host)
{
uint32_t reg;
/* qspi softreset first */
reg = _readl(QSPI0_MCR);
reg |= QSPI_MCR_SWRSTHD | QSPI_MCR_SWRSTSD;
qspi_writel_check(host, reg, QSPI0_MCR, QSPI_MCR_RESV);
/* Test show no delay is needed */
Delay(1);
//cpu_cycle_delay(10);
reg &= ~(QSPI_MCR_SWRSTHD | QSPI_MCR_SWRSTSD);
qspi_writel_check(host, reg, QSPI0_MCR, QSPI_MCR_RESV);
}
static void qspi_clk_enable(void)
{
*(VUINT_T *)PMUA_QSPI_CLK_RES_CTRL |= 0x1 << 4 | 0x1 << 3;
*(VUINT_T *)PMUA_QSPI_CLK_RES_CTRL |= 0x3;
}
static void qspi_clk_disable(void)
{
*(VUINT_T *)PMUA_QSPI_CLK_RES_CTRL &= ~(0x1 << 4 | 0x1 << 3);
}
static int qspi_set_func_clk_fc(struct qspi_host *host, int mhz)
{
uint32_t timeout = 5*1000;
uint32_t reg;
int sel, div;
/* for dtr, qspi pmu clk must be 4x bus clk */
if (host->has_dtr)
mhz = mhz << 2;
#ifdef NZAS
/* enable PLL1_DIV23 */
reg = _readl(APB_SPARE3_REG);
reg |= APB_SPARE3_REG_PLL1_DIV23_EN | APB_SPARE3_REG_PLL1_DIV13_EN |
APB_SPARE3_REG_PLL1_DIV11_EN;
_writel(reg, APB_SPARE3_REG);
#endif
#if LAPW
reg = _readl(APB_SPARE12_REG);
reg |= APB_SPARE12_REG_PLL1_DIV23_EN | APB_SPARE12_REG_PLL1_DIV13_EN |
APB_SPARE12_REG_PLL1_DIV11_EN;
_writel(reg, APB_SPARE12_REG);
#endif
/* Enabled QSPI clock, then take out of reset */
reg = _readl(PMUA_QSPI_CLK_RES_CTRL);
reg |= (PMUA_QSPI_CLK_RES_CTRL_CLK_EN |
PMUA_QSPI_CLK_RES_CTRL_AXICLK_EN);
_writel(reg, PMUA_QSPI_CLK_RES_CTRL);
reg = _readl(PMUA_QSPI_CLK_RES_CTRL);
reg |= (PMUA_QSPI_CLK_RES_CTRL_CLK_RST |
PMUA_QSPI_CLK_RES_CTRL_AXI_RST);
_writel(reg, PMUA_QSPI_CLK_RES_CTRL);
if (mhz >= 416) {
sel = 0;
div = 0;
} else if (mhz >= 312) {
div = 0;
#ifdef KSTR
sel = 1;
#else
sel = 2;
#endif
} else if (mhz >= 208) {
sel = 0;
div = 1;
} else if (mhz >= 104) {
sel = 0;
div = 3;
} else if (mhz >= 78) {
#ifdef KSTR
sel = 1;
div = 3;
#else
sel = 2;
div = 3;
#endif
} else if (mhz >= 52) {
sel = 0;
div = 7;
} else {
/* default 13M */
#ifdef KSTR
sel = 7;
div = 3;
#else
sel = 5;
div = 7;
#endif
}
reg = _readl(PMUA_QSPI_CLK_RES_CTRL);
reg &= ~(PMUA_QSPI_CLK_RES_CTRL_CLK_DIV_MSK |
PMUA_QSPI_CLK_RES_CTRL_CLK_SEL_MSK);
reg |= (div << PMUA_QSPI_CLK_RES_CTRL_CLK_DIV_BASE |
sel << PMUA_QSPI_CLK_RES_CTRL_CLK_SEL_BASE);
_writel(reg, PMUA_QSPI_CLK_RES_CTRL);
reg = _readl(PMUA_QSPI_CLK_RES_CTRL);
reg |= PMUA_QSPI_CLK_RES_CTRL_CLK_FC_REQ;
_writel(reg, PMUA_QSPI_CLK_RES_CTRL);
do {
reg = _readl(PMUA_QSPI_CLK_RES_CTRL);
if (!(reg & PMUA_QSPI_CLK_RES_CTRL_CLK_FC_REQ))
break;
Delay(1);
timeout--;
if (!timeout) {
obm_printf("qspi fc timeout!\r\n");
return QspiFcTimeOutError;
}
} while (1);
return NoError;
}
static void qspi_set_func_clk_nofc(int mhz)
{
int clk_sel;
int freq;
/* Default qspi clock is divided by 4 in PMU */
freq = mhz << 2;
freq *= 1000000;
if (freq >= 416000000)
clk_sel = QSPI_FUNC_CLK_416MHZ;
else if (freq >= 312000000)
clk_sel = QSPI_FUNC_CLK_312MHZ;
else if (freq >= 208000000)
clk_sel = QSPI_FUNC_CLK_208MHZ;
else if (freq >= 156000000)
clk_sel = QSPI_FUNC_CLK_156MHZ;
else if (freq >= 104000000)
clk_sel = QSPI_FUNC_CLK_104MHZ;
else if (freq >= 78000000)
clk_sel = QSPI_FUNC_CLK_78MHZ;
else if (freq >= 52000000)
clk_sel = QSPI_FUNC_CLK_52MHZ;
else
clk_sel = QSPI_FUNC_CLK_26MHZ;
qspi_clk_disable();
*(VUINT_T *)PMUA_QSPI_CLK_RES_CTRL &= ~(0x7 << 6);
*(VUINT_T *)PMUA_QSPI_CLK_RES_CTRL |= clk_sel << 6;
qspi_clk_enable();
}
#ifdef QSPI_SUPPORT_DQS
int qspi_config_dqs_clk(struct qspi_host *host, int dcode)
{
uint32_t reg;
reg = _readl(QSPI0_MCR);
if (dcode <= 0 || dcode > 255) {
reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN |
QSPI_MCR_DQS_INV_EN);
_writel(reg, QSPI0_MCR);
return 0;
}
/* DQS enabled, use sample point N/1 */
_writel(0x0, QSPI0_SMPR);
reg |= QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN | QSPI_MCR_DQS_INV_EN;
_writel(reg, QSPI0_MCR);
reg = _readl(QSPI0_SOCCR);
reg |= QSPI_SOCCR_DLINE_EN;
_writel(reg, QSPI0_SOCCR);
reg = _readl(QSPI0_DLACR);
reg &= ~QSPI_DLACR_DLINE_STEP_MASK;
reg = 0x7 << QSPI_DLACR_DLINE_STEP_SHIFT;
reg |= dcode & QSPI_DLACR_DLINE_CODE_MASK;
_writel(reg, QSPI0_DLACR);
qspi_invalid_ahb(host);
return 1;
}
void qspi_config_disable_dqs(struct qspi_host *host)
{
uint32_t reg;
qspi_enter_mode(host, QSPI_DISABLE_MODE);
reg = _readl(QSPI0_MCR);
reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN |
QSPI_MCR_DQS_INV_EN);
_writel(reg, QSPI0_MCR);
qspi_enter_mode(host, QSPI_NORMAL_MODE);
qspi_invalid_ahb(host);
}
#ifdef SPI_FLASH_DQS_SCAN_WIN
void qspi_config_dqs_delay(struct qspi_host *host, int dcode)
{
uint32_t reg;
qspi_enter_mode(host, QSPI_DISABLE_MODE);
reg = _readl(QSPI0_DLACR);
reg &= ~QSPI_DLACR_DLINE_STEP_MASK;
reg = 0x7 << QSPI_DLACR_DLINE_STEP_SHIFT;
reg |= dcode & QSPI_DLACR_DLINE_CODE_MASK;
_writel(reg, QSPI0_DLACR);
qspi_enter_mode(host, QSPI_NORMAL_MODE);
qspi_invalid_ahb(host);
}
#endif
#endif
void qspi_set_func_clk(struct qspi_host *host, int mhz, int use_dtr,
int tclqv, int tset, int thold)
{
uint32_t t = 1000000 / mhz; /* in ps */
uint32_t reg;
int delay = 0;
int dcode;
#if LAPW || FACT || KSTRZ
qspi_set_func_clk_fc(host, mhz);
#else
qspi_set_func_clk_nofc(mhz);
#endif
host->bus_clk = mhz;
obm_printf("Bus clock: %dMHz QSPI_CLK_RES_CTRL: 0x%x\r\n",
mhz, *(VUINT_T *)PMUA_QSPI_CLK_RES_CTRL);
/* clock settings */
qspi_enter_mode(host, QSPI_DISABLE_MODE);
if (tclqv == 0) {
tclqv = 8;
tset = thold = 2;
}
delay = (tclqv + tset + 1) * 1000;
if (delay <= t)
reg = 0; /* sample point N1 */
else
reg = QSPI_SMPR_FSPHS; /* sample point I1 */
if (use_dtr) {
int ddr_point;
delay -= t/2;
if (delay > 0)
ddr_point = (delay + t/8 - 1) / (t/8);
else
ddr_point = 0;
reg |= ddr_point << QSPI_SMPR_DDRSMP_SHIFT;
}
#ifdef QSPI_SUPPORT_DQS
else if (host->support_dqs && mhz > 52) {
/*
* Do not use QDS for DDR, since SDR/DDR can not share
* same delay code.
* If DQS enabled, must use sample point N/1, clear SMPR.
*
* delay step: 52ps
*/
delay = tclqv * 1000 - t/2;
dcode = delay / 52;
if (qspi_config_dqs_clk(host, dcode))
reg = 0;
}
#endif
_writel(reg, QSPI0_SMPR);
reg = _readl(QSPI0_SMPR);
obm_printf("QSPI_SMPR=0x%x t=%dps tclqv=%dns delay=%dps\r\n",
reg, t, tclqv, delay);
/* set tx hold time */
reg = 0x202;
if (use_dtr)
reg |= QSPI_FLSHCR_TDH_HALF_2X;
_writel(reg, QSPI0_FLSHCR);
reg = _readl(QSPI0_FLSHCR);
/* Module enabled */
qspi_enter_mode(host, QSPI_NORMAL_MODE);
}
static void qspi_enable_dma(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr |= QSPI_RSER_TBFDE;
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
}
static void qspi_disable_dma(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr &= ~QSPI_RSER_TBFDE;
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
}
#ifdef QSPI_USE_INTR
static void qspi_config_interrupt(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
#if LAPW
resr |= QSPI_RSER_ILLINIE | QSPI_RSER_ABSEIE | QSPI_RSER_AITIE |
QSPI_RSER_AIBSIE | QSPI_RSER_IUEIE |
QSPI_RSER_IPIEIE | QSPI_RSER_IPGEIE;
#else
resr |= QSPI_RSER_ILLINIE | QSPI_RSER_ABSEIE | QSPI_RSER_AITIE |
QSPI_RSER_AIBSIE | QSPI_RSER_ABOIE | QSPI_RSER_IUEIE |
QSPI_RSER_IPIEIE | QSPI_RSER_IPGEIE;
#endif
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
}
static void qspi_enable_interrupt(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr |= QSPI_RSER_TFIE;
if (cmd->n_tx) {
resr |= QSPI_RSER_TBUIE;
if (!host->use_dma && host->bytes_left > 0) {
resr |= QSPI_RSER_TBFIE;
}
} else if (cmd->n_rx) {
resr |= QSPI_RSER_RBOIE | QSPI_RSER_RBDIE;
}
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
}
static void qspi_disable_interrupt(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t resr;
(void)host;
resr = _readl(QSPI0_RSER);
resr &= ~QSPI_RSER_TFIE;
if (cmd->n_tx)
resr &= ~(QSPI_RSER_TBUIE | QSPI_RSER_TBFIE);
else if (cmd->n_rx)
resr &= ~(QSPI_RSER_RBOIE | QSPI_RSER_RBDIE);
if (host->use_dma)
resr &= ~QSPI_RSER_TBFDE;
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
}
static int qspi_fill_to_txbuff(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
int left_bytes = host->bytes_left;
int total_cnt;
uint32_t reg;
uint32_t data;
if (!cmd) {
obm_printf("unexpected tx intr\n");
return 1;
}
total_cnt = ALIGN_UP(cmd->n_tx, QSPI_TX_BUFF_POP_MIN);
while (left_bytes > 0) {
_writel(QSPI_FR_TBFF, QSPI0_SR);
reg = _readl(QSPI0_SR);
if (reg & QSPI_FR_TBFF)
break;
data = 0;
if (cmd->n_tx - total_cnt + left_bytes >= 4)
data = (*(uint32_t *)&cmd->tx_buf[total_cnt - left_bytes]);
else if (cmd->n_tx - total_cnt + left_bytes > 0)
memcpy(&data, &cmd->tx_buf[total_cnt - left_bytes],
left_bytes);
_writel(data, QSPI0_TBDR);
left_bytes -= 4;
}
host->bytes_left = left_bytes;
if (left_bytes <= 0)
return 0;
return 1;
}
static void qspi_read_from_rxbuff(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
int left_bytes = host->bytes_left;
uint32_t sr;
uint32_t data;
int i;
while (left_bytes > 0) {
sr = _readl(QSPI0_FR);
if (!(sr & QSPI_FR_RBDF))
break;
/* Check RXWE flag for data comming */
for (i = 0; i <= host->wmrk; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
//obm_printf("i=%d data=0x%x\r\n", i , data);
if (left_bytes >= 4)
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, left_bytes);
left_bytes -= 4;
}
/* Set RBDF to trigger RX Buffer POP */
_writel(0x1 << 16, QSPI0_FR);
}
host->bytes_left = left_bytes;
return;
}
static void qspi_xfer_done(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
int left_bytes = host->bytes_left;
uint32_t rdbfl;
uint32_t data;
uint32_t i;
if (host->use_dma)
left_bytes = 0;
if (cmd->n_rx && left_bytes > 0) {
rdbfl = _readl(QSPI0_RBSR);
rdbfl &= QSPI_RBSR_RDBFL_MASK;
rdbfl = rdbfl >> QSPI_RBSR_RDBFL_SHIFT;
for (i = 0; i <= rdbfl; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
if (left_bytes >= 4)
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, left_bytes);
left_bytes -= 4;
}
if (left_bytes > 0) {
obm_printf("err: left_bytes=%d, cmd->n_rx=%d\r\n",
left_bytes, cmd->n_rx);
}
}
host->bytes_left = left_bytes;
host->complete = 1;
return;
}
void qspi_irq_handler(void *data)
{
struct qspi_host *host = &qspi_host;
struct spi_flash_cmd *cmd = host->cmd;
uint32_t fr, resr;
(void)data;
fr = _readl(QSPI0_FR);
_writel(fr & ~QSPI_FR_RBDF, QSPI0_FR);
if (!cmd) {
obm_printf("unexpected intr, fr=0x%x\r\n", fr);
return;
}
resr = _readl(QSPI0_RSER);
qspi_writel_check(host, 0, QSPI0_RSER, QSPI_RSER_RESV);
if (fr & (QSPI_FR_ILLINE | QSPI_FR_IUEF | QSPI_FR_IPAEF |
QSPI_FR_IPIEF | QSPI_FR_IPGEF |
QSPI_FR_RBOF | QSPI_FR_TBUF)) {
if (fr & QSPI_FR_ILLINE)
obm_printf("Err: ILLINE\r\n");
if (fr & QSPI_FR_IUEF)
obm_printf("Err: IUEF\r\n");
if (fr & QSPI_FR_IPAEF)
obm_printf("Err: IPAEF\r\n");
if (fr & QSPI_FR_IPIEF)
obm_printf("Err: IPIEF)\r\n");
if (fr & QSPI_FR_IPGEF)
obm_printf("Err: IPGEF\r\n");
if (fr & QSPI_FR_RBOF)
obm_printf("Err: RX Overflow\r\n");
if (fr & QSPI_FR_TBUF) {
int mcr;
mcr = _readl(QSPI0_MCR);
mcr |= QSPI_MCR_CLR_TXF;
qspi_writel_clear(host, mcr, QSPI0_MCR,
~QSPI_MCR_CLR_TXF);
cmd->error = -EAGAIN;
obm_printf("Err: TX Underrun\r\n");
}
host->complete = 1;
}
if (!host->use_dma && host->bytes_left > 0) {
if (cmd->n_tx && (fr & QSPI_FR_TBFF)) {
if (!qspi_fill_to_txbuff(host))
resr &= ~QSPI_RSER_TBFIE;
}
if (cmd->n_rx && (fr & QSPI_FR_RBDF))
qspi_read_from_rxbuff(host);
}
if (fr & QSPI_FR_TFF)
qspi_xfer_done(host);
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
return;
}
#else
void qspi_irq_handler(void *data)
{
}
#endif
struct qspi_host * qspi_host_init(int cs, int mhz, int use_xip)
{
struct qspi_host *host = &qspi_host;
uint32_t reg;
memset(host, 0, sizeof(struct qspi_host));
#if KSTR
/*
if (PlatformIsKestrelZ1())
host->cs_addr[QSPI_CS_A1] = QSPI0_FLASH_A1_BASE_Z1;
else
*/
host->cs_addr[QSPI_CS_A1] = QSPI0_FLASH_A1_BASE;
#else
host->cs_addr[QSPI_CS_A1] = QSPI0_FLASH_A1_BASE;
#endif
host->cs_addr[QSPI_CS_A2] = QSPI0_FLASH_A2_BASE;
host->cs_addr[QSPI_CS_B1] = QSPI0_FLASH_B1_BASE;
host->cs_addr[QSPI_CS_B2] = QSPI0_FLASH_B2_BASE;
#ifdef QSPI_USE_INTR
host->use_intr = 1;
#else
host->use_intr = 0;
#endif
host->en_tx_dma = 1;
host->use_xip = use_xip;
#if FACT || LAPW
host->has_dtr = 1;
#endif
#ifdef QSPI_SUPPORT_DQS
#if LAPW
/* for LAPW, only B0 support DQS */
if (PlatformIsLapwB0())
host->support_dqs = 1;
else
host->support_dqs = 0;
#else
host->support_dqs = 1;
#endif
#endif
ChipSelectQSPI();
/* Enable qspi clk, and release reset */
qspi_set_func_clk(host, mhz, 0, 0, 0, 0);
/* qspi softreset first */
qspi_invalid_ahb(host);
qspi_enter_mode(host, QSPI_DISABLE_MODE);
/* Give the default source address */
qspi_write_sfar(host, host->cs_addr[QSPI_CS_A1]);
qspi_writel_check(host, 0x0, QSPI0_SFACR, QSPI_SFACR_RESV);
//qspi_init_ahb(0); /* config ahb */
/* Set flash memory map */
#if KSTR
/* abandoned
if (PlatformIsKestrelZ1())
_writel(QSPI0_FLASH_A1_TOP_Z1 & 0xfffffc00, QSPI0_SFA1AD);
else
*/
_writel(QSPI0_FLASH_A1_TOP & 0xfffffc00, QSPI0_SFA1AD);
#else
_writel(QSPI0_FLASH_A1_TOP & 0xfffffc00, QSPI0_SFA1AD);
#endif
_writel(QSPI0_FLASH_A2_TOP & 0xfffffc00, QSPI0_SFA2AD);
_writel(QSPI0_FLASH_B1_TOP & 0xfffffc00, QSPI0_SFB1AD);
_writel(QSPI0_FLASH_B2_TOP & 0xfffffc00, QSPI0_SFB2AD);
/*
* ISD3FB, ISD2FB, ISD3FA, ISD2FA = 1; ENDIAN = 0x3; END_CFG=0x3
* DELAY_CLK4X_EN = 1
*/
reg = _readl(QSPI0_MCR);
reg &= ~(QSPI_MCR_END_CFD_MASK | QSPI_MCR_ISDX_MASK);
reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN | QSPI_MCR_DQS_INV_EN);
reg |= QSPI_MCR_END_CFD_LE | 0xf << QSPI_MCR_ISDX_SHIFT;
if (host->has_dtr)
reg |= QSPI_MCR_DDR_EN;
else
reg &= ~QSPI_MCR_DDR_EN;
qspi_writel_check(host, reg, QSPI0_MCR, QSPI_MCR_RESV);
/* Module enabled */
qspi_enter_mode(host, QSPI_NORMAL_MODE);
/* Read using the IP Bus registers QSPI_RBDR0 to QSPI_RBDR31*/
qspi_write_rbct(host, QSPI_RBCT_RXBRD);
#ifdef QSPI_USE_INTR
if (host->use_intr) {
uint32_t fr;
EnablePeripheralIRQInterrupt(INT_QSPI);
fr = _readl(QSPI0_FR);
if (fr)
_writel(fr, QSPI0_FR);
qspi_config_interrupt(host);
}
#endif
obm_printf("use_intr=%d en_tx_dma=%d use_xip=%d\r\n",
host->use_intr, host->en_tx_dma, host->use_xip);
return host;
}
#ifndef QSPI_USE_INTR
static void qspi_wait_cmd_done(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t fr;
/*
* Known BUG:
* Poll QSPI register during TX may lead to bus hang, add
* a delay here for this requirement.
*/
if (cmd->n_tx && !host->use_intr)
Delay(5);
do {
fr = _readl(QSPI0_FR);
if (fr)
_writel(fr, QSPI0_FR);
if (fr & QSPI_FR_ILLINE) {
obm_printf("Err: ILLINE\r\n");
break;
}
if (fr & QSPI_FR_IUEF) {
obm_printf("Err: IUEF\r\n");
break;
}
if (fr & QSPI_FR_IPAEF) {
obm_printf("Err: IPAEF\r\n");
break;
}
if (fr & QSPI_FR_IPIEF) {
obm_printf("Err: IPIEF\r\n");
break;
}
if (fr & QSPI_FR_IPGEF) {
obm_printf("Err: IPGEF\r\n");
break;
}
if (fr & QSPI_FR_TFF) {
_writel(0x1, QSPI0_FR);
break;
}
} while(1);
if (cmd->rx_buf && (fr & QSPI_FR_RBOF))
obm_printf("RX Overflow\r\n");
if (cmd->tx_buf && (fr & QSPI_FR_TBUF)) {
int mcr;
mcr = _readl(QSPI0_MCR);
mcr |= QSPI_MCR_CLR_TXF;
qspi_writel_clear(host, mcr, QSPI0_MCR, ~QSPI_MCR_CLR_TXF);
cmd->error = -EAGAIN;
obm_printf("TX Underrun\r\n");
}
}
static void qspi_poll_rx_buff(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
int left_bytes = host->bytes_left;
uint32_t sr;
uint32_t data;
int rdbfl, i;
do {
/* Check RXWE flag for data comming */
sr = _readl(QSPI0_FR);
if (!(sr & QSPI_FR_RBDF))
continue;
for (i = 0; i <= host->wmrk; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
//obm_printf("1 data = 0x%x\r\n", data);
if (left_bytes >= 4)
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, left_bytes);
left_bytes -= 4;
}
/* Set RBDF to trigger RX Buffer POP */
_writel(QSPI_FR_RBDF, QSPI0_FR);
if (left_bytes < 0) {
break;
} else if ((left_bytes + 3)/4 < (host->wmrk + 1)) {
/* Left bytes < wmrk will not trigger RXWE */
break;
}
} while(1);
/* Wait cmd to be finished */
qspi_wait_cmd_done(host);
if (left_bytes > 0) {
rdbfl = _readl(QSPI0_RBSR);
rdbfl = (rdbfl & QSPI_RBSR_RDBFL_MASK) >> QSPI_RBSR_RDBFL_SHIFT;
for (i = 0; i <= rdbfl; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
//obm_printf("2 data = 0x%x\r\n", data);
if (left_bytes >= 4)
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, left_bytes);
left_bytes -= 4;
}
}
if (left_bytes > 0) {
obm_printf("Error: left_bytes=%d, cmd->n_rx=%d\r\n",
left_bytes, cmd->n_rx);
}
return;
}
static void qspi_fill_tx_buff(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
int left_bytes = host->bytes_left;
int total_cnt;
uint32_t reg;
uint32_t data;
total_cnt = ALIGN_UP(cmd->n_tx, QSPI_TX_BUFF_POP_MIN);
while (left_bytes > 0) {
reg = _readl(QSPI0_SR);
if (reg & QSPI_SR_TXFULL)
continue;
data = 0;
if (cmd->n_tx - total_cnt + left_bytes >= 4)
data = (*(uint32_t *)&cmd->tx_buf[total_cnt - left_bytes]);
else if (cmd->n_tx - total_cnt + left_bytes > 0)
memcpy(&data, &cmd->tx_buf[total_cnt - left_bytes],
left_bytes);
_writel(data, QSPI0_TBDR);
left_bytes -= 4;
}
host->bytes_left = left_bytes;
qspi_wait_cmd_done(host);
return;
}
int qspi_cmd_done_pio(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
/* receive rx data */
if (cmd->n_rx)
qspi_poll_rx_buff(host);
else if (cmd->n_tx)
qspi_fill_tx_buff(host);
else
qspi_wait_cmd_done(host);
return 0;
}
#else
int qspi_cmd_done_interrupt(struct qspi_host *host)
{
while (!(host->complete));
qspi_disable_interrupt(host);
return 0;
}
#endif
int qspi_start_dma_xfer(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
DMA_CMDx_T TX_data;
TX_data.value = 0;
TX_data.bits.IncSrcAddr = 1;
TX_data.bits.FlowTrg = 1;
TX_data.bits.Width = 3;
TX_data.bits.MaxBurstSize = 3;
TX_data.bits.Length = host->bytes_left;
#if KSTR
XllpDmacMapDeviceToChannel(45, QSPI_DMA_TX_CHANNEL); //TX
#else
XllpDmacMapDeviceToChannel(99, QSPI_DMA_TX_CHANNEL); //TX
#endif
alignChannel(QSPI_DMA_TX_CHANNEL, 1);
#if 0
configDescriptor(tx_desc, NULL, (unsigned int)cmd->tx_buf,
(unsigned int)QSPI0_TBDR, &TX_data, host->bytes_left, 1);
loadDescriptor (tx_desc, QSPI_DMA_TX_CHANNEL);
#if ENABLE_MMU
dcache_clean_range((UINT32_T)tx_desc, sizeof(XLLP_DMAC_DESCRIPTOR_T));
#endif
#else
XllpDmacNoDescriptorFetch(QSPI_DMA_TX_CHANNEL);
loadNonDescriptor((unsigned int)cmd->tx_buf,(unsigned int)QSPI0_TBDR - 4,
&TX_data, QSPI_DMA_TX_CHANNEL);
#endif
#if ENABLE_MMU
dcache_clean_invalidate_range((UINT32_T)cmd->tx_buf, cmd->n_tx);
#endif
XllpDmacStartTransfer(QSPI_DMA_TX_CHANNEL);
return 0;
}
/* Read out the data from the AHB buffer. */
void qspi_ahb_read(struct spi_flash_chip *chip,
uint32_t addr, uint8_t *rxbuf, int len)
{
struct qspi_host *host = chip->host;
uint32_t mcr_reg;
void *rx_addr;
mcr_reg = _readl(QSPI0_MCR);
_writel(QSPI_MCR_CLR_RXF | QSPI_MCR_CLR_TXF |
QSPI_MCR_ISDX_MASK | QSPI_MCR_END_CFD_LE, QSPI0_MCR);
rx_addr = host->cs_addr[chip->cs] + addr;
/* Read out the data directly from the AHB buffer. */
memcpy(rxbuf, rx_addr, len);
_writel(mcr_reg, QSPI0_MCR);
}
static void qspi_prepare_recv(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t reg;
int wmrk;
/* Clear RX FIFO. Invalidate the RX Buffer */
reg = _readl(QSPI0_MCR);
reg |= QSPI_MCR_CLR_RXF;
qspi_writel_clear(host, reg, QSPI0_MCR, ~QSPI_MCR_CLR_RXF);
/* Set RX Buffer Watermark */
if (cmd->n_rx <= (QSPI_RX_BUFF_MAX << 2))
wmrk = (ALIGN_UP(cmd->n_rx, 4) >> 2) - 1;
else
wmrk = 0x1; /* Water Mark: 16*4byte */
reg = _readl(QSPI0_RBCT);
reg &= ~QSPI_RBCT_WMRK_MASK;
reg |= wmrk;
qspi_write_rbct(host, reg);
host->wmrk = wmrk;
host->bytes_left = cmd->n_rx;
}
static void qspi_prepare_transmit(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t reg;
int wmrk;
/* Clear TX FIFO/Buffer */
reg = _readl(QSPI0_MCR);
reg |= QSPI_MCR_CLR_TXF;
qspi_writel_clear(host, reg, QSPI0_MCR, ~QSPI_MCR_CLR_TXF);
host->bytes_left = ALIGN_UP(cmd->n_tx, QSPI_TX_BUFF_POP_MIN);
if (host->en_tx_dma && host->bytes_left >= 32) {
wmrk = 0x7; /* 32bytes watermark */
reg = _readl(QSPI0_TBCT);
reg &= ~0x1f;
reg |= wmrk;
qspi_writel_check(host, reg, QSPI0_TBCT, QSPI_TBCT_RESV);
host->wmrk = wmrk;
host->use_dma = 1;
} else {
int i, left_bytes, tx_cnt;
/* Copy initial data into the circular buffer */
host->use_dma = 0;
tx_cnt = min(host->bytes_left >> 2, QSPI_TX_BUFF_MAX);
for (i = 0; i < tx_cnt; i++) {
int data = 0;
left_bytes = cmd->n_tx - (i << 2);
if (left_bytes >= 4)
data = (*(uint32_t *)&cmd->tx_buf[i << 2]);
else if (left_bytes > 0)
memcpy(&data, &cmd->tx_buf[i << 2], left_bytes);
_writel(data, QSPI0_TBDR);
}
host->bytes_left -= tx_cnt << 2;
}
}
int qspi_start_cmd(struct spi_flash_chip *chip, struct spi_flash_cmd *cmd)
{
struct qspi_host *host = chip->host;
struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg;
uint32_t ipcr, sfar, fr, tmp;
int seq_id = cmd_cfg->seq_id;
int i;
do {
tmp = _readl(QSPI0_SR);
if (!(tmp & QSPI_SR_BUSY))
break;
Delay(1);
//obm_printf("The controller is busy, 0x%x\r\n", tmp);
} while (1);
host->cmd = cmd;
host->bytes_left = 0;
host->complete = 0;
host->use_dma = 0;
if (seq_id < 0 || !(host->lut_map & 1 << seq_id))
seq_id = qspi_update_shared_lut(chip, cmd);
/* Reset the IP sequence pointers */
tmp = _readl(QSPI0_SPTRCLR);
tmp |= QSPI_SPTRCLR_IPPTRC;
qspi_writel_clear(host, tmp, QSPI0_SPTRCLR, QSPI_SPTRCLR_RESV);
/* Set flash address to be accessed */
sfar = 0;
for (i = 0; i < cmd->n_addr; i++) {
sfar <<= 8;
sfar |= cmd->addr[i];
}
sfar += host->cs_addr[chip->cs];
qspi_write_sfar(host, sfar);
/*
* AHB memory-map to be changed, invalidate d-cache here
* For spi-nand, only one-page mapped, use AHB_MAP_SIZE_PAGE
* for this situation.
*/
if (host->use_xip && (cmd->flag & RST_AHB_DOMAIN)) {
int addr, size;
if (cmd->tx_buf) {
addr = sfar;
size = cmd->n_tx;
} else if (cmd->flag & AHB_MAP_SIZE_PAGE) {
addr = host->cs_addr[chip->cs];
size = chip->page_size;
} else {
addr = sfar;
size = chip->block_size;
}
}
/* Clear FR before trigger command */
fr = _readl(QSPI0_FR);
if (fr)
_writel(fr, QSPI0_FR);
/* Set SFACR to fix issue of 1-byte address command */
if (cmd->n_addr == 1)
qspi_writel_check(host, 0x8, QSPI0_SFACR, QSPI_SFACR_RESV);
if (cmd->n_rx) {
qspi_prepare_recv(host);
} else if (cmd->n_tx) {
qspi_prepare_transmit(host);
if (host->use_dma) {
qspi_enable_dma(host);
/*
* Disable usb interrupt to avoid usb access AHB bus
* to fix known asic bug
*/
DisablePeripheralIRQInterrupt(INT_USB);
qspi_start_dma_xfer(host);
#if 1
/*
* Before trigger qspi to send data to externl bus,FIFO
* need to have some data, or FIFO underflow error may happen.
* DMA need some time to write data to TX FIFO, but
* poll QSPI register may lead to bus hang(known bug), so we add
* a delay here for this requirement.
*/
Delay(5);
//cpu_cycle_delay(20);
#else
while(1) {
uint32_t tbsr;
tbsr = _readl(QSPI0_TBSR);
tbsr = tbsr >> 8;
tbsr &= 0x3f;
if (tbsr >= min(host->bytes_left/4, QSPI_TX_BUFF_MAX))
break;
//obm_printf("====tbsr=%d\r\n", tbsr);
}
#endif
}
}
/* trigger command */
ipcr = (seq_id & 0xf) << 24;
if (cmd->rx_buf)
ipcr |= (cmd->n_rx & 0xffff);
else if (cmd->tx_buf)
ipcr |= (cmd->n_tx & 0xffff);
restart:
_writel(ipcr, QSPI0_IPCR);
if (cmd->n_tx && host->use_dma) {
uint32_t status;
int start_time = GetOSCR0();
do {
status = readDmaStatusRegister(QSPI_DMA_TX_CHANNEL);
status &= XLLP_DMAC_DCSR_STOP_INTR;
if (status == XLLP_DMAC_DCSR_STOP_INTR) {
/*
* Add extra delay to make sure dma transfer
* finished on APB bus
*/
Delay(2);
break;
}
if (OSCR0IntervalInMilli(start_time, GetOSCR0()) > 100) {
fr = _readl(QSPI0_FR);
if (fr & (QSPI_FR_IPAEF | QSPI_FR_IPIEF |
QSPI_FR_IPGEF)) {
obm_printf("qspi err, fr=0x%x\n", fr);
_writel(fr, QSPI0_FR);
goto restart;
}
XllpDmacStopTransfer(QSPI_DMA_TX_CHANNEL);
obm_printf("dma timeout\r\n");
if (fr & (QSPI_FR_TBUF | QSPI_FR_TFF))
cmd->error = -EAGAIN;
break;
}
} while (1);
EnablePeripheralIRQInterrupt(INT_USB);
}
fr = _readl(QSPI0_FR);
if (fr & (QSPI_FR_IPAEF | QSPI_FR_IPIEF | QSPI_FR_IPGEF)) {
obm_printf("qspi err, fr=0x%x\n", fr);
_writel(fr, QSPI0_FR);
goto restart;
}
#ifdef QSPI_USE_INTR
qspi_enable_interrupt(host);
qspi_cmd_done_interrupt(host);
#else
if (host->use_dma) {
qspi_wait_cmd_done(host);
qspi_disable_dma(host);
host->bytes_left = 0;
} else {
qspi_cmd_done_pio(host);
}
#endif
/* Resume SFACR */
if (cmd->n_addr == 1)
qspi_writel_check(host, 0x0, QSPI0_SFACR, QSPI_SFACR_RESV);
if (cmd->flag & RST_AHB_DOMAIN)
qspi_invalid_ahb(host);
host->cmd = NULL;
return cmd->error;
}