blob: e2a7fb3f7d8b1d3db0003c3087072d578b769d65 [file] [log] [blame]
#include "qspi_host.h"
#include "spi_nor.h"
#include "Errors.h"
#include "misc.h"
#include "timer.h"
#include "PlatformConfig.h"
#if COPYIMAGESTOFLASH
#include "BootLoader.h"
#endif
enum normal_cmd {
ENABLE_4BYTE,
DISABLE_4BYTE,
READ_STATUS1,
READ_STATUS2,
READ_STATUS3,
READ_STATUS2_MXIC,
WRITE_STATUS,
WRITE_STATUS2,
READ_SLOW,
READ_FAST,
READ_FAST_X2,
READ_FAST_X4,
READ_FAST_DUAL,
READ_FAST_QUAD,
SECTOR_ERASE_4K,
BLK_ERASE_32K,
BLK_ERASE_64K,
CHIP_ERASE,
PROG,
PROG_X4,
PROG_X4_MXIC,
WR_ENABLE,
WR_DISABLE,
READ_ID,
ENABLE_QPI,
ENABLE_QPI_MXIC,
PGM_ERS_SUSPEND,
PGM_ERS_RESUME,
PGM_ERS_SUSPEND_MXIC,
PGM_ERS_RESUME_MXIC,
EN_RST,
RESET,
MAX_CMD,
};
enum qspi_cmd {
ENABLE_4BYTE_QPI,
DISABLE_4BYTE_QPI,
READ_STATUS1_QPI,
READ_STATUS2_QPI,
READ_STATUS3_QPI,
READ_STATUS2_MXIC_QPI,
WRITE_STATUS_QPI,
WRITE_STATUS2_QPI,
READ_FAST_QPI,
READ_FAST_QUAD_QPI,
SECTOR_ERASE_4K_QPI,
BLK_ERASE_32K_QPI,
BLK_ERASE_64K_QPI,
CHIP_ERASE_QPI,
PROG_QPI,
WR_ENABLE_QPI,
WR_DISABLE_QPI,
READ_ID_QPI,
READ_QPIID,
DISABLE_QPI,
DISABLE_QPI_MXIC,
SET_READ_PARA_QPI,
PGM_ERS_SUSPEND_QPI,
PGM_ERS_RESUME_QPI,
PGM_ERS_SUSPEND_MXIC_QPI,
PGM_ERS_RESUME_MXIC_QPI,
EN_RST_QPI,
RESET_QPI,
MAX_CMD_QPI,
};
static struct spi_nor_info spi_nor_table[] = {
/* Macronix */
SPI_NOR_INFO("MX25U25643G", 0xC2, 0x2539, 256, 256, 32*1024*1024,
SPINOR_QE_USE_BIT6, 0,
READ_FAST_X4),
SPI_NOR_INFO("MX25U12835", 0xC2, 0x2538, 256, 256, 16*1024*1024,
SPINOR_QE_USE_BIT6, 0,
READ_FAST_X4),
SPI_NOR_INFO("MX25U3273", 0xC2, 0x2536, 256, 256, 4*1024*1024,
SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_X4),
/* GigaDeivce */
SPI_NOR_INFO("GD25LQ256C", 0xC8, 0x6019, 256, 256, 32*1024*1024, 0, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("GD25LQ128D", 0xC8, 0x6018, 256, 256, 16*1024*1024, 0, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("GD25LQ64C", 0xC8, 0x6017, 256, 256, 8*1024*1024, 0, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("GD25LE32E", 0xC8, 0x6016, 256, 256, 4*1024*1024, 0, 0,
READ_FAST_QUAD),
/* Winbond */
SPI_NOR_INFO("W25Q256JW", 0xEF, 0x6019, 256, 256, 32*1024*1024, 0, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("W25Q256JW-IM*", 0xEF, 0x8019, 256, 256, 32*1024*1024, 0, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("W25Q128JW", 0xEF, 0x6018, 256, 256, 16*1024*1024, 0, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("W25Q64FW", 0xEF, 0x6017, 256, 256, 8*1024*1024, 0, 0,
READ_FAST_QUAD),
/* Dosilicon */
SPI_NOR_INFO("FM25M4AA", 0xF8, 0x4218, 256, 256, 16*1024*1024, 0, 0,
READ_FAST_X4),
/* DouQi */
SPI_NOR_INFO("DQ25Q128AL", 0x54, 0x6018, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2, 0,
READ_FAST_X4),
/* Puya */
SPI_NOR_INFO("P25Q128L", 0x85, 0x6018, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2, 78,
READ_FAST_QUAD),
SPI_NOR_INFO("P25Q64LE", 0x85, 0x6017, 256, 256, 8*1024*1024,
SPINOR_HAVE_WRITE_STATUS2, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("P25Q32SL", 0x85, 0x6016, 256, 256, 4*1024*1024,
SPINOR_HAVE_WRITE_STATUS2, 0,
READ_FAST_QUAD),
SPI_NOR_INFO("P25Q128LA", 0x85, 0x6518, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_QUAD),
/* ZettaDevice */
SPI_NOR_INFO("ZD25Q128", 0xBA, 0x4218, 256, 256, 16*1024*1024, 0, 0,
READ_FAST_QUAD),
/* Wuhan XinXin */
SPI_NOR_INFO("XM25QU64B", 0x20, 0x5017, 256, 256, 8*1024*1024,
SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_X4),
SPI_NOR_INFO("XM25QU64C", 0x20, 0x4117, 256, 256, 8*1024*1024,
0, 0,
READ_FAST_X4),
SPI_NOR_INFO("XM25QU128B", 0x20, 0x5018, 256, 256, 16*1024*1024,
SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_X4),
SPI_NOR_INFO("XM25QU128C", 0x20, 0x4118, 256, 256, 16*1024*1024,
0, 0,
READ_FAST_X4),
SPI_NOR_INFO("XM25QU256B", 0x20, 0x7019, 256, 256, 32*1024*1024,
SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_X4),
SPI_NOR_INFO("XM25LU32C", 0x20, 0x5016, 256, 256, 4*1024*1024,
0, 0,
READ_FAST_X4),
/* Fudan Microelectronics */
SPI_NOR_INFO("FM25W128", 0xA1, 0x2818, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2, 52,
READ_FAST_X4),
/* Zbit Semiconductor */
SPI_NOR_INFO("ZB25LQ64A", 0x5E, 0x7017, 256, 256, 8*1024*1024,
0, 0, READ_FAST_QUAD),
SPI_NOR_INFO("ZB25LQ128", 0x5E, 0x5018, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2, 0,
READ_FAST_QUAD),
/* XTX */
SPI_NOR_INFO("XT25Q128D", 0x0B, 0x6018, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_QUAD),
/* Elite */
SPI_NOR_INFO("EN25SX128A", 0x1c, 0x7818, 256, 256, 16*1024*1024,
SPINOR_HAVE_WRITE_STATUS2 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_X4),
/* Silicon Kaiser */
SPI_NOR_INFO("SK25LP128", 0x17, 0x7018, 256, 256, 16*1024*1024,
SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
READ_FAST_X4),
{.name = NULL},
};
/**
* spi_nor_scan_id_table - scan chip info in id table
* @chip: SPI-NOR device structure
* Description:
* If found in id table, config chip with table information.
*/
static int spi_nor_scan_id_table(struct spi_flash_chip *chip)
{
struct spi_nor_info *type = spi_nor_table;
for (; type->name; type++) {
if (chip->mfr_id == type->mfr_id && chip->dev_id == type->dev_id) {
chip->name = type->name;
chip->size = type->total_size;
chip->block_size = type->page_size
* type->pages_per_blk;
chip->page_size = type->page_size;
chip->options = type->options;
chip->max_mhz = type->max_mhz;
chip->flash_info = type;
obm_printf("SPI-NOR: %s is found in table\r\n",
type->name);
return true;
}
}
return false;
}
/* Standard SPI-NOR flash normal 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 */
[ENABLE_4BYTE] = SPI_CMD(0xb7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[DISABLE_4BYTE] = SPI_CMD(0xe9, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[READ_STATUS1] = SPI_CMD(0x05, 0, 0, 0, 0, 0, 0, 1, 2, 1),
[READ_STATUS2] = SPI_CMD(0x35, 0, 0, 0, 0, 0, 0, 1, -1, 1),
[READ_STATUS3] = SPI_CMD(0x15, 0, 0, 0, 0, 0, 0, 1, -1, 1),
[WRITE_STATUS] = SPI_CMD(0x01, 0, 0, 0, 0, 0, 0, 1, -1, 2),
[WRITE_STATUS2] = SPI_CMD(0x31, 0, 0, 0, 0, 0, 0, 1, -1, 2),
[READ_SLOW] = SPI_CMD(0x03, 3, 1, 0, 0, 0, 0, 1, -1, 1),
[READ_FAST] = SPI_CMD(0x0b, 3, 1, 0, 0, 8, 1, 1, 3, 1),
[READ_FAST_X2] = SPI_CMD(0x3b, 3, 1, 0, 0, 8, 1, 2, -1, 1),
[READ_FAST_X4] = SPI_CMD(0x6b, 3, 1, 0, 0, 8, 1, 4, 4, 1),
[READ_FAST_DUAL] = SPI_CMD(0xbb, 3, 2, 8, 2, 0, 0, 2, -1, 1),
[READ_FAST_QUAD] = SPI_CMD(0xeb, 3, 4, 8, 4, 4, 4, 4, 5, 1),
[SECTOR_ERASE_4K] = SPI_CMD(0x20, 3, 1, 0, 0, 0, 0, 0, -1, 0),
[BLK_ERASE_32K] = SPI_CMD(0x52, 3, 1, 0, 0, 0, 0, 0, -1, 0),
[BLK_ERASE_64K] = SPI_CMD(0xd8, 3, 1, 0, 0, 0, 0, 0, -1, 0),
[CHIP_ERASE] = SPI_CMD(0xc7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PROG] = SPI_CMD(0x02, 3, 1, 0, 0, 0, 0, 1, 6, 2),
[PROG_X4] = SPI_CMD(0x32, 3, 1, 0, 0, 0, 0, 4, 7, 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),
[ENABLE_QPI] = SPI_CMD(0x38, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_SUSPEND] = SPI_CMD(0x75, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_RESUME] = SPI_CMD(0x7a, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[EN_RST] = SPI_CMD(0x66, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[RESET] = SPI_CMD(0x99, 0, 0, 0, 0, 0, 0, 0, -1, 0),
/* Vendor specific command */
[READ_STATUS2_MXIC] = SPI_CMD(0x15, 0, 0, 0, 0, 0, 0, 1, -1, 1),
[PROG_X4_MXIC] = SPI_CMD(0x38, 3, 4, 0, 0, 0, 0, 4, 8, 2),
[ENABLE_QPI_MXIC] = SPI_CMD(0x35, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_SUSPEND_MXIC] = SPI_CMD(0xb0, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_RESUME_MXIC] = SPI_CMD(0x30, 0, 0, 0, 0, 0, 0, 0, -1, 0),
/* END Mark */
[MAX_CMD] = SPI_CMD(0x00, 0, 0, 0, 0, 0, 0, 0, -1, 0),
};
#ifdef QSPINOR_ENABLE_QPI_MODE
/* Standard SPI-NOR flash QPI commands */
static struct spi_flash_cmd_cfg cmd_table_qpi[] = {
/*opcode addr_bytes addr_pins mode_bits mode_pins dummy_cycles
dummy_pins data_pins seq_id cmd_type */
[ENABLE_4BYTE_QPI] = SPI_CMD(0xb7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[DISABLE_4BYTE_QPI] = SPI_CMD(0xe9, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[READ_STATUS1_QPI] = SPI_CMD(0x05, 0, 0, 0, 0, 0, 0, 4, 2, 1),
[READ_STATUS2_QPI] = SPI_CMD(0x35, 0, 0, 0, 0, 0, 0, 4, -1, 1),
[READ_STATUS3_QPI] = SPI_CMD(0x11, 0, 0, 0, 0, 0, 0, 4, -1, 1),
[WRITE_STATUS_QPI] = SPI_CMD(0x01, 0, 0, 0, 0, 0, 0, 4, -1, 2),
[WRITE_STATUS2_QPI] = SPI_CMD(0x31, 0, 0, 0, 0, 0, 0, 4, -1, 2),
[READ_FAST_QPI] = SPI_CMD(0x0b, 3, 4, 0, 0, 8, 4, 4, 3, 1),
[READ_FAST_QUAD_QPI] = SPI_CMD(0xeb, 3, 4, 0, 0, 8, 4, 4, 4, 1),
[SECTOR_ERASE_4K_QPI] = SPI_CMD(0x20, 3, 4, 0, 0, 0, 0, 0, -1, 0),
[BLK_ERASE_32K_QPI] = SPI_CMD(0x52, 3, 4, 0, 0, 0, 0, 0, -1, 0),
[BLK_ERASE_64K_QPI] = SPI_CMD(0xd8, 3, 4, 0, 0, 0, 0, 0, -1, 0),
[CHIP_ERASE_QPI] = SPI_CMD(0xc7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PROG_QPI] = SPI_CMD(0x02, 3, 4, 0, 0, 0, 0, 4, 5, 2),
[WR_ENABLE_QPI] = SPI_CMD(0x06, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[WR_DISABLE_QPI] = SPI_CMD(0x04, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[READ_ID_QPI] = SPI_CMD(0x9f, 0, 0, 0, 0, 0, 0, 4, -1, 1),
[READ_QPIID] = SPI_CMD(0xaf, 0, 0, 0, 0, 0, 0, 4, -1, 1),
[DISABLE_QPI] = SPI_CMD(0xff, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[SET_READ_PARA_QPI] = SPI_CMD(0xc0, 0, 0, 0, 0, 0, 0, 4, -1, 2),
[PGM_ERS_SUSPEND_QPI] = SPI_CMD(0x75, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_RESUME_QPI] = SPI_CMD(0x7a, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[EN_RST_QPI] = SPI_CMD(0x66, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[RESET_QPI] = SPI_CMD(0x99, 0, 0, 0, 0, 0, 0, 0, -1, 0),
/* Vendor specific command */
[READ_STATUS2_MXIC_QPI] = SPI_CMD(0x15, 0, 0, 0, 0, 0, 0, 4, -1, 1),
[DISABLE_QPI_MXIC] = SPI_CMD(0xf5, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_SUSPEND_MXIC_QPI]= SPI_CMD(0xb0, 0, 0, 0, 0, 0, 0, 0, -1, 0),
[PGM_ERS_RESUME_MXIC_QPI]= SPI_CMD(0x30, 0, 0, 0, 0, 0, 0, 0, -1, 0),
/* END Mark */
[MAX_CMD_QPI] = SPI_CMD(0x00, 0, 0, 0, 0, 0, 0, 0, -1, 0),
};
#endif
static struct spi_flash_chip nor_chip;
/**
* spi_nor_read_id - send 9Fh command to get ID
* @chip: SPI_FLASH device structure
* @buf: buffer to store id
*/
static int spi_nor_read_id(struct spi_flash_chip *chip, uint8_t *buf)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + READ_ID_QPI;
else
cmd.cmd_cfg = chip->table + READ_ID;
cmd.n_rx = 3;
cmd.rx_buf = buf;
return qspi_start_cmd(chip, &cmd);
}
static int spi_nor_read_qpiid(struct spi_flash_chip *chip, uint8_t *buf)
{
struct spi_flash_cmd cmd;
if (!chip->qpi_enabled) {
obm_printf("err: not in qpi mode\r\n");
return -1;
}
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + READ_QPIID;
cmd.n_rx = 3;
cmd.rx_buf = buf;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nor_reset - send command to reset chip.
* @chip: SPI_FLASH device structure
*/
static int spi_nor_reset(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + EN_RST_QPI;
else
cmd.cmd_cfg = chip->table + EN_RST;
if (qspi_start_cmd(chip, &cmd) < 0)
obm_printf("spi_nor enable reset failed!\n");
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + RESET_QPI;
else
cmd.cmd_cfg = chip->table + RESET;
if (qspi_start_cmd(chip, &cmd) < 0)
obm_printf("spi_nor reset failed!\n");
/* elapse 2ms before issuing any other command */
Delay(2000);
return 0;
}
/**
* spi_nor_write_enable - send command 06h to enable write or erase the
* Nand cells
* @chip: SPI_FLASH 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_nor_write_enable(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + WR_ENABLE_QPI;
else
cmd.cmd_cfg = chip->table + WR_ENABLE;
return qspi_start_cmd(chip, &cmd);
}
int spi_nor_suspend_resume(struct spi_flash_chip *chip, int cmd_index)
{
struct spi_flash_cmd cmd;
qspi_memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + cmd_index;
return qspi_start_cmd(chip, &cmd);
}
int spi_nor_suspend(struct spi_flash_chip *chip)
{
return spi_nor_suspend_resume(chip, PGM_ERS_SUSPEND);
}
int spi_nor_resume(struct spi_flash_chip *chip)
{
return spi_nor_suspend_resume(chip, PGM_ERS_RESUME);
}
/**
* spi_nor_read_status - get status register value
* @chip: SPI_FLASH 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_nor_read_status(struct spi_flash_chip *chip, uint8_t index,
uint8_t *status)
{
struct spi_flash_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + index;
cmd.n_rx = 1;
cmd.rx_buf = status;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: read register %d\n", ret);
return ret;
}
static int spi_nor_read_status1(struct spi_flash_chip *chip, uint8_t *status)
{
uint8_t cmd_index;
if (chip->qpi_enabled)
cmd_index = READ_STATUS1_QPI;
else
cmd_index = READ_STATUS1;
return spi_nor_read_status(chip, cmd_index, status);
}
static int spi_nor_read_status2(struct spi_flash_chip *chip, uint8_t *status)
{
uint8_t cmd_index;
if (chip->mfr_id == SPIFLASH_MFR_MXIC) {
/* Not support SPINOR_CMD_READ_STATUS2 for 32MB spi-nor */
if (chip->dev_id == 0x2536)
return 0;
if (chip->qpi_enabled)
cmd_index = READ_STATUS2_MXIC_QPI;
else
cmd_index = READ_STATUS2_MXIC;
} else {
if (chip->qpi_enabled)
cmd_index = READ_STATUS2_QPI;
else
cmd_index = READ_STATUS2;
}
return spi_nor_read_status(chip, cmd_index, status);
}
static int spi_nor_read_status3(struct spi_flash_chip *chip, uint8_t *status)
{
uint8_t cmd_index;
if (chip->qpi_enabled)
cmd_index = READ_STATUS3_QPI;
else
cmd_index = READ_STATUS3;
return spi_nor_read_status(chip, cmd_index, status);
}
/**
* spi_nor_write_reg - send command 1Fh to write register
* @chip: SPI_FLASH device structure
* @reg; register to write
* @buf: buffer stored value
*/
static int spi_nor_write_status1(struct spi_flash_chip *chip, uint8_t *buf,
uint8_t count)
{
struct spi_flash_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + WRITE_STATUS_QPI;
else
cmd.cmd_cfg = chip->table + WRITE_STATUS;
cmd.n_tx = count;
cmd.tx_buf = buf;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: %d write status\n", ret);
return ret;
}
static int spi_nor_write_status2(struct spi_flash_chip *chip, uint8_t *buf)
{
struct spi_flash_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + WRITE_STATUS2_QPI;
else
cmd.cmd_cfg = chip->table + WRITE_STATUS2;
cmd.n_tx = 1;
cmd.tx_buf = buf;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: %d write status2\n", ret);
return ret;
}
/**
* spi_nor_wait - wait until the command is done
* @chip: SPI_FLASH device structure
* @s: buffer to store status register(can be NULL)
*/
static int spi_nor_wait(struct spi_flash_chip *chip, uint8_t *s)
{
uint8_t status;
unsigned long ret = -ETIMEDOUT;
while (1) {
spi_nor_read_status1(chip, &status);
if ((status & STATUS_OIP_MASK) == STATUS_READY) {
ret = 0;
goto out;
}
}
out:
if (s)
*s = status;
return ret;
}
/**
* spi_nor_erase_block_erase - send command D8h to erase a block
* @chip: SPI_FLASH device structure
* @addr: the flash addr to erase.
* Description:
* Need to wait for tERS.
*/
static int spi_nor_erase_sector(struct spi_flash_chip *chip,
uint32_t addr)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + SECTOR_ERASE_4K_QPI;
else
cmd.cmd_cfg = chip->table + SECTOR_ERASE_4K;
if (chip->en_addr_4byte) {
cmd.n_addr = 4;
cmd.addr[0] = (uint8_t)(addr >> 24);
cmd.addr[1] = (uint8_t)(addr >> 16);
cmd.addr[2] = (uint8_t)(addr >> 8);
cmd.addr[3] = (uint8_t)addr;
} else {
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)(addr >> 16);
cmd.addr[1] = (uint8_t)(addr >> 8);
cmd.addr[2] = (uint8_t)addr;
}
cmd.flag = RST_AHB_DOMAIN;
return qspi_start_cmd(chip, &cmd);
}
/**
* spi_nor_erase_block_erase - send command D8h to erase a block
* @chip: SPI_FLASH device structure
* @addr: the flash addr to erase.
* Description:
* Need to wait for tERS.
*/
static int spi_nor_erase_block(struct spi_flash_chip *chip,
uint32_t addr, uint8_t opcode)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + opcode;
if (chip->en_addr_4byte) {
cmd.n_addr = 4;
cmd.addr[0] = (uint8_t)(addr >> 24);
cmd.addr[1] = (uint8_t)(addr >> 16);
cmd.addr[2] = (uint8_t)(addr >> 8);
cmd.addr[3] = (uint8_t)addr;
} else {
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)(addr >> 16);
cmd.addr[1] = (uint8_t)(addr >> 8);
cmd.addr[2] = (uint8_t)addr;
}
cmd.flag = RST_AHB_DOMAIN;
return qspi_start_cmd(chip, &cmd);
}
static int spi_nor_erase_32k_blk(struct spi_flash_chip *chip, uint32_t addr)
{
uint8_t opcode;
if (chip->qpi_enabled)
opcode = BLK_ERASE_32K_QPI;
else
opcode = BLK_ERASE_32K;
return spi_nor_erase_block(chip, addr, opcode);
}
static int spi_nor_erase_64k_blk(struct spi_flash_chip *chip, uint32_t addr)
{
uint8_t opcode;
if (chip->qpi_enabled)
opcode = BLK_ERASE_64K_QPI;
else
opcode = BLK_ERASE_64K;
return spi_nor_erase_block(chip, addr, opcode);
}
static int spi_nor_erase_chip(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + CHIP_ERASE_QPI;
else
cmd.cmd_cfg = chip->table + CHIP_ERASE;
cmd.flag = RST_AHB_DOMAIN;
return qspi_start_cmd(chip, &cmd);
}
static int spi_nor_erase_all(struct spi_flash_chip *chip)
{
uint8_t status;
int ret = 0;
spi_nor_write_enable(chip);
spi_nor_erase_chip(chip);
ret = spi_nor_wait(chip, &status);
if (ret < 0) {
obm_printf("chip erase command wait failed\n");
return ret;
}
return 0;
}
static int spi_nor_enable_4byte_mode(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
uint8_t data[3];
uint8_t en4b_shift;
uint32_t sdata;
int ret;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + ENABLE_4BYTE_QPI;
else
cmd.cmd_cfg = chip->table + ENABLE_4BYTE;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0) {
obm_printf("enable_4byte mode failed\r\n");
return ret;
}
chip->en_addr_4byte = 1;
/*
* Macronix use status register bit S13 for EN4B
* Gigadevice use status register bit S11
* WinBond use status register bit S17
*/
if (chip->mfr_id == SPIFLASH_MFR_MXIC)
en4b_shift = 13;
else if (chip->mfr_id == SPIFLASH_MFR_GIGADEVICE)
en4b_shift = 11;
else if (chip->mfr_id == SPIFLASH_MFR_WINBOND)
en4b_shift = 16;
else {
obm_printf("EN4B: lack vendor status bit info\r\n");
return 0;
}
if (en4b_shift >= 16)
spi_nor_read_status3(chip, &data[2]);
else if (en4b_shift >= 8)
spi_nor_read_status2(chip, &data[1]);
else
spi_nor_read_status1(chip, &data[0]);
sdata = data[2] << 16 | data[1] << 8 | data[0];
if (!(sdata & (1 << en4b_shift))) {
obm_printf("Failed to enable 4byte mode: bit%d\r\n", en4b_shift);
chip->en_addr_4byte = 0;
return -1;
}
obm_printf("Enter 4byte address mode, bit%d\r\n", en4b_shift);
return 0;
}
static int spi_nor_disable_4byte_mode(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
int ret;
if (!chip->en_addr_4byte)
return 0;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->qpi_enabled)
cmd.cmd_cfg = chip->table + DISABLE_4BYTE_QPI;
else
cmd.cmd_cfg = chip->table + DISABLE_4BYTE;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0) {
obm_printf("Failed to disable 4byte_mode\r\n");
return ret;
}
chip->en_addr_4byte = 0;
return 0;
}
/**
* spi_nor_erase - [Interface] erase block(s)
* @chip: spi flash 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_nor_erase(struct spi_flash_chip *chip, int addr, int len)
{
uint8_t status;
int erase_len;
int ret = 0;
//obm_printf("%s: address = 0x%x, len = %d\r\n", __func__, addr, len);
/* check address align on 4K boundary */
if (addr & (4*1024 - 1)) {
obm_printf("%s: Unaligned address\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;
}
while (len > 0) {
spi_nor_write_enable(chip);
if (len >= 0x10000 && !(addr & (0x10000 - 1))) {
erase_len = 0x10000;
spi_nor_erase_64k_blk(chip, addr);
} else if (len >= 0x8000 && !(addr & (0x8000 - 1))) {
erase_len = 0x8000;
spi_nor_erase_32k_blk(chip, addr);
} else {
erase_len = 0x1000;
spi_nor_erase_sector(chip, addr);
}
ret = spi_nor_wait(chip, &status);
if (ret < 0) {
obm_printf("erase command wait failed, erase_len=0x%x\n",
erase_len);
break;
}
/* Increment page address and decrement length */
len -= erase_len;
addr += erase_len;
if (addr >= chip->size)
break;
}
return ret;
}
static int __spi_nor_read(struct spi_flash_chip *chip, int addr,
int size, uint8_t *rbuf)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + chip->read_op;
if (chip->en_addr_4byte) {
cmd.n_addr = 4;
cmd.addr[0] = (uint8_t)((addr >> 24) & 0xff);
cmd.addr[1] = (uint8_t)((addr >> 16) & 0xff);
cmd.addr[2] = (uint8_t)((addr >> 8) & 0xff);
cmd.addr[3] = (uint8_t)(addr & 0xff);
} else {
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)((addr >> 16) & 0xff);
cmd.addr[1] = (uint8_t)((addr >> 8) & 0xff);
cmd.addr[2] = (uint8_t)(addr & 0xff);
}
cmd.n_rx = size;
cmd.rx_buf = rbuf;
cmd.mode = 0xff;
return qspi_start_cmd(chip, &cmd);
}
#define SPI_NOR_MAX_RETRY 3
static int spi_nor_read(struct spi_flash_chip *chip, int addr,
int size, uint8_t *rbuf)
{
int ret;
int rx_len, i;
/* Do not allow reads past end of device */
if ((addr + size) > chip->size) {
obm_printf("%s: attempt to read beyond end of device\r\n",
__func__);
return -EINVAL;
}
if (chip->host->xip_read) {
addr += chip->host->cs_addr[chip->cs];
memcpy(rbuf, (void *)addr, size);
return 0;
}
rx_len = chip->rx_max_len ? chip->rx_max_len : size;
do {
int real_len;
i = 0;
retry:
real_len = min(size, rx_len);
ret = __spi_nor_read(chip, addr, real_len, rbuf);
if (ret == -EAGAIN && ++i <= SPI_NOR_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);
}
addr += real_len;
rbuf += real_len;
size -= real_len;
} while (size);
return ret;
}
/**
* spi_nor_program_data_to_cache - write data to cache register
* @chip: SPI_FLASH 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_nor_program_data(struct spi_flash_chip *chip,
uint32_t addr, int len, const uint8_t *wbuf)
{
struct spi_flash_cmd cmd;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + chip->write_op;
if (chip->en_addr_4byte) {
cmd.n_addr = 4;
cmd.addr[0] = (uint8_t)((addr >> 24) & 0xff);
cmd.addr[1] = (uint8_t)((addr >> 16) & 0xff);
cmd.addr[2] = (uint8_t)((addr >> 8) & 0xff);
cmd.addr[3] = (uint8_t)(addr & 0xff);
} else {
cmd.n_addr = 3;
cmd.addr[0] = (uint8_t)((addr >> 16) & 0xff);
cmd.addr[1] = (uint8_t)((addr >> 8) & 0xff);
cmd.addr[2] = (uint8_t)(addr & 0xff);
}
cmd.n_tx = len;
cmd.tx_buf = wbuf;
cmd.flag = RST_AHB_DOMAIN;
return qspi_start_cmd(chip, &cmd);
}
static int spi_nor_write_page(struct spi_flash_chip *chip, int addr,
int size, uint8_t *buf)
{
uint8_t status;
int ret = 0;
spi_nor_write_enable(chip);
spi_nor_program_data(chip, addr, size, buf);
ret = spi_nor_wait(chip, &status);
if (ret < 0) {
obm_printf("error %d reading page 0x%x from cache\n",
ret, addr);
return ret;
}
return ret;
}
static int spi_nor_write(struct spi_flash_chip *chip,
int addr, int size, uint8_t *buf)
{
int len, ret;
/* Do not allow reads past end of device */
if ((addr + size) > chip->size) {
obm_printf("%s: attempt to read beyond end of device\r\n",
__func__);
return -EINVAL;
}
while (size) {
len = chip->page_size - (addr & chip->page_mask);
len = min(min(len, size), chip->tx_max_len);
ret = spi_nor_write_page(chip, addr, len, buf);
if (ret < 0) {
obm_printf("page program failed\n");
break;
}
/* Increment page address and decrement length */
size -= len;
addr += len;
buf += len;
}
return ret;
}
static void spi_nor_set_rd_wr_op(struct spi_flash_chip *chip,
uint32_t op_mode_rx, uint32_t op_mode_tx)
{
struct spi_nor_info *type = (struct spi_nor_info *)chip->flash_info;
struct spi_flash_cmd_cfg *read_cmd;
if (op_mode_rx & SPI_OPM_RX_QUAD) {
if (chip->qpi_enabled) {
chip->read_op = READ_FAST_QPI;
} else {
if (type)
chip->read_op = type->quad_cmd_index;
else
chip->read_op = READ_FAST_X4;
}
} else if (op_mode_rx & SPI_OPM_RX_DUAL) {
chip->read_op = READ_FAST_DUAL;
} else {
chip->read_op = READ_FAST;
}
if (op_mode_tx & SPI_OPM_TX_QUAD) {
if (chip->qpi_enabled) {
chip->write_op = PROG_QPI;
} else {
if (chip->mfr_id == SPIFLASH_MFR_MXIC)
chip->write_op = PROG_X4_MXIC;
else
chip->write_op = PROG_X4;
}
} else {
chip->write_op = PROG;
}
read_cmd = chip->table + chip->read_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_set_read_parameters(struct spi_flash_chip *chip, uint8_t *buf)
{
struct spi_flash_cmd cmd;
int ret = 0;
if (!chip->qpi_enabled)
return 0;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
cmd.cmd_cfg = chip->table + SET_READ_PARA_QPI;
cmd.n_tx = 1;
cmd.tx_buf = buf;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
obm_printf("err: %d set_read_parameters\n", ret);
return ret;
}
static int spi_nor_set_quad(struct spi_flash_chip *chip, int enable)
{
uint8_t data[2];
uint8_t status, qe_shift;
uint16_t sdata;
int ret;
enable = !!enable;
/*
* Macronix use status register bit s6 for QE enable
* Winbond/Gigadevice use status register bit S9
*/
if (chip->options & SPINOR_QE_USE_BIT6)
qe_shift = 6;
else
qe_shift = 9;
spi_nor_read_status1(chip, &data[0]);
spi_nor_read_status2(chip, &data[1]);
sdata = data[1] << 8 | data[0];
if (((sdata >> qe_shift) & 0x1) == enable) {
obm_printf("QE(bit%d) already set to %d\r\n", qe_shift, enable);
return 0;
}
if (enable)
sdata |= 1 << qe_shift;
else
sdata &= ~(1 << qe_shift);
data[0] = sdata & 0xff;
data[1] = sdata >> 8;
spi_nor_write_enable(chip);
if (qe_shift > 7) {
if (chip->options & SPINOR_HAVE_WRITE_STATUS2)
spi_nor_write_status2(chip, &data[1]);
else
spi_nor_write_status1(chip, data, 2);
} else {
if (chip->options & SPINOR_WRITE_STATUS1_1BYTE)
spi_nor_write_status1(chip, data, 1);
else
spi_nor_write_status1(chip, data, 2);
}
ret = spi_nor_wait(chip, &status);
if (ret < 0) {
obm_printf("error %d write status register\n", ret);
return ret;
}
spi_nor_read_status1(chip, &data[0]);
spi_nor_read_status2(chip, &data[1]);
sdata = data[1] << 8 | data[0];
if (((sdata >> qe_shift) & 0x1) != enable) {
obm_printf("err: failed to %s QE\r\n",
enable ? "enable" : "disable");
return -1;
}
obm_printf("==>%s QE succeed\r\n", enable ? "enable" : "disable");
return 0;
}
static int spi_nor_enable_quad(struct spi_flash_chip *chip)
{
return spi_nor_set_quad(chip, 1);
}
static int spi_nor_disable_quad(struct spi_flash_chip *chip)
{
return spi_nor_set_quad(chip, 0);
}
#ifdef QSPINOR_ENABLE_QPI_MODE
static int spi_nor_disable_qpi(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
int ret = 0;
if (!chip->qpi_enabled)
return 0;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->mfr_id == SPIFLASH_MFR_MXIC)
cmd.cmd_cfg = chip->table + DISABLE_QPI_MXIC;
else
cmd.cmd_cfg = chip->table + DISABLE_QPI;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
return ret;
chip->qpi_enabled = 0;
chip->table = cmd_table;
obm_printf("QPI Disabled\r\n");
return 0;
}
/*
* QPI dummy clock depend on bus frequency
*
* Giga: {4, 6, 8, 8}, Winbond: {2, 4, 6, 8}
* Macronix use a different way to set dummy clock
*/
static uint8_t qpi_dummy_wb[4][2] = { {2, 26}, {4, 50}, {6, 80}, {8, 104} };
static uint8_t qpi_dummy_gd[4][2] = { {4, 80}, {4, 80}, {6, 100}, {8, 133} };
static int spi_nor_enable_qpi(struct spi_flash_chip *chip)
{
struct spi_flash_cmd cmd;
int ret = 0, i;
uint8_t cfg;
uint8_t (*qpi_dummy)[2];
if (chip->qpi_enabled)
return 0;
memset(&cmd, 0, sizeof(struct spi_flash_cmd));
if (chip->mfr_id == SPIFLASH_MFR_MXIC)
cmd.cmd_cfg = chip->table + ENABLE_QPI_MXIC;
else
cmd.cmd_cfg = chip->table + ENABLE_QPI;
ret = qspi_start_cmd(chip, &cmd);
if (ret < 0)
return ret;
chip->qpi_enabled = 1;
chip->table = cmd_table_qpi;
if(chip->mfr_id == SPIFLASH_MFR_MXIC) {
chip->qpi_dummy = 6;
} else {
if (chip->mfr_id == SPIFLASH_MFR_GIGADEVICE)
qpi_dummy = qpi_dummy_gd;
else
qpi_dummy = qpi_dummy_wb;
for (i = 0; i < 4; i++) {
if(chip->host->bus_clk <= qpi_dummy[i][1]) {
cfg = i << 4;
chip->qpi_dummy = qpi_dummy[i][0];
break;
}
}
ret = spi_set_read_parameters(chip, &cfg);
if (ret < 0)
return ret;
}
spi_nor_set_rd_wr_op(chip, SPI_OPM_RX_QUAD, SPI_OPM_TX_QUAD);
obm_printf("QPI Enabled, P5-4:%d dummy_cycles:%d\r\n", i, chip->qpi_dummy);
return 0;
}
#endif
static int spi_nor_do_reset(FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nor_chip;
return spi_nand_reset();
}
static int spi_nor_do_read(UINT_T FlashOffset, UINT_T buffer,
UINT_T size, FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nor_chip;
int ret;
ret = spi_nor_read(chip, FlashOffset, size, (UINT8_T *)buffer);
if (ret == -EINVAL) {
return InvalidAddressRangeError;
} else if (ret < 0) {
return ReadError;
}
return ret;
}
static int spi_nor_do_write(UINT_T FlashOffset, UINT_T buffer,
UINT_T size, FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nor_chip;
int ret;
ret = spi_nor_write(chip, FlashOffset, size, (UINT8_T *)buffer);
if (ret == -EINVAL) {
return InvalidAddressRangeError;
} else if (ret < 0) {
return WriteError;
}
return ret;
}
static int spi_nor_do_erase(UINT_T FlashOffset, UINT_T size,
FlashBootType_T fbt)
{
struct spi_flash_chip *chip = &nor_chip;
int ret;
ret = spi_nor_erase(chip, FlashOffset, size);
if (ret == -EINVAL) {
return InvalidAddressRangeError;
} else if (ret < 0) {
return EraseError;
}
return ret;
}
static int spi_nor_exit()
{
struct spi_flash_chip *chip = &nor_chip;
#ifdef QSPI_SUPPORT_DQS
qspi_config_disable_dqs(chip->host);
#endif
return NoError;
}
UINT_T SPINOR_Wipe(void)
{
struct spi_flash_chip *chip = &nor_chip;
return spi_nor_erase_all(chip);
}
void SPINOR_Disable4BytesMode(void)
{
struct spi_flash_chip *chip = &nor_chip;
spi_nor_disable_4byte_mode(chip);
}
/**
* spi_nor_init - [Interface] Init SPI_FLASH device driver
* @spi: spi device structure
* @chip_ptr: pointer point to spi nand device structure pointer
*/
struct spi_flash_chip *spi_nor_init(struct qspi_host *host, int cs,
int rx_mode, int tx_mode, int qpi)
{
struct spi_flash_chip *chip = &nor_chip;
uint8_t id[SPINOR_MAX_ID_LEN] = {0};
#ifdef QSPINOR_ENABLE_QPI_MODE
if (chip->qpi_enabled)
spi_nor_disable_qpi(chip);
#endif
memset(chip, 0, sizeof(struct spi_flash_chip));
chip->cs = cs >= QSPI_CS_MAX ? QSPI_CS_A1 : cs;
chip->table = cmd_table;
chip->host = host;
chip->size = 16*1024*1024;
chip->block_size = 1 << 16;
chip->page_size = 1 << 8;
spi_nor_reset(chip);
spi_nor_read_id(chip, id);
if((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
(id[0] == 0x0 && id[1] == 0x0 && id[2] == 0x0))
return NULL;
chip->mfr_id = id[0];
chip->dev_id = id[1] << 8 | id[2];
spi_nor_scan_id_table(chip);
chip->block_shift = ilog2(chip->block_size);
chip->page_shift = ilog2(chip->page_size);
chip->page_mask = chip->page_size - 1;
obm_printf("SPI-NOR: mfr_id: 0x%x, dev_id: 0x%x\r\n",
chip->mfr_id, chip->dev_id);
/*
* The Enter 4-Byte Address Mode instruction will allow 32-bit address
* (A31-A0) to be used to access the memory array beyond 128Mb
*/
if (chip->size > 16*1024*1024)
spi_nor_enable_4byte_mode(chip);
spi_nor_enable_quad(chip);
#ifdef QSPINOR_ENABLE_QPI_MODE
if (qpi)
spi_nor_enable_qpi(chip);
#endif
spi_nor_set_rd_wr_op(chip, rx_mode, tx_mode);
/*
* TODO:
* Maybe change due to different vendor
*/
qspi_enable_xip(chip, chip->table + chip->read_op);
if (qspi_preinit_lookup_tbl(chip) < 0) {
obm_printf("preinit_lookup_tbl failed, check cmd table\r\n");
return NULL;
}
return chip;
}
UINT_T InitializeQSPIDevice(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;
int freq = 104;
host = qspi_host_init(0, 13, 1);
chip = spi_nor_init(host, 0, SPI_OPM_RX_QUAD, SPI_OPM_TX_QUAD, 0);
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;
}
qspi_set_func_clk(host, freq, 0, 0, 0, 0);
//setup Flash Properties info
//Fixed the block size to 32KB
pFlashP->BlockSize = 1 << 15; // 32KB
pFlashP->PageSize = chip->page_size; // 256B
pFlashP->NumBlocks = chip->size / pFlashP->BlockSize;
pFlashP->ResetFlash = NULL;
pFlashP->FlashSettings.UseBBM = 0;
pFlashP->FlashSettings.UseSpareArea = 0;
pFlashP->FlashSettings.SASize = 0;
pFlashP->FlashSettings.UseHwEcc = 0;
pFlashP->StreamingFlash = FALSE;
pFlashP->StagedInitRequired = FALSE;
pFlashP->FlashType = SPI_FLASH;
pFlashP->FinalizeFlash = NULL;
pFlashP->TimFlashAddress = 0;
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;
pFlashP->ReadFromFlash = spi_nor_do_read;
pFlashP->WriteToFlash = spi_nor_do_write;
pFlashP->EraseFlash = spi_nor_do_erase;
pFlashP->FinalizeFlash = spi_nor_exit;
obm_printf("pFlashP->NumBlocks: %d\r\n", pFlashP->NumBlocks);
return NoError;
}