blob: b0c68fadf6e61164de8d96031bd48164366c1474 [file] [log] [blame]
#include "qspi_host.h"
#include "spi_nand.h"
#include "Errors.h"
#include "misc.h"
#include "timer.h"
#include "PlatformConfig.h"
#if COPYIMAGESTOFLASH
#include "BootLoader.h"
#endif
static void generic_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected, uint32_t *ecc_error);
static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected, uint32_t *ecc_error);
static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected, uint32_t *ecc_error);
static void micron_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected,
uint32_t *ecc_error);
static void gd_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void gd_spi_nand_ecc_status2(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void xtx_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void xtx2_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void yxsc_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void wb_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
static void wb_spi_nand_ecc_status2(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error);
enum {
GET_FEATURE,
SET_FEATURE,
PAGE_READ,
READ_PAGE_CACHE_RDM,
READ_PAGE_CACHE_LAST,
READ_FROM_CACHE,
READ_FROM_CACHE_GD_C,
READ_FROM_CACHE_FAST,
READ_FROM_CACHE_X2,
READ_FROM_CACHE_DUAL,
READ_FROM_CACHE_X4,
READ_FROM_CACHE_QUAD,
READ_FROM_CACHE_DTR,
BLK_ERASE,
PROG_EXC,
PROG_LOAD,
PROG_LOAD_RDM_DATA,
PROG_LOAD_X4,
PROG_LOAD_RDM_DATA_X4,
WR_ENABLE,
WR_DISABLE,
READ_ID,
RESET,
READ_FROM_CACHE_QUAD_GD,
READ_FROM_CACHE_QUAD_GD2,
READ_ECCSR_MXIC,
MAX_CMD,
};
static struct spi_flash_chip nand_chip;
static struct spi_nand_info spi_nand_table[] = {
/* Macronix */
SPI_NAND_INFO("MX35UF1GE4AC", 0xC2, 0x92, 2048, 64, 64, 1024,
1, 4, SPINAND_NEED_SET_BFT, 3, 0,
READ_FROM_CACHE_QUAD, mxic_spi_nand_ecc_status),
SPI_NAND_INFO("MX35UF2GE4AC", 0xC2, 0xA2, 2048, 64, 64, 2048,
1, 4, SPINAND_NEED_SET_BFT, 3, 0,
READ_FROM_CACHE_QUAD, mxic_spi_nand_ecc_status),
SPI_NAND_INFO("MX35UF1GE4AD", 0xC2, 0x96, 2048, 128, 64, 1024,
1, 8, SPINAND_NEED_SET_BFT, 6, 0,
READ_FROM_CACHE_QUAD, mxic2_spi_nand_ecc_status),
SPI_NAND_INFO("MX35UF2GE4AD", 0xC2, 0xA6, 2048, 128, 64, 2048,
1, 8, SPINAND_NEED_SET_BFT, 6, 0,
READ_FROM_CACHE_QUAD, mxic2_spi_nand_ecc_status),
/* GigaDeivce 1.8V */
SPI_NAND_INFO("GD5F1GQ4RBxIG", 0xC8, 0xC1, 2048, 128, 64, 1024,
1, 8, 0, 6, 0,
READ_FROM_CACHE_QUAD_GD, gd_spi_nand_ecc_status),
SPI_NAND_INFO("GD5F2GQ4RBxIG", 0xC8, 0xC2, 2048, 128, 64, 2048,
1, 8, 0, 6, 0,
READ_FROM_CACHE_QUAD_GD, gd_spi_nand_ecc_status),
SPI_NAND_INFO("GD5F1GQ4RCxIG", 0xC8, 0xA148, 2048, 128, 64, 1024,
1, 7, 0, 6, 0,
READ_FROM_CACHE_QUAD_GD, gd_spi_nand_ecc_status),
SPI_NAND_INFO("GD5F1GQ5RExxG", 0xC8, 0x41, 2048, 128, 64, 1024,
1, 4, 0, 3, 0,
READ_FROM_CACHE_QUAD, gd_spi_nand_ecc_status2),
SPI_NAND_INFO("GD5F2GQ5RExxG", 0xC8, 0x42, 2048, 128, 64, 2048,
1, 4, 0, 3, 0,
READ_FROM_CACHE_QUAD_GD2, gd_spi_nand_ecc_status2),
#if 1
SPI_NAND_INFO_DTR("GD5F1GM7RE", 0xC8, 0x81, 2048, 128, 64, 1024,
1, 8, SPINAND_SUPPORT_DTR, 6, 9, 2, 2, 80,
READ_FROM_CACHE_QUAD, READ_FROM_CACHE_DTR,
gd_spi_nand_ecc_status),
#else
SPI_NAND_INFO_TIMING("GD5F1GM7RE", 0xC8, 0x81, 2048, 128, 64, 1024,
1, 8, 0, 6, 9, 2, 2, 0,
READ_FROM_CACHE_QUAD, gd_spi_nand_ecc_status),
#endif
SPI_NAND_INFO("GD5F2GM7RE", 0xC8, 0x82, 2048, 128, 64, 2048,
1, 8, 0, 6, 0,
READ_FROM_CACHE_QUAD, gd_spi_nand_ecc_status),
SPI_NAND_INFO_TIMING("GD5F4GQ6RExxG", 0xC8, 0x45, 2048, 128, 64, 2048,
2, 4, 0, 3, 11, 2, 2, 0,
READ_FROM_CACHE_QUAD_GD2, gd_spi_nand_ecc_status2),
SPI_NAND_INFO("GD5F4GM8RExxG", 0xC8, 0x85, 2048, 128, 64, 4096,
1, 8, 0, 6, 0,
READ_FROM_CACHE_QUAD, gd_spi_nand_ecc_status),
/* GigaDeivce 3.3V */
SPI_NAND_INFO("GD5F1GQ4UExxG", 0xC8, 0xD1, 2048, 128, 64, 1024,
1, 8, 0, 6, 0,
READ_FROM_CACHE_QUAD_GD, gd_spi_nand_ecc_status),
/* Winbond */
SPI_NAND_INFO("W25N512GWxxR/T", 0xEF, 0x20BA, 2048, 64, 64, 512,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
SPI_NAND_INFO("W25N01GWxxIx", 0xEF, 0x21BA, 2048, 64, 64, 1024,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
SPI_NAND_INFO("W25N01KW", 0xEF, 0x21BE, 2048, 64, 64, 1024,
1, 4, 0, 3, 0,
READ_FROM_CACHE_QUAD, wb_spi_nand_ecc_status),
SPI_NAND_INFO("W25N02KW", 0xEF, 0x22BA, 2048, 64, 64, 2048,
1, 8, 0, 6, 0,
READ_FROM_CACHE_QUAD, wb_spi_nand_ecc_status2),
SPI_NAND_INFO("W25M02GWxxIx", 0xEF, 0x21BB, 2048, 64, 64, 2048,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
SPI_NAND_INFO("W25M01JW", 0xEF, 0x21BC, 2048, 64, 64, 1024,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
/* Dosilicon */
SPI_NAND_INFO("DS35M1GAxx", 0xE5, 0x21, 2048, 64, 64, 1024,
1, 4, 0, 4, 0,
READ_FROM_CACHE_X4, generic_spi_nand_ecc_status),
SPI_NAND_INFO("DS35M2GAxx", 0xE5, 0x22, 2048, 64, 64, 2048,
1, 4, SPINAND_NEED_PLANE_SELECT, 4, 0,
READ_FROM_CACHE_X4, generic_spi_nand_ecc_status),
SPI_NAND_INFO("DS35M2GBxx", 0xE5, 0xA2, 2048, 128, 64, 2048,
1, 8, SPINAND_NEED_PLANE_SELECT, 4, 83,
READ_FROM_CACHE_X4, micron_spi_nand_ecc_status),
SPI_NAND_INFO("DS35M4GMxx", 0xE5, 0xA4, 2048, 128, 64, 4096,
1, 8, SPINAND_NEED_PLANE_SELECT, 4, 83,
READ_FROM_CACHE_X4, micron_spi_nand_ecc_status),
/* ZettaDevice */
SPI_NAND_INFO("ZD35M1GAxx", 0xBA, 0x21, 2048, 64, 64, 1024,
1, 4, 0, 4, 0,
READ_FROM_CACHE_X4, generic_spi_nand_ecc_status),
/* XTX */
SPI_NAND_INFO("PN26Q01AWSIUG", 0xA1, 0xC1, 2048, 64, 64, 1024,
1, 4, SPINAND_RDM_CMD_NEED_PAGE_READ | SPINAND_ECC_EN_ADDR_90H,
8, 52, READ_FROM_CACHE_X4, xtx_spi_nand_ecc_status),
SPI_NAND_INFO("XT26Q01D-BE", 0x0B, 0x51, 2048, 128, 64, 1024,
1, 8, 0, 5, 0,
READ_FROM_CACHE_QUAD_GD, xtx2_spi_nand_ecc_status),
SPI_NAND_INFO("XT26Q02D", 0x0B, 0x52, 2048, 128, 64, 2048,
1, 8, 0, 5, 0,
READ_FROM_CACHE_QUAD_GD, xtx2_spi_nand_ecc_status),
SPI_NAND_INFO("XT26Q02E", 0x2C, 0x25, 2048, 128, 64, 2048,
1, 8, SPINAND_NEED_PLANE_SELECT, 4, 52,
READ_FROM_CACHE_QUAD, micron_spi_nand_ecc_status),
/* YXSC */
SPI_NAND_INFO("TX25G01", 0xA1, 0xF1, 2048, 64, 64, 1024,
1, 4, SPINAND_RDM_CMD_NEED_PAGE_READ | SPINAND_ECC_EN_ADDR_90H,
2, 0, READ_FROM_CACHE_QUAD_GD, yxsc_spi_nand_ecc_status),
/* ESMT */
SPI_NAND_INFO("F50D1G41LB", 0xC8, 0x11, 2048, 128, 64, 1024,
1, 7, 0, 4, 52,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
/* Fudan Microelectronics */
SPI_NAND_INFO("FM25LS01", 0xA1, 0xA5, 2048, 128, 64, 1024,
1, 4, 0, 4, 0,
READ_FROM_CACHE_X4, generic_spi_nand_ecc_status),
SPI_NAND_INFO("FM25LG02B", 0xA1, 0xB2, 2048, 128, 64, 2048,
1, 8, 0, 4, 0,
READ_FROM_CACHE_X4, fm_spi_nand_ecc_status),
/* Micron */
SPI_NAND_INFO("MT29F1G01ABBFD", 0x2C, 0x15, 2048, 64, 64, 2048,
1, 8, SPINAND_NEED_PLANE_SELECT, 4, 0,
READ_FROM_CACHE_X4, micron_spi_nand_ecc_status),
/* SiliconGo 3.3V */
SPI_NAND_INFO("SGM7000I-S24W1GH", 0xEA, 0xC1, 2048, 64, 64, 1024,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
SPI_NAND_INFO("SGM7000I-S25W2GH", 0xEA, 0xC2, 2048, 64, 64, 2048,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
SPI_NAND_INFO("SGM7000I-S25W4GH", 0xEA, 0xC4, 2048, 64, 64, 4096,
1, 4, 0, 4, 0,
READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
{.name = NULL},
};
/* Standard SPI-NAND flash commands */
static struct spi_flash_cmd_cfg cmd_table[] = {
/*opcode addr_bytes addr_pins mode_bits mode_pins dummy_cycles
dummy_pins data_pins seq_id cmd_type */
[GET_FEATURE] = SPI_CMD(0x0f, 1, 1, 0, 0, 0, 0, 1, 2, 1),
[SET_FEATURE] = SPI_CMD(0x1f, 1, 1, 0, 0, 0, 0, 1, -1, 2),
[PAGE_READ] = SPI_CMD(0x13, 3, 1, 0, 0, 0, 0, 0, 3, 0),
[READ_PAGE_CACHE_RDM] = SPI_CMD(0x30, 3, 1, 0, 0, 0, 0, 0, -1, 0),
[READ_PAGE_CACHE_LAST] = SPI_CMD(0x3f, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[READ_FROM_CACHE] = SPI_CMD(0x03, 2, 1, 0, 0, 8, 1, 1, -1, 1),
[READ_FROM_CACHE_FAST] = SPI_CMD(0x0b, 2, 1, 0, 0, 8, 1, 1, -1, 1),
[READ_FROM_CACHE_X2] = SPI_CMD(0x3b, 2, 1, 0, 0, 8, 1, 2, -1, 1),
[READ_FROM_CACHE_DUAL] = SPI_CMD(0xbb, 2, 2, 0, 0, 4, 2, 2, -1, 1),
[READ_FROM_CACHE_X4] = SPI_CMD(0x6b, 2, 1, 0, 0, 8, 1, 4, 4, 1),
[READ_FROM_CACHE_QUAD] = SPI_CMD(0xeb, 2, 4, 0, 0, 4, 4, 4, 5, 1),
[BLK_ERASE] = SPI_CMD(0xd8, 3, 1, 0, 0, 0, 0, 0, -1, 0),
[PROG_EXC] = SPI_CMD(0x10, 3, 1, 0, 0, 0, 0, 0, 6, 0),
[PROG_LOAD] = SPI_CMD(0x02, 2, 1, 0, 0, 0, 0, 1, 7, 2),
[PROG_LOAD_RDM_DATA] = SPI_CMD(0x84, 2, 1, 0, 0, 0, 0, 1, -1, 2),
[PROG_LOAD_X4] = SPI_CMD(0x32, 2, 1, 0, 0, 0, 0, 4, 8, 2),
[PROG_LOAD_RDM_DATA_X4] = SPI_CMD(0x34, 2, 1, 0, 0, 0, 0, 4, -1, 2),
[WR_ENABLE] = SPI_CMD(0x06, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[WR_DISABLE] = SPI_CMD(0x04, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[READ_ID] = SPI_CMD(0x9f, 0, 0, 0, 0, 0, 0, 1, -1, 1),
[RESET] = SPI_CMD(0xff, 0, 0, 0, 0, 0, 0, 0, -1, 0),
/* Vendor Specific command */
[READ_FROM_CACHE_QUAD_GD] = SPI_CMD(0xeb, 2, 4, 0, 0, 2, 4, 4, 9, 1),
[READ_FROM_CACHE_QUAD_GD2] = SPI_CMD(0xeb, 2, 4, 0, 0, 8, 4, 4, 10, 1),
[READ_ECCSR_MXIC] = SPI_CMD(0x7c, 0, 0, 0, 0, 8, 1, 1, -1, 1),
[READ_FROM_CACHE_GD_C] = SPI_CMD(0x03, 3, 1, 0, 0, 0, 0, 1, -1, 1),
[READ_FROM_CACHE_DTR] = SPI_CMD_DTR(0xee, 0, 4, 4, 1, 0, 0, 0, 8, 4, 4, 1, 11, 1),
/* END Mark */
[MAX_CMD] = SPI_CMD(0x00, 0, 0, 0, 0, 0, 0, 0, -1, 0),
};
/**
* spi_nand_read_reg - send command 0Fh to read register
* @chip: SPI_FLASH device structure
* @reg; register to read
* @buf: buffer to store value
*/
static int spi_nand_read_reg(struct spi_flash_chip *chip,
uint8_t reg, uint8_t *buf)
{
struct spi_flash_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + GET_FEATURE;
cmd.n_addr = 1;
cmd.addr[0] = reg;
cmd.n_rx = 1;
cmd.rx_buf = buf;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: %d read register %d\r\n", ret, reg);
return ret;
}
/**
* spi_nand_write_reg - send command 1Fh to write register
* @chip: SPI-NAND device structure
* @reg; register to write
* @buf: buffer stored value
*/
static int spi_nand_write_reg(struct spi_flash_chip *chip,
uint8_t reg, uint8_t *buf)
{
struct spi_flash_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + SET_FEATURE;
cmd.n_addr = 1;
cmd.addr[0] = reg;
cmd.n_tx = 1;
cmd.tx_buf = buf;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: %d write register %d\r\n", ret, reg);
return ret;
}
/**
* spi_nand_read_status - get status register value
* @chip: SPI-NAND device structure
* @status: buffer to store value
* Description:
* After read, write, or erase, the Nand device is expected to set the
* busy status.
* This function is to allow reading the status of the command: read,
* write, and erase.
* Once the status turns to be ready, the other status bits also are
* valid status bits.
*/
static int spi_nand_read_status(struct spi_flash_chip *chip, uint8_t *status)
{
return spi_nand_read_reg(chip, REG_STATUS, status);
}
/**
* spi_nand_get_cfg - get configuration register value
* @chip: SPI-NAND device structure
* @cfg: buffer to store value
* Description:
* Configuration register includes OTP config, Lock Tight enable/disable
* and Internal ECC enable/disable.
*/
static int spi_nand_get_cfg(struct spi_flash_chip *chip, uint8_t *cfg)
{
return spi_nand_read_reg(chip, REG_CFG, cfg);
}
/**
* spi_nand_set_cfg - set value to configuration register
* @chip: SPI-NAND device structure
* @cfg: buffer stored value
* Description:
* Configuration register includes OTP config, Lock Tight enable/disable
* and Internal ECC enable/disable.
*/
static int spi_nand_set_cfg(struct spi_flash_chip *chip, uint8_t *cfg)
{
return spi_nand_write_reg(chip, REG_CFG, cfg);
}
/**
* spi_nand_enable_ecc - enable internal ECC
* @chip: SPI-NAND device structure
* Description:
* There is one bit( bit 0x10 ) to set or to clear the internal ECC.
* Enable chip internal ECC, set the bit to 1
* Disable chip internal ECC, clear the bit to 0
*/
static int spi_nand_enable_ecc(struct spi_flash_chip *chip)
{
uint8_t cfg = 0;
/* For XTX spi-nand, ECC_EN locate in 0x90 feature register */
if (chip->options & SPINAND_ECC_EN_ADDR_90H)
spi_nand_read_reg(chip, 0x90, &cfg);
else
spi_nand_get_cfg(chip, &cfg);
if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE)
return 0;
cfg |= CFG_ECC_ENABLE;
if (chip->options & SPINAND_ECC_EN_ADDR_90H)
return spi_nand_write_reg(chip, 0x90, &cfg);
else
return spi_nand_set_cfg(chip, &cfg);
}
/**
* spi_nand_disable_ecc - disable internal ECC
* @chip: SPI-NAND device structure
* Description:
* There is one bit( bit 0x10 ) to set or to clear the internal ECC.
* Enable chip internal ECC, set the bit to 1
* Disable chip internal ECC, clear the bit to 0
*/
static int spi_nand_disable_ecc(struct spi_flash_chip *chip)
{
uint8_t cfg = 0;
/* For XTX spi-nand, ECC_EN locate in 0x90 feature register */
if (chip->options & SPINAND_ECC_EN_ADDR_90H)
spi_nand_read_reg(chip, 0x90, &cfg);
else
spi_nand_get_cfg(chip, &cfg);
if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE) {
cfg &= ~CFG_ECC_ENABLE;
if (chip->options & SPINAND_ECC_EN_ADDR_90H)
return spi_nand_write_reg(chip, 0x90, &cfg);
else
return spi_nand_set_cfg(chip, &cfg);
}
return 0;
}
static int spi_nand_enable_quad(struct spi_flash_chip *chip)
{
uint8_t cfg = 0;
spi_nand_get_cfg(chip, &cfg);
if ((cfg & CFG_QE_MASK) == CFG_QE_ENABLE)
return 0;
cfg |= CFG_QE_ENABLE;
return spi_nand_set_cfg(chip, &cfg);
}
/**
* generic_spi_nand_ecc_status - decode status regisger to get ecc info
* @status: status register value to decode
* @corrected: bitflip count that ecc corrected
* @ecc_error: uncorrected bitflip happen or not
*/
static void generic_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
/* 1~4 error bits detected and corrected */
*corrected = 4;
break;
case 0x2:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
default:
obm_printf("%s: unexpected status: %d\r\n",
__func__, ecc_status);
*ecc_error = 1;
break;
}
}
static int mxic_spi_nand_set_bft(struct spi_flash_chip *chip, uint8_t threshold)
{
uint8_t val, bft;
int ret;
ret = spi_nand_read_reg(chip, 0x10, &val);
if (ret)
return ret;
bft = (val & 0xf0) >> 4;
obm_printf("%s: read BFT=0x%x threshold=%d\n", __func__, val, threshold);
if (bft != threshold) {
val = threshold << 4;
ret = spi_nand_write_reg(chip, 0x10, &val);
if (ret)
return ret;
obm_printf("%s: update BFT=0x%x\n", __func__, val);
}
return 0;
}
static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
/* 1~4 error bits detected and corrected */
*corrected = 4;
break;
case 0x2:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
default:
*corrected = 4;
break;
}
}
static int mxic_spi_nand_read_eccsr(struct spi_flash_chip *chip, uint8_t *eccsr)
{
struct spi_flash_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + READ_ECCSR_MXIC;
cmd.n_rx = 1;
cmd.rx_buf = eccsr;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: %d read eccsr register\r\n", ret);
return ret;
}
static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
uint8_t eccsr;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x2:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
case 0x1:
/* 1~8 error bits detected and corrected */
*corrected = 4;
default:
mxic_spi_nand_read_eccsr(chip, &eccsr);
eccsr &= 0xf;
if (eccsr == 0xf)
*ecc_error = 1;
else
*corrected = eccsr;
break;
}
}
static void micron_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected,
uint32_t *ecc_error)
{
uint8_t ecc_status = (status & 0x70) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
/* 1~3 error bits detected and corrected */
*corrected = 3;
break;
case 0x2:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
case 0x3:
/* 4~6 error bits detected and corrected */
*corrected = 6;
break;
case 0x5:
/* 7~8 error bits detected and corrected */
*corrected = 8;
break;
default:
obm_printf("%s: unexpected status: %d\r\n",
__func__, ecc_status);
*ecc_error = 1;
break;
}
}
static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip,
uint8_t status, uint32_t *corrected,
uint32_t *ecc_error)
{
uint8_t ecc_status = (status & 0x70) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
*corrected = 3;
break;
case 0x2:
*corrected = 4;
break;
case 0x3:
*corrected = 5;
break;
case 0x4:
*corrected = 6;
break;
case 0x5:
*corrected = 7;
break;
case 0x6:
*corrected = 8;
break;
case 0x7:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
default:
obm_printf("%s: unexpected status: %d\r\n",
__func__, ecc_status);
*ecc_error = 1;
break;
}
}
static void xtx_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
/* 1~7 error bits detected and corrected */
*corrected = 7;
break;
case 0x2:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
case 0x3:
/* 8 error bits detected and corrected */
*corrected = 8;
break;
}
}
static void xtx2_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
uint8_t ecc_status2 = (status & 0xf0) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
switch (ecc_status2) {
case 1:
*corrected = 4;
break;
case 5:
*corrected = 5;
break;
case 9:
*corrected = 6;
break;
case 0xd:
*corrected = 7;
break;
}
break;
case 0x2:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
case 0x3:
/* 8 error bits detected and corrected */
*corrected = 8;
break;
}
}
static void yxsc_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & 0x70) >> SPI_NAND_ECC_SHIFT;
*ecc_error = 0;
switch (ecc_status) {
case 0x0:
/* No bit error detected */
*corrected = 0;
break;
case 0x1:
/* 1~7 error bits detected and corrected */
*corrected = 1;
break;
case 0x2:
/* 2 error bits detected and corrected */
*ecc_error = 2;
break;
case 0x3:
/* 3 error bits detected and corrected */
*corrected = 3;
break;
case 0x4:
/* 4 error bits detected and corrected */
*corrected = 4;
break;
case 0x7:
/* Error detected and can not corrected */
*ecc_error = 1;
break;
default:
obm_printf("%s: unexpected status: %d\r\n",
__func__, ecc_status);
*ecc_error = 1;
break;
}
}
static void gd_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
uint8_t ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0xf0, &ext_status);
if (ret) {
obm_printf("gd_spi_nand_ecc_status failed\n");
return;
}
ext_status = (ext_status & SPI_NAND_ECC_MASK) >>
SPI_NAND_ECC_SHIFT;
switch (ext_status) {
case 0x0:
/* 1~4 error bits detected and corrected */
*corrected = 4;
break;
case 0x1:
/* 5 error bits detected and corrected */
*corrected = 5;
break;
case 0x2:
/* 6 error bits detected and corrected */
*corrected = 6;
break;
case 0x3:
/* 7 error bits detected and corrected */
*corrected = 7;
break;
}
} else if (ecc_status == 0x2) {
/* Error detected and can not corrected */
*ecc_error = 1;
} else if (ecc_status == 0x3) {
/* 8 error bits detected and corrected */
*corrected = 8;
} else if (ecc_status == 0x0) {
/* No bit error detected */
*corrected = 0;
}
}
static void gd_spi_nand_ecc_status2(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
uint8_t ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x0) {
/* No bit error detected */
*corrected = 0;
} else if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0xf0, &ext_status);
if (ret) {
obm_printf("gd_spi_nand_ecc_status failed\n");
return;
}
ext_status = (ext_status & SPI_NAND_ECC_MASK) >>
SPI_NAND_ECC_SHIFT;
switch (ext_status) {
case 0x0:
*corrected = 1;
break;
case 0x1:
*corrected = 2;
break;
case 0x2:
*corrected = 3;
break;
case 0x3:
*corrected = 4;
break;
}
} else if (ecc_status == 0x2) {
/* Error bit > 4 detected and can not corrected */
*ecc_error = 1;
} else {
obm_printf("%s: unexpected status: %d\r\n",
__func__, ecc_status);
*ecc_error = 1;
}
}
static void wb_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
uint8_t ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0x30, &ext_status);
if (ret) {
obm_printf("wb_spi_nand_ecc_status failed\n");
return;
}
ext_status = (ext_status & 0x70) >> 4;
if (ext_status == 7)
*ecc_error = 1;
else
*corrected = ext_status;
} else if (ecc_status == 0x2) {
/* Error detected and can not corrected */
*ecc_error = 1;
} else if (ecc_status == 0x3) {
/* 4 error bits detected and corrected */
*corrected = 4;
} else if (ecc_status == 0x0) {
/* No bit error detected */
*corrected = 0;
}
}
static void wb_spi_nand_ecc_status2(struct spi_flash_chip *chip, uint8_t status,
uint32_t *corrected, uint32_t *ecc_error)
{
uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
uint8_t ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0x30, &ext_status);
if (ret) {
obm_printf("wb_spi_nand_ecc_status2 failed\n");
return;
}
ext_status = (ext_status & 0xf0) >> 4;
if (ext_status == 0xf)
*ecc_error = 1;
else
*corrected = ext_status;
} else if (ecc_status == 0x2) {
/* Error detected and can not corrected */
*ecc_error = 1;
} else if (ecc_status == 0x3) {
/* 4 error bits detected and corrected */
*corrected = 4;
} else if (ecc_status == 0x0) {
/* No bit error detected */
*corrected = 0;
}
}
/**
* spi_nand_write_enable - send command 06h to enable write or erase the
* Nand cells
* @chip: SPI-NAND device structure
* Description:
* Before write and erase the Nand cells, the write enable has to be set.
* After the write or erase, the write enable bit is automatically
* cleared (status register bit 2)
* Set the bit 2 of the status register has the same effect
*/
static int spi_nand_write_enable(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + WR_ENABLE;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_read_page_to_cache - send command 13h to read data from Nand to cache
* @chip: SPI-NAND device structure
* @page_addr: page to read
*/
static int spi_nand_read_page_to_cache(struct spi_flash_chip *chip,
uint32_t page_addr)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + PAGE_READ;
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)(page_addr >> 16);
cmd.addr[1] = (uint8_t)(page_addr >> 8);
cmd.addr[2] = (uint8_t)page_addr;
cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_read_from_cache - read data out from cache register
* @chip: SPI-NAND device structure
* @page_addr: page to read
* @column: the location to read from the cache
* @len: number of bytes to read
* @rbuf: buffer held @len bytes
* Description:
* Command can be 03h, 0Bh, 3Bh, 6Bh, BBh, EBh
* The read can specify 1 to (page size + spare size) bytes of data read at
* the corresponding locations.
* No tRd delay.
*/
static int spi_nand_read_from_cache(struct spi_flash_chip *chip,
uint32_t page_addr, uint32_t column,
uint32_t len, uint8_t *rbuf)
{
struct spi_flash_cmd cmd;
if (chip->options & SPINAND_NEED_PLANE_SELECT) {
column |= (((page_addr >>
(chip->block_shift - chip->page_shift)) & 0x1) << 12);
}
if (chip->host->xip_read) {
uint32_t addr;
addr = column;
addr += chip->host->cs_addr[chip->cs];
memcpy(rbuf, (void *)addr, len);
return 0;
}
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + chip->read_cache_op;
cmd.n_addr = 2;
cmd.addr[0] = (uint8_t)(column >> 8);
cmd.addr[1] = (uint8_t)column;
cmd.n_rx = len;
cmd.rx_buf = rbuf;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_program_data_to_cache - write data to cache register
* @chip: SPI-NAND device structure
* @page_addr: page to write
* @column: the location to write to the cache
* @len: number of bytes to write
* @wrbuf: buffer held @len bytes
* @clr_cache: clear cache register or not
* Description:
* Command can be 02h, 32h, 84h, 34h
* 02h and 32h will clear the cache with 0xff value first
* Since it is writing the data to cache, there is no tPROG time.
*/
static int spi_nand_program_data_to_cache(struct spi_flash_chip *chip,
uint32_t page_addr, uint32_t column, uint32_t len,
const uint8_t *wbuf, uint8_t clr_cache)
{
struct spi_flash_cmd cmd;
if (chip->options & SPINAND_NEED_PLANE_SELECT) {
column |= (((page_addr >>
(chip->block_shift - chip->page_shift)) & 0x1) << 12);
}
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (clr_cache)
cmd.cmd_cfg = chip->table + chip->write_cache_op;
else
cmd.cmd_cfg = chip->table + chip->write_cache_rdm_op;
cmd.n_addr = 2;
cmd.addr[0] = (uint8_t)(column >> 8);
cmd.addr[1] = (uint8_t)column;
cmd.n_tx = len;
cmd.tx_buf = wbuf;
cmd.flag = RST_AHB_DOMAIN;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_program_execute - send command 10h to write a page from
* cache to the Nand array
* @chip: SPI-NAND device structure
* @page_addr: the physical page location to write the page.
* Description:
* Need to wait for tPROG time to finish the transaction.
*/
static int spi_nand_program_execute(struct spi_flash_chip *chip, uint32_t page_addr)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + PROG_EXC;
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)(page_addr >> 16);
cmd.addr[1] = (uint8_t)(page_addr >> 8);
cmd.addr[2] = (uint8_t)page_addr;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_erase_block_erase - send command D8h to erase a block
* @chip: SPI-NAND device structure
* @page_addr: the page to erase.
* Description:
* Need to wait for tERS.
*/
static int spi_nand_erase_block(struct spi_flash_chip *chip,
uint32_t page_addr)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + BLK_ERASE;
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)(page_addr >> 16);
cmd.addr[1] = (uint8_t)(page_addr >> 8);
cmd.addr[2] = (uint8_t)page_addr;
cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_wait - wait until the command is done
* @chip: SPI-NAND device structure
* @s: buffer to store status register(can be NULL)
*/
static int spi_nand_wait(struct spi_flash_chip *chip, uint8_t *s)
{
uint8_t status;
unsigned long ret = -ETIMEDOUT;
while (1) {
spi_nand_read_status(chip, &status);
if ((status & STATUS_OIP_MASK) == STATUS_READY) {
ret = 0;
goto out;
}
}
out:
if (s)
*s = status;
return ret;
}
/**
* spi_nand_read_id - send 9Fh command to get ID
* @chip: SPI_FLASH device structure
* @buf: buffer to store id
*/
static int spi_nand_read_id(struct spi_flash_chip *chip, uint8_t *buf)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + READ_ID;
cmd.n_rx = 4;
cmd.rx_buf = buf;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nand_reset - send command FFh to reset chip.
* @chip: SPI_FLASH device structure
*/
static int spi_nand_reset(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + RESET;
if (qspi_start_cmd(chip, &cmd))
obm_printf("spi_nand reset failed!\r\n");
/* elapse 2ms before issuing any other command */
Delay(2000);
return 0;
}
/**
* spi_nand_lock_block - [Interface] write block lock register to
* lock/unlock device
* @spi: spi device structure
* @lock: value to set to block lock register
* Description:
* After power up, all the Nand blocks are locked. This function allows
* one to unlock the blocks, and so it can be written or erased.
*
* Register Bit Address
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* |---------------------------------------------------------|
* | BRWD2 | BP3 | BP2 | BP1 | BP0 | TB | WP | Reversed |
*
* Block Lock Register Block Protection Bits
*
* TB BP3 BP2 BP1 BP0 Protected Portion
* 0 0 0 0 0 All unlocked
* 0 0 0 0 1 Upper 1/1024 locked
* 0 0 0 1 0 Upper 1/512 locked
* 0 0 0 1 1 Upper 1/256 locked
* 0 0 1 0 0 Upper 1/128 locked
* 0 0 1 0 1 Upper 1/64 locked
* 0 0 1 1 0 Upper 1/32 locked
* 0 0 1 1 1 Upper 1/16 locked
* 0 1 0 0 0 Upper 1/8 locked
* 0 1 0 0 1 Upper 1/4 locked
* 0 1 0 1 0 Upper 1/2 locked
* 0 1 0 1 1 All Locked
* 0 1 1 0 0 All Locked
* 0 1 1 0 1 All Locked
* 0 1 1 1 0 All Locked
* 0 1 1 1 1 All Locked
* 1 0 0 0 0 All unlocked
* 1 0 0 0 1 Lower 1/1024 locked
* 1 0 0 1 0 Lower 1/512 locked
* 1 0 0 1 1 Lower 1/256 locked
* 1 0 1 0 0 Lower 1/128 locked
* 1 0 1 0 1 Lower 1/64 locked
* 1 0 1 1 0 Lower 1/32 locked
* 1 0 1 1 1 Lower 1/16 locked
* 1 1 0 0 0 Lower 1/8 locked
* 1 1 0 0 1 Lower 1/4 locked
* 1 1 0 1 0 Lower 1/2 locked
* 1 1 0 1 1 All Locked
* 1 1 1 0 0 All Locked
* 1 1 1 0 1 All Locked
* 1 1 1 1 0 All Locked
* 1 1 1 1 1 All Locked
*/
int spi_nand_lock_block(struct spi_flash_chip *chip, uint8_t lock)
{
return spi_nand_write_reg(chip, REG_BLOCK_LOCK, &lock);
}
/**
* spi_nand_change_mode - switch chip to OTP/OTP protect/Normal mode
* @chip: SPI-NAND device structure
* @mode: mode to enter
*/
static int spi_nand_change_mode(struct spi_flash_chip *chip, uint8_t mode)
{
uint8_t cfg;
spi_nand_get_cfg(chip, &cfg);
switch (mode) {
case OTP_MODE:
cfg = (cfg & ~CFG_OTP_MASK) | CFG_OTP_ENTER;
break;
case OTP_PROTECT_MODE:
cfg = (cfg & ~CFG_OTP_MASK) | CFG_OTP_PROTECT;
break;
case SNOR_READ_ENABLE_MODE:
cfg = (cfg & ~CFG_OTP_MASK) | CFG_SNOR_ENABLE;
break;
case NORMAL_MODE:
cfg = (cfg & ~CFG_OTP_MASK) | CFG_OTP_EXIT;
break;
}
spi_nand_set_cfg(chip, &cfg);
return 0;
}
/**
* spi_nand_scan_id_table - scan chip info in id table
* @chip: SPI-NAND device structure
* @id: point to manufacture id and device id
* Description:
* If found in id table, config chip with table information.
*/
static int spi_nand_scan_id_table(struct spi_flash_chip *chip)
{
struct spi_nand_info *type = spi_nand_table;
int id;
for (; type->name; type++) {
/* ignore high byte if not used */
if (!(type->dev_id >> 8))
id = (chip->dev_id & 0xff);
else
id = chip->dev_id;
if (chip->mfr_id == type->mfr_id && id == type->dev_id) {
chip->name = type->name;
chip->size = type->page_size * type->pages_per_blk
* type->blks_per_lun * type->luns_per_chip;
chip->block_size = type->page_size
* type->pages_per_blk;
chip->page_size = type->page_size;
chip->oob_size = type->oob_size;
chip->lun_shift =
ilog2(chip->block_size * type->blks_per_lun);
chip->ecc_strength = type->ecc_strength;
chip->options = type->options;
chip->refresh_threshold = type->bitflip_threshold;
chip->get_ecc_status = type->get_ecc_status;
chip->max_mhz = type->max_mhz;
chip->flash_info = type;
obm_printf("SPI-NAND: %s is found in table\r\n",
type->name);
return true;
}
}
return false;
}
static uint16_t onfi_crc16(uint16_t crc, uint8_t const *p, uint32_t len)
{
int i;
while (len--) {
crc ^= *p++ << 8;
for (i = 0; i < 8; i++)
crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
}
return crc;
}
/* Sanitize ONFI strings so we can safely print them */
static void sanitize_string(char *s, uint32_t len)
{
int i = len - 1;
int j = 0;
/* Null terminate */
s[i--] = 0;
/* Remove unnecessary space */
while (i >= 0 && (s[i] <= ' ' || s[i] > 127)) {
s[i--] = 0;
}
/* Remove non printable chars */
for (j = 0; j <= i; j++) {
if (s[j] < ' ' || s[j] > 127)
s[j] = '?';
}
}
/**
* spi_nand_detect_onfi - config chip with parameter page
* @chip: SPI-NAND device structure
* Description:
* This function is called when we can not get info from id table.
*/
static int spi_nand_detect_onfi(struct spi_flash_chip *chip)
{
struct spi_nand_onfi_params *p;
uint8_t *buffer = NULL;
int read_cache_op;
int ret = true;
int i;
buffer = malloc(chip->block_size);
if(buffer == NULL) {
return HeapExhaustedError;
}
obm_printf("spi_nand_detect_onfi: buffer=0x%x\r\n", buffer);
memset(buffer, 0x0, 256*3);
spi_nand_change_mode(chip, OTP_MODE);
spi_nand_read_page_to_cache(chip, 0x01);
spi_nand_wait(chip, NULL);
/*
* read parameter page can only ues 1-1-1 mode
*/
read_cache_op = chip->read_cache_op;
chip->read_cache_op = READ_FROM_CACHE;
spi_nand_read_from_cache(chip, 0x01, 0, 256 * 3, buffer);
chip->read_cache_op = read_cache_op;
spi_nand_change_mode(chip, NORMAL_MODE);
p = (struct spi_nand_onfi_params *)buffer;
for (i = 0; i < 3; i++, p++) {
if (p->sig[0] != 'O' || p->sig[1] != 'N' ||
p->sig[2] != 'F' || p->sig[3] != 'I')
continue;
//if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
// (p->crc))
break;
}
if (i == 3) {
obm_printf("Could not find valid ONFI, use default settings\r\n");
ret = false;
goto out;
}
memcpy(&chip->onfi_params, p, sizeof(*p));
p = &chip->onfi_params;
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
sanitize_string(p->model, sizeof(p->model));
chip->name = p->model;
chip->size = (p->byte_per_page) *
(p->pages_per_block) *
(p->blocks_per_lun) * p->lun_count;
chip->block_size = (p->byte_per_page) *
(p->pages_per_block);
chip->page_size = (p->byte_per_page);
chip->oob_size = (p->spare_bytes_per_page);
chip->lun_shift = ilog2(chip->block_size * (p->blocks_per_lun));
// if (chip->mfr_id == SPIFLASH_MFR_MICRON) {
// if (p->vendor.micron_sepcific.two_plane_page_read)
// chip->options |= SPINAND_NEED_PLANE_SELECT;
// if (p->vendor.micron_sepcific.die_selection)
// chip->options |= SPINAND_NEED_DIE_SELECT;
// }
chip->ecc_strength = p->vendor.micron_sepcific.ecc_ability;
out:
free(buffer);
return ret;
}
#define SPI_NAND_MAX_RETRY 3
/**
* spi_nand_do_read_page - read page from flash to buffer
* @chip: spi nand chip structure
* @page_addr: page address/raw address
* @column: column address
* @ecc_off: without ecc or not
* @corrected: how many bit error corrected
* @buf: data buffer
* @len: data length to read
* Description:
* Return -EBADMSG when internal ecc can not correct bitflips.
* The command sequence to transfer data from NAND array to output is
* follows:
* 13h (PAGE READ to cache register)
* 0Fh (GET FEATURES command to read the status)
* 0Bh/03h/3Bh/6Bh (Read from Cache Xn); or BBh/EBh (Read From
* Cache Dual/Quad IO)
*/
static int __spi_nand_do_read_page(struct spi_flash_chip *chip, uint32_t page_addr,
uint32_t column, uint8_t ecc_off,
uint32_t *corrected, uint8_t *buf, uint32_t len)
{
uint32_t ecc_error;
uint8_t status;
int ret;
int rx_len, i;
spi_nand_read_page_to_cache(chip, page_addr);
ret = spi_nand_wait(chip, &status);
if (ret < 0) {
obm_printf("error %d waiting page 0x%x to cache\r\n",
ret, page_addr);
return ret;
}
if (!ecc_off && chip->get_ecc_status) {
chip->get_ecc_status(chip, status, corrected, &ecc_error);
if (ecc_error) {
obm_printf("internal ECC error reading page 0x%x"
" status=0x%x\r\n", page_addr, status);
ret = -EBADMSG;
return ret;
}
}
rx_len = chip->rx_max_len ? chip->rx_max_len : len;
do {
int real_len;
i = 0;
retry:
real_len = min(len, rx_len);
ret = spi_nand_read_from_cache(chip, page_addr, column,
real_len, buf);
if (ret == -EAGAIN && ++i <= SPI_NAND_MAX_RETRY) {
rx_len = rx_len / 2;
goto retry;
} else if (ret) {
return -EIO;
} else if (i) {
obm_printf("Pass after the %dth retry\r\n", i);
}
column += real_len;
buf += real_len;
len -= real_len;
} while (len);
return ret;
}
static int spi_nand_do_read_page(struct spi_flash_chip *chip, uint32_t page_addr,
uint32_t column, uint8_t ecc_off,
uint32_t *corrected, uint8_t *buf, uint32_t len)
{
uint8_t *tmp_buf = NULL;
int read_len;
int ret;
if (upload_nand_spare == TRUE)
len += chip->oob_size;
if (!column && len == chip->page_size)
return __spi_nand_do_read_page(chip, page_addr, column, ecc_off,
corrected, buf, len);
tmp_buf = malloc(chip->page_size + chip->oob_size);
if(tmp_buf == NULL)
return HeapExhaustedError;
read_len = chip->page_size;
if (column + len > chip->page_size)
read_len += chip->oob_size;
ret = __spi_nand_do_read_page(chip, page_addr, 0, ecc_off,
corrected, tmp_buf, read_len);
if (!ret)
memcpy(buf, tmp_buf + column, len);
free(tmp_buf);
return ret;
}
/**
* spi_nand_do_write_page - write data from buffer to flash
* @chip: spi nand chip structure
* @page_addr: page address/raw address
* @column: column address
* @buf: data buffer
* @len: data length to write
* @clr_cache: clear cache register with 0xFF or not
* Description:
* Page program sequence is as follows:
* 06h (WRITE ENABLE)
* 02h/32h/84h/34h (PROGRAM LOAD (RAMDOM_DATA) Xn)
* 10h (PROGRAM EXECUTE)
* 0Fh (GET FEATURE command to read the status)
* PROGRAM LOAD Xn instruction will reset the cache resigter with 0xFF,
* while PROGRAM LOAD RANDOM DATA Xn instruction will only update the
* data bytes that are specified by the command input sequence and the rest
* of data in the cache buffer will remain unchanged.
*/
static int spi_nand_do_write_page(struct spi_flash_chip *chip, uint32_t page_addr,
uint32_t column, const uint8_t *buf,
uint32_t len, uint8_t clr_cache)
{
uint32_t column_save, len_save;
uint8_t *buf_save;
uint8_t status;
uint8_t p_fail = false;
int tx_len, i;
int ret = 0;
int cache_done = 0;
buf_save = buf;
column_save = column;
len_save = len;
tx_len = chip->tx_max_len;
load_cache:
if (tx_len < len && (chip->options & SPINAND_RDM_CMD_NEED_PAGE_READ)) {
/*
* XTX spi-nand PROGRAM LOAD RANDOM DATA cmd only used for
* Internal Data Move, need to send 13H (PAGE READ TO CACHE)
* before use this command.
*
* If spi-nand from new vendor have same limitation as XTX, must
* set SPINAND_RDM_CMD_NEED_PAGE_READ in options of spi_nand_table.
*/
spi_nand_read_page_to_cache(chip, page_addr);
ret = spi_nand_wait(chip, &status);
if (ret < 0) {
obm_printf("error %d waiting page 0x%x to cache\r\n",
ret, page_addr);
return ret;
}
cache_done = 1;
clr_cache = 0;
}
spi_nand_write_enable(chip);
do {
int real_len;
i = 0;
retry:
real_len = min(len, tx_len);
ret = spi_nand_program_data_to_cache(chip, page_addr, column,
real_len, buf, clr_cache);
if (ret == -EAGAIN && ++i <= SPI_NAND_MAX_RETRY) {
tx_len = tx_len / 2;
if (!cache_done &&
(chip->options & SPINAND_RDM_CMD_NEED_PAGE_READ)) {
buf = buf_save;
column = column_save;
len = len_save;
goto load_cache;
}
goto retry;
} else if (ret) {
return -EIO;
} else if (i) {
obm_printf("Write after the %dth retry\r\n", i);
}
clr_cache = 0;
column += real_len;
buf += real_len;
len -= real_len;
} while (len);
spi_nand_program_execute(chip, page_addr);
ret = spi_nand_wait(chip, &status);
if (ret < 0) {
obm_printf("error %d reading page 0x%x from cache\r\n",
ret, page_addr);
return ret;
}
if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
obm_printf("program page 0x%x failed\r\n", page_addr);
p_fail = true;
}
if (p_fail)
ret = -EIO;
return ret;
}
/**
* spi_nand_read_pages - read data from flash to buffer
* @chip: spi nand chip structure
* @from: offset to read from
* @ops: oob operations description structure
* Description:
* Normal read function, read one page to buffer before issue
* another. Return -EUCLEAN when bitflip is over threshold.
* Return -EBADMSG when internal ecc can not correct bitflips.
*/
static int spi_nand_read_pages(struct spi_flash_chip *chip, uint32_t from,
uint32_t len, uint32_t *retlen, uint8_t *buf,
uint8_t ecc_off)
{
int page_addr, page_offset, size;
int ret;
uint32_t corrected = 0;
uint32_t max_bitflip = 0;
int readlen = len;
uint32_t failed = 0;
uint32_t _retlen;
page_addr = from >> chip->page_shift;
page_offset = from & chip->page_mask;
_retlen = 0;
while (1) {
size = min(readlen, chip->page_size - page_offset);
ret = spi_nand_do_read_page(chip, page_addr, page_offset,
ecc_off, &corrected, buf + _retlen, size);
if (ret == -EBADMSG) {
failed++;
} else if (ret) {
obm_printf("error %d reading page 0x%x\r\n",
ret, page_addr);
goto out;
}
max_bitflip = max(corrected, max_bitflip);
_retlen += size;
if (upload_nand_spare == TRUE)
_retlen += chip->oob_size;
readlen -= size;
page_offset = 0;
if (!readlen)
break;
page_addr++;
}
out:
*retlen = _retlen;
#ifdef BITFLIPS_SCRUBBING
if (chip->refresh_threshold &&
max_bitflip >= chip->refresh_threshold)
ret = ReadDisturbError;
#endif
if (failed)
ret = -EBADMSG;
return ret;
}
/**
* spi_nand_read - [Interface] SPI-NAND read
* @chip: spi nand device structure
* @from: offset to read from
* @len: number of bytes to read
* @retlen: pointer to variable to store the number of read bytes
* @buf: the databuffer to put data
*/
static int __spi_nand_read(struct spi_flash_chip *chip, uint32_t from,
uint32_t len, uint32_t *retlen, uint8_t *buf,
uint8_t ecc_off)
{
int ret;
/* Do not allow reads past end of device */
if (from >= chip->size) {
obm_printf("%s: attempt to read beyond end of device\r\n",
__func__);
return -EINVAL;
}
if (ecc_off)
spi_nand_disable_ecc(chip);
ret = spi_nand_read_pages(chip, from, len, retlen, buf, ecc_off);
if (ecc_off)
spi_nand_enable_ecc(chip);
return ret;
}
static int spi_nand_read(struct spi_flash_chip *chip,
int addr, int size, uint8_t *rbuf)
{
uint32_t retlen;
return __spi_nand_read(chip, addr, size, &retlen, rbuf,
upload_disable_ecc);
}
/**
* spi_nand_write - [Interface] SPI-NAND write
* @chip: spi nand device structure
* @to: offset to write to
* @len: number of bytes to write
* @retlen: pointer to variable to store the number of written bytes
* @buf: the data to write
*/
static int __spi_nand_write(struct spi_flash_chip *chip, uint32_t to,
uint32_t len, uint32_t *retlen, const uint8_t *buf,
uint8_t ecc_off)
{
int page_addr, page_offset, size;
int writelen = len;
int ret = 0;
uint8_t clr_cache = true;
uint32_t _retlen;
/* Do not allow reads past end of device */
if (to >= chip->size) {
obm_printf("%s: attempt to write beyond end of device\r\n",
__func__);
return -EINVAL;
}
page_addr = to >> chip->page_shift;
page_offset = to & chip->page_mask;
_retlen = 0;
if (ecc_off)
spi_nand_disable_ecc(chip);
while (1) {
size = min(writelen, chip->page_size - page_offset);
ret = spi_nand_do_write_page(chip, page_addr, page_offset,
buf + _retlen, size, clr_cache);
if (ret) {
obm_printf("error %d writing page 0x%x\r\n",
ret, page_addr);
goto out;
}
_retlen += size;
writelen -= size;
if (!writelen)
break;
page_offset = 0;
page_addr++;
}
out:
*retlen = _retlen;
if (ecc_off)
spi_nand_enable_ecc(chip);
return ret;
}
static int spi_nand_write(struct spi_flash_chip *chip,
int addr, int size, uint8_t *wbuf)
{
uint32_t retlen;
return __spi_nand_write(chip, addr, size, &retlen, wbuf, 0);
}
/**
* spi_nand_erase - [Interface] erase block(s)
* @chip: spi nand device structure
* @addr: address that erase start with, should be blocksize aligned
* @len: length that want to be erased, should be blocksize aligned
* Description:
* Erase one ore more blocks
* The command sequence for the BLOCK ERASE operation is as follows:
* 06h (WRITE ENBALE command)
* D8h (BLOCK ERASE command)
* 0Fh (GET FEATURES command to read the status register)
*/
int spi_nand_erase(struct spi_flash_chip *chip, uint32_t addr, uint32_t len)
{
int page_addr, pages_per_block;
uint8_t status;
int ret = 0;
//obm_printf("%s: %d: addr=0x%x size=%d\r\n",
// __func__, __LINE__, addr, len);
/* check address align on block boundary */
if (addr & (chip->block_size - 1)) {
obm_printf("%s: Unaligned address\r\n", __func__);
return -EINVAL;
}
if (len & (chip->block_size - 1)) {
obm_printf("%s: Length not block aligned\r\n", __func__);
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((len + addr) > chip->size) {
obm_printf("%s: Erase past end of device len=%d"
" addr=0x%x size=0x%x\r\n",
__func__, len, addr, chip->size);
return -EINVAL;
}
pages_per_block = 1 << (chip->block_shift - chip->page_shift);
page_addr = addr >> chip->page_shift;
while (len) {
/* Check if we have a bad block, we do not erase bad blocks! */
// if (spi_nand_block_isbad(chip, ((uint32_t) page_addr) <<
// chip->page_shift)) {
// obm_printf("%s: attempt to erase a bad block at 0x%x\r\n",
// __func__, ((uint32_t) page_addr) << chip->page_shift);
// goto erase_exit;
// }
spi_nand_write_enable(chip);
spi_nand_erase_block(chip, page_addr);
ret = spi_nand_wait(chip, &status);
if (ret < 0) {
obm_printf("block erase command wait failed\r\n");
goto erase_exit;
}
if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
obm_printf("erase block 0x%x failed\r\n",
((uint32_t) page_addr) << chip->page_shift);
ret = -EIO;
goto erase_exit;
}
/* Increment page address and decrement length */
len -= chip->block_size;
page_addr += pages_per_block;
}
erase_exit:
return ret;
}
/**
* spi_nand_set_rd_wr_op - Chose the best read write command
* @chip: SPI-NAND device structure
* Description:
* Chose the fastest r/w command according to spi controller's ability.
* Note:
* If 03h/0Bh follows SPI NAND protocol, there is no difference,
* while if follows SPI NOR protocol, 03h command is working under
* <=20Mhz@3.3V,<=5MHz@1.8V; 0Bh command is working under
* 133Mhz@3.3v, 83Mhz@1.8V.
*/
static void spi_nand_set_rd_wr_op(struct spi_flash_chip *chip,
uint32_t op_mode_rx, uint32_t op_mode_tx)
{
struct spi_nand_info *type = (struct spi_nand_info *)chip->flash_info;
struct spi_flash_cmd_cfg *read_cmd;
if (op_mode_rx & SPI_OPM_RX_QUAD) {
if (type) {
if (chip->options & SPINAND_SUPPORT_DTR)
chip->read_cache_op = type->quad_cmd_dtr_index;
else
chip->read_cache_op = type->quad_cmd_index;
} else {
chip->read_cache_op = READ_FROM_CACHE_X4;
}
} else if (op_mode_rx & SPI_OPM_RX_DUAL) {
chip->read_cache_op = READ_FROM_CACHE_DUAL;
} else {
if (chip->gd_ver_c)
chip->read_cache_op = READ_FROM_CACHE_GD_C;
else
chip->read_cache_op = READ_FROM_CACHE_FAST;
}
if (op_mode_tx & SPI_OPM_TX_QUAD) {
chip->write_cache_op = PROG_LOAD_X4;
chip->write_cache_rdm_op = PROG_LOAD_RDM_DATA_X4;
} else {
chip->write_cache_op = PROG_LOAD;
chip->write_cache_rdm_op = PROG_LOAD_RDM_DATA;
}
read_cmd = chip->table + chip->read_cache_op;
obm_printf("Set rx_pins: %d, tx_pins: %d, Read_CMD:0x%x\r\n",
op_mode_rx, op_mode_tx, read_cmd->opcode);
}
static int spi_nand_do_reset(FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nand_chip;
(void) fbt;
return spi_nand_reset(chip);
}
static int spi_nand_do_read(UINT_T FlashOffset, UINT_T buffer, UINT_T size,
FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nand_chip;
int ret;
(void) fbt;
ret = spi_nand_read(chip, FlashOffset, size, (UINT8_T *)buffer);
if (ret == -EINVAL) {
return InvalidAddressRangeError;
} else if (ret == -EBADMSG) {
return FlashReadEccError;
} else if (ret < 0) {
return ReadError;
}
return ret;
}
static int spi_nand_do_write(UINT_T FlashOffset, UINT_T buffer, UINT_T size,
FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nand_chip;
int ret;
(void) fbt;
ret = spi_nand_write(chip, FlashOffset, size, (UINT8_T *)buffer);
if (ret == -EINVAL) {
return InvalidAddressRangeError;
} else if (ret < 0) {
return WriteError;
}
return ret;
}
static int spi_nand_do_erase(UINT_T FlashOffset, UINT_T size,
FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nand_chip;
int ret;
(void) fbt;
ret = spi_nand_erase(chip, FlashOffset, size);
if (ret == -EINVAL) {
return InvalidAddressRangeError;
} else if (ret < 0) {
return EraseError;
}
return ret;
}
static int spi_nand_gen_fbbt(UINT16 *badblocklist)
{
struct spi_flash_chip *chip = &nand_chip;
P_FlashProperties_T pFlashP = GetFlashProperties(BOOT_FLASH);
unsigned int i, max, bad_blks = 0;
uint8_t *buf = NULL;
uint32_t corrected = 0;
int page_addr, ret;
buf = malloc(chip->block_size);
if(buf == NULL)
return HeapExhaustedError;
/* Max number of relocation is 2% of device */
max = (pFlashP->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100;
/* Scan from block 1, block 0 is for TIM, should be good */
for (i = 1; i < pFlashP->NumBlocks; i++) {
page_addr = i << (chip->block_shift - chip->page_shift);
ret = spi_nand_do_read_page(chip, page_addr, chip->page_size,
1, &corrected, buf, chip->oob_size);
if (ret) {
obm_printf("error %d reading page 0x%x\r\n",
ret, page_addr);
break;
}
if ((buf[0] & 0xFF) != 0xFF) {
badblocklist[bad_blks++] = i;
if(bad_blks >= max)
break;
}
}
if (bad_blks) {
obm_printf("Scan factory bad blocks:\r\n");
for (i = 0; i < bad_blks;) {
obm_printf("%-4d ", badblocklist[i]);
if (++i % 8 == 0)
obm_printf("\r\n");
}
obm_printf("\r\n");
}
free(buf);
return bad_blks;
}
static int spi_nand_exit()
{
struct spi_flash_chip *chip = &nand_chip;
#ifdef QSPI_SUPPORT_DQS
qspi_config_disable_dqs(chip->host);
#endif
return NoError;
}
/**
* spi_nand_init - [Interface] Init SPI-NAND device driver
* @spi: spi device structure
* @chip_ptr: pointer point to spi nand device structure pointer
*/
struct spi_flash_chip *spi_nand_init(struct qspi_host *host, int cs,
int rx_mode, int tx_mode)
{
uint8_t id[SPINAND_MAX_ID_LEN] = {0};
struct spi_flash_chip *chip = &nand_chip;
int dev_in_tbl = 0;
memset(chip, 0, sizeof(struct spi_flash_chip));
chip->cs = cs >= QSPI_CS_MAX ? QSPI_CS_A1 : cs;
chip->host = host;
chip->table = cmd_table;
chip->page_size = 2048;
chip->block_size = 64*2048;
chip->size = 1024*64*2048;
spi_nand_reset(chip);
spi_nand_read_id(chip, id);
/* First check if this is a GD version-C spi-nand */
if (id[0] == SPIFLASH_MFR_GIGADEVICE) {
chip->mfr_id = id[0];
chip->dev_id = id[1] << 8 | id[2];
chip->gd_ver_c = 1;
obm_printf("GigaDeivce version-C spi-nand\r\n");
/* For debug, change to 1-bit read/write command*/
rx_mode = SPI_OPM_RX;
tx_mode = SPI_OPM_TX;
chip->max_mhz = 13;
} else {
chip->mfr_id = id[1];
chip->dev_id = id[2] | id[3] << 8;
chip->gd_ver_c = 0;
}
if (spi_nand_scan_id_table(chip))
dev_in_tbl = 1;
obm_printf("SPI-NAND type mfr_id: %x, dev_id: %x\r\n",
chip->mfr_id, chip->dev_id);
if (!chip->host->has_dtr)
chip->options &= ~SPINAND_SUPPORT_DTR;
spi_nand_set_rd_wr_op(chip, rx_mode, tx_mode);
/* Not every vendor show QE bit in CFG register */
if (chip->mfr_id != SPIFLASH_MFR_MICRON &&
chip->mfr_id != SPIFLASH_MFR_WINBOND)
spi_nand_enable_quad(chip);
/*
* TODO:
* Maybe change due to different vendor
*/
qspi_enable_xip(chip, chip->table + chip->read_cache_op);
if (qspi_preinit_lookup_tbl(chip) < 0) {
obm_printf("preinit_lookup_tbl failed, check cmd table\r\n");
return NULL;
}
if (chip->options & SPINAND_NEED_SET_BFT)
mxic_spi_nand_set_bft(chip, chip->refresh_threshold);
spi_nand_disable_ecc(chip);
if(!dev_in_tbl) {
/*
* Since Program Load Random Data cmd only valid for some vendor's
* device, set below flag for new device unknown in table for safe.
*/
chip->options |= SPINAND_RDM_CMD_NEED_PAGE_READ;
obm_printf("unknown device, set SPINAND_RDM_CMD_NEED_PAGE_READ\r\n");
if (spi_nand_detect_onfi(chip))
obm_printf("nand support onfi\r\n");
chip->get_ecc_status = generic_spi_nand_ecc_status;
}
spi_nand_lock_block(chip, BL_ALL_UNLOCKED);
spi_nand_enable_ecc(chip);
chip->block_shift = ilog2(chip->block_size);
chip->page_shift = ilog2(chip->page_size);
chip->page_mask = chip->page_size - 1;
chip->lun = 0;
chip->page_mask = chip->page_size - 1;
if (!chip->enable_ecc)
chip->enable_ecc = spi_nand_enable_ecc;
if (!chip->disable_ecc)
chip->disable_ecc = spi_nand_disable_ecc;
obm_printf("block_size=0x%x page_size=0x%x bitflip_threshold=%d\r\n",
chip->block_size, chip->page_size, chip->refresh_threshold);
return chip;
}
#ifdef SPI_FLASH_DQS_SCAN_WIN
static void spi_nand_scan_dqs_window(struct spi_flash_chip *chip, int freq)
{
struct qspi_host *host = chip->host;
uint8_t *buffer = NULL;
uint8_t pattern = 0x5a;
int flash_offset = 0x0;
int min, max;
int len = 0, avg, i;
int fail;
if (chip->options & SPINAND_SUPPORT_DTR) {
obm_printf("==> DQS not support DTR mode\r\n");
return;
}
buffer = malloc(chip->page_size);
memset(buffer, pattern, chip->page_size);
/* switch qspi bus to 13M to write pattern data */
qspi_set_func_clk(host, 13, 0, 0, 0, 0);
spi_nand_do_erase(flash_offset, chip->block_size, BOOT_FLASH);
spi_nand_do_write(flash_offset, buffer, chip->page_size, BOOT_FLASH);
/* switch qspi bus back to high frequency to scan valid window */
qspi_set_func_clk(host, freq, 0, 0, 0, 0);
min = 0;
len = 0;
do {
while (min <= 0xff) {
memset(buffer, 0x0, chip->page_size);
qspi_config_dqs_delay(chip->host, min);
spi_nand_do_read(flash_offset, buffer, chip->page_size, BOOT_FLASH);
fail = 0;
for (i = 0; i < chip->page_size; i++) {
if (buffer[i] != pattern) {
fail = 1;
break;
}
}
if (!fail)
break;
min++;
}
if (min > 0xff)
break;
max = min + 1;
while (max <= 0xff) {
memset(buffer, 0x0, chip->page_size);
qspi_config_dqs_delay(chip->host, max);
spi_nand_do_read(flash_offset, buffer, chip->page_size, BOOT_FLASH);
fail = 0;
for (i = 0; i < chip->page_size; i++) {
if (buffer[i] != pattern) {
fail = 1;
break;
}
}
if (fail)
break;
max++;
}
if (max - min > len) {
len = max - min;
avg = (min + max - 1) / 2;
}
obm_printf("pass window [%d : %d], len = %d\r\n",
min, max - 1, max - min);
min = max + 1;
} while (min <= 0xff);
obm_printf("max window len = %d, at %d\r\n", len, avg);
qspi_config_dqs_delay(chip->host, avg);
free(buffer);
}
#endif
/***********************************************************
* InitializeQSPINAND()
* Initializes the SSP port on the platform and issues
* a Read ID command to see if a device is connected
* returns:
* NoError - on a successful read ID
* NotFoundError - when read ID value is bogus (0xFFFF or 0x0000)
************************************************************/
UINT_T InitializeQSPINAND(UINT8_T FlashNum, FlashBootType_T FlashBootType,
UINT8_T* P_DefaultPartitionNum)
{
P_FlashProperties_T pFlashP = GetFlashProperties(FlashBootType);
struct spi_flash_chip *chip;
struct qspi_host *host;
struct spi_nand_info *type;
int tclqv, tset, thold;
#if KSTR
int freq = 52;
#else
int freq = 78;
#endif
/*
* Use low frequency during init and increase later, since some
* device max may not support such large frequency
*/
host = qspi_host_init(0, 13, 1);
chip = spi_nand_init(host, 0, SPI_OPM_RX_QUAD, SPI_OPM_TX_QUAD);
if (!chip)
return NotFoundError;
if (chip->max_mhz && freq > chip->max_mhz) {
obm_printf("warn: device max frequency is %d MHz!!!\r\n",
chip->max_mhz);
freq = chip->max_mhz;
}
/* flash_info may be inited in spi_nand_init */
type = (struct spi_nand_info *)chip->flash_info;
if (type) {
tclqv = type->tclqv;
tset = type->tset;
thold = type->thold;
} else {
tclqv = 0;
tset = 0;
thold = 0;
}
if (!PlatformIsFPGA())
qspi_set_func_clk(host, freq,
(chip->options & SPINAND_SUPPORT_DTR),
tclqv, tset, thold);
//define functions
pFlashP->ReadFromFlash = spi_nand_do_read;
pFlashP->WriteToFlash = spi_nand_do_write;
pFlashP->EraseFlash = spi_nand_do_erase;
pFlashP->FinalizeFlash = spi_nand_exit;
pFlashP->ResetFlash = spi_nand_do_reset;
pFlashP->GenerateFBBT = spi_nand_gen_fbbt;
pFlashP->FlashSettings.UseBBM = 1;
pFlashP->FlashSettings.UseSpareArea = 0;
pFlashP->PageSize = chip->page_size;
pFlashP->BlockSize = chip->block_size;
pFlashP->FlashSettings.SASize = chip->oob_size;
pFlashP->FlashSettings.UseHwEcc = 0;
pFlashP->StreamingFlash = FALSE;
pFlashP->FlashType = SPI_NAND;
pFlashP->FinalizeFlash = NULL;
pFlashP->TimFlashAddress = 0;
pFlashP->NumBlocks = chip->size >> chip->block_shift;
if (!host->en_tx_dma)
chip->tx_max_len = QSPI_TX_BUFF_MAX << 2;
else
chip->tx_max_len = chip->page_size;
if (chip->host->xip_read)
chip->rx_max_len = chip->page_size;
else
chip->rx_max_len = QSPI_RX_BUFF_MAX << 2;
*P_DefaultPartitionNum = 0;
obm_printf("pFlashP->NumBlocks: %d\r\n", pFlashP->NumBlocks);
#ifdef SPI_FLASH_DQS_SCAN_WIN
spi_nand_scan_dqs_window(chip, freq);
#endif
return NoError;
}