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