Files
banban/ql-bootloader/common/src/qspi_host.c
T

1321 lines
30 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "qspi_dma.h"
#include "qspi_flash.h"
#include "qspi_host.h"
#include "qspi_nor.h"
#include "bsp.h"
#define uart_printf(...)
struct qspi_host qspi_host;
__align(16) DMA_DESCRIPTOR dma_desc;
P_DMA_DESCRIPTOR tx_desc = &dma_desc;
void *qspi_memcpy(void *dst, const void *src, int num)
{
char * psrc = (char *)src;
char * pdst = (char *)dst;
while (num-- > 0) *pdst++ = *psrc++;
return dst;
}
void _writel(unsigned int data , unsigned int addr )
{
*(volatile unsigned int*)(addr)=data;
}
unsigned int _readl(unsigned int addr)
{
return *(volatile unsigned int*)(addr);
}
void udelay(unsigned int cnt)
{
#if 1
volatile unsigned int i,j;
for(i=0;i<cnt;i++)
{
for(j=0;j<1;j++)
;
}
#endif
}
void dcache_clean_invalidate_range(unsigned int addr, unsigned int len)
{
}
static void qspi_config_mfp(int cs)
{
if (cs == QSPI_CS_A1 || cs == QSPI_CS_A2) {
_writel(0x1000, 0xd401e2c4); // QSPI_DAT3
_writel(0x1000, 0xd401e2c8); // QSPI_DAT2
_writel(0x1000, 0xd401e2cc); // QSPI_DAT1
_writel(0x1000, 0xd401e2d0); // QSPI_DAT0
_writel(0x1000, 0xd401e2d4); // QSPI_CLK
_writel(0x1000, 0xd401e2d8); // QSPI_CS1
// _writel(0x1000, 0xd401e2dc); // QSPI_CS2
}
if (cs == QSPI_CS_B1 || cs == QSPI_CS_B2) {
#if 0
_writel(0x1007, 0xd401e2e8); // QSPI2_DAT3
_writel(0x1007, 0xd401e2ec); // QSPI2_DAT2
_writel(0x1007, 0xd401e2f0); // QSPI2_DAT1
_writel(0x1007, 0xd401e2f4); // QSPI2_DAT0
_writel(0x1007, 0xd401e2f8); // QSPI2_CLK
_writel(0x1007, 0xd401e2fc); // QSPI2_CS1
_writel(0x1007, 0xd401e300); // QSPI2_CS2
#else
_writel(0x1007, 0xd401e2ec); // QSPI2_DAT3
_writel(0x1007, 0xd401e2f0); // QSPI2_DAT2
_writel(0x1007, 0xd401e2f4); // QSPI2_DAT1
_writel(0x1007, 0xd401e2f8); // QSPI2_DAT0
_writel(0x1007, 0xd401e2fc); // QSPI2_CLK
_writel(0x1007, 0xd401e300); // QSPI2_CS1
_writel(0x1007, 0xd401e2e8); // QSPI2_CS2
#endif
}
}
static void qspi_write_rbct(struct qspi_host *host, uint32_t val)
{
uint32_t fr;
(void) host;
do {
_writel(val, QSPI0_RBCT);
fr = _readl(QSPI0_FR);
if (!(fr & QSPI0_FR_IPIEF))
break;
fr &= QSPI0_FR_IPIEF;
_writel(fr, QSPI0_FR);
} while (1);
}
static void qspi_write_sfar(struct qspi_host *host, uint32_t val)
{
uint32_t fr;
(void) host;
do {
_writel(val, QSPI0_SFAR);
fr = _readl(QSPI0_FR);
if (!(fr & QSPI0_FR_IPIEF))
break;
fr &= QSPI0_FR_IPIEF;
_writel(fr, QSPI0_FR);
} while (1);
}
void qspi_enter_mode(uint32_t mode)
{
uint32_t mcr;
mcr = _readl(QSPI0_MCR);
if (mode == QSPI_NORMAL_MODE)
mcr &= ~(0x1 << 14);
else if (mode == QSPI_DISABLE_MODE)
mcr |= (0x1 << 14);
_writel(mcr, QSPI0_MCR);
}
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 = 0x1 << 31 | (data_size / 8) << 8;
/*
* Config the ahb buffer
* Disable BUF0~BUF1, use BUF3 for all masters
*/
_writel(0, QSPI0_BUF0IND);
_writel(0, QSPI0_BUF1IND);
_writel(0, QSPI0_BUF2IND);
/* AHB Master port */
_writel(0xe, QSPI0_BUF0CR);
_writel(0xe, QSPI0_BUF1CR);
_writel(0xe, QSPI0_BUF2CR);
_writel(buf_cfg, QSPI0_BUF3CR); // other masters
#endif
_writel(lut << 12, QSPI0_BFGENCR);
uart_printf("AHB data transfer size: %d\r\n", data_size);
}
static void qspi_lock_lut(void)
{
uint32_t lckcr;
lckcr = _readl(QSPI0_LCKCR);
if (lckcr & 0x1)
return;
_writel(0x5af05af0, QSPI0_LUTKEY);
_writel(0x1, QSPI0_LCKCR);
}
static void qspi_unlock_lut(void)
{
uint32_t lckcr;
lckcr = _readl(QSPI0_LCKCR);
if (lckcr & 0x2)
return;
_writel(0x5af05af0, QSPI0_LUTKEY);
_writel(0x2, QSPI0_LCKCR);
}
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;
/* Set Lookup table entry: CMD, ADDR, MODE, DUMMY, DATA, etc */
if (chip->qpi_enabled)
lut_entry[seq++] = QSPI_INSTR_CMD << 10 |
QSPI_PAD_4X << 8 | cmd_cfg->opcode;
else
lut_entry[seq++] = 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++] = 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 = QSPI_INSTR_MODE2;
mode &= 0x3;
} else if (cmd_cfg->mode_bits == 4) {
mode_instr = QSPI_INSTR_MODE4;
mode &= 0xf;
} else {
mode_instr = 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++] = 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++] = QSPI_INSTR_WRITE << 10 |
pins[cmd_cfg->data_pins] << 8;
} else {
uart_printf("err: type of cmd %d is wrong in table\r\n",
cmd_cfg->opcode);
return;
}
}
/* Add stop at the end */
lut_entry[seq++] = QSPI_INSTR_STOP << 10;
for (i = 0; i < seq/2; i++) {
lut_value = lut_entry[i*2] | (lut_entry[i*2 + 1] << 16);
_writel(lut_value, QSPI0_LUT0 + seq_id*0x10 + i*0x4);
}
if (seq % 2) {
lut_value =lut_entry[seq - 1];
_writel(lut_value, QSPI0_LUT0 + seq_id*0x10 + (seq/2)*0x4);
}
// for (i = 0; i < seq; i++)
// uart_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 != 0xFF) {
if (lut_map & 1 << cmd_cfg->seq_id) {
uart_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;
uart_printf("config 0x%x, 0x%x\r\n",cmd_cfg->seq_id, lut_map);
}
}
chip->host->lut_map = lut_map;
uart_printf("Fixed LUT bit-map: 0x%x\r\n", lut_map);
return 0;
}
void qspi_enable_ahb_preempt(int on)
{
uint32_t mcr;
mcr = _readl(QSPI0_MCR);
if(on)
mcr |= QSPI0_MCR_SW_PROG_ERASE;
else
mcr &= ~QSPI0_MCR_SW_PROG_ERASE;
_writel(mcr, QSPI0_MCR);
}
void qspi_enter_xip_mode(int enable)
{
uint32_t mcr;
mcr = _readl(QSPI0_MCR);
if (enable)
mcr |= QSPI0_MCR_XIP_EN;
else
mcr &= ~(QSPI0_MCR_XIP_EN | QSPI0_MCR_SW_PROG_ERASE);
_writel(mcr, QSPI0_MCR);
}
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(QSPI_DISABLE_MODE);
qspi_init_ahb(QSPI_LUT_SEQID0, chip->page_size);
qspi_enter_mode(QSPI_NORMAL_MODE);
if (host->use_xip) {
host->xip_read = 1;
uart_printf("XIP Read mode enabled\r\n");
} else {
host->xip_read = 0;
uart_printf("IPS Read mode enabled\r\n");
}
return 0;
}
static void qspi_reset(void)
{
uint32_t reg;
/* qspi softreset first */
reg = _readl(QSPI0_MCR);
reg |= 0x3;
_writel(reg, QSPI0_MCR);
reg = _readl(QSPI0_MCR);
if ((reg & 0x3) != 0x3)
uart_printf("reset ignored 0x%x\r\n", reg);
udelay(1);
//cpu_cycle_delay(10);
reg &= ~0x3;
_writel(reg, QSPI0_MCR);
}
static void qspi_clk_enable(void)
{
// PMUA->QSPI_CLK_RES_CTRL |= 0x1 << 4 | 0x1 << 3;;
// PMUA->QSPI_CLK_RES_CTRL |= 0x3;
UINT32 val;
UINT32 QSPI_CLK_RES_CTRL;
QSPI_CLK_RES_CTRL=0xD4282800+0x60;
val=_readl(QSPI_CLK_RES_CTRL);
val |= ((0x1 << 4) | (0x1 << 3));
_writel(val,QSPI_CLK_RES_CTRL);
val |= 0x3;
_writel(val,QSPI_CLK_RES_CTRL);
}
static void qspi_clk_disable(void)
{
// PMUA->QSPI_CLK_RES_CTRL &= ~(0x1 << 4 | 0x1 << 3);
// PMUA->QSPI_CLK_RES_CTRL &= ~0x3;
UINT32 val;
UINT32 QSPI_CLK_RES_CTRL;
QSPI_CLK_RES_CTRL=0xD4282800+0x60;
val=_readl(QSPI_CLK_RES_CTRL);
val &= ~(0x1 << 4 | 0x1 << 3);
_writel(val,QSPI_CLK_RES_CTRL);
val &= ~0x3;
_writel(val,QSPI_CLK_RES_CTRL);
}
static void qspi_set_func_clk(struct qspi_host *host, int mhz)
{
int clk_sel;
int freq;
UINT32 val;
UINT32 QSPI_CLK_RES_CTRL;
/* 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();
// PMUA->QSPI_CLK_RES_CTRL &= ~(0x7 << 6);
// PMUA->QSPI_CLK_RES_CTRL |= clk_sel << 6;
QSPI_CLK_RES_CTRL=0xD4282800+0x60;
val=_readl(QSPI_CLK_RES_CTRL);
val &= ~(0x7 << 6);
_writel(val,QSPI_CLK_RES_CTRL);
val |= clk_sel << 6;
_writel(val,QSPI_CLK_RES_CTRL);
qspi_clk_enable();
host->bus_clk = mhz;
// uart_printf("Bus clock: %dMHz QSPI_CLK_RES_CTRL: 0x%x\r\n",mhz, PMUA->QSPI_CLK_RES_CTRL);
}
#define QSPI_CLK_RES_CTRL_REG 0xD4282860
void qspi_clock_change(int mhz) //new implementation on CraneLR
{
UINT32 clk_sel, val;
/* new qspi controller clock is the same as flash device clock*/
switch (mhz)
{
case 104:
clk_sel = 0;
break;
case 89:
clk_sel = 1;
break;
case 94:
clk_sel = 2;
break;
case 69:
clk_sel = 3;
break;
case 57:
clk_sel = 4;
break;
default:
clk_sel = 0; //104
break;
}
val=_readl(QSPI_CLK_RES_CTRL_REG);
val &= ~(0x3F << 6);
val |= (0x3<<9)/* clk_div=4*/ | (clk_sel<<6);
_writel(val,QSPI_CLK_RES_CTRL_REG);
_writel((val|(1<<12)),QSPI_CLK_RES_CTRL_REG); //qspi clock fc
while (_readl(QSPI_CLK_RES_CTRL_REG)&(1<<12));
}
static void qspi_set_func_clk_new(struct qspi_host *host, int mhz)
{
UINT32 val;
val=_readl(QSPI_CLK_RES_CTRL_REG);
val &= ~((0x1<<4)|(0x1<<3)|(0x1<<1)|(0x1<<0));
_writel(val,QSPI_CLK_RES_CTRL_REG); //clock disable
val |= ((0x1<<4)|(0x1<<3)|(0x1<<1)|(0x1<<0));
_writel(val,QSPI_CLK_RES_CTRL_REG); //clock enable, thus reset qspi ctrl
qspi_clock_change(mhz);
host->bus_clk = mhz;
}
static void qspi_enable_dma(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr |= QSPI0_RSER_TBFDE;
_writel(resr, QSPI0_RSER);
}
static void qspi_disable_dma(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr &= ~QSPI0_RSER_TBFDE;
_writel(resr, QSPI0_RSER);
}
static void qspi_enable_xip_on_int(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr |= QSPI0_RSER_XIP_ON;
_writel(resr, QSPI0_RSER);
}
static void qspi_disable_xip_on_int(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr &= ~QSPI0_RSER_XIP_ON;
_writel(resr, QSPI0_RSER);
}
static void qspi_config_interrupt(struct qspi_host *host)
{
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr |= QSPI0_RSER_ILLINIE | QSPI0_RSER_ABSEIE | QSPI0_RSER_AITIE |
QSPI0_RSER_AIBSIE | QSPI0_RSER_ABOIE |QSPI0_RSER_IUEIE |
QSPI0_RSER_IPIEIE | QSPI0_RSER_IPGEIE | QSPI0_RSER_XIP_ON;
_writel(resr, QSPI0_RSER);
}
static void qspi_enable_interrupt(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr |= QSPI0_RSER_TFIE;
if (cmd->n_tx) {
resr |= QSPI0_RSER_TBUIE;
if (!host->use_dma && host->bytes_left > 0) {
resr |= QSPI0_RSER_TBFIE;
}
} else if (cmd->n_rx) {
resr |= QSPI0_RSER_RBOIE | QSPI0_RSER_RBDIE;
}
_writel(resr, QSPI0_RSER);
}
static void qspi_disable_interrupt(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t resr;
resr = _readl(QSPI0_RSER);
resr &= ~QSPI0_RSER_TFIE;
if (cmd->n_tx)
resr &= ~(QSPI0_RSER_TBUIE | QSPI0_RSER_TBFIE);
else if (cmd->n_rx)
resr &= ~(QSPI0_RSER_RBOIE | QSPI0_RSER_RBDIE);
if (host->use_dma)
resr &= ~QSPI0_RSER_TBFDE;
_writel(0, QSPI0_RSER);
}
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) {
uart_printf("err: receive tx interrupt while no cmd sent\n");
return 1;
}
total_cnt = (cmd->n_tx + QSPI_TX_BUFF_POP_MIN - 1) / QSPI_TX_BUFF_POP_MIN;
total_cnt = total_cnt*QSPI_TX_BUFF_POP_MIN;
while (left_bytes > 0) {
_writel(QSPI0_FR_TBFF, QSPI0_SR);
reg = _readl(QSPI0_SR);
if (reg & QSPI0_FR_TBFF)
break;
if (cmd->n_tx - total_cnt + left_bytes <= 0) {
data = 0;
} else if (cmd->n_tx - total_cnt + left_bytes < 4) {
data = 0;
qspi_memcpy(&data, &cmd->tx_buf[total_cnt - left_bytes],
left_bytes);
} else {
data = (*(uint32_t *)&cmd->tx_buf[total_cnt - 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 & QSPI0_FR_RBDF))
break;
/* Check RXWE flag for data comming */
for (i = 0; i <= host->wmrk; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
//uart_printf("i=%d data=0x%x\r\n", i , data);
if (left_bytes >= 4)
qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
qspi_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 = (rdbfl >> 8) & 0x3f;
for (i = 0; i <= rdbfl; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
if (left_bytes >= 4)
qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, left_bytes);
left_bytes -= 4;
}
if (left_bytes > 0) {
uart_printf("Error: Not read enough data: "
"left_bytes=%d, cmd->n_rx=%d\r\n",
left_bytes, cmd->n_rx);
}
}
host->bytes_left = left_bytes;
host->complete = 1;
return;
}
static 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 & ~QSPI0_FR_RBDF, QSPI0_FR);
if (fr & QSPI0_FR_XIP_ON)
uart_printf("XIP suspend/resume happen-----fr=0x%x->\r\n", fr);
if (!cmd) {
uart_printf("Interrupt happen while no cmd sent, fr=0x%x\r\n", fr);
return;
}
resr = _readl(QSPI0_RSER);
_writel(0x0, QSPI0_RSER);
if (fr & (QSPI0_FR_ILLINE | QSPI0_FR_IUEF | QSPI0_FR_IPAEF |
QSPI0_FR_IPIEF | QSPI0_FR_IPGEF |
QSPI0_FR_RBOF | QSPI0_FR_TBUF)) {
if (fr & QSPI0_FR_ILLINE)
uart_printf("Err: Illegal Instruction Error Flag\r\n");
if (fr & QSPI0_FR_IUEF)
uart_printf("Err: IP Command Usage Error Flag\r\n");
if (fr & QSPI0_FR_IPAEF)
uart_printf("Err: IP Command Trigger during AHB Access Error Flag\r\n");
if (fr & QSPI0_FR_IPIEF)
uart_printf("Err: IP Command Trigger could not be executed Error Flag\r\n");
if (fr & QSPI0_FR_IPGEF)
uart_printf("Err: IP Command Trigger during AHB Grant Error Flag\r\n");
if (fr & QSPI0_FR_RBOF)
uart_printf("Error: RX Buffer Overflow\r\n");
if (fr & QSPI0_FR_TBUF)
uart_printf("Error: TX Buffer Underrun Flag\r\n");
host->complete = 1;
}
if (!host->use_dma && host->bytes_left > 0) {
if (cmd->n_tx && (fr & QSPI0_FR_TBFF)) {
if (!qspi_fill_to_txbuff(host))
resr &= ~QSPI0_RSER_TBFIE;
}
if (cmd->n_rx && (fr & QSPI0_FR_RBDF))
qspi_read_from_rxbuff(host);
}
if (fr & QSPI0_FR_TFF)
qspi_xfer_done(host);
_writel(resr, QSPI0_RSER);
return;
}
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));
host->cs_addr[QSPI_CS_A1] = QSPI0_FLASH_A1_BASE;
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;
host->use_intr = 0;
host->en_tx_dma = 0;
host->use_xip = use_xip;
qspi_config_mfp(cs);
/* Enable qspi clk, and release reset */
if (CHIP_IS_CRANELR || CHIP_IS_CRANELS_A0 || CHIP_IS_CRANELG || CHIP_IS_CRANELRH)
qspi_set_func_clk_new(host, mhz);
else
qspi_set_func_clk(host, mhz);
/* qspi softreset first */
qspi_reset();
/* clock settings */
qspi_enter_mode(QSPI_DISABLE_MODE);
/* sampled by sfif_clk_b; half cycle delay; */
/* NOT SETTING SMPR[5] FOR QSPI @104M SINCE SOME FLASH DEVICE MAY GET STUCK ON INIT,
KEEP PAYING ATTENTION TO THIS WHEN APPLYING CRANE A0 OR OTHER FLASH DEVICES.*/
#if 1
_writel(0x0, QSPI0_SMPR);
_writel(0x8, QSPI0_SOCCR); //good for erase/write stability
#else
if (mhz < 104)
_writel(0x0, QSPI0_SMPR);
else
_writel(0x1 << 5, QSPI0_SMPR);
#endif
//_writel(0x202, QSPI0_FLSHCR);
/* Give the default source address */
qspi_write_sfar(host, host->cs_addr[QSPI_CS_A1]);
_writel(0x0, QSPI0_SFACR);
//qspi_init_ahb(0); /* config ahb */
/* Set flash memory map */
#if (defined (ENABLE_SLT_FEATURE) && (!defined (ENABLE_SLT_V2_FEATURE)))
_writel(QSPI0_FLASH_A1_BASE & 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 &= ~0x000f000c;
reg |= 0x000f000c;
_writel(reg, QSPI0_MCR);
/* Module enabled */
qspi_enter_mode(QSPI_NORMAL_MODE);
/* Read using the IP Bus registers QSPI_RBDR0 to QSPI_RBDR31*/
qspi_write_rbct(host, 0x1 << 8);
if (host->use_intr) {
uint32_t fr;
// ISR_Connect(45, qspi_irq_handler, 0);
// INT_Enable(45, 0, 15);
fr = _readl(QSPI0_FR);
if (fr)
_writel(fr, QSPI0_FR);
qspi_config_interrupt(host);
}
/*
uart_printf("tx_desc: 0x%x\r\n", tx_desc);
uart_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;
}
static void qspi_wait_cmd_done(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
uint32_t fr;
do {
fr = _readl(QSPI0_FR);
if (fr & QSPI0_FR_ILLINE) {
uart_printf("Err: Illegal Instruction Error Flag\r\n");
break;
}
if (fr & QSPI0_FR_IUEF) {
uart_printf("Err: IP Command Usage Error Flag\r\n");
break;
}
if (fr & QSPI0_FR_IPAEF) {
uart_printf("Err: IP Command Trigger during AHB Access Error Flag\r\n");
break;
}
if (fr & QSPI0_FR_IPIEF) {
uart_printf("Err: IP Command Trigger could not be executed Error Flag\r\n");
break;
}
if (fr & QSPI0_FR_IPGEF) {
uart_printf("Err: IP Command Trigger during AHB Grant Error Flag\r\n");
break;
}
if (fr & QSPI0_FR_TFF) {
_writel(0x1, QSPI0_FR);
break;
}
} while(1);
if (cmd->rx_buf && (fr & QSPI0_FR_RBOF))
uart_printf("Error: RX Buffer Overflow\r\n");
if (cmd->tx_buf && (fr & QSPI0_FR_TBUF))
uart_printf("Error: TX Buffer Underrun Flag\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 & (0x1 << 16)))
continue;
for (i = 0; i <= host->wmrk; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
if (left_bytes >= 4)
qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
qspi_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);
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 >> 8) & 0x3f;
for (i = 0; i <= rdbfl; i++) {
data = _readl(QSPI0_RBDR0 + i*4);
if (left_bytes >= 4)
qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, 4);
else
qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes],
&data, left_bytes);
left_bytes -= 4;
}
}
if (left_bytes > 0) {
uart_printf("Error: Not read enough data: 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 = (cmd->n_tx + QSPI_TX_BUFF_POP_MIN - 1) / QSPI_TX_BUFF_POP_MIN;
total_cnt = total_cnt*QSPI_TX_BUFF_POP_MIN;
while (left_bytes > 0) {
reg = _readl(QSPI0_SR);
if (reg & (0x1 << 27))
continue;
if (cmd->n_tx - total_cnt + left_bytes <= 0) {
data = 0;
} else if (cmd->n_tx - total_cnt + left_bytes < 4) {
data = 0;
qspi_memcpy(&data, &cmd->tx_buf[total_cnt - left_bytes],
left_bytes);
} else {
data = (*(uint32_t *)&cmd->tx_buf[total_cnt - 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;
}
int qspi_cmd_done_interrupt(struct qspi_host *host)
{
while (!(host->complete));
qspi_disable_interrupt(host);
return 0;
}
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;
dma_map_device_to_channel(99, QSPI_DMA_TX_CHANNEL); //TX
/* alignment is handled in qspi, should not set this in DMAC */
//set_user_aligment(QSPI_DMA_TX_CHANNEL);
#if 0
dma_config_descriptor((uint32_t *)tx_desc, 0, (uint32_t)cmd->tx_buf,
QSPI0_TBDR, TX_data.value, 1);
dma_load_descriptor((void *)tx_desc, QSPI_DMA_TX_CHANNEL);
#else
dma_set_mode(DMA_MODE_NONFETCH, QSPI_DMA_TX_CHANNEL);
dma_set_reg_nf((uint32_t)cmd->tx_buf, QSPI0_TBDR, &TX_data,
QSPI_DMA_TX_CHANNEL);
#endif
dcache_clean_invalidate_range((uint32_t)cmd->tx_buf, cmd->n_tx);
dma_xfer_start(QSPI_DMA_TX_CHANNEL);
return 0;
}
static 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, mcr, sr, fr;
uint32_t tmp, data, cpsr;
int seq_id = cmd_cfg->seq_id;
int left_bytes, tx_cnt, i;
int wmrk = 0;
host->cmd = cmd;
host->bytes_left = 0;
host->complete = 0;
host->use_dma = 0;
// if (seq_id < 0 || !(host->lut_map & 1 << seq_id))
if ((seq_id == 0xff) || (!(host->lut_map & (1 << seq_id))))
{
// uart_printf("qspi_update_shared_lut 0x%x 0x%x\r\n", seq_id, (host->lut_map & 1 << seq_id));
seq_id = qspi_update_shared_lut(chip, cmd);
}
/* Reset the IP sequence pointers */
tmp = _readl(QSPI0_SPTRCLR);
tmp |= 0x1 << 8;
_writel(tmp, QSPI0_SPTRCLR);
if (cmd->n_rx) {
/* Clear RX FIFO. Invalidate the RX Buffer */
mcr = _readl(QSPI0_MCR);
mcr |= 0x1 << 10; //CLR_RXF
_writel(mcr, QSPI0_MCR);
/* Set RX Buffer Watermark */
if (cmd->n_rx <= (QSPI_RX_BUFF_MAX*4))
wmrk = (cmd->n_rx + 3)/4 - 1;
else
wmrk = 0x1; /* Water Mark: 16*4byte */
tmp = _readl(QSPI0_RBCT);
tmp &= ~0x1f;
tmp |= wmrk;
qspi_write_rbct(host, tmp);
host->wmrk = wmrk;
host->bytes_left = cmd->n_rx;
} else if (cmd->n_tx) {
/* Clear TX FIFO/Buffer */
mcr = _readl(QSPI0_MCR);
mcr |= 0x1 << 11; //CLR_TXF
_writel(mcr, QSPI0_MCR);
/* Set QSPI_SFACR[CAS] QSPI_SFACR[WA] if needed */
tx_cnt = (cmd->n_tx + QSPI_TX_BUFF_POP_MIN - 1) / QSPI_TX_BUFF_POP_MIN;
host->bytes_left = tx_cnt*QSPI_TX_BUFF_POP_MIN;
if (host->en_tx_dma && host->bytes_left >= 32) {
host->use_dma = 1;
wmrk = 0x7; //0x1; // 32bytes watermark
tmp = _readl(QSPI0_TBCT);
tmp &= ~0x1f;
tmp |= wmrk;
_writel(tmp, QSPI0_TBCT);
host->wmrk = wmrk;
qspi_enable_dma(host);
qspi_start_dma_xfer(host);
#if 0
/*
* 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.
*/
udelay(5);
//cpu_cycle_delay(20);
#else
while(1) {
uint32_t tbsr;
tbsr = _readl(QSPI0_TBSR);
tbsr &= QSPI0_TBSR_TRBFL_MASK;
tbsr = tbsr >> QSPI0_TBSR_TRBFL_SHIFT;
if (tbsr >= min(host->bytes_left/4, QSPI_TX_BUFF_MAX))
break;
//uart_printf("====tbsr=%d\r\n", tbsr);
}
#endif
} else {
/*
* Provide initial data for the program
* command into the circular buffer
*/
host->use_dma = 0;
tx_cnt = (tx_cnt*QSPI_TX_BUFF_POP_MIN)/4;
if (tx_cnt > QSPI_TX_BUFF_MAX)
tx_cnt = QSPI_TX_BUFF_MAX;
for (i = 0; i < tx_cnt; i++) {
left_bytes = cmd->n_tx - i*4;
if (left_bytes <= 0) {
data = 0;
} else if (left_bytes < 4) {
data = 0;
qspi_memcpy(&data, &cmd->tx_buf[i*4], left_bytes);
} else {
data = (*(uint32_t *)&cmd->tx_buf[i*4]);
}
_writel(data, QSPI0_TBDR);
}
host->bytes_left -= tx_cnt*4;
}
}
/* 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);
if (cmd->flag & RST_AHB_DOMAIN) {
int size;
if (cmd->tx_buf)
size = cmd->n_tx;
else
size = chip->block_size;
// dcache_clean_invalidate_range(sfar, size);
}
/* 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);
/// cpsr=disableInterrupts();
do {
sr = _readl(QSPI0_SR);
if (sr & QSPI0_SR_BUSY)
continue;
fr = _readl(QSPI0_FR);
if (fr & QSPI0_FR_XIP_ON)
continue;
/* Resume Done */
_writel(QSPI0_FR_TFF, QSPI0_FR);
/* trigger command */
_writel(ipcr, QSPI0_IPCR);
fr = _readl(QSPI0_FR);
fr &= QSPI0_FR_IPAEF | QSPI0_FR_IPIEF | QSPI0_FR_IPGEF;
if (!fr)
break;
_writel(fr, QSPI0_FR);
} while(1);
if (host->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);
}
}
//#ifndef CONFIG_XIP
// if (cmd->flag & RST_AHB_DOMAIN) {
// qspi_reset();
// }
//#endif
host->cmd = NULL;
return 0;
}
int qspi_start_cmd(struct spi_flash_chip *chip, struct spi_flash_cmd *cmd)
{
#if 0
struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg;
int ret;
do {
ret = __qspi_start_cmd(chip, cmd);
if (!ret || (ret != EAGAIN && ret != ETIMEDOUT))
break;
uart_printf("cmd%d error %d, retry...\r\n", cmd_cfg->opcode,ret);
} while(1);
return ret;
#else
struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg;
int ret;
ret = __qspi_start_cmd(chip, cmd);
return ret;
#endif
}