blob: 6bd5aa97bf936a45b6b16db0b0a260e57ea096e8 [file] [log] [blame]
#include <common.h>
#include <malloc.h>
#include <linux/mtd/mtd.h>
#include <asm-generic/errno.h>
#include <mtd/pxa3xx_bbm.h>
#include <linux/mtd/bbm.h>
#include <linux/mtd/nand.h>
#include <ubi_uboot.h>
#include <spi_flash_chip.h>
#include <spi_nand.h>
#define OOB_REQ_ALIGN_TO_SPARE_LEN
static void generic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void micron_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void gd_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void gd_spi_nand_ecc_status2(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void xtx_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void xtx2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void yxsc_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void wb_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static void wb_spi_nand_ecc_status2(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error);
static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo);
/**
* Default OOB area specification layout
*/
static struct nand_ecclayout ecc_layout_64 = {
.eccbytes = 32,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
24, 25, 26, 27, 28, 29, 30, 21,
40, 41, 42, 43, 44, 45, 46, 47,
56, 57, 58, 59, 60, 61, 62, 63},
.oobavail = 30,
.oobfree = {
{.offset = 2,
.length = 6},
{.offset = 16,
.length = 8},
{.offset = 32,
.length = 8},
{.offset = 48,
.length = 8}, }
};
static struct nand_ecclayout wb_ecc_layout_64 = {
.eccbytes = 0,
.oobavail = 62,
.oobfree = {
{.offset = 2,
.length = 62},}
};
static struct nand_ecclayout ecc_layout_128 = {
.eccbytes = 64,
.eccpos = {
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127},
.oobavail = 62,
.oobfree = {
{.offset = 2,
.length = 62}, }
};
static struct nand_ecclayout xtx_ecc_layout_128 = {
.eccbytes = 52,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 51,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63},
.oobavail = 74,
.oobfree = {
{.offset = 2,
.length = 4},
{.offset = 19,
.length = 2},
{.offset = 34,
.length = 2},
{.offset = 49,
.length = 2},
{.offset = 64,
.length = 64}, }
};
// Add by mbtk
static struct nand_ecclayout xtx_ecc_layout_256 = {
.eccbytes = 52,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 51,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63},
.oobavail = 74,
.oobfree = {
{.offset = 2,
.length = 4},
{.offset = 19,
.length = 2},
{.offset = 34,
.length = 2},
{.offset = 49,
.length = 2},
{.offset = 64,
.length = 64}, }
};
static struct nand_ecclayout esmt_ecc_layout_256 = {
.eccbytes = 128,
.eccpos = {
128, 129, 130, 131, 132, 133, 134, 135,
136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151,
152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183,
184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215,
216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247,
248, 249, 250, 251, 252, 253, 254, 255},
.oobavail = 124,
.oobfree = {
{.offset = 4,
.length = 124},
}
};
enum {
GET_FEATURE,
SET_FEATURE,
PAGE_READ,
READ_PAGE_CACHE_RDM,
READ_PAGE_CACHE_LAST,
READ_FROM_CACHE,
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_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,
&ecc_layout_64, 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,
&ecc_layout_64, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, gd_spi_nand_ecc_status),
SPI_NAND_INFO("GD5F1GQ5RExxG", 0xC8, 0x41, 2048, 128, 64, 1024,
1, 4, 0, 3, 0, READ_FROM_CACHE_QUAD,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, gd_spi_nand_ecc_status2),
SPI_NAND_INFO("GD5F4GM8RExxG", 0xC8, 0x85, 2048, 128, 64, 4096,
1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD,
&ecc_layout_128, gd_spi_nand_ecc_status),
/*add by mbtk_tanggaoyou for GD5F4GM8RExxG:DID=0x85,pgsz=2k,blksz=64*pgsz,lun=1,ecc_strength=8*/
SPI_NAND_INFO_TIMING("GD5F4GM8RExxG", 0xC8, 0x85, 2048, 128, 64, 4096,
1, 8, 0, 6, 9, 2, 2, 0,READ_FROM_CACHE_X4,
&ecc_layout_128,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,
&ecc_layout_128, 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,
&ecc_layout_64, generic_spi_nand_ecc_status),
SPI_NAND_INFO("W25N01GWxxIx", 0xEF, 0x21BA, 2048, 64, 64, 1024,
1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
&ecc_layout_64, 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_ecc_layout_64, 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_ecc_layout_64, wb_spi_nand_ecc_status2),
SPI_NAND_INFO("W25M02GWxxIx", 0xEF, 0x21BB, 2048, 64, 64, 2048,
1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
&ecc_layout_64, generic_spi_nand_ecc_status),
SPI_NAND_INFO("W25M01JW", 0xEF, 0x21BC, 2048, 64, 64, 1024,
1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
&ecc_layout_64, 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,
&ecc_layout_64, 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,
&ecc_layout_64, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_64, generic_spi_nand_ecc_status),
/* XTX */
SPI_NAND_INFO("PN26Q01AWSIUG", 0xA1, 0xC1, 2048, 128, 64, 1024,
1, 8, SPINAND_RDM_CMD_NEED_PAGE_READ | SPINAND_ECC_EN_ADDR_90H,
8, 52, READ_FROM_CACHE_X4,
&xtx_ecc_layout_128, 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,
&xtx_ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_64, yxsc_spi_nand_ecc_status),
/* ESMT */
SPI_NAND_INFO("F50D1G41LB", 0xC8, 0x11, 2048, 128, 64, 1024,
1, 7, 0, 4, 52, READ_FROM_CACHE_X4,
&ecc_layout_64, generic_spi_nand_ecc_status),
/* ESMT 4Gb : Add by mbtk */
SPI_NAND_INFO("F50D4G41XB", 0x2C, 0x35, 4096, 256, 64, 2048,
1, 8, 0, 4, 52, READ_FROM_CACHE_X4,
&esmt_ecc_layout_256, micron_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,
&ecc_layout_128, generic_spi_nand_ecc_status),
SPI_NAND_INFO("FM25LG02B", 0xA1, 0xB2, 2048, 128, 64, 2048,
1, 8, 0, 4, 0, READ_FROM_CACHE_X4,
&ecc_layout_128, 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,
&ecc_layout_128, 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,
&ecc_layout_64, 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,
&ecc_layout_64, 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,
&ecc_layout_64, 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_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_get_device - [GENERIC] Get chip for selected access
* @mtd: MTD device structure
* @new_state: the state which is requested
*
* Get the device and lock it for exclusive access
*/
static int spi_nand_get_device(struct mtd_info *mtd, int new_state)
{
struct spi_flash_chip *this = mtd->priv;
this->state = new_state;
return 0;
}
/**
* spi_nand_release_device - [GENERIC] release chip
* @mtd: MTD device structure
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
static void spi_nand_release_device(struct mtd_info *mtd)
{
return;
}
/**
* 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,
u8 reg, u8 *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 = chip->issue_cmd(chip, &cmd);
if (ret < 0)
printf("err: %d read register %d\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,
u8 reg, u8 *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 = chip->issue_cmd(chip, &cmd);
if (ret < 0)
printf("err: %d write register %d\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, u8 *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, u8 *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, u8 *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)
{
u8 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)
{
u8 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)
{
u8 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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 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:
printf("%s: unexpected status: %d\n", __func__, ecc_status);
*ecc_error = 1;
break;
}
}
static int mxic_spi_nand_set_bft(struct spi_flash_chip *chip, u8 threshold)
{
u8 val, bft;
int ret;
ret = spi_nand_read_reg(chip, 0x10, &val);
if (ret)
return ret;
bft = (val & 0xf0) >> 4;
printf("%s: read BFT=0x%x threshold=%d\r\n", __func__, val, threshold);
if (bft != threshold) {
val = threshold << 4;
ret = spi_nand_write_reg(chip, 0x10, &val);
if (ret)
return ret;
printf("%s: update BFT=0x%x\r\n", __func__, val);
}
return 0;
}
static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *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, u8 *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 = chip->issue_cmd(chip, &cmd);
if (ret < 0)
pr_info("err: %d read eccsr register\r\n", ret);
return ret;
}
static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
u8 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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 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:
printf("%s: unexpected status: %d\n", __func__, ecc_status);
*ecc_error = 1;
break;
}
}
static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
u8 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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 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:
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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
u8 ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0xf0, &ext_status);
if (ret) {
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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
u8 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) {
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 {
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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
u8 ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0x30, &ext_status);
if (ret) {
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, u8 status,
u32 *corrected, u32 *ecc_error)
{
u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
u8 ext_status;
int ret;
*ecc_error = 0;
*corrected = 0;
if (ecc_status == 0x1) {
ret = spi_nand_read_reg(chip, 0x30, &ext_status);
if (ret) {
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 chip->issue_cmd(chip, &cmd);
}
/**
* spi_nand_set_ds - set value to die select 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_ds(struct spi_flash_chip *chip, u8 *ds)
{
return spi_nand_write_reg(chip, REG_DIE_SELECT, ds);
}
/**
* spi_nand_lun_select - send die select command if needed
* @chip: SPI-NAND device structure
* @lun: lun need to access
*/
static int spi_nand_lun_select(struct spi_flash_chip *chip, u8 lun)
{
u8 ds = 0;
int ret = 0;
if (chip->lun != lun) {
ds = (lun == 1) ? DIE_SELECT_DS1 : DIE_SELECT_DS0;
ret = spi_nand_set_ds(chip, &ds);
chip->lun = lun;
}
return ret;
}
/**
* 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,
u32 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] = (u8)(page_addr >> 16);
cmd.addr[1] = (u8)(page_addr >> 8);
cmd.addr[2] = (u8)page_addr;
cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
return chip->issue_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,
u32 page_addr, u32 column,
u32 len, u8 *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->xip_read && chip->memmap_read) {
chip->memmap_read(chip, rbuf, column, 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] = (u8)(column >> 8);
cmd.addr[1] = (u8)column;
cmd.n_rx = len;
cmd.rx_buf = rbuf;
return chip->issue_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,
u32 page_addr, u32 column, u32 len,
const u8 *wbuf, u8 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] = (u8)(column >> 8);
cmd.addr[1] = (u8)column;
cmd.n_tx = len;
cmd.tx_buf = wbuf;
cmd.flag = RST_AHB_DOMAIN;
return chip->issue_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, u32 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] = (u8)(page_addr >> 16);
cmd.addr[1] = (u8)(page_addr >> 8);
cmd.addr[2] = (u8)page_addr;
return chip->issue_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,
u32 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] = (u8)(page_addr >> 16);
cmd.addr[1] = (u8)(page_addr >> 8);
cmd.addr[2] = (u8)page_addr;
cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
return chip->issue_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, u8 *s)
{
u8 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, u8 *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 chip->issue_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 (chip->issue_cmd(chip, &cmd))
printf("spi_nand reset failed!\n");
/* elapse 2ms before issuing any other command */
udelay(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, u8 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, u8 mode)
{
u8 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 bool 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->ecclayout = type->ecclayout;
chip->get_ecc_status = type->get_ecc_status;
chip->max_mhz = type->max_mhz;
chip->tclqv = type->tclqv;
chip->tset = type->tset;
chip->thold = type->thold;
chip->flash_info = type;
printf("SPI-NAND: %s is found.\n", type->name);
return true;
}
}
chip->refresh_threshold = 1;
return false;
}
static u16 onfi_crc16(u16 crc, u8 const *p, u32 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, u32 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;
u8 *buffer;
int read_cache_op;
int ret = true;
int i;
buffer = malloc(256 * 3);
printf("spi_nand_detect_onfi: buffer=0x%x\n", (unsigned int)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 * 4, 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, (u8 *)p, 254) ==
// (p->crc))
break;
}
if (i == 3) {
printf("Could not find valid ONFI, use default settings\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 mtd_info *mtd, u32 page_addr,
u32 column, bool ecc_off, u32 *corrected,
u8 *buf, int size, bool oob_only)
{
struct spi_flash_chip *chip = mtd->priv;
u32 ecc_error;
u8 status;
int ret;
int len = size;
int rx_len, i;
if (!buf) {
if (!oob_only) {
len = chip->page_size + chip->oob_size;
buf = chip->buf;
column = 0;
} else {
len = chip->oob_size;
buf = chip->oobbuf;
column = chip->page_size;
}
}
spi_nand_read_page_to_cache(chip, page_addr);
ret = spi_nand_wait(chip, &status);
if (ret < 0) {
printf("error %d waiting page 0x%x to cache\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) {
printf("internal ECC error reading page 0x%x\n",
page_addr);
mtd->ecc_stats.failed++;
} else if (*corrected) {
if (*corrected < chip->refresh_threshold)
/*
* Do not report bit-flip to upper layer if
* less than refresh_threshold, which may
* make mtd->_read failure
*/
*corrected = 0;
else
mtd->ecc_stats.corrected += *corrected;
}
}
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) {
printf("Pass after the %dth retry\n", i);
}
column += real_len;
buf += real_len;
len -= real_len;
} while (len);
return ret;
}
/**
* spi_nand_transfer_oob - transfer oob to client buffer
* @chip: SPI-NAND device structure
* @oob: oob destination address
* @ops: oob ops structure
* @len: size of oob to transfer
*/
static void spi_nand_transfer_oob(struct spi_flash_chip *chip, u8 *oob,
struct mtd_oob_ops *ops, size_t len)
{
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
case MTD_OPS_RAW:
memcpy(oob, chip->oobbuf + ops->ooboffs, len);
return;
case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecclayout->oobfree;
u32 boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0;
for (; free->length && len; free++, len -= bytes) {
/* Read request not from offset 0? */
if (unlikely(roffs)) {
if (roffs >= free->length) {
roffs -= free->length;
continue;
}
boffs = free->offset + roffs;
bytes = min_t(size_t, len,
(free->length - roffs));
roffs = 0;
} else {
bytes = min_t(size_t, len, free->length);
boffs = free->offset;
}
memcpy(oob, chip->oobbuf + boffs, bytes);
oob += bytes;
}
return;
}
default:
BUG();
}
}
/**
* spi_nand_fill_oob - transfer client buffer to oob
* @chip: SPI-NAND device structure
* @oob: oob data buffer
* @len: oob data write length
* @ops: oob ops structure
*/
static void spi_nand_fill_oob(struct spi_flash_chip *chip, u8 *oob,
size_t len, struct mtd_oob_ops *ops)
{
memset(chip->oobbuf, 0xff, chip->oob_size);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
case MTD_OPS_RAW:
memcpy(chip->oobbuf + ops->ooboffs, oob, len);
return;
case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecclayout->oobfree;
u32 boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
for (; free->length && len; free++, len -= bytes) {
/* Write request not from offset 0? */
if (unlikely(woffs)) {
if (woffs >= free->length) {
woffs -= free->length;
continue;
}
boffs = free->offset + woffs;
bytes = min_t(size_t, len,
(free->length - woffs));
woffs = 0;
} else {
bytes = min_t(size_t, len, free->length);
boffs = free->offset;
}
memcpy(chip->oobbuf + boffs, oob, bytes);
oob += bytes;
}
return;
}
default:
BUG();
}
}
/**
* spi_nand_read_pages - read data from flash to buffer
* @mtd: MTD device structure
* @from: offset to read from
* @ops: oob operations description structure
* @max_bitflips: maximum bitflip count
* Description:
* Normal read function, read one page to buffer before issue
* another.
*/
static int spi_nand_read_pages(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops, unsigned int *max_bitflips)
{
struct spi_flash_chip *chip = mtd->priv;
struct mtd_ecc_stats stats;
int page_addr, page_offset, page_blk_shift, size, ret;
u32 corrected = 0;
int readlen = ops->len;
int oobreadlen = ops->ooblen;
bool ecc_off = ops->mode == MTD_OPS_RAW;
#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
int ooblen = mtd->oobsize;
#else
int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
#endif
int lun_num;
int real_page;
int blk_addr = -1;
u8 *buf;
page_blk_shift = chip->block_shift - chip->page_shift;
real_page = from >> chip->page_shift;
page_offset = from & chip->page_mask;
lun_num = from >> chip->lun_shift;
ops->retlen = 0;
*max_bitflips = 0;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
stats = mtd->ecc_stats;
page_addr = real_page;
while (1) {
if (chip->search_bbm_table &&
(blk_addr != real_page >> page_blk_shift)) {
int addr;
blk_addr = real_page >> page_blk_shift;
addr = real_page << chip->page_shift;
addr = chip->search_bbm_table(chip, addr);
page_addr = addr >> chip->page_shift;
lun_num = addr >> chip->lun_shift;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
}
size = min(readlen, chip->page_size - page_offset);
if (unlikely(ops->oobbuf) || page_offset ||
(size < chip->page_size))
buf = NULL;
else {
chip->cached_page = -1;
buf = ops->datbuf + ops->retlen;
}
if (page_addr != chip->cached_page
|| ecc_off != chip->cached_page_ecc_off) {
ret = spi_nand_do_read_page(mtd, page_addr, page_offset,
ecc_off, &corrected, buf,
size, false);
if (ret)
break;
if (chip->low_level_scrub) {
corrected = chip->low_level_scrub(chip,
real_page, corrected);
blk_addr = -1;
}
chip->cached_page_bitflips = corrected;
if (!buf && !(mtd->ecc_stats.failed - stats.failed)) {
chip->cached_page = page_addr;
chip->cached_page_ecc_off = ecc_off;
}
}
if (!buf)
memcpy(ops->datbuf + ops->retlen,
chip->buf + page_offset, size);
*max_bitflips = max(*max_bitflips, chip->cached_page_bitflips);
ops->retlen += size;
readlen -= size;
page_offset = 0;
if (unlikely(ops->oobbuf)) {
size = min(oobreadlen, ooblen);
spi_nand_transfer_oob(chip,
ops->oobbuf + ops->oobretlen, ops, size);
ops->oobretlen += size;
oobreadlen -= size;
}
if (!readlen)
break;
page_addr++;
real_page++;
/* Check, if we cross lun boundary */
if (!(real_page &
((1 << (chip->lun_shift - chip->page_shift)) - 1))
&& (chip->options & SPINAND_NEED_DIE_SELECT)) {
lun_num++;
spi_nand_lun_select(chip, lun_num);
}
}
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;
return ret;
}
/**
* spi_nand_do_read_ops - read data from flash to buffer
* @mtd: MTD device structure
* @from: offset to read from
* @ops: oob ops structure
* Description:
* Disable internal ECC before reading when MTD_OPS_RAW set.
*/
static int spi_nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct spi_flash_chip *chip = mtd->priv;
int ret;
unsigned int max_bitflips = 0;
int oobreadlen = ops->ooblen;
bool ecc_off = ops->mode == MTD_OPS_RAW;
#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
int ooblen = mtd->oobsize;
#else
int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
#endif
/* Do not allow reads past end of device */
if (unlikely(from >= mtd->size)) {
pr_err("%s: attempt to read beyond end of device\n",
__func__);
return -EINVAL;
}
/* for oob */
if (oobreadlen > 0) {
if (unlikely(ops->ooboffs >= ooblen)) {
pr_err("%s: attempt to start read outside oob\n",
__func__);
return -EINVAL;
}
if (unlikely(ops->ooboffs + oobreadlen >
((mtd->size >> chip->page_shift) - (from >> chip->page_shift))
* ooblen)) {
pr_err("%s: attempt to read beyond end of device\n",
__func__);
return -EINVAL;
}
ooblen -= ops->ooboffs;
ops->oobretlen = 0;
}
if (ecc_off)
chip->disable_ecc(chip);
ret = spi_nand_read_pages(mtd, from, ops, &max_bitflips);
if (ecc_off)
chip->enable_ecc(chip);
if (ret < 0)
return ret;
return max_bitflips;
}
static int spi_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u8 *buf)
{
struct mtd_oob_ops ops;
int ret;
spi_nand_get_device(mtd, FL_READING);
memset(&ops, 0, sizeof(ops));
ops.len = len;
ops.datbuf = buf;
ops.mode = MTD_OPS_PLACE_OOB;
ret = spi_nand_do_read_ops(mtd, from, &ops);
*retlen = ops.retlen;
spi_nand_release_device(mtd);
return ret;
}
/**
* spi_nand_do_read_oob - read out-of-band
* @mtd: MTD device structure
* @from: offset to read from
* @ops: oob operations description structure
* Description:
* Disable internal ECC before reading when MTD_OPS_RAW set.
*/
static int spi_nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct spi_flash_chip *chip = mtd->priv;
int page_addr, page_blk_shift;
u32 corrected = 0;
struct mtd_ecc_stats stats;
int readlen = ops->ooblen;
int len;
int ret = 0;
bool ecc_off = ops->mode == MTD_OPS_RAW;
int lun_num;
int real_page;
int blk_addr = -1;
pr_debug("%s: from = 0x%08Lx, len = %i\n",
__func__, (unsigned long long)from, readlen);
stats = mtd->ecc_stats;
#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
len = mtd->oobsize;
#else
len = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
#endif
if (unlikely(ops->ooboffs >= len)) {
pr_err("%s: attempt to start read outside oob\n",
__func__);
return -EINVAL;
}
/* Do not allow reads past end of device */
if (unlikely(from >= mtd->size ||
ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
(from >> chip->page_shift)) * len)) {
pr_err("%s: attempt to read beyond end of device\n",
__func__);
return -EINVAL;
}
page_blk_shift = chip->block_shift - chip->page_shift;
real_page = from >> chip->page_shift;
lun_num = from >> chip->lun_shift;
len -= ops->ooboffs;
ops->oobretlen = 0;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
if (ecc_off)
chip->disable_ecc(chip);
page_addr = real_page;
while (1) {
if (chip->search_bbm_table &&
(blk_addr != real_page >> page_blk_shift)) {
int addr;
blk_addr = real_page >> page_blk_shift;
addr = real_page << chip->page_shift;
addr = chip->search_bbm_table(chip, addr);
page_addr = addr >> chip->page_shift;
lun_num = addr >> chip->lun_shift;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
}
/*read data from chip*/
ret = spi_nand_do_read_page(mtd, page_addr, 0,
ecc_off, &corrected, NULL,
0, true);
if (ret)
goto out;
if (chip->low_level_scrub) {
corrected = chip->low_level_scrub(chip,
real_page, corrected);
blk_addr = -1;
}
len = min(len, readlen);
spi_nand_transfer_oob(chip, ops->oobbuf + ops->oobretlen,
ops, len);
readlen -= len;
ops->oobretlen += len;
if (!readlen)
break;
page_addr++;
real_page++;
/* Check, if we cross lun boundary */
if (!(real_page &
((1 << (chip->lun_shift - chip->page_shift)) - 1))
&& (chip->options & SPINAND_NEED_DIE_SELECT)) {
lun_num++;
spi_nand_lun_select(chip, lun_num);
}
}
out:
if (ecc_off)
chip->enable_ecc(chip);
if (ret < 0)
return ret;
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
/**
* spi_nand_read_oob - [MTD Interface] read data and/or out-of-band
* @mtd: MTD device structure
* @from: offset to read from
* @ops: oob operation description structure
*/
static int spi_nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int ret = -ENOTSUPP;
ops->retlen = 0;
/* Do not allow reads past end of device */
if (ops->datbuf && (from + ops->len) > mtd->size) {
printf("%s: attempt to read beyond end of device\n",
__func__);
return -EINVAL;
}
spi_nand_get_device(mtd, FL_READING);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
case MTD_OPS_AUTO_OOB:
case MTD_OPS_RAW:
break;
default:
goto out;
}
if (!ops->datbuf)
ret = spi_nand_do_read_oob(mtd, from, ops);
else
ret = spi_nand_do_read_ops(mtd, from, ops);
out:
spi_nand_release_device(mtd);
return ret;
}
/**
* spi_nand_do_write_page - write data from buffer to flash
* @mtd: MTD device 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
*/
static int spi_nand_do_write_page(struct mtd_info *mtd, u32 page_addr,
u32 column, const u8 *buf,
u32 size, u8 clr_cache, bool oob_only)
{
struct spi_flash_chip *chip = mtd->priv;
u32 column_save, len_save;
u8 *buf_save;
u8 status;
int len = size, tx_len, i;
int ret = 0;
int cache_done = 0;
if (!buf) {
if (!oob_only) {
len = chip->page_size + chip->oob_size;
buf = chip->buf;
column = 0;
} else {
len = chip->oob_size;
buf = chip->oobbuf;
column = chip->page_size;
}
}
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) {
printf("error %d waiting page 0x%x to cache\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) {
printf("Write pass after the %dth retry\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) {
pr_err("error %d reading page 0x%x from cache\n",
ret, page_addr);
return ret;
}
if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
pr_err("program page 0x%x failed\n", page_addr);
ret = -EIO;
}
return ret;
}
/**
* spi_nand_do_write_ops - write data from buffer to flash
* @mtd: MTD device structure
* @to: offset to write to
* @ops: oob operations description structure
* Description:
* Disable internal ECC before writing when MTD_OPS_RAW set.
*/
static int spi_nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
struct spi_flash_chip *chip = mtd->priv;
int page_addr, page_offset, page_blk_shift, size;
int writelen = ops->len;
int oobwritelen = ops->ooblen;
int ret = 0;
#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
int ooblen = mtd->oobsize;
#else
int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
#endif
bool ecc_off = ops->mode == MTD_OPS_RAW;
int lun_num;
int real_page;
int blk_addr = -1;
u8 *buf;
/* Do not allow reads past end of device */
if (unlikely(to >= mtd->size)) {
pr_err("%s: attempt to write beyond end of device\n",
__func__);
return -EINVAL;
}
page_blk_shift = chip->block_shift - chip->page_shift;
real_page = to >> chip->page_shift;
page_offset = to & chip->page_mask;
lun_num = to >> chip->lun_shift;
ops->retlen = 0;
/* for oob */
if (oobwritelen > 0) {
/* Do not allow write past end of page */
if ((ops->ooboffs + oobwritelen) > ooblen) {
pr_err("%s: attempt to write past end of page\n",
__func__);
return -EINVAL;
}
if (unlikely(ops->ooboffs >= ooblen)) {
pr_err("%s: attempt to start write outside oob\n",
__func__);
return -EINVAL;
}
if (unlikely(ops->ooboffs + oobwritelen >
((mtd->size >> chip->page_shift) - (to >> chip->page_shift))
* ooblen)) {
pr_err("%s: attempt to write beyond end of device\n",
__func__);
return -EINVAL;
}
ooblen -= ops->ooboffs;
ops->oobretlen = 0;
}
chip->cached_page = -1;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
if (ecc_off)
chip->disable_ecc(chip);
page_addr = real_page;
while (1) {
if (chip->search_bbm_table &&
(blk_addr != real_page >> page_blk_shift)) {
int addr;
blk_addr = real_page >> page_blk_shift;
addr = real_page << chip->page_shift;
addr = chip->search_bbm_table(chip, addr);
page_addr = addr >> chip->page_shift;
lun_num = addr >> chip->lun_shift;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
}
if (unlikely(ops->oobbuf)) {
size = min(oobwritelen, ooblen);
spi_nand_fill_oob(chip, ops->oobbuf + ops->oobretlen,
size, ops);
ops->oobretlen += size;
oobwritelen -= size;
buf = NULL;
} else {
buf = ops->datbuf + ops->retlen;
memset(chip->oobbuf, 0xff, chip->oob_size);
}
size = min(writelen, chip->page_size - page_offset);
if (!buf) {
memcpy(chip->buf + page_offset,
ops->datbuf + ops->retlen, size);
if (page_offset)
memset(chip->buf, 0xff, page_offset);
if (size < chip->page_size - page_offset)
memset(chip->buf + page_offset + size, 0xff,
chip->page_size - page_offset - size);
}
ret = spi_nand_do_write_page(mtd, page_addr, page_offset,
buf, size, 1, false);
if (ret) {
pr_err("error %d writing page 0x%x\n",
ret, page_addr);
goto out;
}
ops->retlen += size;
writelen -= size;
page_offset = 0;
if (!writelen)
break;
page_addr++;
real_page++;
/* Check, if we cross lun boundary */
if (!(real_page &
((1 << (chip->lun_shift - chip->page_shift)) - 1))
&& (chip->options & SPINAND_NEED_DIE_SELECT)) {
lun_num++;
spi_nand_lun_select(chip, lun_num);
}
}
out:
if (ecc_off)
chip->enable_ecc(chip);
return ret;
}
static int spi_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u8 *buf)
{
struct mtd_oob_ops ops;
int ret;
spi_nand_get_device(mtd, FL_WRITING);
memset(&ops, 0, sizeof(ops));
ops.len = len;
ops.datbuf = (u8 *)buf;
ops.mode = MTD_OPS_PLACE_OOB;
ret = spi_nand_do_write_ops(mtd, to, &ops);
*retlen = ops.retlen;
spi_nand_release_device(mtd);
return ret;
}
/**
* spi_nand_do_write_oob - write out-of-band
* @mtd: MTD device structure
* @to: offset to write to
* @ops: oob operation description structure
* Description:
* Disable internal ECC before writing when MTD_OPS_RAW set.
*/
static int spi_nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int page_addr, len, ret;
struct spi_flash_chip *chip = mtd->priv;
int writelen = ops->ooblen;
bool ecc_off = ops->mode == MTD_OPS_RAW;
int lun_num;
pr_debug("%s: to = 0x%08x, len = %i\n",
__func__, (unsigned int)to, (int)writelen);
#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
len = mtd->oobsize;
#else
len = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
#endif
/* Do not allow write past end of page */
if ((ops->ooboffs + writelen) > len) {
pr_err("%s: attempt to write past end of page\n",
__func__);
return -EINVAL;
}
if (unlikely(ops->ooboffs >= len)) {
pr_err("%s: attempt to start write outside oob\n",
__func__);
return -EINVAL;
}
/* Do not allow write past end of device */
if (unlikely(to >= mtd->size ||
ops->ooboffs + writelen >
((mtd->size >> chip->page_shift) -
(to >> chip->page_shift)) * len)) {
pr_err("%s: attempt to write beyond end of device\n",
__func__);
return -EINVAL;
}
if (chip->search_bbm_table)
to = chip->search_bbm_table(chip, to);
/* Shift to get page */
page_addr = to >> chip->page_shift;
lun_num = to >> chip->lun_shift;
spi_nand_fill_oob(chip, ops->oobbuf, writelen, ops);
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
if (ecc_off)
chip->disable_ecc(chip);
//ret = spi_nand_do_write_page(mtd, page_addr, true);
ret = spi_nand_do_write_page(mtd, page_addr, 0,
NULL, 0, 1, true);
if (ret) {
pr_err("error %d writing page 0x%x\n",
ret, page_addr);
goto out;
}
ops->oobretlen = writelen;
out:
if (ecc_off)
chip->enable_ecc(chip);
return ret;
}
/**
* spi_nand_write_oob - [MTD Interface] write data and/or out-of-band
* @mtd: MTD device structure
* @to: offset to write to
* @ops: oob operation description structure
*/
static int spi_nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int ret = -ENOTSUPP;
ops->retlen = 0;
/* Do not allow writes past end of device */
if (ops->datbuf && (to + ops->len) > mtd->size) {
pr_err("%s: attempt to write beyond end of device\n",
__func__);
return -EINVAL;
}
spi_nand_get_device(mtd, FL_WRITING);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
case MTD_OPS_AUTO_OOB:
case MTD_OPS_RAW:
break;
default:
goto out;
}
if (!ops->datbuf)
ret = spi_nand_do_write_oob(mtd, to, ops);
else
ret = spi_nand_do_write_ops(mtd, to, ops);
out:
spi_nand_release_device(mtd);
return ret;
}
/**
* spi_nand_block_bad - Check if block at offset is bad
* @mtd: MTD device structure
* @offs: offset relative to mtd start
* @getchip: 0, if the chip is already selected
*/
static int spi_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
struct spi_flash_chip *chip = mtd->priv;
struct mtd_oob_ops ops = {0};
u32 block_addr;
u8 bad[2] = {0, 0};
u8 ret = 0;
block_addr = ofs >> chip->block_shift;
ops.mode = MTD_OPS_PLACE_OOB;
ops.ooblen = 2;
ops.oobbuf = bad;
if (getchip)
spi_nand_get_device(mtd, FL_READING);
spi_nand_do_read_oob(mtd, block_addr << chip->block_shift, &ops);
if (getchip)
spi_nand_release_device(mtd);
if (bad[0] != 0xFF || bad[1] != 0xFF)
ret = 1;
return ret;
}
/**
* spi_nand_block_checkbad - Check if a block is marked bad
* @mtd: MTD device structure
* @ofs: offset from device start
* @getchip: 0, if the chip is already selected
* @allowbbt: 1, if its allowed to access the bbt area
*
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
*/
static int spi_nand_block_checkbad(struct mtd_info *mtd, loff_t ofs,
int getchip, int allowbbt)
{
struct spi_flash_chip *chip = mtd->priv;
if (!chip->bbt)
return chip->block_bad(mtd, ofs, getchip);
return 0;
}
/**
* spi_nand_block_isbad - [MTD Interface] Check if block at offset is bad
* @mtd: MTD device structure
* @offs: offset relative to mtd start
*/
static int spi_nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
return spi_nand_block_checkbad(mtd, offs, 1, 0);
}
/**
* spi_nand_is_bad_bbm - [BBT Interface] Check if block at offset is factory bad
* @mtd: MTD device structure
* @offs: offset relative to mtd start
*/
static int spi_nand_is_bad_bbm(struct mtd_info *mtd, loff_t ofs)
{
return spi_nand_block_bad(mtd, ofs, 1);
}
/**
* spi_nand_block_markbad_lowlevel - mark a block bad
* @mtd: MTD device structure
* @ofs: offset from device start
*
* This function performs the generic bad block marking steps (i.e., bad
* block table(s) and/or marker(s)). We only allow the hardware driver to
* specify how to write bad block markers to OOB (chip->block_markbad).
*
* We try operations in the following order:
* (1) erase the affected block, to allow OOB marker to be written cleanly
* (2) write bad block marker to OOB area of affected block (unless flag
* NAND_BBT_NO_OOB_BBM is present)
* (3) update the BBT
* Note that we retain the first error encountered in (2) or (3), finish the
* procedures, and dump the error in the end.
*/
//static int spi_nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
//{
// struct spi_flash_chip *chip = mtd->priv;
// struct mtd_oob_ops ops = {0};
// struct erase_info einfo = {0};
// u32 block_addr;
// u8 buf[2] = {0, 0};
// int res, ret = 0;
//
// if (!chip->bbt || !(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
// /*erase bad block before mark bad block*/
// einfo.mtd = mtd;
// einfo.addr = ofs;
// einfo.len = 1UL << chip->block_shift;
// spi_nand_erase(mtd, &einfo);
//
// block_addr = ofs >> chip->block_shift;
// ops.mode = MTD_OPS_PLACE_OOB;
// ops.ooblen = 2;
// ops.oobbuf = buf;
// spi_nand_get_device(mtd, FL_WRITING);
// ret = spi_nand_do_write_oob(mtd,
// block_addr << chip->block_shift, &ops);
// spi_nand_release_device(mtd);
// }
//
// /* Mark block bad in BBT */
// if (chip->bbt) {
// res = nand_bbt_markbad(chip->bbt, ofs);
// if (!ret)
// ret = res;
// }
//
// if (!ret)
// mtd->ecc_stats.badblocks++;
//
// return ret;
//}
/**
* spi_nand_block_markbad - [MTD Interface] Mark block at the given offset
* as bad
* @mtd: MTD device structure
* @ofs: offset relative to mtd start
*/
static int spi_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct spi_flash_chip *chip = mtd->priv;
int ret;
ret = spi_nand_block_isbad(mtd, ofs);
if (ret) {
/* If it was bad already, return success and do nothing */
if (ret > 0)
return 0;
return ret;
}
return chip->block_markbad(mtd, ofs);
}
/**
* __spi_nand_erase - erase block(s)
* @mtd: MTD device structure
* @einfo: erase instruction
* @allowbbt: allow to access bbt
*
* Erase one ore more blocks
*/
static int __spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo,
int allowbbt)
{
struct spi_flash_chip *chip = mtd->priv;
int page_addr, pages_per_block;
loff_t len;
u8 status;
int ret = 0;
int lun_num;
int real_page;
/* check address align on block boundary */
if (einfo->addr & (chip->block_size - 1)) {
pr_err("%s: Unaligned address\n", __func__);
return -EINVAL;
}
if (einfo->len & (chip->block_size - 1)) {
pr_err("%s: Length not block aligned\n", __func__);
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((einfo->len + einfo->addr) > chip->size) {
pr_err("%s: Erase past end of device\n", __func__);
return -EINVAL;
}
einfo->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
/* Grab the lock and see if the device is available */
spi_nand_get_device(mtd, FL_ERASING);
pages_per_block = 1 << (chip->block_shift - chip->page_shift);
real_page = einfo->addr >> chip->page_shift;
len = einfo->len;
lun_num = einfo->addr >> chip->lun_shift;
chip->cached_page = -1;
einfo->state = MTD_ERASING;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
while (len) {
if (chip->search_bbm_table) {
int addr;
addr = real_page << chip->page_shift;
addr = chip->search_bbm_table(chip, addr);
page_addr = addr >> chip->page_shift;
lun_num = addr >> chip->lun_shift;
if (chip->options & SPINAND_NEED_DIE_SELECT)
spi_nand_lun_select(chip, lun_num);
} else {
page_addr = real_page;
}
/* Check if we have a bad block, we do not erase bad blocks! */
if (spi_nand_block_checkbad(mtd, ((loff_t) page_addr) <<
chip->page_shift, 0, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at 0x%012llx\n",
__func__, ((loff_t) page_addr) << chip->page_shift);
einfo->state = MTD_ERASE_FAILED;
goto erase_exit;
}
spi_nand_write_enable(chip);
spi_nand_erase_block(chip, page_addr);
ret = spi_nand_wait(chip, &status);
if (ret < 0) {
pr_err("block erase command wait failed\n");
einfo->state = MTD_ERASE_FAILED;
goto erase_exit;
}
if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
pr_err("erase block 0x%012llx failed\n",
((loff_t) page_addr) << chip->page_shift);
einfo->state = MTD_ERASE_FAILED;
einfo->fail_addr = (loff_t)page_addr
<< chip->page_shift;
goto erase_exit;
}
/* Increment page address and decrement length */
len -= (1ULL << chip->block_shift);
real_page += pages_per_block;
/* Check, if we cross lun boundary */
if (len && !(real_page &
((1 << (chip->lun_shift - chip->page_shift)) - 1))
&& (chip->options & SPINAND_NEED_DIE_SELECT)) {
lun_num++;
spi_nand_lun_select(chip, lun_num);
}
}
einfo->state = MTD_ERASE_DONE;
erase_exit:
ret = einfo->state == MTD_ERASE_DONE ? 0 : -EIO;
spi_nand_release_device(mtd);
/* Do call back function */
if (!ret)
mtd_erase_callback(einfo);
/* Return more or less happy */
return ret;
}
/**
* spi_nand_erase - [MTD Interface] erase block(s)
* @mtd: MTD device structure
* @einfo: erase instruction
*
* Erase one ore more blocks
*/
static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo)
{
struct spi_flash_chip *chip = mtd->priv;
int ret, page, retries = 0;
page = (int)(einfo->addr >> chip->page_shift);
retry:
ret = __spi_nand_erase(mtd, einfo, 0);
if (ret) {
if (chip->options & BBT_RELOCATION_IFBAD) {
if (retries++ < 3)
goto retry;
ret = chip->block_markbad(mtd,
(loff_t)(page << chip->page_shift));
}
}
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)
{
struct spi_nand_info *type = (struct spi_nand_info *)chip->flash_info;
struct spi_flash_cmd_cfg *read_cmd;
if (chip->rx_mode & 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 (chip->rx_mode & SPI_OPM_RX_DUAL) {
chip->read_cache_op = READ_FROM_CACHE_DUAL;
} else {
chip->read_cache_op = READ_FROM_CACHE_FAST;
}
if (chip->tx_mode & 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;
printf("Set rx_pins: %d, tx_pins: %d, Read_CMD:0x%x\r\n",
chip->rx_mode, chip->tx_mode, read_cmd->opcode);
}
/**
* 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_scan_ident(struct mtd_info *mtd)
{
struct spi_flash_chip *chip = mtd->priv;
u8 id[SPINAND_MAX_ID_LEN] = {0};
int dev_in_tbl = 0;
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);
chip->mfr_id = id[1];
chip->dev_id = id[2] | id[3] << 8;
if (spi_nand_scan_id_table(chip))
dev_in_tbl = 1;
printf("SPI-NAND type mfr_id: %x, dev_id: %x\n",
chip->mfr_id, chip->dev_id);
if (!chip->check_dtr || !chip->check_dtr(chip))
chip->options &= ~SPINAND_SUPPORT_DTR;
spi_nand_set_rd_wr_op(chip);
/* 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);
if (chip->setup_memmap_read) {
if (chip->setup_memmap_read(chip,
chip->table + chip->read_cache_op) < 0) {
pr_info("preinit_lookup_tbl failed, check cmd table\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;
printf("unknown device, set SPINAND_RDM_CMD_NEED_PAGE_READ\r\n");
if (spi_nand_detect_onfi(chip))
printf("nand support onfi\r\n");
if (chip->oob_size == 128)
chip->ecclayout = &ecc_layout_128;
else
chip->ecclayout = &ecc_layout_64;
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;
chip->buf = kzalloc(chip->page_size + chip->oob_size, GFP_KERNEL);
if (!chip->buf)
return NULL;
chip->oobbuf = chip->buf + chip->page_size;
printf("block_size=0x%x page_size=0x%x bitflip_threshold=%d\n",
chip->block_size, chip->page_size,
chip->refresh_threshold);
return chip;
}
/**
* spi_nand_scan_tail - [SPI-NAND Interface] Scan for the SPI-NAND device
* @mtd: MTD device structure
* Description:
* This is the second phase of the initiazation. It fills out all the
* uninitialized fields of spi_flash_chip and mtd fields.
*/
int spi_nand_scan_tail(struct mtd_info *mtd)
{
struct spi_flash_chip *chip = mtd->priv;
int ret;
if (chip->options & SPINAND_ECC_TYPE_HRADWARE)
spi_nand_disable_ecc(chip);
mtd->name = chip->name;
mtd->size = chip->size;
mtd->erasesize = chip->block_size;
mtd->writesize = chip->page_size;
mtd->owner = THIS_MODULE;
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
if (!mtd->ecc_strength)
mtd->ecc_strength = chip->ecc_strength ?
chip->ecc_strength : 1;
mtd->ecclayout = chip->ecclayout;
mtd->oobsize = chip->oob_size;
mtd->oobavail = chip->ecclayout->oobavail;
mtd->_erase = spi_nand_erase;
mtd->_point = NULL;
mtd->_unpoint = NULL;
mtd->_read = spi_nand_read;
mtd->_write = spi_nand_write;
mtd->_read_oob = spi_nand_read_oob;
mtd->_write_oob = spi_nand_write_oob;
mtd->_lock = NULL;
mtd->_unlock = NULL;
mtd->_block_isbad = spi_nand_block_isbad;
mtd->_block_markbad = spi_nand_block_markbad;
if (!mtd->bitflip_threshold)
mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
ret = spi_flash_register(chip);
if (ret)
return ret;
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
/* Build bad block table */
return chip->scan_bbt(mtd);
}