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