Files
test/drivers/source/spi_nor/sdrv_spi_nor.c
2025-11-07 20:19:23 +08:00

1906 lines
61 KiB
C

/**
* @file sdrv_spi_nor.c
* @brief spi norfash driver C code.
*
* @copyright Copyright (c) 2020 Semidrive Semiconductor.
* All rights reserved.
*/
#if !(CONFIG_DISABLE_SPIBUS_COMPILE)
#include <stdio.h>
#include <compiler.h>
#include <string.h>
#include <debug.h>
#include <param.h>
#include <sdrv_ckgen.h>
#include <sdrv_spi_nor.h>
#include <sdrv_xspi.h>
#include "udelay/udelay.h"
#include <part.h>
#define ARRAY_NUMS(array) (sizeof(array) / sizeof((array)[0]))
#define PROTO(_opcode, _dq) \
((uint32_t)(_opcode) << SNOR_OPCODE_PROTO_LSB | (_dq))
#define ID_PROTO(dummy, _dq) \
((uint32_t)(dummy) << SNOR_READID_DUMMY_LSB | (_dq))
/* Flash opcodes. */
#define SPINOR_OP_WREN 0x06 /* Write enable */
#define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_RDSR 0x05 /* Read status register */
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
#define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */
#define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_READ_1_1_8 0x8b /* Read data bytes (Octal Output SPI) */
#define SPINOR_OP_READ_1_8_8 0xcb /* Read data bytes (Octal I/O SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
#define SPINOR_OP_PP_1_1_8 0x82 /* Octal page program */
#define SPINOR_OP_PP_1_8_8 0xc2 /* Octal page program */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
#define SPINOR_OP_RDSFDP 0x5a /* Read SFDP */
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
#define SPINOR_OP_RSTEN 0x66 /* Reset Enable register */
#define SPINOR_OP_RSTMEM 0x99 /* Reset Memory */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_READ_1_1_8_4B 0x7c /* Read data bytes (Octal Output SPI) */
#define SPINOR_OP_READ_1_8_8_4B 0xcc /* Read data bytes (Octal I/O SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */
#define SPINOR_OP_PP_1_1_8_4B 0x84 /* Octal page program */
#define SPINOR_OP_PP_1_8_8_4B 0x8e /* Octal page program */
#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
/* issi giga */
#define SPINOR_OP_READ_4S_4D_4D 0xed
/* giga */
#define SPINOR_OP_PP_1_4_4_GIGA 0xc2
/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
#define SPINOR_OP_READ_1_1_1_DTR 0x0d
#define SPINOR_OP_READ_1_2_2_DTR 0xbd
#define SPINOR_OP_READ_1_4_4_DTR 0xed
#define SPINOR_OP_READ_1_1_8_DTR 0x9d
#define SPINOR_OP_READ_1_1_1_DTR_4B 0x0e
#define SPINOR_OP_READ_1_2_2_DTR_4B 0xbe
#define SPINOR_OP_READ_1_4_4_DTR_4B 0xee
#define SPINOR_OP_READ_1_8_8_DTR_4B 0xfd
#define SPINOR_ID_CAPACITY_OFFSET 2
uint8_t training_pattern[32] __ALIGNED(CONFIG_ARCH_CACHE_LINE) = {
0x44, 0x1c, 0x39, 0x05, 0xd3, 0x7a, 0x3c, 0x04,
0x16, 0x42, 0x0c, 0x8b, 0x7d, 0x12, 0x89, 0xa2,
0xb8, 0xb1, 0xf7, 0xe8, 0xb7, 0x49, 0xca, 0x1c,
0xaa, 0x9b, 0xf2, 0x7e, 0x01, 0x97, 0x60, 0x8c
};
uint8_t training_buf[32] __ALIGNED(CONFIG_ARCH_CACHE_LINE) = {0};
static int sdrv_spi_nor_general_set_4byte_addr_mode(struct spi_nor *nor,
bool enable);
static int sdrv_miron_default_init(struct spi_nor *nor);
static int sdrv_miron_octal_dtr_enable(struct spi_nor *nor, bool enable);
static int sdrv_issi_quad_enable(struct spi_nor *nor, bool enable);
static int sdrv_issi_enter_quad(struct spi_nor *nor, bool enable);
static int sdrv_issi_default_init(struct spi_nor *nor);
static int sdrv_cypress_default_init(struct spi_nor *nor);
static int sdrv_cypress_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
static int sdrv_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable);
static int sdrv_giga_enter_quad(struct spi_nor *nor, bool enable);
static int sdrv_giga_default_init(struct spi_nor *nor);
static int sdrv_giga_quad_enable(struct spi_nor *nor, bool enable);
static inline int sdrv_spibus_write_enable_locked(struct spi_nor *nor,
bool enable);
static int sdrv_spibus_wait_idle(struct spi_nor *nor);
static int sdrv_spibus_write_enable(struct spi_nor *nor, bool enable);
static int sdrv_spibus_init(struct spi_nor *nor, struct spi_nor_host *host,
const struct spi_nor_config *config);
static void sdrv_spibus_deinit(struct spi_nor *nor);
static int sdrv_spibus_read(struct spi_nor *nor, flash_addr_t addr, uint8_t *buf,
flash_size_t size);
static int sdrv_spibus_write(struct spi_nor *nor, flash_addr_t addr, const uint8_t *buf,
flash_size_t size);
static int sdrv_spibus_erase(struct spi_nor *nor, flash_addr_t addr, flash_size_t size);
static int sdrv_spibus_cancel(struct spi_nor *nor);
static void sdrv_spibus_main_function(struct spi_nor *nor);
static const struct flash_info* sdrv_spibus_get_flash_table(uint32_t *num);
flash_ops_t spi_nor_ops = {
.fls_wait_idle= sdrv_spibus_wait_idle,
.fls_write_enable= sdrv_spibus_write_enable,
.fls_init= sdrv_spibus_init,
.fls_deinit= sdrv_spibus_deinit,
.fls_read= sdrv_spibus_read,
.fls_write= sdrv_spibus_write,
.fls_erase= sdrv_spibus_erase,
.fls_cancel= sdrv_spibus_cancel,
.fls_main_function= sdrv_spibus_main_function,
.fls_get_flash_table = sdrv_spibus_get_flash_table,
};
static struct flash_info default_spi_nor_ids = {
.name = "default",
.flash_id = {0x00, 0x00},
.read_proto = PROTO(SPINOR_OP_READ_FAST, SNOR_PROTO_1_1_1),
.write_proto = PROTO(SPINOR_OP_PP, SNOR_PROTO_1_1_1),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_1),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_1), // erase 4Kb, 0: nonsupport
0, // erase 32Kb, 0: nonsupport
0, // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 8,
.status_dummy = 0,
.page_size = 256,
};
/* fill flash info, according to different flash type */
static struct flash_info spi_nor_ids[] = {
/* miron */
{
.name = "mt35xu",
.flash_id = {0x2c, 0x5b},
.read_proto = PROTO(SPINOR_OP_READ_1_8_8, SNOR_PROTO_8_8_8_DTR),
.write_proto = PROTO(SPINOR_OP_PP_1_8_8, SNOR_PROTO_8_8_8_DTR),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_8_8_8_DTR), // erase 32Kb, 0: nonsupport
0, // erase 64Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_8_8_8_DTR), // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 20,
.status_dummy = 8,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.octal_dtr_enable = sdrv_miron_octal_dtr_enable,
.default_init = sdrv_miron_default_init
},
/* miron */
{
.name = "mt35xl",
.flash_id = {0x2c, 0x5a},
.read_proto = PROTO(SPINOR_OP_READ_1_8_8, SNOR_PROTO_8_8_8_DTR),
.write_proto = PROTO(SPINOR_OP_PP_1_8_8, SNOR_PROTO_8_8_8_DTR),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_8_8_8_DTR), // erase 32Kb, 0: nonsupport
0, // erase 64Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_8_8_8_DTR), // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 20,
.status_dummy = 8,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.octal_dtr_enable = sdrv_miron_octal_dtr_enable,
.default_init = sdrv_miron_default_init
},
/* issi */
{
.name = "is25wp",
.flash_id = {0x9d, 0x70},
.read_proto = PROTO(SPINOR_OP_READ_1_4_4, SNOR_PROTO_4_4_4),
.write_proto = PROTO(SPINOR_OP_PP, SNOR_PROTO_4_4_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_4_4_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_4_4_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 14,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.quad_enable = sdrv_issi_quad_enable,
.enter_quad = sdrv_issi_enter_quad,
.default_init = sdrv_issi_default_init
},
{
.name = "is25lp",
.flash_id = {0x9d, 0x60},
.read_proto = PROTO(SPINOR_OP_READ_1_4_4, SNOR_PROTO_4_4_4),
.write_proto = PROTO(SPINOR_OP_PP, SNOR_PROTO_4_4_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_4_4_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_4_4_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 14,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.quad_enable = sdrv_issi_quad_enable,
.enter_quad = sdrv_issi_enter_quad,
.default_init = sdrv_issi_default_init
},
/* giga */
{
.name = "gd25lb",
.flash_id = {0xc8, 0x67},
.read_proto = PROTO(SPINOR_OP_READ_1_1_4, SNOR_PROTO_4_4_4),
.write_proto = PROTO(SPINOR_OP_PP_1_4_4_GIGA, SNOR_PROTO_4_4_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_4_4_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_4_4_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 10,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.enter_quad = sdrv_giga_enter_quad,
.default_init = sdrv_giga_default_init
},
{
.name = "gd25q",
.flash_id = {0xc8, 0x40},
.read_proto = PROTO(SPINOR_OP_READ_1_1_4, SNOR_PROTO_1_1_4),
.write_proto = PROTO(SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_1_1_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_1_1_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 8,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.quad_enable = sdrv_giga_quad_enable,
},
{
.name = "gd25wq",
.flash_id = {0xc8, 0x65},
.read_proto = PROTO(SPINOR_OP_READ_1_1_4, SNOR_PROTO_1_1_4),
.write_proto = PROTO(SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_1_1_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_1_1_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 8,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.quad_enable = sdrv_giga_quad_enable,
},
{
.name = "gd25f",
.flash_id = {0xc8, 0x43},
.read_proto = PROTO(SPINOR_OP_READ_1_1_4, SNOR_PROTO_1_1_4),
.write_proto = PROTO(SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_1_1_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_1_1_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 8,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.quad_enable = sdrv_giga_quad_enable,
},
{
.name = "gd25lx",
.flash_id = {0xc8, 0x68},
.read_proto = PROTO(SPINOR_OP_READ_1_8_8, SNOR_PROTO_8_8_8_DTR),
.write_proto = PROTO(SPINOR_OP_PP_1_8_8, SNOR_PROTO_8_8_8_DTR),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_8_8_8_DTR), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_8_8_8_DTR), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 14,
.status_dummy = 8,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.default_init = sdrv_miron_default_init,
.octal_dtr_enable = sdrv_miron_octal_dtr_enable,
},
{
.name = "gd25x",
.flash_id = {0xc8, 0x48},
.read_proto = PROTO(SPINOR_OP_READ_1_8_8, SNOR_PROTO_8_8_8_DTR),
.write_proto = PROTO(SPINOR_OP_PP_1_8_8, SNOR_PROTO_8_8_8_DTR),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_8_8_8_DTR), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_8_8_8_DTR), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 14,
.status_dummy = 8,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.default_init = sdrv_miron_default_init,
.octal_dtr_enable = sdrv_miron_octal_dtr_enable,
},
{
.name = "gd25b",
.flash_id = {0xc8, 0x47},
.read_proto = PROTO(SPINOR_OP_READ_1_1_4, SNOR_PROTO_4_4_4),
.write_proto = PROTO(SPINOR_OP_PP_1_4_4_GIGA, SNOR_PROTO_4_4_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_4_4_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_8_8_8_DTR), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_8_8_8_DTR), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 10,
.status_dummy = 0,
.page_size = 256,
.set_4byte_addr_mode = sdrv_spi_nor_general_set_4byte_addr_mode,
.enter_quad = sdrv_giga_enter_quad,
.default_init = sdrv_giga_default_init
},
{
.name = "w25q",
.flash_id = {0xef, 0x40},
.read_proto = PROTO(SPINOR_OP_READ_1_1_4, SNOR_PROTO_1_1_4),
.write_proto = PROTO(SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4),
.erase_proto = PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K, SNOR_PROTO_1_1_4), // erase 4Kb, 0: nonsupport
PROTO(SPINOR_OP_BE_32K, SNOR_PROTO_1_1_4), // erase 32Kb, 0: nonsupport
PROTO(SPINOR_OP_SE, SNOR_PROTO_1_1_4), // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
0, // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_4K_SIZE,
.read_dummy = 8,
.status_dummy = 0,
.page_size = 256,
.quad_enable = sdrv_giga_quad_enable,
},
{
.name = "S28hl",
.flash_id = {0x34, 0x5a},
.read_proto = PROTO(SPINOR_OP_READ_1_4_4_DTR_4B, SNOR_PROTO_8_8_8_DTR),
.write_proto = PROTO(SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR),
.erase_proto = PROTO(SPINOR_OP_SE_4B, SNOR_PROTO_8_8_8_DTR),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K_4B, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
0, // erase 32Kb, 0: nonsupport
0, // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
PROTO(SPINOR_OP_SE_4B, SNOR_PROTO_8_8_8_DTR), // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_256K_SIZE,
.read_dummy = 23,
.status_dummy = 8,
.page_size = 256,
.set_4byte_addr_mode = sdrv_cypress_set_4byte_addr_mode,
.default_init = sdrv_cypress_default_init,
.octal_dtr_enable = sdrv_cypress_octal_dtr_enable,
},
{
.name = "S28hs",
.flash_id = {0x34, 0x5b},
.read_proto = PROTO(SPINOR_OP_READ_1_4_4_DTR_4B, SNOR_PROTO_8_8_8_DTR),
.write_proto = PROTO(SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR),
.erase_proto = PROTO(SPINOR_OP_SE_4B, SNOR_PROTO_8_8_8_DTR),
.erase_proto_list = {
PROTO(SPINOR_OP_BE_4K_4B, SNOR_PROTO_8_8_8_DTR), // erase 4Kb, 0: nonsupport
0, // erase 32Kb, 0: nonsupport
0, // erase 64Kb, 0: nonsupport
0, // erase 128Kb, 0: nonsupport
PROTO(SPINOR_OP_SE_4B, SNOR_PROTO_8_8_8_DTR), // erase 256Kb, 0: nonsupport
},
.sector_size = SPINOR_SECTOR_256K_SIZE,
.read_dummy = 23,
.status_dummy = 8,
.page_size = 256,
.set_4byte_addr_mode = sdrv_cypress_set_4byte_addr_mode,
.default_init = sdrv_cypress_default_init,
.octal_dtr_enable = sdrv_cypress_octal_dtr_enable,
},
};
/**
* @brief spi nor read register
* @param[in] flash_handle spi norflash instance contex handle.
* @param[in] cmd spinor cmd ptr
* @param[in] addr flash read address
* @param[in] buf read to buf addr
* @param[in] length read size
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_reg_read(struct spi_nor *flash_handle,
struct spi_nor_cmd *cmd, flash_addr_t addr,
uint8_t *buf, flash_size_t length)
{
int ret;
flash_handle->host->ops->prepare(flash_handle, SPI_NOR_OPS_LOCK);
ret = flash_handle->host->ops->reg_read(flash_handle, cmd, addr, buf, length);
flash_handle->host->ops->unprepare(flash_handle, SPI_NOR_OPS_LOCK);
return ret;
}
/**
* @brief spi nor write register
* @param[in] flash_handle spi norflash instance contex handle.
* @param[in] cmd spinor cmd ptr
* @param[in] addr flash read address
* @param[in] buf write from buf addr
* @param[in] length write size
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_reg_write(struct spi_nor *flash_handle,
struct spi_nor_cmd *cmd, flash_addr_t addr,
uint8_t *buf, flash_size_t length)
{
int ret;
flash_handle->host->ops->prepare(flash_handle, SPI_NOR_OPS_LOCK);
ret = flash_handle->host->ops->reg_write(flash_handle, cmd, addr, buf, length);
flash_handle->host->ops->unprepare(flash_handle, SPI_NOR_OPS_LOCK);
return ret;
}
/**
* @brief spi nor get flash current status
* @param[in] nor spi norflash ptr.
* @param[out] status get spinor current status
* @return spinor current status
*/
static int sdrv_spi_nor_get_status(struct spi_nor *nor, uint8_t *status)
{
uint8_t reg[2] = {0};
int ret;
struct spi_nor_cmd read_cmd = {
.opcode = SPINOR_OP_RDSR,
.addr_bytes = 0,
.dummy = nor->info.status_dummy,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
};
ret = nor->host->ops->reg_read(nor, &read_cmd, 0, reg, 2);
if (ret) {
return ret;
}
*status = reg[0];
return 0;
}
static int sdrv_spibus_write_enable(struct spi_nor *nor, bool enable)
{
int ret;
struct spi_nor_cmd wr_en_cmd = {
.opcode = enable ? SPINOR_OP_WREN : SPINOR_OP_WRDI,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = nor->host->ops->reg_write(nor, &wr_en_cmd, 0, 0, 0);
return SPI_NOR_ERR_STATUS(SPI_NOR_COMMON, ret);
}
/**
* @brief spinor write enable,use it before write flash
* @param[in] nor spi norflash ptr.
* @param[out] enable write enable not not
* @return int
* @retval 0: success
* @retval other: failed
*/
static inline int sdrv_spibus_write_enable_locked(struct spi_nor *nor,
bool enable)
{
int ret;
nor->host->ops->prepare(nor, SPI_NOR_OPS_LOCK);
ret = sdrv_spibus_write_enable(nor, enable);
nor->host->ops->unprepare(nor, SPI_NOR_OPS_LOCK);
return ret;
}
/**
* @brief wait for the flash to be idle
* @param[in] nor spi norflash ptr.
* @return int
* @retval 0: success
* @retval other: failed
*/
static inline int sdrv_spibus_wait_idle_locked(struct spi_nor *nor)
{
int ret;
nor->host->ops->prepare(nor, SPI_NOR_OPS_LOCK);
ret = sdrv_spibus_wait_idle(nor);
nor->host->ops->unprepare(nor, SPI_NOR_OPS_LOCK);
return ret;
}
/* wait flash entry idle status in 3 seconds */
static int sdrv_spibus_wait_idle(struct spi_nor *nor)
{
int ret = SPI_NOR_OK_STATUS;
uint8_t flash_status;
uint32_t tick_count = 0;
uint32_t parallel_count = 0;
while (1) {
ret = sdrv_spi_nor_get_status(nor, &flash_status);
if (ret) {
if ((nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE)
&& (parallel_count < 10000)) {
parallel_count++;
continue;
} else {
ret = SPI_NOR_UNREACHABLE;
ssdk_printf(SSDK_CRIT,
"spi_nor get flash status failed, ret: %d!\r\n", ret);
break;
}
}
ssdk_printf(SSDK_INFO, "flash_status = 0x%x \r\n", flash_status);
if (!(flash_status & BIT(0)))
break;
if (tick_count > 3000) {
ret = SPI_NOR_TIMEOUT;
ssdk_printf(SSDK_CRIT, "wait flash idle timeout, ret = %d!\r\n", ret);
break;
}
udelay(1000u);
tick_count++;
}
return SPI_NOR_ERR_STATUS(SPI_NOR_COMMON, ret);
}
/**
* @brief spi nor flash soft reset
* @param[in] nor spi norflash ptr.
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_soft_reset(struct spi_nor *nor)
{
int ret = 0;
struct spi_nor_cmd rst_en_cmd = {
.opcode = SPINOR_OP_RSTEN,
.dummy = 0,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
};
struct spi_nor_cmd rst_mem_cmd = {
.opcode = SPINOR_OP_RSTMEM,
.dummy = 0,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
};
ret = sdrv_spi_nor_reg_write(nor, &rst_en_cmd, 0, 0, 0);
if (ret) {
return ret;
}
ret = sdrv_spi_nor_reg_write(nor, &rst_mem_cmd, 0, 0, 0);
return ret;
}
/**
* @brief spi nor read device info
* 1.use SNOR_PROTO_1_1_1 or
* SNOR_PROTO_2_2_2 or SNOR_PROTO_4_4_4 or SNOR_PROTO_8_8_8_DTR mode
* try to read device id
* 2.read flash capacity
* @param[in] nor spi norflash ptr.
* @retval 0: success
* @retval !0: failed
*/
static int sdrv_spi_nor_id_read(struct spi_nor *nor)
{
int ret = 0;
uint8_t id[8];
struct flash_info *info;
uint32_t reg_lans_list[] = {ID_PROTO(0, SNOR_PROTO_1_1_1), ID_PROTO(0, SNOR_PROTO_2_2_2),
ID_PROTO(0, SNOR_PROTO_4_4_4), ID_PROTO(8, SNOR_PROTO_8_8_8_DTR)
};
struct spi_nor_cmd read_cmd = {
.opcode = SPINOR_OP_RDID,
.dummy = reg_lans_list[0] >> SNOR_READID_DUMMY_LSB,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(reg_lans_list[0]),
};
for (int i = 0; i < ARRAY_SIZE(reg_lans_list); i++) {
read_cmd.dummy = (reg_lans_list[i] >> SNOR_READID_DUMMY_LSB) & 0xff;
read_cmd.inst_type = SNOR_INST_LANS(reg_lans_list[i]);
nor->reg_proto = reg_lans_list[i] & SNOR_PROTO_MASK;
if (reg_lans_list[i] & SNOR_DTR_PROTO) {
nor->octal_dtr_en = 1;
nor->addr_width = 4;
}
ssdk_printf(SSDK_NOTICE, "octal_dtr_en: %d, dummy = %d, inst_type = %d, \
proto = %x\r\n", nor->octal_dtr_en, read_cmd.dummy, read_cmd.inst_type,
nor->reg_proto);
ret = sdrv_spi_nor_reg_read(nor, &read_cmd, 0, id, 4);
if (ret) return -1;
ssdk_printf(SSDK_CRIT, "norflash id0: %x, id1: %x, id2: %x\r\n", id[0], id[1],
id[2]);
for (uint32_t i = 0; i < ARRAY_NUMS(spi_nor_ids); i++) {
info = &spi_nor_ids[i];
if (!memcmp(info->flash_id, id, 2)) {
memcpy(&(nor->info), info, sizeof(struct flash_info));
nor->info.size = 1 << id[SPINOR_ID_CAPACITY_OFFSET];
return 0;
}
}
}
return -1;
}
/**
* @brief spi nor flash octal dtr enable,if device support octal dtr mode and enable it
* @param[in] nor spi norflash ptr.
* @param[in] enable spi norflash octal dtr enable or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
int ret;
if (!nor->info.octal_dtr_enable) {
return 0;
}
if (!(SNOR_PROTO_DTR(nor->info.read_proto) == SNOR_PROTO_8_8_8_DTR &&
SNOR_PROTO_DTR(nor->info.write_proto) == SNOR_PROTO_8_8_8_DTR)) {
return 0;
}
ret = nor->info.octal_dtr_enable(nor, enable);
if (ret) {
return ret;
}
if (enable) {
nor->octal_dtr_en = 1;
nor->dqs_en = 1;
nor->reg_proto = SNOR_PROTO_8_8_8_DTR;
nor->addr_width = 4;
}
else {
nor->octal_dtr_en = 0;
nor->dqs_en = 0;
nor->reg_proto = SNOR_PROTO_1_1_1;
nor->addr_width = 3;
}
return ret;
}
/**
* @brief spi nor flash quad enable,according to read/write data lans and enable it or not
* @param[in] nor spi norflash ptr.
* @param[in] enable spi norflash quad enable or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_quad_enable(struct spi_nor *nor, bool enable)
{
int ret;
if (!nor->info.quad_enable) {
return 0;
}
if (!(SNOR_DATA_LANS(nor->info.read_proto) == 2 ||
SNOR_DATA_LANS(nor->info.write_proto) == 2)) {
return 0;
}
ret = nor->info.quad_enable(nor, enable);
return ret;
}
/**
* @brief spi nor flash qpi enable,according to read/write data lans and enable it or not
* @param[in] nor spi norflash ptr.
* @param[in] enable spi norflash qpi enable or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_enter_quad(struct spi_nor *nor, bool enable)
{
int ret;
if (!nor->info.enter_quad) {
return 0;
}
if (SNOR_PROTO(nor->info.read_proto) != SNOR_PROTO_4_4_4 ||
SNOR_PROTO(nor->info.write_proto) != SNOR_PROTO_4_4_4) {
return 0;
}
ret = nor->info.enter_quad(nor, enable);
if (ret) {
return ret;
}
nor->reg_proto = enable ? SNOR_PROTO_4_4_4 : SNOR_PROTO_1_1_1;
return ret;
}
/**
* @brief spi nor flash enable qpi 4byte mode ,according to read flash capacity and enable it or not, if more than 16MB, set 4 byte mode
* @param[in] nor spi norflash ptr.
* @param[in] enable spi norflash qpi enable or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_set_4byte(struct spi_nor *nor, bool enable)
{
int ret;
if (!nor->info.set_4byte_addr_mode ||
nor->info.size <= 0x1000000) {
return 0;
}
ret = nor->info.set_4byte_addr_mode(nor, enable);
if (ret) {
return ret;
}
nor->addr_width = enable ? 4 : 3;
return ret;
}
/**
* @brief entry function for Autofill information.
* 1. auto fill flash info
* @param[in] spi nor ptr.
* @return void
*/
static void sdrv_spibus_auto_fill(struct spi_nor *nor)
{
uint32_t i;
// auto fill flash info
memset(nor->info.erase_map, 0, sizeof(nor->info.erase_map));
for (i = 0; i < SPI_NOR_SECTOR_TYPE_MAX; i++) {
if (!(nor->info.erase_proto_list[i])) {
continue; // skip nonsupport proto
}
nor->info.erase_map[i].erase_proto = nor->info.erase_proto_list[i];
nor->info.erase_map[i].erase_size = SECTOR_TYPE_TO_SIZE(i);
nor->info.erase_map[i].post = SECTOR_TYPE_TO_POST(i);
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
nor->info.erase_map[i].erase_size <<= 1u;
nor->info.erase_map[i].post++;
}
}
}
/**
* @brief entry function for init spi norflash.
* 1. read flash id info
* 2. soft reset or not
* 3. according to different flash info filled and use callback func
* 4. spi norflash read training
* @param[in] spi nor ptr.
* @param[in] spi nor_host config ptr.
* @param[in] spi nor config config.
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spibus_init(struct spi_nor *nor, struct spi_nor_host *host,
const struct spi_nor_config *config)
{
int ret = SPI_NOR_OK_STATUS;
uint32_t training_len = 32;
flash_addr_t training_addr;
if (config->cs >= SWITCH_DEVICE_MAX_NUM) {
ssdk_printf(SSDK_ERR, "flash cs%d out of scope\r\n", config->cs);
return SPI_NOR_ERR_STATUS(SPI_NOR_INIT, SPI_NOR_FAIL);
}
memset(nor, 0, sizeof(struct spi_nor));
for (uint8_t i = 0; i < config->cs; i++) {
if (host->nor_tab[i]) {
nor->offset_address += host->nor_tab[i]->info.size;
} else {
ssdk_printf(SSDK_ERR, "flash cs%d need init firstly\r\n", i);
return SPI_NOR_ERR_STATUS(SPI_NOR_INIT, SPI_NOR_FAIL);
}
}
nor->ops = &spi_nor_ops;
nor->id = config->id;
nor->cs = config->cs;
nor->baudrate = config->baudrate;
nor->xfer_mode = config->xfer_mode;
nor->dev_mode = config->dev_mode;
nor->host = host;
nor->sw_rst = config->sw_rst;
nor->hyperbus_mode = false;
nor->reg_proto = SNOR_PROTO_1_1_1;
nor->addr_width = 3;
nor->octal_dtr_en = 0;
nor->dqs_en = 0;
memcpy(&(nor->info), &default_spi_nor_ids, sizeof(struct flash_info));
if (host->xip_mode) {
nor->host->ops->xip_proto_setup(nor);
} else {
ret = sdrv_spi_nor_id_read(nor);
if (ret) {
return SPI_NOR_ERR_STATUS(SPI_NOR_INIT, SPI_NOR_FAIL);
}
}
sdrv_spibus_auto_fill(nor);
ssdk_printf(SSDK_NOTICE, "flash size: %llx\r\n", nor->info.size);
if (!host->xip_mode) {
if (nor->sw_rst) {
sdrv_spi_nor_soft_reset(nor);
/* 1ms delay */
udelay(1000u);
nor->octal_dtr_en = 0;
nor->reg_proto = SNOR_PROTO_1_1_1;
nor->addr_width = 3;
nor->dqs_en = 0;
}
/* set dummy ... */
if (nor->info.default_init) {
nor->info.default_init(nor);
}
/* if more than 16MB, set 4 byte mode */
sdrv_spi_nor_set_4byte(nor, true);
if (config->force_size) {
nor->info.size = config->force_size;
}
if (nor->dev_mode == SPI_NOR_DEV_PARALLEL_MODE) {
nor->info.sector_size *= 2u;
nor->info.size *= 2u;
}
sdrv_spi_nor_octal_dtr_enable(nor, true);
sdrv_spi_nor_quad_enable(nor, true);
sdrv_spi_nor_enter_quad(nor, true);
if (nor->host->ops->training) {
training_addr = nor->info.sector_size;
sdrv_spibus_read(nor, training_addr, training_buf, 32);
if (0u != memcmp(training_pattern, training_buf, training_len)) {
if (sdrv_spibus_erase(nor, training_addr, nor->info.sector_size)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_INIT, SPI_NOR_UNREACHABLE);
}
if (sdrv_spibus_write(nor, training_addr, training_pattern, training_len)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_INIT, SPI_NOR_UNREACHABLE);
}
}
if (nor->host->clk != NULL) {
sdrv_ckgen_set_rate(nor->host->clk, nor->host->ref_clk_hz);
ssdk_printf(SSDK_CRIT, "spinor host clock rate is %u!\r\n",
sdrv_ckgen_get_rate(nor->host->clk));
}
ret = nor->host->ops->training(nor, training_addr, training_buf,
training_pattern, training_len);
if (ret) {
ret = SPI_NOR_INIT_TRAIN_E;
ssdk_printf(SSDK_CRIT, "spinor training failed!!");
}
}
/* Don't use async mode for training */
nor->async_mode = config->async_mode;
}
// bind nor to host
if (!ret) {
host->total_size += nor->info.size;
host->nor_tab[nor->cs] = nor;
}
return SPI_NOR_ERR_STATUS(SPI_NOR_INIT, ret);
}
static const struct flash_info* sdrv_spibus_get_flash_table(uint32_t *num) {
*num = ARRAY_NUMS(spi_nor_ids);
return spi_nor_ids;
}
/**
* @brief record the information, and record tansfer not complete
* @param[in] nor spi norflash ptr
* @param[in] opt record flash_opt
* @param[in] addr record flash address
* @param[in] buf record buf addr
* @param[in] size record size
*/
static void inline sdrv_spi_nor_setup_xfer(struct spi_nor *nor,
enum flash_opt opt,
flash_addr_t addr, uint8_t *buf,
flash_size_t size)
{
nor->xfer_info.opt_type = opt;
nor->xfer_info.addr = addr;
nor->xfer_info.buf = buf;
nor->xfer_info.size = size;
nor->xfer_info.opt_result = FLASH_OPT_PENDING;
ssdk_printf(SSDK_INFO,
"spinor setup xfer: type = %d, addr = 0x%llx, size = %lld\r\n", opt, addr, size);
}
/**
* @brief record the information, and record tansfer complete
* @param[in] nor spi norflash ptr
*/
static void inline sdrv_spi_nor_xfer_comp(struct spi_nor *nor)
{
nor->xfer_info.size = 0;
nor->xfer_info.opt_result = FLASH_OPT_COMPLETE;
nor->host->ops->unprepare(nor, (enum spi_nor_ops)nor->xfer_info.opt_type);
nor->xfer_info.opt_type = FLASH_OPT_NONE;
}
/**
* @brief record the information, and record tansfer failed
* @param[in] nor spi norflash ptr
*/
static void inline sdrv_spi_nor_xfer_error(struct spi_nor *nor)
{
nor->xfer_info.size = 0;
nor->xfer_info.opt_result = FLASH_OPT_FAILED;
nor->host->ops->unprepare(nor, (enum spi_nor_ops)nor->xfer_info.opt_type);
nor->xfer_info.opt_type = FLASH_OPT_NONE;
ssdk_printf(SSDK_CRIT, "spi_nor xfer failed\n");
}
static int sdrv_spibus_read(struct spi_nor *nor, flash_addr_t addr, uint8_t *buf,
flash_size_t size)
{
int ret;
if (!IS_ALIGNED(addr, 4) || !IS_ALIGNED(buf, 4)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_READ, SPI_NOR_ADDRESS_INVALID);
}
nor->host->ops->prepare(nor, SPI_NOR_OPS_READ);
sdrv_spi_nor_setup_xfer(nor, FLASH_OPT_READ, addr, buf, size);
ret = nor->host->ops->read(nor, addr, buf, size);
if (ret) {
sdrv_spi_nor_xfer_error(nor);
ret = SPI_NOR_FAIL;
}
else if (!nor->async_mode) {
sdrv_spi_nor_xfer_comp(nor);
}
return SPI_NOR_ERR_STATUS(SPI_NOR_READ, ret);
}
static int sdrv_spibus_write(struct spi_nor *nor, flash_addr_t addr,
const uint8_t *buf,
flash_size_t size)
{
int ret;
if (!IS_ALIGNED(addr, 4) || !IS_ALIGNED(buf, 4)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_WRITE, SPI_NOR_ADDRESS_INVALID);
} else if (!IS_ALIGNED(size, 4)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_WRITE, SPI_NOR_LENGTH_INVALID);
}
nor->host->ops->prepare(nor, SPI_NOR_OPS_WRITE);
sdrv_spi_nor_setup_xfer(nor, FLASH_OPT_WRITE, addr, (uint8_t *)buf, size);
ret = nor->host->ops->write(nor, addr, buf, size);
if (ret) {
sdrv_spi_nor_xfer_error(nor);
ret = SPI_NOR_FAIL;
}
else if (!nor->async_mode) {
sdrv_spi_nor_xfer_comp(nor);
}
return SPI_NOR_ERR_STATUS(SPI_NOR_WRITE, ret);
}
#ifdef CONFIG_NORFLASH_TESTER
static uint32_t erase_count[SPI_NOR_SECTOR_TYPE_MAX];
#endif
/* the arguments erase length and dst address must 4K alined */
static int sdrv_spibus_erase(struct spi_nor *nor, flash_addr_t addr,
flash_size_t size)
{
int ret = SPI_NOR_OK_STATUS;
if (!IS_ALIGNED(addr, nor->info.sector_size)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_ERASE, SPI_NOR_ADDRESS_INVALID);
} else if (!IS_ALIGNED(size, nor->info.sector_size)) {
return SPI_NOR_ERR_STATUS(SPI_NOR_ERASE, SPI_NOR_LENGTH_INVALID);
}
#ifdef CONFIG_NORFLASH_TESTER
memset(erase_count, 0, sizeof(erase_count));
#endif
nor->host->ops->prepare(nor, SPI_NOR_OPS_ERASE);
sdrv_spi_nor_setup_xfer(nor, FLASH_OPT_ERASE, addr, NULL, size);
if (!nor->async_mode) {
while (nor->xfer_info.size) {
int i;
uint32_t count = 0;
uint32_t prev_index = SPI_NOR_SECTOR_TYPE_MAX;
for (i = SPI_NOR_SECTOR_TYPE_MAX - 1; i >= 0; i--) {
if (!(nor->info.erase_proto_list[i])) {
continue; // skip nonsupport proto
}else if (nor->xfer_info.size < nor->info.erase_map[i].erase_size) {
continue; // skip oversize proto
} else if (!IS_ALIGNED(nor->xfer_info.addr, nor->info.erase_map[i].erase_size)) {
prev_index = i;
continue; // skip unaligned proto
} else if (prev_index != SPI_NOR_SECTOR_TYPE_MAX) {
count = (MIN(ROUNDUP(nor->xfer_info.addr, nor->info.erase_map[prev_index].erase_size)
- nor->xfer_info.addr, nor->xfer_info.size)) >> nor->info.erase_map[i].post;
} else {
count = nor->xfer_info.size >> nor->info.erase_map[i].post;
}
if (count) {
break;
}
}
#ifdef CONFIG_NORFLASH_TESTER
erase_count[i] += count;
ssdk_printf(SSDK_EMERG, "[SYNC] 4k: %u, 32k: %u, 64k: %u, 128k: %u, 256k: %u\r\n",
erase_count[SPI_NOR_SECTOR_4KB],
erase_count[SPI_NOR_SECTOR_32KB],
erase_count[SPI_NOR_SECTOR_64KB],
erase_count[SPI_NOR_SECTOR_128KB],
erase_count[SPI_NOR_SECTOR_256KB]);
#endif
while (count) {
struct spi_nor_erase_cmd erase_cmd = {
.addr = nor->xfer_info.addr,
.map = &nor->info.erase_map[i],
};
ret = nor->host->ops->complex_erase(nor, &erase_cmd);
if (ret) {
ret = SPI_NOR_FAIL;
break;
}
/* wait for flash idle */
ret = sdrv_spibus_wait_idle(nor);
if (ret) {
ret = SPI_NOR_TIMEOUT;
break;
}
nor->xfer_info.addr += nor->info.erase_map[i].erase_size;
nor->xfer_info.size -= nor->info.erase_map[i].erase_size;
count--;
}
if (count || (i < 0)) {
if (!ret) {
ret = SPI_NOR_FAIL;
}
break; // Something went wrong, exit
}
}
if (ret) {
sdrv_spi_nor_xfer_error(nor);
}
else {
sdrv_spi_nor_xfer_comp(nor);
}
}
return SPI_NOR_ERR_STATUS(SPI_NOR_ERASE, ret);
}
static int sdrv_spibus_cancel(struct spi_nor *nor)
{
int ret = SPI_NOR_OK_STATUS;
if (nor->host->ops->cancel) {
ret = nor->host->ops->cancel(nor);
if (ret) {
ret = SPI_NOR_FAIL;
}
} else {
ret = SPI_NOR_UNSUPPORT;
}
return SPI_NOR_ERR_STATUS(SPI_NOR_CANCEL, ret);
}
/**
* @brief spi nor erase flash use polling mode
* @param[in] nor spi norflash ptr
*/
static void sdrv_spibus_erase_polling(struct spi_nor *nor)
{
int ret = 0;
uint8_t flash_status;
uint32_t sector_size = nor->info.sector_size;
if (nor->xfer_info.size != 0) {
ret = sdrv_spi_nor_get_status(nor, &flash_status);
if (ret) {
sdrv_spi_nor_xfer_error(nor);
}
else if (!(flash_status & BIT(0))) {
uint32_t count = 0;
int i;
for (i = SPI_NOR_SECTOR_TYPE_MAX - 1; i >= 0; i--) {
if (!(nor->info.erase_proto_list[i]) ||
!IS_ALIGNED(nor->xfer_info.addr, nor->info.erase_map[i].erase_size)) {
continue; // skip nonsupport&unaligned proto
}
count = nor->xfer_info.size >> nor->info.erase_map[i].post;
if (count) {
sector_size = nor->info.erase_map[i].erase_size;
break;
}
}
if (count) {
struct spi_nor_erase_cmd erase_cmd = {
.addr = nor->xfer_info.addr,
.map = &nor->info.erase_map[i],
};
ret = nor->host->ops->complex_erase(nor, &erase_cmd);
if (ret) {
sdrv_spi_nor_xfer_error(nor);
} else {
#ifdef CONFIG_NORFLASH_TESTER
erase_count[i]++;
ssdk_printf(SSDK_EMERG, "[SYNC] 4k: %u, 32k: %u, 64k: %u, 128k: %u, 256k: %u\r\n",
erase_count[SPI_NOR_SECTOR_4KB],
erase_count[SPI_NOR_SECTOR_32KB],
erase_count[SPI_NOR_SECTOR_64KB],
erase_count[SPI_NOR_SECTOR_128KB],
erase_count[SPI_NOR_SECTOR_256KB]);
#endif
nor->xfer_info.size -= sector_size;
nor->xfer_info.addr += sector_size;
}
} else {
sdrv_spi_nor_xfer_error(nor);
}
}
}
if (nor->xfer_info.size == 0) {
ret = sdrv_spi_nor_get_status(nor, &flash_status);
if (ret) {
sdrv_spi_nor_xfer_error(nor);
}
else if (!(flash_status & BIT(0))) {
sdrv_spi_nor_xfer_comp(nor);
}
}
}
extern void sdrv_spi_nor_drv_main_function(struct spi_nor *nor);
static void sdrv_spibus_main_function(struct spi_nor *nor)
{
if (!nor->async_mode)
return;
if (nor->xfer_info.opt_result == FLASH_OPT_PENDING) {
if (nor->xfer_info.opt_type != FLASH_OPT_ERASE) {
/* spi nor host polling handler */
sdrv_spi_nor_drv_main_function(nor);
if (nor->xfer_info.opt_result != FLASH_OPT_PENDING) {
nor->host->ops->unprepare(nor, (enum spi_nor_ops)nor->xfer_info.opt_type);
nor->xfer_info.opt_type = FLASH_OPT_NONE;
}
}
else {
sdrv_spibus_erase_polling(nor);
}
}
else {
if (nor->xfer_info.opt_type) {
nor->host->ops->unprepare(nor, (enum spi_nor_ops)nor->xfer_info.opt_type);
nor->xfer_info.opt_type = FLASH_OPT_NONE;
}
}
}
extern void sdrv_spi_nor_drv_deinit(struct spi_nor *nor);
int debugret = 0;
static void sdrv_spibus_deinit(struct spi_nor *nor)
{
if (nor->sw_rst) {
debugret = sdrv_spi_nor_soft_reset(nor);
udelay(1000u);
ssdk_printf(SSDK_INFO,"debugret: 0x%x \r\n", debugret);
}
ssdk_printf(SSDK_INFO,"REdebugret: 0x%x \r\n", debugret);
sdrv_spi_nor_drv_deinit(nor);
}
/**
* @brief spi nor set 4byte addr mode
* @param[in] nor spi norflash ptr
* @param[in] enable spi norflash 4byte mode enable or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_spi_nor_general_set_4byte_addr_mode(struct spi_nor *nor,
bool enable)
{
int ret;
struct spi_nor_cmd byte_cmd = {
.opcode = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = sdrv_spi_nor_reg_write(nor, &byte_cmd, 0, 0, 0);
return ret;
}
/**
* @brief set read dummy for miron spi norflash
* @param[in] nor spi norflash ptr
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_miron_default_init(struct spi_nor *nor)
{
int ret;
uint8_t data[2];
struct spi_nor_cmd byte_cmd = {
.opcode = 0x81,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] = nor->info.read_dummy;
ret = sdrv_spi_nor_reg_write(nor, &byte_cmd, 1, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
struct spi_nor_cmd read_cmd = {
.opcode = 0x85,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 8,
};
ret = sdrv_spi_nor_reg_read(nor, &read_cmd, 1, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret || data[0] != nor->info.read_dummy) {
ssdk_printf(SSDK_EMERG, "dummy: %d, %d\r\n", nor->info.read_dummy, data[0]);
return -1;
}
return ret;
}
/**
* @brief set read dummy for issi spi norflash
* @param[in] nor spi norflash ptr
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_issi_default_init(struct spi_nor *nor)
{
int ret;
uint8_t data[2];
struct spi_nor_cmd byte_cmd = {
.opcode = 0xc0,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] = nor->info.read_dummy << 3;
#ifdef CONFIG_IS25LP064A_DUMMY_FIXUP
if (CONFIG_IS25LP064A_DUMMY_FIXUP & (1ul << nor->id)) {
/* Fix up for IS25LP064A. Refer to descriptions in part.h */
nor->info.read_dummy = 10;
data[0] = 0x3 << 3;
}
#endif
ret = sdrv_spi_nor_reg_write(nor, &byte_cmd, 1, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
return ret;
}
/**
* @brief spi nor set octal dtr mode for miron spi norflash
* @param[in] nor spi norflash ptr
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_miron_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
int ret;
uint8_t data[3];
struct spi_nor_cmd byte_cmd = {
.opcode = 0x81,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] = enable ? 0xe7 : 0xff;
ret = sdrv_spi_nor_reg_write(nor, &byte_cmd, 0, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (enable) {
nor->octal_dtr_en = 1;
nor->dqs_en = 1;
struct spi_nor_cmd read_cmd = {
.opcode = 0x85,
.addr_bytes = 4,
.inst_type = 3,
.dummy = 8,
};
ret = sdrv_spi_nor_reg_read(nor, &read_cmd, 0, data, 2);
if (ret || data[0] != 0xe7) {
ssdk_printf(SSDK_EMERG, "I/O mode: %x, %x, ret = %d\r\n", data[0], data[1], ret);
return -1;
}
}
return ret;
}
/**
* @brief set quad mode for issi spi norflash
* @param[in] nor spi norflash ptr
* @param[in] enable quad mode use or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_issi_quad_enable(struct spi_nor *nor, bool enable)
{
int ret = 0;
uint8_t data[2] = {0};
struct spi_nor_cmd r_cmd = {
.opcode = SPINOR_OP_RDSR,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = sdrv_spi_nor_reg_read(nor, &r_cmd, 0, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) return ret;
if (!(data[0] & (1u << 6))) {
struct spi_nor_cmd w_cmd = {
.opcode = SPINOR_OP_WRSR,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] |= (1u << 6);
ret = sdrv_spi_nor_reg_write(nor, &w_cmd, 0, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
ret = sdrv_spibus_wait_idle_locked(nor);
}
return ret;
}
/**
* @brief set qpi mode for issi spi norflash
* @param[in] nor spi norflash ptr
* @param[in] enable qpi mode use or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_issi_enter_quad(struct spi_nor *nor, bool enable)
{
int ret = 0;
struct spi_nor_cmd q_cmd = {
.opcode = enable ? 0x35 : 0xf5,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = sdrv_spi_nor_reg_write(nor, &q_cmd, 0, 0, 0);
return ret;
}
/**
* @brief set qpi mode for giga spi norflash
* @param[in] nor spi norflash ptr
* @param[in] enable qpi mode use or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_giga_enter_quad(struct spi_nor *nor, bool enable)
{
int ret = 0;
struct spi_nor_cmd cmd = {
.opcode = enable ? 0x38 : 0xff,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = sdrv_spi_nor_reg_write(nor, &cmd, 0, 0, 0);
//if clock frequency is higher than 104MHz, set 8 dummy clocks
switch (nor->info.flash_id[1]) {
case 0x66: /* gd25lt */
case 0x67: /* gd25lb */
if (nor->host->ref_clk_hz >= 208000000u) {
nor->info.status_dummy = 8;
}
break;
default:
break;
}
return ret;
}
/**
* @brief set read dummy for giga spi norflash
* @param[in] nor spi norflash ptr
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_giga_default_init(struct spi_nor *nor)
{
int ret;
uint8_t data[2];
struct spi_nor_cmd byte_cmd = {
.opcode = 0x81,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] = nor->info.read_dummy;
ret = sdrv_spi_nor_reg_write(nor, &byte_cmd, 1, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
struct spi_nor_cmd read_cmd = {
.opcode = 0x85,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 8,
};
ret = sdrv_spi_nor_reg_read(nor, &read_cmd, 1, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret || data[0] != nor->info.read_dummy) {
ssdk_printf(SSDK_EMERG, "dummy: %d, %d\r\n", nor->info.read_dummy, data[0]);
return -1;
}
return ret;
}
/**
* @brief set quad mode for giga spi norflash
* @param[in] nor spi norflash ptr
* @param[in] enable quad mode use or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_giga_quad_enable(struct spi_nor *nor, bool enable)
{
int ret = 0;
uint8_t data[2] = {0};
uint8_t rdata[2] = {0};
struct spi_nor_cmd r_cmd = {
.opcode = 0x35,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = sdrv_spi_nor_reg_read(nor, &r_cmd, 0, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
if (!(data[0] & (1u << 1))) {
struct spi_nor_cmd w_cmd = {
.opcode = 0x31,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] |= (1u << 1);
ret = sdrv_spi_nor_reg_write(nor, &w_cmd, 0, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
}
ret = sdrv_spi_nor_reg_read(nor, &r_cmd, 0, rdata,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret || rdata[0] != 0x02) {
ssdk_printf(SSDK_CRIT, "I/O mode: %x, %x, ret = %d\r\n", data[0], data[1], ret);
return -1;
}
return ret;
}
/**
* @brief set 256KB sector size for cypress spi norflash
* @param[in] nor spi norflash ptr
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_cypress_default_init(struct spi_nor *nor)
{
int ret = 0;
uint8_t data[2] = {0};
/* set erase 256kb */
struct spi_nor_cmd e_r_cmd = {
.opcode = 0x65,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 8,
};
ret = sdrv_spi_nor_reg_read(nor, &e_r_cmd, 0x800004, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
if (!(data[0] & BIT(3))) {
struct spi_nor_cmd e_w_cmd = {
.opcode = 0x71,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[0] |= (1 << 3);//256KB
ret = sdrv_spi_nor_reg_write(nor, &e_w_cmd, 0x04, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
ret = sdrv_spibus_wait_idle_locked(nor);
if (ret) {
return ret;
}
ret = sdrv_spi_nor_reg_read(nor, &e_r_cmd, 0x800004, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (!(data[0] & BIT(3))) {
ssdk_printf(SSDK_CRIT, "erase 256kb failed, read data = 0x%x\r\n", data[0]);
return -1;
}
}
return ret;
}
/**
* @brief set 4byte addr mode size for cypress spi norflash
* @param[in] nor spi norflash ptr
* @param[in] enable 4byte addr mode use or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_cypress_set_4byte_addr_mode(struct spi_nor *nor,
bool enable)
{
int ret;
struct spi_nor_cmd byte_cmd = {
.opcode = enable ? SPINOR_OP_EN4B : 0xb8,
.addr_bytes = 0,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
ret = sdrv_spi_nor_reg_write(nor, &byte_cmd, 0, 0, 0);
return ret;
}
/**
* @brief set octal dtr mode for cypress spi norflash
* @param[in] nor spi norflash ptr
* @param[in] enable octal dtr mode use or not
* @return int
* @retval 0: success
* @retval other: failed
*/
static int sdrv_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable)
{
int ret = 0;
uint8_t data[3] = {0};
struct spi_nor_cmd r_cmd = {
.opcode = 0x65,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 8,
};
ret = sdrv_spi_nor_reg_read(nor, &r_cmd, 0x800006, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (ret) {
return ret;
}
struct spi_nor_cmd w_cmd = {
.opcode = 0x71,
.addr_bytes = nor->addr_width,
.inst_type = SNOR_INST_LANS(nor->reg_proto),
.dummy = 0,
};
sdrv_spibus_write_enable_locked(nor, true);
data[2] = enable ? data[0] |= 0x03 : 0;
ret = sdrv_spi_nor_reg_write(nor, &w_cmd, 0x800006, data,
SNOR_INST_LANS(nor->reg_proto) == 3 ? 2 : 1);
if (enable) {
nor->octal_dtr_en = 1;
nor->dqs_en = 1;
struct spi_nor_cmd read_cmd = {
.opcode = 0x65,
.addr_bytes = 4,
.inst_type = 3,
.dummy = 8,
};
ret = sdrv_spi_nor_reg_read(nor, &read_cmd, 0x800006, data, 2);
if (ret || data[0] != data[2]) {
ssdk_printf(SSDK_EMERG, "I/O mode: %x, %x, %x, ret = %d\r\n", data[0], data[1],
data[2], ret);
return -1;
}
}
return ret;
}
#endif