2138 lines
70 KiB
C
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
|
|
|