Files
2025-12-03 11:12:34 +08:00

2138 lines
70 KiB
C

#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".nandflash.data.bss")
#pragma data_seg(".nandflash.data")
#pragma const_seg(".nandflash.text.const")
#pragma code_seg(".nandflash.text")
#endif
#include "nandflash.h"
#include "timer.h"
#include "app_config.h"
#include "clock.h"
#include "gpio.h"
#if (defined TCFG_NANDFLASH_DEV_ENABLE && TCFG_NANDFLASH_DEV_ENABLE)
/* #undef LOG_TAG_CONST */
#define LOG_TAG "[nandFLASH]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"
#define NANDFLASH_SFC1_READ_EN 0
#define NAND_FLASH_TIMEOUT 1000000
#define NAND_BLOCK_SIZE 0x20000
#define NANDFLASH_ERROR_RETRY 3
//nandflash
#define XT26G01C 0x0b11
#define XT26G02C 0x2c24
#define XCSP4AAWH 0x9cb1
#define F35SQA002G 0xcd72
#define F35SQA001G 0xcd71
#define F35SQA512M 0xcd70
#define F35UQA001G 0xcd61
#define ZB35Q01C 0x5e41
#define GD5F1GM7UEYIG 0xc891
#define GD5F2GM7UE 0xc892
#define DS35Q1GA_IB 0xe571
struct nandflash_data {
u32 nand_id;
u32 plane_number; //设备plane数量,默认填1
u32 capacity;
u32 max_erase_cnt; //block的最大擦除次数
u16 block_number; //flash中块数量
u16 page_num; //块中page数量
u16 page_size;
u16 oob_user_offset[2]; //oob(obb区域偏移地址)
u16 oob_user_size[2]; //oob(obb写入bytes)
u16 oob_size; //oob区域大小(byte)
u8 quad_mode_dummy_num: 4; //0XEB指令dummy数量
u8 quad_mode_qe: 1; //featrue(0xb0):bit0:QE
u8 ecc_mask;
u8 ecc_err;
u8 plane_select;
u8 write_enable_position;//写使能的位置,1表示在写入缓存区指令前进行写使能,0表示在写入缓存区指令后写使能
};
struct nandflash_data nand_flash = {0, 0, 0, 0, 0, 0, 0, 0};
static u8 spi_data_width = SPI_MODE_BIDIR_1BIT;
static u32 spi_clk = 24000000;
#define MAX_NANDFLASH_PART_NUM 4
struct nandflash_partition {
const char *name;
u32 start_addr;
u32 size;
struct device device;
};
static struct nandflash_partition nor_part[MAX_NANDFLASH_PART_NUM];
struct nandflash_info {
u32 flash_id;
int spi_num;
int spi_err;
u8 spi_cs_io;
u8 spi_r_width;
u8 part_num;
u8 open_cnt;
struct nandflash_partition *const part_list;
OS_MUTEX mutex;
u32 max_end_addr;
};
static struct nandflash_info _nandflash = {
.spi_num = (int) - 1,
.part_list = nor_part,
};
void nand_flash_reset(void);
int nand_flash_read(u32 addr, u8 *buf, u32 len);
int nand_flash_erase(u32 addr);
static void nandflash_power_check();
void nandflash_power_set(int enable);
void nandflash_poweron(int priv);
#define spi_cs_init() \
do { \
gpio_hw_set_pull_up(IO_PORT_SPILT(_nandflash.spi_cs_io), GPIO_PULLUP_10K); \
gpio_hw_set_pull_down(IO_PORT_SPILT(_nandflash.spi_cs_io), GPIO_PULLDOWN_DISABLE); \
gpio_set_drive_strength(IO_PORT_SPILT(_nandflash.spi_cs_io), PORT_DRIVE_STRENGT_64p0mA); \
gpio_set_mode(IO_PORT_SPILT(_nandflash.spi_cs_io),PORT_OUTPUT_HIGH); \
} while (0)
#define spi_cs_uninit() \
do { \
gpio_hw_set_pull_up(IO_PORT_SPILT(_nandflash.spi_cs_io), GPIO_PULLUP_DISABLE); \
gpio_hw_set_pull_down(IO_PORT_SPILT(_nandflash.spi_cs_io), GPIO_PULLDOWN_DISABLE); \
gpio_set_mode(IO_PORT_SPILT(_nandflash.spi_cs_io),PORT_HIGHZ); \
} while (0)
#define spi_cs_h() gpio_write(_nandflash.spi_cs_io, 1)
#define spi_cs_l() gpio_write(_nandflash.spi_cs_io, 0)
#define spi_read_byte() spi_recv_byte(_nandflash.spi_num, &_nandflash.spi_err)
#define spi_write_byte(x) spi_send_byte(_nandflash.spi_num, x)
#define spi_dma_read(x, y) spi_dma_recv(_nandflash.spi_num, x, y)
#define spi_dma_write(x, y) spi_dma_send(_nandflash.spi_num, x, y)
#define spi_set_width(x) spi_set_bit_mode(_nandflash.spi_num, x)
extern const struct spi_platform_data spix_p_data[HW_SPI_MAX_NUM];
static void nandflash_gpio_disable(hw_spi_dev spi)
{
u8 *port = NULL;
if (spi == HW_SPI1) {
port = (u8 *)spix_p_data[1].port;
} else if (spi == HW_SPI2) {
port = (u8 *)spix_p_data[2].port;
}
ASSERT(port, "spi_dev %d not find", spi);
//clk d0 di d2(wp) d3(hold)
for (int i = 0; i <= 4; i++) {
if (port[i] != (u8) - 1) {
gpio_set_mode(IO_PORT_SPILT(port[i]), PORT_HIGHZ);
}
}
}
static struct nandflash_partition *nandflash_find_part(const char *name)
{
struct nandflash_partition *part = NULL;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
continue;
}
if (!strcmp(part->name, name)) {
return part;
}
}
return NULL;
}
static struct nandflash_partition *nandflash_new_part(const char *name, u32 addr, u32 size)
{
struct nandflash_partition *part;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
break;
}
}
if (part->name != NULL) {
log_error("create nandflash part fail\n");
return NULL;
}
memset(part, 0, sizeof(*part));
part->name = name;
part->start_addr = addr;
part->size = size;
if (part->start_addr + part->size > _nandflash.max_end_addr) {
_nandflash.max_end_addr = part->start_addr + part->size;
}
_nandflash.part_num++;
return part;
}
static void nandflash_delete_part(const char *name)
{
struct nandflash_partition *part;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
continue;
}
if (!strcmp(part->name, name)) {
part->name = NULL;
_nandflash.part_num--;
}
}
}
static int nandflash_verify_part(struct nandflash_partition *p)
{
struct nandflash_partition *part = NULL;
u32 idx;
for (idx = 0; idx < MAX_NANDFLASH_PART_NUM; idx++) {
part = &_nandflash.part_list[idx];
if (part->name == NULL) {
continue;
}
if ((p->start_addr >= part->start_addr) && (p->start_addr < part->start_addr + part->size)) {
if (strcmp(p->name, part->name) != 0) {
return -1;
}
}
}
return 0;
}
void mdelay(unsigned int ms);
u16 nand_flash_read_id()
{
u16 id;
spi_cs_l();
spi_write_byte(GD_READ_ID);
spi_write_byte(0x0);
id = spi_read_byte();
id <<= 8;
id |= spi_read_byte();
spi_cs_h();
return id;
}
static void nand_write_enable()
{
spi_cs_l();
spi_write_byte(GD_WRITE_ENABLE);
spi_cs_h();
}
static void nand_write_disable()
{
spi_cs_l();
spi_write_byte(GD_WRITE_DISABLE);
spi_cs_h();
}
static void nand_set_features(u8 addr, u8 dat)
{
spi_cs_l();
spi_write_byte(GD_SET_FEATURES); //命令头
spi_write_byte(addr); //发送protection寄存器地址
spi_write_byte(dat); //需要设置的数据
spi_cs_h();
}
static u8 nand_get_features(u8 addr)
{
u8 temp;
spi_cs_l();
spi_write_byte(GD_GET_FEATURES); //命令头
spi_write_byte(addr); //发送protection寄存器地址
temp = spi_read_byte();
spi_cs_h();
return temp;
}
static u8 no_cs_nand_get_features(u8 addr)
{
u8 temp;
// spi_cs_l();
spi_write_byte(GD_GET_FEATURES); //命令头
spi_write_byte(addr); //发送protection寄存器地址
temp = spi_read_byte();
// spi_cs_h();
return temp;
}
void nand_flash_set_ecc(u8 en)
{
int ret = 0;
u8 cfg_reg = nand_get_features(GD_FEATURES);
if ((!!(cfg_reg & BIT(4))) == en) {
printf("nandflash_set_ecc succ\n");
return;
}
if (en) {
cfg_reg |= BIT(4);
} else {
cfg_reg &= ~ BIT(4);
}
/* printf("nand_flash_set_ecc cfg_reg:0x%x\n", cfg_reg); */
nand_set_features(GD_FEATURES, cfg_reg);
}
void nand_flash_set_quad(u8 en)
{
int ret = 0;
u8 cfg_reg = nand_get_features(GD_FEATURES);
if ((!!(cfg_reg & BIT(0))) == en) {
log_debug("nandflash_set_quad succ\n");
return;
}
if (en) {
cfg_reg |= BIT(0);
} else {
cfg_reg &= ~ BIT(0);
}
nand_set_features(GD_FEATURES, cfg_reg);
}
enum {
NANDFLASH_ECC_FAIL = 1,//ecc已纠正
NANDFLASH_BAD_BLOCK, //ecc 无法纠正
NANDFLASH_OIP_FAIL, //nandflash 忙
NANDFLASH_E_FAIL, //擦除错
NANDFLASH_P_FAIL, //写错误
};
void udelay(u32 us);
static u8 nand_flash_wait_ok(u32 timeout)
{
u8 sta;
while (--timeout) {
sta = nand_get_features(GD_GET_STATUS);
if (sta & NAND_STATUS_OIP) {
continue;
}
if (_nandflash.flash_id == 0) {
sta = 0;
goto _exit;
}
//It will also be set if the user attempts to program an invalid
//address or a locked or a protected region, including the OTP area.
if (sta & NAND_STATUS_P_FAIL) { //ing
log_error("nand_flash prom error![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_P_FAIL;
goto _exit;
}
//It will also be set if the user attempts to erase a locked region.
if (sta & NAND_STATUS_E_FAIL) { //erase fail
log_error("nand_flash erase error![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_E_FAIL;
goto _exit;
}
if ((sta & nand_flash.ecc_mask) == nand_flash.ecc_err) {
log_error("nand_flash block should be marked as a bad block![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_BAD_BLOCK;//坏块
break;
} else if (sta & nand_flash.ecc_mask) {
log_error("nand_flash block data should be refreshed![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = 0;
break;
} else {
sta = 0;
break;
}
udelay(1);
};
if (timeout == 0) {
log_error("nand_flash_wait_ok timeout!\r\n");
sta = NANDFLASH_OIP_FAIL;
}
_exit:
if (sta) {
#if (TCFG_EX_FLASH_POWER_IO != NO_CONFIG_PORT)
nandflash_power_set(0);
udelay(200);
nandflash_power_set(1);
#else
/*flash异常时软件复位不一定有效*/
nand_flash_reset();
#endif
}
return sta;
}
static u8 no_cs_nand_flash_wait_ok(u32 timeout)
{
udelay(1);
/* delay(20); */
u8 sta;
do {
sta = no_cs_nand_get_features(GD_GET_STATUS);
// printf("STA:%d\n",sta);
if ((sta & NAND_STATUS_OIP) == 0) {
sta = 0;
break;
}
#if 1
//It will also be set if the user attempts to program an invalid
//address or a locked or a protected region, including the OTP area.
if (sta & NAND_STATUS_P_FAIL) { //ing
log_error("nand_flash prom error![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_P_FAIL;
goto _exit;
}
//It will also be set if the user attempts to erase a locked region.
if (sta & NAND_STATUS_E_FAIL) { //erase fail
log_error("nand_flash erase error![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_E_FAIL;
goto _exit;
}
if ((sta & nand_flash.ecc_mask) == nand_flash.ecc_err) {
log_error("nand_flash block should be marked as a bad block![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_BAD_BLOCK;//坏块
break;
} else if (sta & nand_flash.ecc_mask) {
log_error("nand_flash block data should be refreshed![status:0x%x,timeout:%d]\r\n", sta, timeout);
sta = NANDFLASH_ECC_FAIL;//ecc错误
/* break; */
}
#endif
udelay(1);
/* delay(20); */
} while (--timeout);
if (timeout == 0) {
log_error("nand_flash_wait_ok timeout!\r\n");
sta = NANDFLASH_OIP_FAIL;
}
_exit:
if (sta) {
#if (TCFG_EX_FLASH_POWER_IO != NO_CONFIG_PORT)
nandflash_power_set(0);
udelay(200);
nandflash_power_set(1);
#else
/*flash异常时软件复位不一定有效*/
nand_flash_reset();
#endif
}
return sta;
}
void nand_flash_reset(void)
{
mdelay(2);//2ms
// os_time_dly(2);
spi_cs_l();
spi_write_byte(0xff); //命令头
spi_cs_h();
mdelay(2);//2ms
// 4Gb:must wait Tpor,2Gb:no wait
mdelay(2);//2ms
// wait ok
if (nand_flash_wait_ok(NAND_FLASH_TIMEOUT)) {
log_error("nand flash reset error");
}
}
static u32 block_page_get(u32 addr, u32 *cache_addr)
{
u32 page = 0;
u32 block = 0, bp_mix = 0;
//<地址超1页范围
if (addr >= nand_flash.page_size) {
if (addr >= NAND_BLOCK_SIZE) {
while (addr >= NAND_BLOCK_SIZE) {
block++;
addr -= NAND_BLOCK_SIZE;
}
goto _page_count;
} else {
goto _page_count;
}
}
//<地址不超1页范围
else {
goto _end_count;
}
_page_count:
while (addr >= nand_flash.page_size) {
page++;
addr -= nand_flash.page_size;
}
_end_count:
*cache_addr = addr;
bp_mix = (block << 6) | page;
/* log_info("addr change:block:%d,page:%d,cache:%d", block, page, addr); */
return bp_mix;
}
#define block_change(x) (x)
int nand_flash_erase(u32 address)
{
os_mutex_pend(&_nandflash.mutex, 0);
nandflash_power_check();
u32 bp_mix = 0;
u32 cache_addr;
bp_mix = block_page_get(address, &cache_addr);
int retry = NANDFLASH_ERROR_RETRY;
u8 sta = 0;
do {
nand_write_enable();
// r_printf("erase 0x%x 0x%x 0x%x 0x%x\n",GD_BLOCK_ERASE,bp_mix >> 16,bp_mix >> 8,bp_mix);
spi_cs_l();
spi_write_byte(GD_BLOCK_ERASE);
spi_write_byte(bp_mix >> 16); //擦除地址:block+page addr
spi_write_byte(bp_mix >> 8);//
spi_write_byte(bp_mix);
spi_cs_h();
/* nand_write_disable(); */
//delay 20ns erase time:4~10ms
mdelay(1);
sta = nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
if (sta) {
log_error("nand flash erase error status reg %x", sta);
retry --;
} else {
retry = 0;
}
} while (retry);
os_mutex_post(&_nandflash.mutex);
return sta;
}
void wdt_clear(void);
static void nand_flash_erase_all()
{
u16 block_num = 1024; //XT26G01C(1Gb)
nand_set_features(0xA0, 0x00);
block_num = nand_flash.block_number;
for (int i = 0; i < block_num; i++) {
nand_flash_erase(NAND_BLOCK_SIZE * i);
wdt_clear();
}
r_printf("nandflash erase all!!!");
}
//return:0:ok, >0:err, <0:bad block(-1表示0,-2:1,,,)
static int nand_page_read_to_cache(u32 block_page_addr) //PageRead
{
u8 reg = 0;
int err_b_addr = 0;
int retry = NANDFLASH_ERROR_RETRY;
do {
spi_cs_l();
spi_write_byte(GD_PAGE_READ_CACHE); //PageRead to cache
spi_write_byte(block_page_addr >> 16); //send the Page/Block Add
spi_write_byte((u8)((block_page_addr) >> 8));
spi_write_byte((u8)block_page_addr);
spi_cs_h();
reg = nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
if (reg) {
retry --;
} else {
retry = 0;
}
} while (retry);
if (reg) {
log_error("[addr:block:%d,page:%d]nand flash read to cache error", block_page_addr >> 6, block_page_addr & 0x3f);
if (reg == NANDFLASH_BAD_BLOCK) {
/* log_error("nand_flash block should be marked as a bad block!\r\n"); */
err_b_addr = block_page_addr >> 6;
err_b_addr = -err_b_addr - 1;
return err_b_addr;
}
}
return reg;
}
//03h/0bh/3bh/6bh/bbh/ebh:addr_bit12:sel plane
static void nand_read_from_cache(u8 *buf, u32 cache_addr, u32 len) //ReadCache
{
/* cache_addr |= 0x4000; */
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_READ_FROM_CACHE); //PageRead to cache
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_write_byte(0xFF); //send 1byte dummy clk
spi_dma_read(buf, len);
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
//3bh/6bh/bbh/ebh:addr_bit12:sel plane
static void nand_read_from_cache_x2(u8 *buf, u32 cache_addr, u32 len) //ReadCache
{
/* cache_addr |= 0x4000; */
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_READ_FROM_CACHE_X2); //PageRead to cache
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_write_byte(0xff);
spi_set_width(SPI_MODE_UNIDIR_2BIT);
spi_dma_read(buf, len);
spi_set_width(spi_data_width);
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
//03h/0bh/3bh/6bh/bbh/ebh:addr_bit12:sel plane
static void nand_read_from_cache_dual_io(u8 *buf, u32 cache_addr, u32 len) //ReadCache
{
/* cache_addr |= 0x4000; */
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_READ_FROM_CACHE_DUAL_IO); //PageRead to cache
spi_set_width(SPI_MODE_UNIDIR_2BIT);
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_write_byte(0xff);
spi_dma_read(buf, len);
spi_set_width(spi_data_width);
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
//3bh/6bh/bbh/ebh:addr_bit12:sel plane
static void nand_read_from_cache_x4(u8 *buf, u32 cache_addr, u32 len) //ReadCache
{
/* cache_addr |= 0x4000; */
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_READ_FROM_CACHE_X4); //PageRead to cache
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_write_byte(0xff);
spi_set_width(SPI_MODE_UNIDIR_4BIT);
spi_dma_read(buf, len);
spi_set_width(spi_data_width);
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
//03h/0bh/3bh/6bh/bbh/ebh:addr_bit12:sel plane
static void nand_read_from_cache_quad_io(u8 *buf, u32 cache_addr, u32 len) //ReadCache
{
u8 temp[4];
temp[0] = (u8)((cache_addr) >> 8);
temp[1] = (u8)cache_addr;
temp[2] = 0xff;
temp[3] = 0xff;
/* cache_addr |= 0x4000; */
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_READ_FROM_CACHE_QUAD_IO); //PageRead to cache
spi_set_width(SPI_MODE_UNIDIR_4BIT);
spi_dma_write(temp, 2 + nand_flash.quad_mode_dummy_num);
spi_dma_read(buf, len);
spi_set_width(spi_data_width);
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
#if NANDFLASH_SFC1_READ_EN
void nand_sfc_read_cache_x4(u8 *buf, u32 offset, u32 len);
#endif
//return:0:ok, >0:err, <0:bad block(-1表示0,-2:1,,,)
static int nand_read(u32 addr, u32 len, u8 *buf)
{
int reg = 0;
u32 bp_mix = 0;
u32 cache_addr;
bp_mix = block_page_get(addr, &cache_addr);
bp_mix = block_change(bp_mix);
cache_addr &= 0x0fff;
if (nand_flash.plane_select == 1) { //XT26G02C(2Gb)
if (bp_mix & BIT(6)) {
cache_addr |= BIT(12);
}
}
reg = nand_page_read_to_cache(bp_mix);
if (reg < 0) {
return reg;
}
//时间间隔需小于trd
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_2BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 or 0x82*/
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* nand_read_from_cache_dual_io(buf, cache_addr, len); */
nand_read_from_cache_x2(buf, cache_addr, len);
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
} else if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 or 0x82*/
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
/* nand_read_from_cache_quad_io(buf, cache_addr, len); */
#if NANDFLASH_SFC1_READ_EN
if ((cache_addr & 0xfff) % 4) {
nand_read_from_cache_x4(buf, cache_addr, len);
} else {
nand_sfc_read_cache_x4(buf, cache_addr, len);
}
#else
nand_read_from_cache_x4(buf, cache_addr, len);
#endif
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
} else {
nand_read_from_cache(buf, cache_addr, len);
}
return reg;
}
int nand_flash_read_page(u16 block, u8 page, u16 offset, u8 *buf, u16 len)
{
os_mutex_pend(&_nandflash.mutex, 0);
nandflash_power_check();
int reg = 0;
u32 bp_mix = (block << 6) | page;
u32 cache_addr = offset & 0x0fff;
if (nand_flash.plane_select == 1) { //XT26G02C(2Gb)
if (bp_mix & BIT(6)) {
cache_addr |= BIT(12);
}
}
reg = nand_page_read_to_cache(bp_mix);
if (reg < 0) {
goto __read_exit;
}
//时间间隔需小于trd
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_2BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 or 0x82*/
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* nand_read_from_cache_dual_io(buf, cache_addr, len); */
nand_read_from_cache_x2(buf, cache_addr, len);
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
} else if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 or 0x82*/
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
/* nand_read_from_cache_quad_io(buf, cache_addr, len); */
#if NANDFLASH_SFC1_READ_EN
if ((cache_addr & 0xfff) % 4) {
nand_read_from_cache_x4(buf, cache_addr, len);
} else {
nand_sfc_read_cache_x4(buf, cache_addr, len);
}
#else
nand_read_from_cache_x4(buf, cache_addr, len);
#endif
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
} else {
nand_read_from_cache(buf, cache_addr, len);
}
__read_exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
//简化接口
//return:0:ok, >0:err, <0:bad block(-1表示0,-2:1,,,)
int nand_flash_read(u32 offset, u8 *buf, u32 len)
{
/* printf("%s() %x l: %x @:%x \n",__func__,(u32)buf,len,offset); */
int reg = 0;
int _len = len;
u8 *_buf = (u8 *)buf;
os_mutex_pend(&_nandflash.mutex, 0);
nandflash_power_check();
u32 first_page_len = nand_flash.page_size - (offset % nand_flash.page_size);
/* printf("first_page_len %x %x \n", first_page_len, offset % nand_flash.page_size); */
first_page_len = _len > first_page_len ? first_page_len : _len;
reg = nand_read(offset, first_page_len, _buf);
if (reg) {
log_error("read nandflash addr:%d fail!", offset);
goto __read_exit;
}
_len -= first_page_len;
_buf += first_page_len;
offset += first_page_len;
while (_len) {
u32 cnt = _len > nand_flash.page_size ? nand_flash.page_size : _len;
reg = nand_read(offset, cnt, _buf);
if (reg) {
log_error("read nandflash addr:%d fail!", offset);
goto __read_exit;
}
_len -= cnt;
offset += cnt;
_buf += cnt;
}
__read_exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
//2Gb-nandflash random read(30h,3fh):
//write:
#if 1
//1Gb 2Gb program 时序不同
//Only four partial-page programs(02h) are allowed on a single page.*******************
//02h/32h/84h/34h:addr_bit12:sel plane
static void nand_program_load(u8 *buf, u32 cache_addr, u16 len)
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_LOAD); //PageLoad to cache ,change the data
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_dma_write(buf, len); //将数据放到总线上
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
//02h/32h/84h/34h:addr_bit12:sel plane
static void nand_program_load_x4(u8 *buf, u32 cache_addr, u16 len)
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_LOAD_X4); //PageLoad to cache ,change the data
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
spi_set_width(SPI_MODE_UNIDIR_4BIT);
spi_dma_write(buf, len); //将数据放到总线上
spi_set_width(spi_data_width);
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
static int nand_program_excute(u32 block_page_addr)
{
int reg;
int retry = NANDFLASH_ERROR_RETRY;
do {
spi_cs_l();
spi_write_byte(GD_PROGRAM_EXECUTE);
spi_write_byte(block_page_addr >> 16); //send the Page/Block Add
spi_write_byte((u8)((block_page_addr) >> 8));
spi_write_byte((u8)block_page_addr);
spi_cs_h();
reg = nand_flash_wait_ok(NAND_FLASH_TIMEOUT);
if (reg) {
log_error("nand flash program error");
retry --;
} else {
retry = 0;
}
} while (retry);
return reg;
}
static int nand_write(u32 addr, u16 len, u8 *buf)
{
int reg;
u32 bp_mix = 0;
u32 cache_addr;
bp_mix = block_page_get(addr, &cache_addr);
bp_mix = block_change(bp_mix);
//printf_u16(cache_addr);
cache_addr &= 0x0fff;
if (nand_flash.plane_select == 1) { //XT26G02C(2Gb)
if (bp_mix & BIT(6)) {
cache_addr |= BIT(12);
}
}
if (nand_flash.write_enable_position == 1) {
nand_write_enable();
}
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
nand_program_load_x4(buf, cache_addr, len);
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):p0x%x\n",nand_get_features(0xa0)); */
} else {
nand_program_load(buf, cache_addr, len);
}
if (nand_flash.write_enable_position == 0) { //XT26G01C(1Gb)
nand_write_enable();
}
reg = nand_program_excute(bp_mix);
return reg;
}/**/
int nand_flash_write_page(u16 block, u8 page, u8 *buf, u16 len)
{
os_mutex_pend(&_nandflash.mutex, 0);
nandflash_power_check();
int reg;
u32 bp_mix = 0;
u32 cache_addr;
/*bp_mix = block_page_get(addr, &cache_addr);
bp_mix = block_change(bp_mix);*/
bp_mix = (block << 6) | page;
//printf_u16(cache_addr);
cache_addr = 0;
if (nand_flash.plane_select == 1) { //XT26G02C(2Gb)
if (bp_mix & BIT(6)) {
cache_addr |= BIT(12);
}
}
if (nand_flash.write_enable_position == 1) {
nand_write_enable();
}
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
nand_program_load_x4(buf, cache_addr, len);
} else {
nand_program_load(buf, cache_addr, len);
}
if (nand_flash.write_enable_position == 0) { //XT26G01C(1Gb)
nand_write_enable();
}
reg = nand_program_excute(bp_mix);
os_mutex_post(&_nandflash.mutex);
return reg;
}
//写前需确保该block(128k)已擦除
int nand_flash_write(u32 offset, u8 *buf, u32 len)
{
/* printf("%s() %x l: %x @:%x \n",__func__,(u32)buf,len,offset); */
int reg;
int _len = len;
u8 *_buf = (u8 *)buf;
os_mutex_pend(&_nandflash.mutex, 0);
nandflash_power_check();
u32 first_page_len = nand_flash.page_size - (offset % nand_flash.page_size);
/* printf("first_page_len %x %x \n", first_page_len, offset % nand_flash.page_size); */
first_page_len = _len > first_page_len ? first_page_len : _len;
reg = nand_write(offset, first_page_len, _buf);
if (reg) {
goto __exit;
}
_len -= first_page_len;
_buf += first_page_len;
offset += first_page_len;
while (_len) {
u32 cnt = _len > nand_flash.page_size ? nand_flash.page_size : _len;
reg = nand_write(offset, cnt, _buf);
if (reg) {
goto __exit;
}
_len -= cnt;
offset += cnt;
_buf += cnt;
}
__exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
//1Gb 2Gb program 时序不同
// If the random data is not sequential, then another PROGRAM LOAD RANDOM DATA x1 (84h) command must be issued with a new column address.*******************
//02h/32h/84h/34h:addr_bit12:sel plane
static void nand_program_load_random_data(u8 *buf, u16 cache_addr, u16 len)
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_LOAD_RANDOM_DATA);
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
if (buf && (len > 0)) {
/* printf("\n >>>[test]:func = %s,line= %d, addr = %d\n",__FUNCTION__, __LINE__, cache_addr); */
/* put_buf(buf, len); */
spi_dma_write(buf, len); //将数据放到总线上
}
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
//02h/32h/84h/34h:addr_bit12:sel plane
static void nand_program_load_random_data_x4(u8 *buf, u16 cache_addr, u16 len)
{
spi_cs_l();//SPI_CS(0);
spi_write_byte(GD_PROGRAM_LOAD_RANDOM_DATA_X4);
spi_write_byte((u8)((cache_addr) >> 8));
spi_write_byte((u8)cache_addr);
if (buf && (len > 0)) {
spi_set_width(SPI_MODE_UNIDIR_4BIT);
spi_dma_write(buf, len); //将数据放到总线上
spi_set_width(spi_data_width);
}
spi_cs_h();//SPI_CS(1);
/* printf("%s\n",__func__); */
}
/*
* 块间或块内页数据移动, 可替换offset位置len长的数据
* page_src_addr :要移动的页
* page_dest_addr:移动目标页(需已擦除)
* offset :需要替换的数据起始地址(<nand_flash.page_size)
* len :需替换数据长度. 无则=0
* buf :存储的替换的数据.无则NULL
* return :nandflash status reg(0:ok, >0:err) ;or bad block(<0:bad block num(-1表示0块,-2:1,,,))
* */
int nand_page_internal_data_move(u32 page_src_addr, u32 page_dest_addr, u16 offset, u16 len, u8 *buf)
{
u32 bp_mix_src = 0, bp_mix_dst = 0;
u32 cache_addr_src, cache_addr_dst;
int bad_block = 0;
bp_mix_src = block_page_get(page_src_addr, &cache_addr_src);
bp_mix_src = block_change(bp_mix_src);
bp_mix_dst = block_page_get(page_dest_addr, &cache_addr_dst);
bp_mix_dst = block_change(bp_mix_dst);
//<1、PAGE READ to cache
bad_block = nand_page_read_to_cache(bp_mix_src);
if (bad_block < 0) {
return bad_block;
}
//<2、PROGRAM LOAD RANDOM DATA
cache_addr_dst &= 0x0fff;
if (nand_flash.plane_select == 1) { //XT26G02C(2Gb)
if (bp_mix_dst & BIT(6)) {
cache_addr_dst |= BIT(12);
}
}
if (nand_flash.write_enable_position == 1) {
nand_write_enable();
}
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
nand_program_load_random_data_x4(buf, cache_addr_dst + offset, len);
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):p0x%x\n",nand_get_features(0xa0)); */
} else {
nand_program_load_random_data(buf, cache_addr_dst + offset, len);
}
// If the random data is not sequential, then another PROGRAM LOAD RANDOM DATA x1 (84h) command must be issued with a new column address.*******************
//<3、WRITE ENABLE
//<4、PROGRAM EXECUTE
if (nand_flash.write_enable_position == 0) { //XT26G01C(1Gb)
nand_write_enable();
}
nand_program_excute(bp_mix_dst);
//<5、GET FEATURE
return nand_get_features(GD_GET_STATUS);
}
/*
* 同一plane中块与块数据移动, 可替换块中page_num页的offset位置len长的数据
* block_src_addr :要移动的块
* block_dest_addr:移动目标块(需已擦除)
* page_num:需修改数据的页号(<=63)
* offset :需要替换的数据起始地址(<nand_flash.page_size)
* len :需替换数据长度. 无则=0
* buf :存储的替换的数据.无则NULL
* return :nandflash status reg(0:ok, >0:err) ;or bad block(<0:bad block num(-1表示0块,-2:1,,,))
* */
int nand_block_internal_data_move(u32 block_src_addr, u32 block_dest_addr, u8 page_num, u16 offset, u16 len, u8 *buf)
{
/* y_printf(">>>[test]:move, src_addr=0x%x(b:%d), dest_addr=0x%x(b:%d),page_num:%d write len=%d\n", block_src_addr,block_src_addr/NAND_BLOCK_SIZE, block_dest_addr,block_dest_addr/NAND_BLOCK_SIZE, page_num, len ); */
u32 bp_mix_src = 0, bp_mix_dst = 0;
u32 cache_addr_src, cache_addr_dst;
u8 page_cnt = 0;
int bad_block = 0;
bp_mix_src = block_page_get(block_src_addr, &cache_addr_src);
bp_mix_src = block_change(bp_mix_src);
bp_mix_src &= ~0x3f;
bp_mix_dst = block_page_get(block_dest_addr, &cache_addr_dst);
bp_mix_dst = block_change(bp_mix_dst);
bp_mix_dst &= ~0x3f;
cache_addr_dst &= 0x0fff;
if (nand_flash.plane_select == 1) { //XT26G02C(2Gb)
if (bp_mix_dst & BIT(6)) {
cache_addr_dst |= BIT(12);
}
}
u32 wnum = (len + offset) / nand_flash.page_size;
u32 wlen;
if (((len + offset) % nand_flash.page_size) || (0 == (len + offset))) {
wnum += 1;
}
for (page_cnt = 0; page_cnt < 64; page_cnt++) {
//<1、PAGE READ to cache
bad_block = nand_page_read_to_cache(bp_mix_src + page_cnt);
if (bad_block < 0) {
return bad_block;
}
//<2、PROGRAM LOAD RANDOM DATA
if (nand_flash.write_enable_position == 1) { //XT26G02C(2Gb)
nand_write_enable();
}
if ((page_cnt >= page_num) && (page_cnt < (page_num + wnum))) {
wlen = len;
if ((wlen + offset) > nand_flash.page_size) {
len -= (nand_flash.page_size - offset);
wlen = (nand_flash.page_size - offset);
}
/* r_printf(">>>[test]:dst-ofset:0x%x, buf-addr:0x%x, len:%d, page_cnt:%d-w", cache_addr_dst + offset, buf, wlen, page_cnt); */
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
nand_program_load_random_data_x4(buf, cache_addr_dst + offset, wlen);
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):p0x%x\n",nand_get_features(0xa0)); */
} else {
nand_program_load_random_data(buf, cache_addr_dst + offset, wlen);
}
if (buf) {
buf += wlen;
offset = 0;
}
} else {
if (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT) {
/* nand_set_features(0xa0,0x02);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("featrue(a0):r0x%x\n",nand_get_features(0xa0)); */
nand_program_load_random_data_x4(NULL, cache_addr_dst, 0);
/* nand_set_features(0xa0,0x00);//bit1=1 */
/* nand_flash_wait_ok(NAND_FLASH_TIMEOUT); */
/* printf("X4featrue(a0):p0x%x\n",nand_get_features(0xa0)); */
} else {
nand_program_load_random_data(NULL, cache_addr_dst, 0);
}
}
// If the random data is not sequential, then another PROGRAM LOAD RANDOM DATA x1 (84h) command must be issued with a new column address.*******************
//<3、WRITE ENABLE
//<4、PROGRAM EXECUTE
if (nand_flash.write_enable_position == 0) { //XT26G01C(1Gb)
nand_write_enable();
}
nand_program_excute(bp_mix_dst + page_cnt);
}
//<5、GET FEATURE
return nand_get_features(GD_GET_STATUS);
}
#endif
/* void nandflash_spi_set_clk_hd(spi_dev spi); */
int _nandflash_init(const char *name, struct nandflash_dev_platform_data *pdata)
{
log_info("nandflash_init ! %x %x", pdata->spi_cs_port, pdata->spi_read_width);
if (_nandflash.spi_num == (int) - 1) {
_nandflash.spi_num = pdata->spi_hw_num;
_nandflash.spi_cs_io = pdata->spi_cs_port;
_nandflash.spi_r_width = pdata->spi_read_width;
_nandflash.flash_id = 0;
os_mutex_create(&_nandflash.mutex);
_nandflash.max_end_addr = 0;
_nandflash.part_num = 0;
spi_data_width = pdata->spi_pdata->mode;
spi_clk = pdata->spi_pdata->clk;
/* nandflash_spi_set_clk_hd(_nandflash.spi_num); */
gpio_set_drive_strength(IO_PORT_SPILT(_nandflash.spi_cs_io), PORT_DRIVE_STRENGT_64p0mA);
}
ASSERT(_nandflash.spi_num == pdata->spi_hw_num);
ASSERT(_nandflash.spi_cs_io == pdata->spi_cs_port);
ASSERT(_nandflash.spi_r_width == pdata->spi_read_width);
struct nandflash_partition *part;
part = nandflash_find_part(name);
if (!part) {
part = nandflash_new_part(name, pdata->start_addr, pdata->size);
ASSERT(part, "not enough nandflash partition memory in array\n");
ASSERT(nandflash_verify_part(part) == 0, "nandflash partition %s overlaps\n", name);
log_info("nandflash new partition %s\n", part->name);
} else {
ASSERT(0, "nandflash partition name already exists\n");
}
return 0;
}
/* static void clock_critical_enter() */
/* { */
/* } */
/* static void clock_critical_exit() */
/* { */
/* if (!(_nandflash.flash_id == 0 || _nandflash.flash_id == 0xffff)) { */
/* spi_set_baud(_nandflash.spi_num, spi_get_baud(_nandflash.spi_num)); */
/* } */
/* } */
/* CLOCK_CRITICAL_HANDLE_REG(spi_nandflash, clock_critical_enter, clock_critical_exit); */
//2Gb上电自动复位,1Gb未介绍
int _nandflash_open(void *arg)
{
int reg = 0;
os_mutex_pend(&_nandflash.mutex, 0);
log_info("nandflash open %d\n", _nandflash.open_cnt);
if (!_nandflash.open_cnt) {
if (spi_clk == 32000000) {
clk_set_api("lsb", 96000000);
}
#if NANDFLASH_SFC1_READ_EN
void sfc1_cache_init();
sfc1_cache_init();
#endif
spi_cs_init();
spi_open(_nandflash.spi_num, get_hw_spi_config(_nandflash.spi_num));
//部分型号如F35SQB002G hold脚要开上拉才能读到id
gpio_set_mode(IO_PORT_SPILT(get_hw_spi_config(_nandflash.spi_num)->port[4]), PORT_INPUT_PULLUP_10K);
// nand_flash_reset();
// nand flash power-up need wait 1.25ms
mdelay(10);
// wait oip ok
_nandflash.flash_id = 0;
if (nand_flash_wait_ok(NAND_FLASH_TIMEOUT) == NANDFLASH_OIP_FAIL) {
log_error("nand flash power-up error");
reg = -ENODEV;
goto __exit;
}
_nandflash.flash_id = nand_flash_read_id();
nand_flash.nand_id = _nandflash.flash_id;
log_info("nandflash_read_id: 0x%x\n", _nandflash.flash_id);
#ifdef TCFG_NANDFLASH_HEX_ID
if (_nandflash.flash_id == TCFG_NANDFLASH_HEX_ID) {
nand_flash.plane_number = TCFG_NANDFLASH_PLANE_NUMBER;
nand_flash.ecc_mask = TCFG_NANDFLASH_ECC_MASK;
nand_flash.ecc_err = TCFG_NANDFLASH_ECC_ERR;
nand_flash.plane_select = TCFG_NANDFLASH_PLANE_SEL;
nand_flash.write_enable_position = TCFG_NANDFLASH_WRITE_ENABLE_POS;
nand_flash.quad_mode_dummy_num = TCFG_NANDFLASH_QUAD_MODE_DUM_NUM;
nand_flash.quad_mode_qe = TCFG_NANDFLASH_QUAD_MODE_QE;
nand_flash.block_number = TCFG_NANDFLASH_BLOCK_NUMBER;
nand_flash.capacity = TCFG_NANDFLASH_CAPACITY;
nand_flash.page_num = TCFG_NANDFLASH_PAGE_NUM;
nand_flash.page_size = TCFG_NANDFLASH_PAGE_SIZE;
nand_flash.oob_size = TCFG_NANDFLASH_OOB_SIZE;
nand_flash.oob_user_offset[0] = TCFG_NANDFLASH_OOB_USER_OFFSET_0;
nand_flash.oob_user_offset[1] = TCFG_NANDFLASH_OOB_USER_OFFSET_1;
nand_flash.oob_user_size[0] = TCFG_NANDFLASH_OOB_USER_SIZE_0;
nand_flash.oob_user_size[1] = TCFG_NANDFLASH_OOB_USER_SIZE_1;
nand_flash.max_erase_cnt = TCFG_NANDFLASH_MAX_ERASE_CNT;
#else
if (0) {
#endif
} else {
switch (_nandflash.flash_id) {
case XT26G01C :
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0xf0;
nand_flash.ecc_err = 0xf0;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 1;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 1024;
nand_flash.capacity = 128 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 128;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 128;
nand_flash.oob_user_size[1] = 0;
break;
case XT26G02C :
nand_flash.plane_number = 2;
nand_flash.ecc_mask = 0x70;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 1;
nand_flash.write_enable_position = 1;
nand_flash.quad_mode_dummy_num = 0;
nand_flash.quad_mode_qe = 0;
nand_flash.block_number = 2048;
nand_flash.capacity = 256 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 128;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 128;
nand_flash.oob_user_size[1] = 0;
break;
case XCSP4AAWH :
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x70;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 1;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 4096;
nand_flash.capacity = 512 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case F35SQA512M :
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 0;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 512;
nand_flash.capacity = 64 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case F35SQA001G :
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 0;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 1024;
nand_flash.capacity = 128 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case F35SQA002G :
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 0;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 2048;
nand_flash.capacity = 256 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case F35UQA001G :
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 0;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 1024;
nand_flash.capacity = 128 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case ZB35Q01C:
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 1;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 1024;
nand_flash.capacity = 128 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 128;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case GD5F1GM7UEYIG:
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 1;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 1024;
nand_flash.capacity = 128 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case GD5F2GM7UE:
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x30;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 1;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 2048;
nand_flash.capacity = 256 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 0;
nand_flash.oob_user_size[0] = 64;
nand_flash.oob_user_size[1] = 0;
break;
case DS35Q1GA_IB:
nand_flash.plane_number = 1;
nand_flash.ecc_mask = 0x70;
nand_flash.ecc_err = 0x20;
nand_flash.plane_select = 0;
nand_flash.write_enable_position = 0;
nand_flash.quad_mode_dummy_num = 1;
nand_flash.quad_mode_qe = 1;
nand_flash.block_number = 1024;
nand_flash.capacity = 128 * 1024 * 1024;
nand_flash.page_num = 64;
nand_flash.page_size = 2048;
nand_flash.oob_size = 64;
nand_flash.max_erase_cnt = 10 * 10000;
nand_flash.oob_user_offset[0] = 0;
nand_flash.oob_user_offset[1] = 16;
nand_flash.oob_user_size[0] = 8;
nand_flash.oob_user_size[1] = 8;
break;
}
}
if ((_nandflash.flash_id == 0) || (_nandflash.flash_id == 0xffff)) {
log_error("read nandflash id error !\n");
reg = -ENODEV;
goto __exit;
}
//power-up:need set featurea[A0]=00h
nand_set_features(0xA0, 0x00);
if ((nand_flash.quad_mode_qe) && (_nandflash.spi_r_width == SPI_MODE_UNIDIR_4BIT)) {
nand_flash_set_quad(1);
}
nand_flash_set_ecc(true);
printf("GD_FEATURES:0x%x\n", nand_get_features(GD_FEATURES));
log_info("nandflash open success !\n");
}
if (_nandflash.flash_id == 0 || _nandflash.flash_id == 0xffff) {
log_error("re-open nandflash id error !\n");
reg = -EFAULT;
goto __exit;
}
_nandflash.open_cnt++;
/* nand_flash_erase_all();// */
__exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
int _nandflash_close(void)
{
os_mutex_pend(&_nandflash.mutex, 0);
log_info("nandflash close :%d\n", _nandflash.open_cnt);
if (_nandflash.open_cnt) {
_nandflash.open_cnt--;
}
if (!_nandflash.open_cnt) {
spi_deinit(_nandflash.spi_num);
nandflash_gpio_disable(_nandflash.spi_num);
spi_cs_uninit();
log_info("nandflash close done\n");
}
os_mutex_post(&_nandflash.mutex);
return 0;
}
u32 ftl_get_nand_id()
{
return _nandflash.flash_id;
}
#include "ftl_api.h"
void ftl_get_nand_info(struct ftl_nand_flash *ftl)
{
ftl->plane_number = nand_flash.plane_number;
ftl->block_begin = 0;
ftl->logic_block_num = nand_flash.block_number * 98 / 100; //98%
ftl->block_end = nand_flash.block_number;
ftl->page_num = nand_flash.page_num;
ftl->page_size = nand_flash.page_size;
ftl->oob_size = nand_flash.oob_size;
ftl->oob_user_size[0] = nand_flash.oob_user_size[0];
ftl->oob_user_size[1] = nand_flash.oob_user_size[1];
ftl->oob_user_offset[0] = nand_flash.oob_user_offset[0];
ftl->oob_user_offset[1] = nand_flash.oob_user_offset[1];
ftl->max_erase_cnt = nand_flash.max_erase_cnt;
}
int _nandflash_ioctl(u32 cmd, u32 arg, u32 unit, void *_part)
{
int reg = 0;
struct nandflash_partition *part = _part;
os_mutex_pend(&_nandflash.mutex, 0);
switch (cmd) {
case IOCTL_GET_STATUS:
*(u32 *)arg = 1;
/* *(u32 *)arg = nand_get_features(GD_GET_STATUS); */
break;
case IOCTL_GET_ID:
*((u32 *)arg) = _nandflash.flash_id;
break;
case IOCTL_GET_BLOCK_SIZE:
*(u32 *)arg = 512;//usb fat
/* *(u32 *)arg = NAND_BLOCK_SIZE; */
break;
case IOCTL_ERASE_BLOCK:
if ((arg + part->start_addr) % NAND_BLOCK_SIZE == 0) {
reg = nand_flash_erase(arg + part->start_addr);
}
break;
case IOCTL_GET_CAPACITY:
if (_nandflash.flash_id) {
*(u32 *)arg = nand_flash.capacity;
} else {
log_error("not supported");
}
break;
case IOCTL_FLUSH:
break;
case IOCTL_CMD_RESUME:
/* log_info("%s IOCTL_CMD_RESUME", __func__); */
break;
case IOCTL_CMD_SUSPEND:
/* log_info("%s IOCTL_CMD_SUSPEND", __func__); */
break;
case IOCTL_CHECK_WRITE_PROTECT:
*(u32 *)arg = 0;
break;
case IOCTL_GET_PART_INFO:
u32 *info = (u32 *)arg;
u32 *start_addr = &info[0];
u32 *part_size = &info[1];
*start_addr = part->start_addr;
*part_size = part->size;
break;
default:
reg = -EINVAL;
break;
}
__exit:
os_mutex_post(&_nandflash.mutex);
return reg;
}
/*************************************************************************************
* 挂钩 device_api
************************************************************************************/
static int nandflash_dev_init(const struct dev_node *node, void *arg)
{
struct nandflash_dev_platform_data *pdata = arg;
return _nandflash_init(node->name, pdata);
}
static int nandflash_dev_open(const char *name, struct device **device, void *arg)
{
struct nandflash_partition *part;
part = nandflash_find_part(name);
if (!part) {
log_error("no nandflash partition is found\n");
return -ENODEV;
}
*device = &part->device;
(*device)->private_data = part;
if (atomic_read(&part->device.ref)) {
return 0;
}
nandflash_poweron(1);
return _nandflash_open(arg);
}
static int nandflash_dev_close(struct device *device)
{
return _nandflash_close();
}
//return: 0:err, =len:ok, <0:block err
static int nandflash_dev_read(struct device *device, void *buf, u32 len, u32 offset)
{
int reg;
offset = offset * 512;
len = len * 512;
/* r_printf("flash read sector = %d, num = %d\n", offset, len); */
struct nandflash_partition *part;
part = (struct nandflash_partition *)device->private_data;
if (!part) {
log_error("nandflash partition invalid\n");
return -EFAULT;
}
offset += part->start_addr;
reg = nand_flash_read(offset, buf, len);
if (reg) {
r_printf(">>>[r error]:\n");
len = 0;
if (reg < 0) {
return reg;
}
}
len = len / 512;
return len;
}
//写前需确保该block(128k)已擦除
static int nandflash_dev_write(struct device *device, void *buf, u32 len, u32 offset)
{
/* r_printf("flash write sector = %d, num = %d\n", offset, len); */
int reg = 0;
offset = offset * 512;
len = len * 512;
struct nandflash_partition *part = device->private_data;
if (!part) {
log_error("nandflash partition invalid\n");
return -EFAULT;
}
offset += part->start_addr;
reg = nand_flash_write(offset, buf, len);
if (reg) {
r_printf(">>>[w error]:\n");
len = 0;
}
len = len / 512;
return len;
}
static bool nandflash_dev_online(const struct dev_node *node)
{
return 1;
}
static int nandflash_dev_ioctl(struct device *device, u32 cmd, u32 arg)
{
struct nandflash_partition *part = device->private_data;
if (!part) {
log_error("nandflash partition invalid\n");
return -EFAULT;
}
return _nandflash_ioctl(cmd, arg, 512, part);
}
const struct device_operations nandflash_dev_ops = {
.init = nandflash_dev_init,
.online = nandflash_dev_online,
.open = nandflash_dev_open,
.read = nandflash_dev_read,
.write = nandflash_dev_write,//写前需确保该block(128k)已擦除
.ioctl = nandflash_dev_ioctl,
.close = nandflash_dev_close,
};
u32 get_nand_flash_info(u8 *buf[])
{
*buf = (u8 *)&nand_flash;
return sizeof(nand_flash);
}
/*{{{*/
#if NANDFLASH_SFC1_READ_EN
#if 0/*{{{*/
#define PORT_SPI0_CSC C
#define SPI0_CSC 3
#define PORT_SPI0_CLKC C
#define SPI0_CLKC 1
#define PORT_SPI0_DOC C
#define SPI0_DOC 2
#define PORT_SPI0_DIC C
#define SPI0_DIC 4
#define PORT_SPI0_D2C C
#define SPI0_D2C 5
#define PORT_SPI0_D3C C
#define SPI0_D3C 0
void spi1_io_init(u32 mode)
{
//CS
SPI_PORT(PORT_SPI0_CSC)->OUT |= BIT(SPI0_CSC);//从上拉,马上切换到输出1
SPI_PORT(PORT_SPI0_CSC)->DIR &= ~BIT(SPI0_CSC);
SPI_PORT(PORT_SPI0_CSC)->PU &= ~BIT(SPI0_CSC);
SPI_PORT(PORT_SPI0_CSC)->PD &= ~BIT(SPI0_CSC);
/* SFC_FUNC_OUT(PORT_SPI0_CSC, SPI0_CSC) &= ~(3); */
//CLK
SPI_PORT(PORT_SPI0_CLKC)->PU &= ~BIT(SPI0_CLKC);
SPI_PORT(PORT_SPI0_CLKC)->PD &= ~BIT(SPI0_CLKC);
SPI_PORT(PORT_SPI0_CLKC)->DIR &= ~BIT(SPI0_CLKC);
SPI_PORT(PORT_SPI0_CLKC)->DIE |= BIT(SPI0_CLKC);
//DO(D0)
SPI_PORT(PORT_SPI0_DOC)->PU &= ~BIT(SPI0_DOC);
SPI_PORT(PORT_SPI0_DOC)->PD |= BIT(SPI0_DOC);
SPI_PORT(PORT_SPI0_DOC)->DIR |= BIT(SPI0_DOC);
SPI_PORT(PORT_SPI0_DOC)->DIE |= BIT(SPI0_DOC);
//DI(D1)
SPI_PORT(PORT_SPI0_DIC)->PU |= BIT(SPI0_DIC);
SPI_PORT(PORT_SPI0_DIC)->PD &= ~BIT(SPI0_DIC);
SPI_PORT(PORT_SPI0_DIC)->DIR |= BIT(SPI0_DIC);
SPI_PORT(PORT_SPI0_DIC)->DIE |= BIT(SPI0_DIC);
if (mode == 4) {
/* //WP(D2) */
SPI_PORT(PORT_SPI0_D2C)->PU |= BIT(SPI0_D2C);
SPI_PORT(PORT_SPI0_D2C)->PD &= ~BIT(SPI0_D2C);
SPI_PORT(PORT_SPI0_D2C)->DIE |= BIT(SPI0_D2C);
SPI_PORT(PORT_SPI0_D2C)->DIR |= BIT(SPI0_D2C);
//HOLD(D3)
SPI_PORT(PORT_SPI0_D3C)->PU |= BIT(SPI0_D3C);
SPI_PORT(PORT_SPI0_D3C)->PD &= ~BIT(SPI0_D3C);
SPI_PORT(PORT_SPI0_D3C)->DIE |= BIT(SPI0_D3C);
SPI_PORT(PORT_SPI0_D3C)->DIR |= BIT(SPI0_D3C);
}
}
#endif/*}}}*/
#define SPI_2WIRE_MODE 0
#define SPI_ODD_MODE 1
#define SPI_DUAL_MODE 2
#define SPI_QUAD_MODE 4
#define FAST_READ_OUTPUT_MODE (0)
#define FAST_READ_IO_MODE (1)
#define FAST_READ_IO_CONTINUOUS_READ_MODE (2)
#define FAST_READ_IO_QPI_READ_MODE (3)
enum {
NORMAL_READ = 0,
FAST_READ,
DUAL_FAST_READ_OUTPUT, //CMD 1 WIRE, ADDR 1 WIRE
QUAD_FAST_READ_OUTPUT, //CMD 1 WIRE, ADDR 1 WIRE
DUAL_FAST_READ_IO, //CMD 1 WIRE, ADDR 2 WIRE
QUAD_FAST_READ_IO, //CMD 1 WIRE, ADDR 4 WIRE
NO_SEND_COMMAND_DUAL_MODE, //ADDR 2 WIRE
NO_SEND_COMMAND_QUAD_MODE, //ADDR 4 WIRE
DUAL_WIRE_SEND_COMMAND_MODE,//CMD 2 WIRE, ADDR 2 WIRE
QUAD_WIRE_SEND_COMMAND_MODE,//CMD 4 WIRE, ADDR 4 WIRE
};
#define SFC_SPIE(x) SFR(sfc_con, 0, 1, x)
#define SFC_BIDIR(x) SFR(sfc_con, 3, 1, x)
#define SFC_RMODE(x) SFR(sfc_con, 8, 4, x)
#define SFC_DUMMY(x) SFR(sfc_con, 16, 4, x)
#define SFC_CSCNT(x) SFR(sfc_con, 20, 4, x)
#define SFC_ADDR4_SEL(x) SFR(sfc_con, 1, 1, x)
static void nand_sfc1_init(u32 data_width, u32 mode, u32 clk, u8 dummy, u8 mid)
{
/* log_debug("sfc1 data_width:%d mode:%d clk:%d,cmd:0x%x,dummy:%d,mid:0x%x\n", data_width, mode, clk,cmd,dummy,mid); */
/* JL_SPI0->CON &= ~BIT(0); */
JL_SFC1->CON = 0xf << 20;//cs
JL_SFC1->CON = 0;//sfc idle cs status 1
JL_SFC1->BAUD = clk;
/* JL_SFC1->CON |= BIT(7);//bit7为0的时候cs空闲时是0,有效时是1 */
u32 sfc_con = BIT(7) | BIT(26) | BIT(25);
/* u32 sfc_con = BIT(7) | BIT(26); */
/* u32 capacity = get_flash_capacity(); */
/* if (capacity > 16 * 1024 * 1024) { */
/* SFC_ADDR4_SEL(1); */
/* } */
/* mode = FAST_READ_IO_CONTINUOUS_READ_MODE; */
if (data_width == SPI_QUAD_MODE) {
if (mode == FAST_READ_OUTPUT_MODE) {
SFC_RMODE(QUAD_FAST_READ_OUTPUT);
SFC_DUMMY(dummy);
} else if (mode == FAST_READ_IO_MODE) {
SFC_RMODE(QUAD_FAST_READ_IO);
SFC_DUMMY(dummy);
} else if (mode == FAST_READ_IO_CONTINUOUS_READ_MODE) {
SFC_RMODE(NO_SEND_COMMAND_QUAD_MODE);
SFC_DUMMY(dummy);
}
} else if (data_width == SPI_DUAL_MODE) {
if (mode == FAST_READ_OUTPUT_MODE) {
SFC_RMODE(DUAL_FAST_READ_OUTPUT);
SFC_DUMMY(dummy);
} else if (mode == FAST_READ_IO_MODE) {
SFC_RMODE(DUAL_FAST_READ_IO);
SFC_DUMMY(dummy);
} else if (mode == FAST_READ_IO_CONTINUOUS_READ_MODE) {
SFC_RMODE(NO_SEND_COMMAND_DUAL_MODE);
SFC_DUMMY(dummy);
}
} else {
SFC_BIDIR(1);
if (mode == FAST_READ_IO_MODE) {
SFC_RMODE(FAST_READ);
SFC_DUMMY(dummy);
} else {
JL_SFC1->CON |= BIT(3);
SFC_RMODE(NORMAL_READ);
SFC_DUMMY(0);
}
}
SFC_CSCNT(0);
/* SFC_SPIE(1); */
SFR(JL_SFC1->CON, 28, 2, 3);
JL_SFC1->CON |= sfc_con;
JL_SFC1->CODE = ((mid) << 8) | 0x00;
/* printf("JL_SFC1->CON = 0x%x\n", JL_SFC1->CON); */
/* printf("JL_SFC1->BAUD = 0x%x\n", JL_SFC1->BAUD); */
/* printf("JL_SFC1->CODE = 0x%x\n", JL_SFC1->CODE); */
/* spi1_io_init(4); */
}
void sfc1_cache_init()
{
JL_SFC1->BASE_ADR = 0;
JL_SFC1->ENC_CON = 0;
IcuInitial();
DcuInitial();
//sfc1 4bit
nand_sfc1_init(4, FAST_READ_IO_CONTINUOUS_READ_MODE, 3, 2, 0xff);
}
static void nand_sfc1_resume(u32 disable_spi, u32 clk_div)
{
if (disable_spi) {
if (_nandflash.spi_num == SPI0) {
JL_SPI0->CON &= ~BIT(0);
} else if (_nandflash.spi_num == SPI1) {
JL_SPI1->CON &= ~BIT(0);
} else if (_nandflash.spi_num == SPI2) {
JL_SPI2->CON &= ~BIT(0);
}
}
JL_SFC1->BAUD = clk_div;
JL_SFC1->CON |= BIT(0);
}
static void nand_sfc1_suspend(u32 enable_spi)
{
IcuWaitIdle();
DcuWaitIdle();
//wait sfc idle
while (JL_SFC1->CON & BIT(31));
//disable sfc
gpio_set_pull_up(IO_PORTC_03, 1);
gpio_set_direction(IO_PORTC_03, 1);
JL_SFC1->BAUD = 0;
JL_SFC1->CON &= ~BIT(0);
gpio_set_output_value(IO_PORTC_03, 1);
gpio_set_pull_up(IO_PORTC_03, 0);
gpio_set_direction(IO_PORTC_03, 0);
if (enable_spi) {
if (_nandflash.spi_num == SPI0) {
JL_SPI0->CON |= BIT(0);
} else if (_nandflash.spi_num == SPI1) {
JL_SPI1->CON |= BIT(0);
} else if (_nandflash.spi_num == SPI2) {
JL_SPI2->CON |= BIT(0);
}
}
}
u32 clock_get_sfc_freq(void);
static u32 iomc_con0_save = 0;
void spi12_iomc_deinit(hw_spi_dev spi, u8 mode);
static void nand_exit_spi_code(u32 cache_addr)
{
u8 temp[3];
temp[0] = GD_READ_FROM_CACHE_X4; //PageRead to cache
temp[1] = (u8)((cache_addr) >> 8);
temp[2] = (u8)cache_addr;
/* local_irq_disable(); */
spi_cs_l();//SPI_CS(0);
spi_dma_write(temp, 3);
gpio_set_pull_up(IO_PORTC_03, 0);
gpio_set_pull_down(IO_PORTC_03, 1);
gpio_set_direction(IO_PORTC_03, 1);
u32 sfc_clk = clock_get_sfc_freq();
if (sfc_clk <= 96000000) {
sfc_clk = 0;
} else if (sfc_clk <= 192000000) {
sfc_clk = 1;
}
/* nand_sfc1_resume(1, 1); */
nand_sfc1_resume(1, sfc_clk);
iomc_con0_save = JL_IOMC->CON0;
SFR(JL_IOMC->CON0, 13, 3, 4);//io mode portc(sfc1)
spi12_iomc_deinit(_nandflash.spi_num, _nandflash.spi_r_width);
/* printf("JL_SFC1->CON = 0x%x\n", JL_SFC1->CON); */
/* printf("JL_SFC1->BAUD = 0x%x,0x%x,0x%x\n", JL_SFC1->BAUD,JL_PORTC->OUT,JL_PORTC->DIR); */
}
void spi12_iomc_init(hw_spi_dev spi, u8 mode);
static void nand_enter_spi_code(void)
{
nand_sfc1_suspend(1);
JL_IOMC->CON0 = iomc_con0_save;
spi12_iomc_init(_nandflash.spi_num, _nandflash.spi_r_width);
/* local_irq_enable(); */
}
#include "asm/dma_copy.h"
void nand_sfc_read_cache_x4(u8 *buf, u32 offset, u32 len)
{
nand_exit_spi_code(offset);
u8 *p = (u8 *)(0x4fcfcfc);
dma_memcpy(buf, p, len);
dma_memcpy_wait_idle();
nand_enter_spi_code();
}
#endif//sfc_read
/*************************************************************************************
* 低功耗管理
************************************************************************************/
#define EX_FLASH_POWER_WORK (0)
#define EX_FLASH_POWER_CLOSE (1)
#define EX_FLASH_POWER_CLOSING (2)
#define EX_FLASH_POWER_OPENING (3)
#define USER_MASK_TYPE (BIT(16)|BIT(17))//删消息池使用
static u16 last_id = 1;//删消息池使用
/* #define FLASH_POWERON_TIME (1000)//1ms flash 上电使用延时 */
/* #define FLASH_EXIT_LOWPOWER_TIME (200)//200us 退出低功耗使用延时 */
#define FLASH_POWEROFF_TIME (200)//200us 掉电延时
static volatile u8 extern_flash_status = EX_FLASH_POWER_CLOSE;
extern void udelay(u32 us);
void nandflash_power_set(int enable)
{
#if (TCFG_EX_FLASH_POWER_IO != NO_CONFIG_PORT)
if (enable) {
/* putchar('\n'); */
/* putchar('n'); */
/* putchar('p'); */
/* putchar('e'); */
/* putchar('\n'); */
gpio_set_drive_strength(IO_PORT_SPILT(TCFG_EX_FLASH_POWER_IO), PORT_DRIVE_STRENGT_8p0mA);
gpio_set_mode(IO_PORT_SPILT(TCFG_EX_FLASH_POWER_IO), CONFIG_EX_FLASH_POWER_IO_CTRL);
udelay(200);
gpio_set_drive_strength(IO_PORT_SPILT(TCFG_EX_FLASH_POWER_IO), PORT_DRIVE_STRENGT_64p0mA);
} else {
/* putchar('\n'); */
/* putchar('n'); */
/* putchar('p'); */
/* putchar('d'); */
/* putchar('\n'); */
/*默认高阻断电,如果电路上有特殊设计,在此处和board_set_soft_poweroff修改 */
gpio_set_mode(IO_PORT_SPILT(TCFG_EX_FLASH_POWER_IO), !CONFIG_EX_FLASH_POWER_IO_CTRL);
}
#endif
}
void nandflash_poweroff(int priv)
{
/* printf("norflash_flash_poweroff \n\n"); */
#if (TCFG_EX_FLASH_POWER_IO != NO_CONFIG_PORT)
/* ASSERT(extern_flash_status == EX_FLASH_POWER_CLOSING); */
os_mutex_pend(&_nandflash.mutex, 0);
ftl_free_page_buf();
_nandflash_close();
udelay(FLASH_POWEROFF_TIME);
nandflash_power_set(0);
extern_flash_status = EX_FLASH_POWER_CLOSE;
os_mutex_post(&_nandflash.mutex);
#else
os_mutex_pend(&_nandflash.mutex, 0);
//预留,目前大多数nandflash没有低功耗处理,如nandflash支持低功耗命令,可在ioctl中添加流程,并使用下列代码处理
/* void *hd = dev_open("nand_flash", NULL); */
/* if (hd) { */
/* dev_ioctl(hd, IOCTL_CMD_SUSPEND, NULL); */
/* } */
extern_flash_status = EX_FLASH_POWER_CLOSE;
os_mutex_post(&_nandflash.mutex);
#endif
}
void nandflash_poweron(int priv)
{
/* printf("norflash_flash_poweron \n\n"); */
if (extern_flash_status == EX_FLASH_POWER_CLOSE) {
#if (TCFG_EX_FLASH_POWER_IO != NO_CONFIG_PORT)
nandflash_power_set(1);
extern_flash_status = EX_FLASH_POWER_WORK;
#else
//预留,目前大多数nandflash没有低功耗处理,如nandflash支持低功耗命令,可在ioctl中添加流程,并使用下列代码处理
/* void *hd = dev_open("nand_flash", NULL); */
/* if (hd) { */
/* dev_ioctl(hd, IOCTL_CMD_RESUME, NULL); */
/* } */
extern_flash_status = EX_FLASH_POWER_WORK;
#endif
}
}
static void nandflash_power_check()
{
/* log_info("%s stu:%d \n",__func__,extern_flash_status); */
#if (TCFG_EX_FLASH_POWER_IO != NO_CONFIG_PORT)
if (!extern_flash_status) {
return;
}
local_irq_disable();
if (extern_flash_status == EX_FLASH_POWER_CLOSING) {
os_taskq_del_type("app_core", Q_CALLBACK | last_id | USER_MASK_TYPE);
extern_flash_status = EX_FLASH_POWER_WORK;
//ASSERT(0);
//验证临界
} else if (extern_flash_status == EX_FLASH_POWER_CLOSE) {
local_irq_enable();
nandflash_power_set(1);
/* udelay(FLASH_POWERON_TIME); */
_nandflash_open(NULL);
local_irq_disable();
extern_flash_status = EX_FLASH_POWER_WORK;
}
local_irq_enable();
#else
if (!extern_flash_status) {
return;
}
local_irq_disable();
if (extern_flash_status == EX_FLASH_POWER_CLOSING) {
os_taskq_del_type("app_core", Q_CALLBACK | last_id | USER_MASK_TYPE);
extern_flash_status = EX_FLASH_POWER_WORK;
//ASSERT(0);
//验证临界
} else if (extern_flash_status == EX_FLASH_POWER_CLOSE) {
local_irq_enable();
//预留,目前大多数nandflash没有低功耗处理,如nandflash支持低功耗命令,可在ioctl中添加流程,并使用下列代码处理
/* void *hd = dev_open("nand_flash", NULL); */
/* if (hd) { */
/* dev_ioctl(hd, IOCTL_CMD_RESUME, NULL); */
/* } */
local_irq_disable();
extern_flash_status = EX_FLASH_POWER_WORK;
}
local_irq_enable();
#endif
}
static u8 extern_flash_suspend(u32 timeout)
{
int msg[3];
/* ASSERT(extern_flash_status != EX_FLASH_POWER_OPENING); */
if (extern_flash_status == EX_FLASH_POWER_CLOSE) {
return 0;
}
if (extern_flash_status == EX_FLASH_POWER_CLOSING) {
return 1;
}
extern_flash_status = EX_FLASH_POWER_CLOSING;
msg[0] = (int)nandflash_poweroff;
msg[1] = 1;
msg[2] = (int)NULL;
last_id++;
if (!last_id) {
last_id = 1;
}
os_taskq_post_type("app_core", Q_CALLBACK | last_id | USER_MASK_TYPE, 3, msg);
return 1;
}
static u8 extern_flash_resume(u32 timeout)
{
/*出低功耗且操作flash时再恢复供电*/
return 0;
}
//低功耗线程请求所有模块关闭,由对应线程处理
REGISTER_LP_REQUEST(power_flash_target) = {
.name = "nandflash",
.request_enter = extern_flash_suspend,
.request_exit = extern_flash_resume,
};
/* #include "nandflash_test.c" */
#endif