blob: 0850a16bf4c71abd657481c936c37b76fac6c519 [file] [log] [blame]
#include <common.h>
#include <malloc.h>
#include <asm/arch/pxa_dma.h>
#include <power/asr1802s_freq.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <mtd/pxa3xx_bbm.h>
#include <asm/arch/cpu.h>
#include <asm/io.h>
#include "asr_qspi.h"
#define SPINAND_SUPPORT_DTR (1 << 7)
#define qspi_writel(val, addr) __raw_writel((val), addr)
#define qspi_readl(addr) __raw_readl(addr)
#define PMUA_QSPI_CLK_RES_CTRL_CLK_FC_REQ BIT_12
#define PMUA_QSPI_CLK_RES_CTRL_CLK_DIV_MSK (0x7 << 9)
#define PMUA_QSPI_CLK_RES_CTRL_CLK_DIV_BASE 9
#define PMUA_QSPI_CLK_RES_CTRL_CLK_SEL_MSK (0x7 << 6)
#define PMUA_QSPI_CLK_RES_CTRL_CLK_SEL_BASE 6
#define PMUA_QSPI_CLK_RES_CTRL_CLK_EN BIT_4
#define PMUA_QSPI_CLK_RES_CTRL_AXICLK_EN BIT_3
#define PMUA_QSPI_CLK_RES_CTRL_CLK_RST BIT_1
#define PMUA_QSPI_CLK_RES_CTRL_AXI_RST BIT_0
struct qspi_host qspi_host;
//static DMA_DESCRIPTOR dma_desc __attribute__ ((aligned (16)));
//static u8 *tx_desc = &dma_desc;
/* qspi write with check*/
static inline void qspi_writel_check(struct qspi_host *host, u32 val,
int reg, u32 mask)
{
u32 tmp;
qspi_writel(val, reg);
val &= ~mask;
do {
tmp = qspi_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, u32 val,
int reg, u32 mask)
{
u32 tmp;
qspi_writel(val, reg);
val &= ~mask;
do {
tmp = qspi_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, u32 val)
{
u32 fr;
do {
qspi_writel_check(host, val, QSPI0_RBCT, QSPI_RBCT_RESV);
fr = qspi_readl(QSPI0_FR);
if (!(fr & QSPI_FR_IPIEF))
break;
fr &= QSPI_FR_IPIEF;
qspi_writel(fr, QSPI0_FR);
} while (1);
}
static void qspi_write_sfar(struct qspi_host *host, u32 val)
{
u32 fr;
do {
qspi_writel_check(host, val, QSPI0_SFAR, 0x0);
fr = qspi_readl(QSPI0_FR);
if (!(fr & QSPI_FR_IPIEF))
break;
fr &= QSPI_FR_IPIEF;
qspi_writel(fr, QSPI0_FR);
} while (1);
}
static void qspi_config_mfp(int cs)
{
if (cs == QSPI_CS_A1) {
qspi_writel(0xd000, 0xd401e2c4); // QSPI_DAT3
qspi_writel(0x1000, 0xd401e2c8); // QSPI_DAT2
qspi_writel(0x1000, 0xd401e2cc); // QSPI_DAT1
qspi_writel(0x1000, 0xd401e2d0); // QSPI_DAT0
qspi_writel(0x1000, 0xd401e2d4); // QSPI_CLK
qspi_writel(0x1000, 0xd401e2d8); // QSPI_CS1
}
}
static void qspi_enter_mode(struct qspi_host *host, u32 mode)
{
u32 mcr;
mcr = qspi_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
qspi_writel(0x20, QSPI0_BUF0IND);
qspi_writel(0x40, QSPI0_BUF1IND);
qspi_writel(0x60, QSPI0_BUF2IND);
//128Byte, master ID?
qspi_writel(0x00001001, QSPI0_BUF0CR);
qspi_writel(0x1006, QSPI0_BUF1CR);
qspi_writel(0x1003, QSPI0_BUF2CR);
qspi_writel(0x80001002, QSPI0_BUF3CR);
#else
u32 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;
if (cpu_is_asr1903()) {
/*
* Config the ahb buffer
* Disable BUF1~BUF2, use BUF0 for all masters
*/
qspi_writel((512/8 - 1) * 8, QSPI0_BUF0IND); // other masters
qspi_writel(512, QSPI0_BUF1IND);
qspi_writel(512, QSPI0_BUF2IND);
/* AHB Master port */
qspi_writel(buf_cfg, QSPI0_BUF0CR);
qspi_writel(0xe, QSPI0_BUF1CR);
qspi_writel(0xe, QSPI0_BUF2CR);
qspi_writel(0xe, QSPI0_BUF3CR);
} else {
/*
* Config the ahb buffer
* Disable BUF0~BUF1, use BUF3 for all masters
*/
qspi_writel(0, QSPI0_BUF0IND);
qspi_writel(0, QSPI0_BUF1IND);
qspi_writel(0, QSPI0_BUF2IND);
/* AHB Master port */
qspi_writel(0xe, QSPI0_BUF0CR);
qspi_writel(0xe, QSPI0_BUF1CR);
qspi_writel(0xe, QSPI0_BUF2CR);
qspi_writel(buf_cfg, QSPI0_BUF3CR); // other masters
}
#endif
qspi_writel(lut << QSPI_BFGENCR_SEQID_SHIFT, QSPI0_BFGENCR);
printf("AHB data transfer size: %d\n", data_size);
}
static void qspi_lock_lut(void)
{
u32 lckcr;
lckcr = qspi_readl(QSPI0_LCKCR);
if (lckcr & 0x1)
return;
qspi_writel(0x5af05af0, QSPI0_LUTKEY);
qspi_writel(0x1, QSPI0_LCKCR);
}
static void qspi_unlock_lut(void)
{
u32 lckcr;
lckcr = qspi_readl(QSPI0_LCKCR);
if (lckcr & 0x2)
return;
qspi_writel(0x5af05af0, QSPI0_LUTKEY);
qspi_writel(0x2, QSPI0_LCKCR);
}
static void qspi_config_lookup_tbl(struct spi_flash_chip *chip,
struct spi_flash_cmd_cfg *cmd_cfg,
u8 mode, int seq_id)
{
u32 lut_value;
u16 lut_entry[8];
u8 pins[] = {0, QSPI_PAD_1X, QSPI_PAD_2X, 0, QSPI_PAD_4X};
u8 seq = 0, i;
u8 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 {
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;
/*
* 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);
qspi_writel(lut_value, lut_addr);
lut_value = qspi_readl(lut_addr);
}
if (seq % 2) {
lut_addr = QSPI0_LUT0 + seq_id*0x10 + (seq/2)*0x4;
lut_value =lut_entry[seq - 1];
qspi_writel(lut_value, lut_addr);
lut_value = qspi_readl(lut_addr);
}
// for (i = 0; i < seq; i++)
// 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;
u32 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
*/
static 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) {
printf("err: LUT %d already used\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;
printf("Fixed LUT bit-map: 0x%x\n", lut_map);
return 0;
}
static 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) {
chip->xip_read = 1;
printf("XIP Read mode enabled\n");
} else {
chip->xip_read = 0;
printf("IPS Read mode enabled\n");
}
return 0;
}
static void qspi_invalid_ahb(struct qspi_host *host)
{
u32 reg;
/* qspi softreset first */
reg = qspi_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 */
udelay(1);
reg &= ~(QSPI_MCR_SWRSTHD | QSPI_MCR_SWRSTSD);
qspi_writel_check(host, reg, QSPI0_MCR, QSPI_MCR_RESV);
}
static void qspi_clk_enable(void)
{
PMUA->QSPI_CLK_RES_CTRL |= 0x1 << 4 | 0x1 << 3;;
PMUA->QSPI_CLK_RES_CTRL |= 0x3;
}
static void qspi_clk_disable(void)
{
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;
if (cpu_is_asr1806()) {
/* enable PLL1_DIV23 */
APBSPARE->PLL3_SW_CTRL |= 0x7;
} else if (cpu_is_asr1903()) {
/* enable PLL1_DIV23/PLL1_DIV13/PLL1_DIV11 */
APBSPARE->apb_spare12_reg |= BIT_16 | BIT_17 | BIT_18;
}
/* Enabled QSPI clock, then take out of reset */
PMUA->QSPI_CLK_RES_CTRL |= (PMUA_QSPI_CLK_RES_CTRL_CLK_EN |
PMUA_QSPI_CLK_RES_CTRL_AXICLK_EN);
PMUA->QSPI_CLK_RES_CTRL |= (PMUA_QSPI_CLK_RES_CTRL_CLK_RST |
PMUA_QSPI_CLK_RES_CTRL_AXI_RST);
if (mhz >= 416) {
sel = 0;
div = 0;
} else if (mhz >= 312) {
div = 0;
if (cpu_is_asr1906())
sel = 1;
else
sel = 2;
} else if (mhz >= 208) {
sel = 0;
div = 1;
} else if (mhz >= 104) {
sel = 0;
div = 3;
} else if (mhz >= 78) {
if (cpu_is_asr1906()) {
sel = 1;
div = 3;
} else {
sel = 2;
div = 3;
}
} else if (mhz >= 52) {
sel = 0;
div = 7;
} else {
/* default 13M */
if (cpu_is_asr1906()) {
sel = 7;
div = 3;
} else {
sel = 5;
div = 7;
}
}
PMUA->QSPI_CLK_RES_CTRL &= ~(PMUA_QSPI_CLK_RES_CTRL_CLK_DIV_MSK |
PMUA_QSPI_CLK_RES_CTRL_CLK_SEL_MSK);
PMUA->QSPI_CLK_RES_CTRL |=
(div << PMUA_QSPI_CLK_RES_CTRL_CLK_DIV_BASE |
sel << PMUA_QSPI_CLK_RES_CTRL_CLK_SEL_BASE);
PMUA->QSPI_CLK_RES_CTRL |= BIT_12;
do {
reg = PMUA->QSPI_CLK_RES_CTRL;
if (!(reg & BIT_12))
break;
udelay(1);
timeout--;
if (!timeout) {
printf("err: qspi fc timeout!\n");
return -1;
}
} while (1);
return 0;
}
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();
PMUA->QSPI_CLK_RES_CTRL &= ~(0x7 << 6);
PMUA->QSPI_CLK_RES_CTRL |= clk_sel << 6;
qspi_clk_enable();
}
static int qspi_config_dqs_clk(struct qspi_host *host, int dcode)
{
uint32_t reg;
reg = qspi_readl(QSPI0_MCR);
if (dcode <= 0 || dcode > 255) {
reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN |
QSPI_MCR_DQS_INV_EN);
qspi_writel(reg, QSPI0_MCR);
return 0;
}
/* DQS enabled, use sample point N/1 */
qspi_writel(0x0, QSPI0_SMPR);
reg |= QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN | QSPI_MCR_DQS_INV_EN;
qspi_writel(reg, QSPI0_MCR);
reg = qspi_readl(QSPI0_SOCCR);
reg |= QSPI_SOCCR_DLINE_EN;
qspi_writel(reg, QSPI0_SOCCR);
reg = qspi_readl(QSPI0_DLACR);
reg &= ~QSPI_DLACR_DLINE_STEP_MASK;
reg = 0x7 << QSPI_DLACR_DLINE_STEP_SHIFT;
reg |= dcode & QSPI_DLACR_DLINE_CODE_MASK;
qspi_writel(reg, QSPI0_DLACR);
qspi_invalid_ahb(host);
return 1;
}
static 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 (cpu_is_asr1806() || cpu_is_asr1906() || cpu_is_asr1903())
qspi_set_func_clk_fc(host, mhz);
else
qspi_set_func_clk_nofc(mhz);
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;
} 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;
}
qspi_writel(reg, QSPI0_SMPR);
reg = qspi_readl(QSPI0_SMPR);
printf("QSPI_SMPR=0x%x t=%d tclqv=%d delay=%d\n",
reg, t, tclqv, delay);
/* set tx hold time */
reg = 0x202;
if (use_dtr)
reg |= QSPI_FLSHCR_TDH_HALF_2X;
qspi_writel(reg, QSPI0_FLSHCR);
/* Module enabled */
qspi_enter_mode(host, QSPI_NORMAL_MODE);
host->bus_clk = mhz;
printf("Bus clock: %dMHz QSPI_CLK_RES_CTRL: 0x%x\n",
mhz, PMUA->QSPI_CLK_RES_CTRL);
}
void asr_qspi_disable_dqs(void)
{
struct qspi_host *host = &qspi_host;
uint32_t reg;
if (!host->support_dqs)
return;
qspi_enter_mode(host, QSPI_DISABLE_MODE);
reg = qspi_readl(QSPI0_MCR);
reg &= ~(QSPI_MCR_DQS_EN | QSPI_MCR_DQS_LP_EN |
QSPI_MCR_DQS_INV_EN);
qspi_writel(reg, QSPI0_MCR);
qspi_enter_mode(host, QSPI_NORMAL_MODE);
qspi_invalid_ahb(host);
return;
}
static void qspi_enable_dma(struct qspi_host *host)
{
uint32_t resr;
resr = qspi_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 = qspi_readl(QSPI0_RSER);
resr &= ~QSPI_RSER_TBFDE;
qspi_writel_check(host, resr, QSPI0_RSER, QSPI_RSER_RESV);
}
static void qspi_config_interrupt(struct qspi_host *host)
{
uint32_t resr;
resr = qspi_readl(QSPI0_RSER);
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;
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 = qspi_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 = qspi_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;
u32 reg;
u32 data;
if (!cmd) {
printf("err: receive tx interrupt while no cmd sent\n");
return 1;
}
total_cnt = ALIGN(cmd->n_tx, QSPI_TX_BUFF_POP_MIN);
while (left_bytes > 0) {
qspi_writel(QSPI_FR_TBFF, QSPI0_SR);
reg = qspi_readl(QSPI0_SR);
if (reg & QSPI_FR_TBFF)
break;
data = 0;
if (cmd->n_tx - total_cnt + left_bytes >= 4)
data = (*(u32 *)&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);
qspi_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;
u32 sr;
u32 data;
int i;
while (left_bytes > 0) {
sr = qspi_readl(QSPI0_FR);
if (!(sr & QSPI_FR_RBDF))
break;
/* Check RXWE flag for data comming */
for (i = 0; i <= host->wmrk; i++) {
data = qspi_readl(QSPI0_RBDR0 + i*4);
//printf("i=%d data=0x%x\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 */
qspi_writel(QSPI_FR_RBDF, 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;
u32 rdbfl;
u32 data;
u32 i;
if (host->use_dma)
left_bytes = 0;
if (cmd->n_rx && left_bytes > 0) {
rdbfl = qspi_readl(QSPI0_RBSR);
rdbfl &= QSPI_RBSR_RDBFL_MASK;
rdbfl = rdbfl >> QSPI_RBSR_RDBFL_SHIFT;
for (i = 0; i <= rdbfl; i++) {
data = qspi_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) {
printf("Error: Not read enough data: "
"left_bytes=%d, cmd->n_rx=%d\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;
u32 fr, resr;
(void)data;
fr = qspi_readl(QSPI0_FR);
qspi_writel(fr & ~QSPI_FR_RBDF, QSPI0_FR);
if (!cmd) {
printf("Interrupt happen while no cmd sent, fr=0x%x\n", fr);
return;
}
resr = qspi_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)
printf("Err: Illegal Instruction Error Flag\n");
if (fr & QSPI_FR_IUEF)
printf("Err: IP Command Usage Error Flag\n");
if (fr & QSPI_FR_IPAEF)
printf("Err: IP Command Trigger during AHB Access Error Flag\n");
if (fr & QSPI_FR_IPIEF)
printf("Err: IP Command Trigger could not be executed Error Flag\n");
if (fr & QSPI_FR_IPGEF)
printf("Err: IP Command Trigger during AHB Grant Error Flag\n");
if (fr & QSPI_FR_RBOF)
printf("Error: RX Buffer Overflow\n");
if (fr & QSPI_FR_TBUF) {
int mcr;
mcr = qspi_readl(QSPI0_MCR);
mcr |= QSPI_MCR_CLR_TXF;
qspi_writel_clear(host, mcr, QSPI0_MCR,
~QSPI_MCR_CLR_TXF);
cmd->error = -EAGAIN;
printf("Error: TX Buffer Underrun Flag\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;
}
struct qspi_host * qspi_host_init(int cs, int mhz, int use_xip)
{
struct qspi_host *host = &qspi_host;
u32 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 = 1;
host->use_xip = use_xip;
if (cpu_is_asr1806() || cpu_is_asr1903_b0()) {
host->has_dtr = 1;
host->support_dqs = 1;
}
qspi_config_mfp(cs);
/* 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 */
qspi_writel(QSPI0_FLASH_A1_TOP & 0xfffffc00, QSPI0_SFA1AD);
qspi_writel(QSPI0_FLASH_A2_TOP & 0xfffffc00, QSPI0_SFA2AD);
qspi_writel(QSPI0_FLASH_B1_TOP & 0xfffffc00, QSPI0_SFB1AD);
qspi_writel(QSPI0_FLASH_B2_TOP & 0xfffffc00, QSPI0_SFB2AD);
/*
* ISD3FB, ISD2FB, ISD3FA, ISD2FA = 1; ENDIAN = 0x3; END_CFG=0x3
* DELAY_CLK4X_EN = 1
*/
reg = qspi_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);
//if (host->use_intr) {
// ISR_Connect(45, qspi_irq_handler, 0);
// INT_Enable(45, 0, 15);
//}
//printf("tx_desc: 0x%x\n", tx_desc);
printf("use_intr=%d en_tx_dma=%d use_xip=%d\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;
u32 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)
udelay(5);
do {
fr = qspi_readl(QSPI0_FR);
if (fr)
qspi_writel(fr, QSPI0_FR);
if (fr & QSPI_FR_ILLINE) {
printf("Err: Illegal Instruction Error Flag\n");
break;
}
if (fr & QSPI_FR_IUEF) {
printf("Err: IP Command Usage Error Flag\n");
break;
}
if (fr & QSPI_FR_IPAEF) {
printf("Err: IP Command Trigger during AHB Access Error Flag\n");
break;
}
if (fr & QSPI_FR_IPIEF) {
printf("Err: IP Command Trigger could not be executed Error Flag\n");
break;
}
if (fr & QSPI_FR_IPGEF) {
printf("Err: IP Command Trigger during AHB Grant Error Flag\n");
break;
}
if (fr & QSPI_FR_TFF) {
qspi_writel(0x1, QSPI0_FR);
break;
}
} while(1);
if (cmd->rx_buf && (fr & QSPI_FR_RBOF))
printf("Error: RX Buffer Overflow\n");
if (cmd->tx_buf && (fr & QSPI_FR_TBUF)) {
int mcr;
mcr = qspi_readl(QSPI0_MCR);
mcr |= QSPI_MCR_CLR_TXF;
qspi_writel_clear(host, mcr, QSPI0_MCR, ~QSPI_MCR_CLR_TXF);
cmd->error = -EAGAIN;
printf("Error: TX Buffer Underrun Flag\n");
}
}
static void qspi_poll_rx_buff(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
int left_bytes = host->bytes_left;
u32 sr;
u32 data;
int rdbfl, i;
do {
/* Check RXWE flag for data comming */
sr = qspi_readl(QSPI0_FR);
if (!(sr & QSPI_FR_RBDF))
continue;
for (i = 0; i <= host->wmrk; i++) {
data = qspi_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;
}
/* Set RBDF to trigger RX Buffer POP */
qspi_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 = qspi_readl(QSPI0_RBSR);
rdbfl = (rdbfl & QSPI_RBSR_RDBFL_MASK) >> QSPI_RBSR_RDBFL_SHIFT;
for (i = 0; i <= rdbfl; i++) {
data = qspi_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) {
printf("Error: Not read enough data: left_bytes=%d, cmd->n_rx=%d\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;
u32 reg;
u32 data;
total_cnt = ALIGN(cmd->n_tx, QSPI_TX_BUFF_POP_MIN);
while (left_bytes > 0) {
reg = qspi_readl(QSPI0_SR);
if (reg & QSPI_SR_TXFULL)
continue;
data = 0;
if (cmd->n_tx - total_cnt + left_bytes >= 4)
data = (*(u32 *)&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);
qspi_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;
dmac_map_device_to_channel(QSPI_DMA_TX_DRCMR, QSPI_DMA_TX_CHANNEL); //TX
dmac_user_aligment(QSPI_DMA_TX_CHANNEL);
#if 0
config_descriptor((u32 *)tx_desc, 0, (u32)cmd->tx_buf,
QSPI0_TBDR, TX_data.value, 1);
load_descriptor((void *)tx_desc, QSPI_DMA_TX_CHANNEL);
#else
// dma_set_mode(DMA_MODE_NONFETCH, QSPI_DMA_TX_CHANNEL);
// dma_set_reg_nf((u32)cmd->tx_buf, QSPI0_TBDR, &TX_data,
// QSPI_DMA_TX_CHANNEL);
#endif
pxa_dma_write(QSPI0_TBDR - 4, (u32)cmd->tx_buf,
host->bytes_left, QSPI_DMA_TX_CHANNEL);
flush_dcache_range((u32)cmd->tx_buf, (u32)cmd->tx_buf + cmd->n_tx);
dmac_start_transfer(QSPI_DMA_TX_CHANNEL);
return 0;
}
static int qspi_check_dtr(struct spi_flash_chip *chip)
{
return chip->host->has_dtr;
}
static int qspi_setup_memmap_read(struct spi_flash_chip *chip,
struct spi_flash_cmd_cfg *xip_cfg)
{
/*
* TODO:
* Maybe change due to different vendor
*/
qspi_enable_xip(chip, xip_cfg);
if (qspi_preinit_lookup_tbl(chip) < 0) {
pr_info("preinit_lookup_tbl failed, check cmd table\n");
return -1;
}
return 0;
}
static int qspi_search_bbm_table(struct spi_flash_chip *chip, int addr)
{
struct pxa3xx_bbm *pxa3xx_bbm = chip->mtd->bbm;
if (pxa3xx_bbm)
addr = pxa3xx_bbm->search(chip->mtd, addr);
return addr;
}
#ifdef CONFIG_SPINAND_BITFLIP_SCRUB
static int qspi_low_level_scrub(struct spi_flash_chip *chip, int page_addr,
int corrected)
{
struct pxa3xx_bbm *pxa3xx_bbm = chip->mtd->bbm;
if (pxa3xx_bbm && pxa3xx_bbm->scrub_read_disturb &&
chip->refresh_threshold) {
if (corrected >= chip->refresh_threshold)
pxa3xx_bbm->scrub_read_disturb(chip->mtd,
page_addr << chip->mtd->writesize_shift);
/* Do not report bit-flip if bbm enabled */
corrected = 0;
}
return corrected;
}
#endif
static int qspi_ahb_read(struct spi_flash_chip *chip,
u8 *buf, u32 from, u32 len)
{
struct qspi_host *host = chip->host;
memcpy(buf, host->cs_addr[chip->cs] + from, len);
return 0;
}
static void qspi_prepare_recv(struct qspi_host *host)
{
struct spi_flash_cmd *cmd = host->cmd;
u32 reg;
int wmrk;
/* Clear RX FIFO. Invalidate the RX Buffer */
reg = qspi_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(cmd->n_rx, 4) >> 2) - 1;
else
wmrk = 0x1; /* Water Mark: 16*4byte */
reg = qspi_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;
u32 reg;
int wmrk;
/* Clear TX FIFO/Buffer */
reg = qspi_readl(QSPI0_MCR);
reg |= QSPI_MCR_CLR_TXF;
qspi_writel_clear(host, reg, QSPI0_MCR, ~QSPI_MCR_CLR_TXF);
host->bytes_left = ALIGN(cmd->n_tx, QSPI_TX_BUFF_POP_MIN);
if (host->en_tx_dma && host->bytes_left >= 32) {
wmrk = 0x7; /* 32bytes watermark */
reg = qspi_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 = (*(u32 *)&cmd->tx_buf[i << 2]);
else if (left_bytes > 0)
memcpy(&data, &cmd->tx_buf[i << 2], left_bytes);
qspi_writel(data, QSPI0_TBDR);
}
host->bytes_left -= tx_cnt << 2;
}
}
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;
u32 ipcr, sfar, fr, tmp;
int seq_id = cmd_cfg->seq_id;
int i;
do {
tmp = qspi_readl(QSPI0_SR);
if (!(tmp & QSPI_SR_BUSY))
break;
udelay(1);
//printf("The controller is busy, 0x%x\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 = qspi_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;
}
flush_dcache_range(addr, size);
}
/* Clear FR before trigger command */
fr = qspi_readl(QSPI0_FR);
if (fr)
qspi_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);
qspi_start_dma_xfer(host);
/*
* 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);
}
}
/* trigger command */
ipcr = (seq_id << QSPI_IPCR_SEQID_SHIFT) & QSPI_IPCR_SEQID_MASK;
if (cmd->rx_buf)
ipcr |= (cmd->n_rx & 0xffff);
else if (cmd->tx_buf)
ipcr |= (cmd->n_tx & 0xffff);
restart:
qspi_writel(ipcr, QSPI0_IPCR);
if (cmd->n_tx && host->use_dma) {
int timeout = 10000;
do {
if (dmac_read_dcsr(QSPI_DMA_TX_CHANNEL) & DCSR_STOPSTATE) {
/*
* Add extra delay to make sure dma transfer
* finished on APB bus
*/
udelay(2);
break;
}
if (--timeout < 0) {
fr = qspi_readl(QSPI0_FR);
if (fr & (QSPI_FR_IPAEF | QSPI_FR_IPIEF |
QSPI_FR_IPGEF)) {
printf("qspi: cmd trigger failed, "
"fr=0x%x. restart...\n", fr);
qspi_writel(fr, QSPI0_FR);
goto restart;
}
DCSR(QSPI_DMA_TX_CHANNEL) &= ~DCSR_RUN;
printf("err: qspi tx dma timeout\n");
if (fr & (QSPI_FR_TBUF | QSPI_FR_TFF)) {
cmd->error = -EAGAIN;
pr_debug("TX Buffer Underrun, retry\n");
break;
}
BUG();
}
udelay(1);
} while (1);
}
fr = qspi_readl(QSPI0_FR);
if (fr & (QSPI_FR_IPAEF | QSPI_FR_IPIEF | QSPI_FR_IPGEF)) {
printf("qspi: cmd trigger failed, fr=0x%x. restart...\n", fr);
qspi_writel(fr, QSPI0_FR);
goto restart;
}
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);
}
}
/* 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;
}
int asr_qspi_probe_flash(int nand, int cs, int mhz,
int rx_mode, int tx_mode)
{
struct spi_flash_chip *chip;
struct mtd_info *mtd;
struct qspi_host *host;
int ret;
host = qspi_host_init(cs, 13, 1);
chip = kzalloc(sizeof(struct spi_flash_chip), GFP_KERNEL);
if (!chip) {
ret = -ENOMEM;
goto err1;
}
chip->host = host;
chip->cs = cs >= QSPI_CS_MAX ? QSPI_CS_A1 : cs;
chip->rx_mode = rx_mode;
chip->tx_mode = tx_mode;
chip->bus_clk = host->bus_clk;
chip->issue_cmd = qspi_start_cmd;
chip->memmap_read = qspi_ahb_read;
chip->search_bbm_table = qspi_search_bbm_table;
#ifdef CONFIG_SPINAND_BITFLIP_SCRUB
chip->low_level_scrub = qspi_low_level_scrub;
#endif
chip->setup_memmap_read = qspi_setup_memmap_read;
chip->check_dtr = qspi_check_dtr;
mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!mtd) {
ret = -ENOMEM;
goto err2;
}
mtd->priv = chip;
chip->mtd = mtd;
/* Init rx_max_len/tx_max_len because spi_nand_scan_tail may need this */
chip->tx_max_len = QSPI_TX_BUFF_MAX << 2;
chip->rx_max_len = QSPI_RX_BUFF_MAX << 2;
if (nand) {
chip->name = "nand0";
chip->options |= BBT_RELOCATION_IFBAD;
#ifdef CONFIG_BBM
chip->scan_bbt = pxa3xx_scan_bbt;
chip->block_bad = pxa3xx_block_bad;
chip->block_markbad = pxa3xx_block_markbad;
#endif
#ifdef CONFIG_CMD_SPIFLASH_NAND
spi_nand_scan_ident(mtd);
spi_nand_scan_tail(mtd);
#endif
} else {
chip->name = "nor0";
#ifdef CONFIG_CMD_SPIFLASH_NOR
spi_nor_scan_ident(mtd);
spi_nor_scan_tail(mtd);
#endif
}
if (chip->max_mhz && mhz > chip->max_mhz) {
printf("warn: device max supported frequency is %d MHz!!!\n",
chip->max_mhz);
mhz = chip->max_mhz;
}
qspi_set_func_clk(host, mhz, chip->options & SPINAND_SUPPORT_DTR,
chip->tclqv, chip->tset, chip->thold);
if (!host->en_tx_dma)
chip->tx_max_len = QSPI_TX_BUFF_MAX << 2;
else
chip->tx_max_len = chip->page_size;
if (chip->xip_read)
chip->rx_max_len = chip->page_size;
else
chip->rx_max_len = QSPI_RX_BUFF_MAX << 2;
#ifdef CONFIG_CMD_UBI
add_mtd_device(mtd);
#endif
return 0;
err3:
kfree(mtd);
err2:
kfree(chip);
err1:
return ret;
}