You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1444 lines
46 KiB
1444 lines
46 KiB
#include<pmon.h>
|
|
#include<asm.h>
|
|
#include<machine/types.h>
|
|
#include<linux/mtd/mtd.h>
|
|
#include<linux/mtd/nand.h>
|
|
#include<linux/mtd/partitions.h>
|
|
#include<sys/malloc.h>
|
|
|
|
#ifndef __iomem
|
|
#define __iomem
|
|
#endif
|
|
|
|
#define DMA_DESP 0x80800000
|
|
#define DMA_DESP_ORDER 0x80800008 // bit3 :1 ,means dma start; otherwize,
|
|
#define DMA_ASK_ORDER 0x80800004
|
|
|
|
#define DDR_PHYADDR 0x82400000
|
|
#define DDR_PHY 0x82400000
|
|
#define DDR_ADDR 0x82400000
|
|
|
|
|
|
#define NAND_BASE 0xbbee0000
|
|
#define DMA_ACCESS_ADDR 0x1bee0040
|
|
#define ORDER_REG_ADDR 0xbbd00100
|
|
#define MAX_BUFF_SIZE 4096
|
|
#define PAGE_SHIFT 12
|
|
#define NO_SPARE_ADDRH(x) ((x) >> (32 - (PAGE_SHIFT - 1 )))
|
|
#define NO_SPARE_ADDRL(x) ((x) << (PAGE_SHIFT - 1))
|
|
#define SPARE_ADDRH(x) ((x) >> (32 - (PAGE_SHIFT )))
|
|
#define SPARE_ADDRL(x) ((x) << (PAGE_SHIFT ))
|
|
#define ALIGN_DMA(x) (((x)+ 3)/4)
|
|
#define CHIP_DELAY_TIMEOUT (2*HZ/10)
|
|
|
|
#define STATUS_TIME_LOOP_R 300
|
|
#define STATUS_TIME_LOOP_WS 50
|
|
#define STATUS_TIME_LOOP_WM 40
|
|
#define STATUS_TIME_LOOP_E 40
|
|
#define NAND_CMD 0x1
|
|
#define NAND_ADDRL 0x2
|
|
#define NAND_ADDRH 0x4
|
|
#define NAND_TIMING 0x8
|
|
#define NAND_IDL 0x10
|
|
#define NAND_STATUS_IDL 0x20
|
|
#define NAND_PARAM 0x40
|
|
#define NAND_OP_NUM 0X80
|
|
#define NAND_CS_RDY_MAP 0x100
|
|
|
|
#define DMA_ORDERAD 0x1
|
|
#define DMA_SADDR 0x2
|
|
#define DMA_DADDR 0x4
|
|
#define DMA_LENGTH 0x8
|
|
#define DMA_STEP_LENGTH 0x10
|
|
#define DMA_STEP_TIMES 0x20
|
|
#define DMA_CMD 0x40
|
|
|
|
|
|
enum{
|
|
ERR_NONE = 0,
|
|
ERR_DMABUSERR = -1,
|
|
ERR_SENDCMD = -2,
|
|
ERR_DBERR = -3,
|
|
ERR_BBERR = -4,
|
|
};
|
|
|
|
enum{
|
|
STATE_READY = 0,
|
|
STATE_BUSY ,
|
|
};
|
|
|
|
struct ls1g_nand_platform_data{
|
|
int enable_arbiter;
|
|
struct mtd_partition *parts;
|
|
unsigned int nr_parts;
|
|
};
|
|
|
|
struct ls1g_nand_cmdset {
|
|
uint32_t cmd_valid:1;
|
|
uint32_t read:1;
|
|
uint32_t write:1;
|
|
uint32_t erase_one:1;
|
|
uint32_t erase_con:1;
|
|
uint32_t read_id:1;
|
|
uint32_t reset:1;
|
|
uint32_t read_sr:1;
|
|
uint32_t op_main:1;
|
|
uint32_t op_spare:1;
|
|
uint32_t done:1;
|
|
uint32_t resv1:5;//11-15 reserved
|
|
uint32_t nand_rdy:4;//16-19
|
|
uint32_t nand_ce:4;//20-23
|
|
uint32_t resv2:8;//24-32 reserved
|
|
};
|
|
|
|
struct ls1g_nand_dma_desc{
|
|
uint32_t orderad;
|
|
uint32_t saddr;
|
|
uint32_t daddr;
|
|
uint32_t length;
|
|
uint32_t step_length;
|
|
uint32_t step_times;
|
|
uint32_t cmd;
|
|
};
|
|
|
|
struct ls1g_nand_dma_cmd{
|
|
uint32_t dma_int_mask:1;
|
|
uint32_t dma_int:1;
|
|
uint32_t dma_sl_tran_over:1;
|
|
uint32_t dma_tran_over:1;
|
|
uint32_t dma_r_state:4;
|
|
uint32_t dma_w_state:4;
|
|
uint32_t dma_r_w:1;
|
|
uint32_t dma_cmd:2;
|
|
uint32_t revl:17;
|
|
};
|
|
|
|
struct ls1g_nand_desc{
|
|
uint32_t cmd;
|
|
uint32_t addrl;
|
|
uint32_t addrh;
|
|
uint32_t timing;
|
|
uint32_t idl;//readonly
|
|
uint32_t status_idh;//readonly
|
|
uint32_t param;
|
|
uint32_t op_num;
|
|
uint32_t cs_rdy_map;
|
|
};
|
|
|
|
struct ls1g_nand_info {
|
|
struct nand_chip nand_chip;
|
|
|
|
// struct platform_device *pdev;
|
|
/* MTD data control*/
|
|
unsigned int buf_start;
|
|
unsigned int buf_count;
|
|
/* NAND registers*/
|
|
void __iomem *mmio_base;
|
|
struct ls1g_nand_desc nand_regs;
|
|
unsigned int nand_addrl;
|
|
unsigned int nand_addrh;
|
|
unsigned int nand_timing;
|
|
unsigned int nand_op_num;
|
|
unsigned int nand_cs_rdy_map;
|
|
unsigned int nand_cmd;
|
|
|
|
/* DMA information */
|
|
|
|
struct ls1g_nand_dma_desc dma_regs;
|
|
unsigned int order_reg_addr;
|
|
unsigned int dma_orderad;
|
|
unsigned int dma_saddr;
|
|
unsigned int dma_daddr;
|
|
unsigned int dma_length;
|
|
unsigned int dma_step_length;
|
|
unsigned int dma_step_times;
|
|
unsigned int dma_cmd;
|
|
unsigned int drcmr_dat;//dma descriptor address;
|
|
unsigned int drcmr_dat_phys;
|
|
size_t drcmr_dat_size;
|
|
unsigned char *data_buff;//dma data buffer;
|
|
unsigned int data_buff_phys;
|
|
size_t data_buff_size;
|
|
unsigned int data_ask;
|
|
unsigned int data_ask_phys;
|
|
unsigned int data_length;
|
|
unsigned int cac_size;
|
|
unsigned int size;
|
|
unsigned int num;
|
|
|
|
/* relate to the command */
|
|
unsigned int state;
|
|
// int use_ecc; /* use HW ECC ? */
|
|
size_t data_size; /* data size in FIFO */
|
|
unsigned int cmd;
|
|
unsigned int cmd_prev;
|
|
unsigned int page_addr;
|
|
// struct completion cmd_complete;
|
|
unsigned int seqin_column;
|
|
unsigned int seqin_page_addr;
|
|
};
|
|
|
|
struct ls1g_nand_ask_regs{
|
|
unsigned int dma_order_addr;
|
|
unsigned int dma_mem_addr;
|
|
unsigned int dma_dev_addr;
|
|
unsigned int dma_length;
|
|
unsigned int dma_step_length;
|
|
unsigned int dma_step_times;
|
|
unsigned int dma_state_tmp;
|
|
};
|
|
|
|
static struct mtd_info *ls1g_soc_mtd = NULL;
|
|
#if 0
|
|
struct mtd_info *_soc_mtd = NULL;
|
|
#define KERNEL_AREA_SIZE 32*1024*1024
|
|
const struct mtd_partition partition_info[] = {
|
|
// {name ,size,offset,mask_flags }
|
|
{"kernel",KERNEL_AREA_SIZE,0,0},
|
|
{"os",0,KERNEL_AREA_SIZE,0},
|
|
{(void *)0,0,0,0}
|
|
};
|
|
#endif
|
|
static struct nand_ecclayout hw_largepage_ecclayout = {
|
|
.eccbytes = 24,
|
|
.eccpos = {
|
|
40, 41, 42, 43, 44, 45, 46, 47,
|
|
48, 49, 50, 51, 52, 53, 54, 55,
|
|
56, 57, 58, 59, 60, 61, 62, 63},
|
|
.oobfree = { {2, 38} }
|
|
};
|
|
|
|
#define show_data_debug 0
|
|
#define show_debug(x,y) show_debug_msk(x,y)
|
|
#define show_debug_msk(x,y) do{ if(show_data_debug) {printk(KERN_ERR "%s:\n",__func__);show_data(x,y);} }while(0)
|
|
|
|
static void show_data(void * base,int num)
|
|
{
|
|
int i=0;
|
|
unsigned char *arry=( unsigned char *) base;
|
|
printk(KERN_ERR "base==0x%08x \n",arry);
|
|
for(i=0;i<num;i++){
|
|
if(!(i % 32)){
|
|
printk(KERN_ERR "\n");
|
|
}
|
|
if(!(i % 16)){
|
|
printk(" ");
|
|
}
|
|
printk("%02x ",arry[i]);
|
|
}
|
|
printk(KERN_ERR "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ls1g_nand_ecc_calculate(struct mtd_info *mtd,
|
|
const uint8_t *dat, uint8_t *ecc_code)
|
|
{
|
|
return 0;
|
|
}
|
|
static int ls1g_nand_ecc_correct(struct mtd_info *mtd,
|
|
uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
|
|
{
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
/*
|
|
* Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we
|
|
* consider it as a ecc error which will tell the caller the
|
|
* read fail We have distinguish all the errors, but the
|
|
* nand_read_ecc only check this function return value
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
static void ls1g_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static int ls1g_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
|
|
{
|
|
//udelay(50);
|
|
return 0;
|
|
}
|
|
|
|
static void ls1g_nand_select_chip(struct mtd_info *mtd, int chip)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static int ls1g_nand_dev_ready(struct mtd_info *mtd)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void ls1g_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
{
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
int i,real_len = min_t(size_t, len, info->buf_count - info->buf_start);
|
|
|
|
memcpy(buf, info->data_buff + info->buf_start, real_len);
|
|
show_debug(info->data_buff,0x40);
|
|
info->buf_start += real_len;
|
|
}
|
|
|
|
static u16 ls1g_nand_read_word(struct mtd_info *mtd)
|
|
{
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
u16 retval = 0xFFFF;
|
|
if(!(info->buf_start & 0x1) && info->buf_start < info->buf_count){
|
|
retval = *(u16 *)(info->data_buff + info->buf_start);
|
|
}
|
|
info->buf_start += 2;
|
|
return retval;
|
|
}
|
|
|
|
static uint8_t ls1g_nand_read_byte(struct mtd_info *mtd)
|
|
{
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
char retval = 0xFF;
|
|
|
|
if (info->buf_start < info->buf_count)
|
|
/* Has just send a new command? */
|
|
retval = info->data_buff[(info->buf_start)++];
|
|
show_debug(info->data_buff,6);
|
|
return retval;
|
|
}
|
|
|
|
static void ls1g_nand_write_buf(struct mtd_info *mtd,const uint8_t *buf, int len)
|
|
{
|
|
int i;
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
|
|
// info->buf_count = real_len;
|
|
|
|
memcpy(info->data_buff + info->buf_start, buf, real_len);
|
|
show_debug(info->data_buff,0x20);
|
|
info->buf_start += real_len;
|
|
}
|
|
|
|
static int ls1g_nand_verify_buf(struct mtd_info *mtd,const uint8_t *buf, int len)
|
|
{
|
|
int i=0;
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
show_debug(info->data_buff,0x20);
|
|
while(len--){
|
|
if(buf[i++] != ls1g_nand_read_byte(mtd) ){
|
|
printk("?????????????????????????????????????????????????????verify error...\n\n");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ls1g_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int column, int page_addr);
|
|
static void ls1g_nand_init_mtd(struct mtd_info *mtd,struct ls1g_nand_info *info);
|
|
|
|
int ls1g_nand_init(struct mtd_info *mtd)
|
|
{
|
|
int ret=0;
|
|
ret = ls1g_nand_pmon_info_init(mtd->priv, mtd);
|
|
ls1g_nand_init_mtd(mtd, (struct ls1g_nand_info *)(mtd->priv));
|
|
return ret;
|
|
}
|
|
|
|
static void ls1g_nand_init_mtd(struct mtd_info *mtd, struct ls1g_nand_info *info)
|
|
{
|
|
struct nand_chip *this = &info->nand_chip;
|
|
|
|
//(各种芯片选项)在一定程度上设置用于告诉nand_scan函数有关特殊的函数操作.
|
|
this->options = 8;//(f->flash_width == 16) ? NAND_BUSWIDTH_16: 0;
|
|
|
|
this->waitfunc = ls1g_nand_waitfunc; /*等待设备准备好 硬件相关函数*/
|
|
this->select_chip = ls1g_nand_select_chip; /*控制CE信号*/
|
|
this->dev_ready = ls1g_nand_dev_ready; /*板特定的设备ready/busy信息*/
|
|
this->cmdfunc = ls1g_nand_cmdfunc; /*命令处理函数*/
|
|
this->read_word = ls1g_nand_read_word; /*从芯片读一个字*/
|
|
this->read_byte = ls1g_nand_read_byte; /*从芯片读一个字节*/
|
|
this->read_buf = ls1g_nand_read_buf; /*将芯片数据读到缓冲区*/
|
|
this->write_buf = ls1g_nand_write_buf; /*将缓冲区内容写入芯片*/
|
|
this->verify_buf = ls1g_nand_verify_buf; /*验证芯片和写入缓冲区中的数据*/
|
|
|
|
// this->ecc.mode = NAND_ECC_NONE;
|
|
this->ecc.mode = NAND_ECC_SOFT; /*ECC模式 这里是软件模式*/
|
|
// this->ecc.hwctl = ls1g_nand_ecc_hwctl;
|
|
// this->ecc.calculate = ls1g_nand_ecc_calculate;
|
|
// this->ecc.correct = ls1g_nand_ecc_correct;
|
|
// this->ecc.size = 2048;
|
|
// this->ecc.bytes = 24;
|
|
|
|
// this->ecc.layout = &hw_largepage_ecclayout;
|
|
// mtd->owner = THIS_MODULE;
|
|
}
|
|
|
|
#define write_z_cmd do{ \
|
|
*((volatile unsigned int *)(0xbbee0000)) = 0; \
|
|
*((volatile unsigned int *)(0xbbee0000)) = 0; \
|
|
*((volatile unsigned int *)(0xbbee0000)) = 400; \
|
|
}while(0)
|
|
|
|
|
|
static unsigned ls1g_nand_status(struct ls1g_nand_info *info)
|
|
{
|
|
struct ls1g_nand_desc *nand_regs = (volatile struct ls1g_nand_desc *)(info->mmio_base);
|
|
struct ls1g_nand_cmdset *nand_cmd = (struct ls1g_nand_cmdset *)(&(nand_regs->cmd));
|
|
udelay(100);
|
|
return(nand_cmd->done);
|
|
}
|
|
|
|
/*
|
|
* flags & 0x1 orderad
|
|
* flags & 0x2 saddr
|
|
* flags & 0x4 daddr
|
|
* flags & 0x8 length
|
|
* flags & 0x10 step_length
|
|
* flags & 0x20 step_times
|
|
* flags & 0x40 cmd
|
|
***/
|
|
static void show_dma_regs(void *dma_regs,int flag)
|
|
{
|
|
unsigned int *regs=dma_regs;
|
|
printf("dump dma register\n");
|
|
printf("0x%08x:0x%08x\n",regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
|
|
regs=0xbbee0000;
|
|
printf("dump nand register\n");
|
|
printf("0x%08x:0x%08x\n",regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
printf("0x%08x:0x%08x\n",++regs,*regs);
|
|
|
|
// if(flag)
|
|
printf("ORDER_REG_ADDR:0x%08x\n",*(volatile unsigned int *)ORDER_REG_ADDR);
|
|
}
|
|
/*before DMA rw=1(hit writeback and invalidated) ;after DMA rw=0(invalidated)*/
|
|
#define __dma_write_ddr2(cmd,bit) (~((cmd)&(1<<bit)))
|
|
static void dma_cache_nand(struct ls1g_nand_info *info,unsigned char rw )
|
|
{
|
|
struct ls1g_nand_dma_desc *dma_base = (volatile struct ls1g_nand_dma_desc *)(info->drcmr_dat);
|
|
|
|
if(__dma_write_ddr2(dma_base->cmd,12))
|
|
{
|
|
// CPU_IOFlushDCache(((dma_base->saddr)&0x1fffffff)|0x80000000,(dma_base->length)*4,rw);
|
|
}
|
|
|
|
}
|
|
|
|
static void nand_cache_inv(unsigned long base,unsigned long num)
|
|
{
|
|
|
|
CPU_IOFlushDCache((base & 0x1fffffff)|0x80000000,num,0);
|
|
}
|
|
|
|
static void nand_cache_wb(unsigned long base,unsigned long num)
|
|
{
|
|
CPU_IOFlushDCache((base & 0x1fffffff)|0x80000000,num,1);
|
|
}
|
|
|
|
static void dma_setup(unsigned int flags,struct ls1g_nand_info *info)
|
|
{
|
|
int order;
|
|
struct ls1g_nand_dma_desc *dma_base = (volatile struct ls1g_nand_dma_desc *)(info->drcmr_dat);
|
|
dma_base->orderad = (flags & DMA_ORDERAD)== DMA_ORDERAD ? info->dma_regs.orderad : info->dma_orderad;
|
|
dma_base->saddr = (flags & DMA_SADDR)== DMA_SADDR ? info->dma_regs.saddr : info->dma_saddr;
|
|
dma_base->daddr = (flags & DMA_DADDR)== DMA_DADDR ? info->dma_regs.daddr : info->dma_daddr;
|
|
dma_base->length = (flags & DMA_LENGTH)== DMA_LENGTH ? info->dma_regs.length: info->dma_length;
|
|
info->data_length = info->dma_regs.length;
|
|
dma_base->step_length = (flags & DMA_STEP_LENGTH)== DMA_STEP_LENGTH ? info->dma_regs.step_length: info->dma_step_length;
|
|
dma_base->step_times = (flags & DMA_STEP_TIMES)== DMA_STEP_TIMES ? info->dma_regs.step_times: info->dma_step_times;
|
|
dma_base->cmd = (flags & DMA_CMD)== DMA_CMD ? info->dma_regs.cmd: info->dma_cmd;
|
|
/*flush cache before DMA operation*/
|
|
if((dma_base->cmd)&(0x1 << 12)){
|
|
//nand_cache_wb((unsigned long)(info->data_buff),info->cac_size);
|
|
}
|
|
//nand_cache_wb((unsigned long)(info->drcmr_dat),0x20);
|
|
|
|
//printf("dump dma descriptor after dma set up \n");
|
|
//printf("orderad = %08x, saddr = %08x, daddr = %08x, length = %08x, setup_length = %08x, cmd = %08x\n", dma_base->orderad, dma_base->saddr, dma_base->daddr , dma_base->length, dma_base->step_length, dma_base->cmd);
|
|
|
|
*(volatile unsigned int *)(info->order_reg_addr) = ((unsigned int )info->drcmr_dat_phys) | 0x1<<3;
|
|
// memset(&(info->dma_regs),0,sizeof(struct ls1g_nand_dma_desc));
|
|
}
|
|
|
|
static void dma_ask(struct ls1g_nand_info *info)
|
|
{
|
|
memset((char *)info->data_ask,0,sizeof(struct ls1g_nand_ask_regs));
|
|
*(volatile unsigned int *)info->order_reg_addr = 0x1<<2|( (info->data_ask_phys)& 0xfffffff0) | 0x80000000;
|
|
//show_dma_regs((void *)(info->data_ask),1);
|
|
}
|
|
|
|
/**
|
|
* flags & 0x1 cmd
|
|
* flags & 0x2 addrl
|
|
* flags & 0x4 addrh
|
|
* flags & 0x8 timing
|
|
* flags & 0x10 idl
|
|
* flags & 0x20 status_idh
|
|
* flags & 0x40 param
|
|
* flags & 0x80 op_num
|
|
* flags & 0x100 cs_rdy_map
|
|
****/
|
|
static void nand_setup(unsigned int flags ,struct ls1g_nand_info *info)
|
|
{
|
|
// printk("addrl+++++++++++++++++++++==%x\n\n",info->nand_regs.addrl);
|
|
int i;
|
|
struct ls1g_nand_desc *nand_base = (struct ls1g_nand_desc *)(info->mmio_base);
|
|
nand_base->cmd = 0;
|
|
nand_base->cmd = 0x40; // reset
|
|
udelay(500);
|
|
|
|
nand_base->addrl = (flags & NAND_ADDRL)==NAND_ADDRL ? info->nand_regs.addrl: info->nand_addrl;
|
|
nand_base->addrh = (flags & NAND_ADDRH)==NAND_ADDRH ? info->nand_regs.addrh: info->nand_addrh;
|
|
//nand_base->timing = (flags & NAND_TIMING)==NAND_TIMING ? info->nand_regs.timing: info->nand_timing;
|
|
nand_base->op_num = (flags & NAND_OP_NUM)==NAND_OP_NUM ? info->nand_regs.op_num: info->nand_op_num; //NAND读写操作Byte数;擦除为块数
|
|
nand_base->cs_rdy_map = (flags & NAND_CS_RDY_MAP)==NAND_CS_RDY_MAP ? info->nand_regs.cs_rdy_map: info->nand_cs_rdy_map;
|
|
if(flags & NAND_CMD){
|
|
nand_base->cmd = (info->nand_regs.cmd) &(~0xff);
|
|
nand_base->cmd = info->nand_regs.cmd;
|
|
//printf("nand cmd: %08x, nand op_num = %08x, info op_num = %08x\n", nand_base->cmd, info->nand_regs.op_num, info->nand_op_num);
|
|
//printf("info->nand_regs.addrl: %08x\t info->nand_regs.addrh: %08x\n", info->nand_regs.addrl, info->nand_regs.addrh);
|
|
if(info->nand_regs.cmd & 0x20){
|
|
i = 100;
|
|
//判断done操作完成 nand_cmd 0xbbee0000
|
|
while(!ls1g_nand_status(info)){
|
|
if(!(i--)){
|
|
write_z_cmd; //写操作
|
|
break;
|
|
}
|
|
udelay(100);
|
|
}
|
|
*(int *)(info->data_buff) = nand_base->idl;
|
|
}
|
|
}
|
|
else
|
|
nand_base->cmd = info->nand_cmd;
|
|
|
|
//printf("cmd: %08x, naddrl: %08x addrh: %08x\n",nand_base->cmd, nand_base->addrl, nand_base->addrh);
|
|
}
|
|
|
|
static int sync_dma(struct ls1g_nand_info *info)
|
|
{
|
|
int *end ;
|
|
struct ls1g_nand_ask_regs *ask = info->data_ask;
|
|
|
|
//end = ((unsigned int)(info->data_buff_phys)&0x1fffffff) + info->data_length*4;
|
|
end = ((unsigned int)(info->data_buff_phys)) + info->data_length*4;
|
|
|
|
|
|
#if 1
|
|
//printf("sync length ...\n");
|
|
dma_ask(info);
|
|
#if 0
|
|
while(1){
|
|
udelay(100);
|
|
if(ask->dma_mem_addr == end)
|
|
break;
|
|
printf("ask->dma_mem_addr = %08x, end = %08x\n", ask->dma_mem_addr, end);
|
|
}
|
|
#endif
|
|
//printf("sync dma step1 Over!\n");
|
|
while((*((unsigned int*) (NAND_BASE)) & (0x1<<10)) == 0){
|
|
udelay(500);
|
|
//printf("sync dma: cmd = %08x ...\n", *((unsigned int*) (NAND_BASE)));
|
|
}
|
|
|
|
#endif
|
|
/*flush cache after DMA operation*/
|
|
// dma_cache_nand(info,0);
|
|
return 0;
|
|
}
|
|
|
|
static void ls1g_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int column, int page_addr)
|
|
{
|
|
struct ls1g_nand_info *info = mtd->priv;
|
|
int ret,i,nandcmd;
|
|
unsigned cmd_prev;
|
|
int status_time,page_prev;
|
|
// int timeout = CHIP_DELAY_TIMEOUT;
|
|
unsigned int base;
|
|
// init_completion(&info->cmd_complete);
|
|
static int ccc=0;
|
|
|
|
cmd_prev = info->cmd;
|
|
page_prev = info->page_addr;
|
|
info->cmd = command;
|
|
info->page_addr = page_addr;
|
|
//show_dma_regs((void *)(info->mmio_base),0);
|
|
switch(command){
|
|
case NAND_CMD_READOOB:
|
|
//printf("NAND_CMD_READOOB: page = %08x\n", page_addr);
|
|
if(info->state == STATE_BUSY){
|
|
printk("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
info->buf_count = mtd->oobsize;
|
|
info->buf_start = 0;
|
|
info->cac_size = info->buf_count;
|
|
if(info->buf_count <=0 )
|
|
break;
|
|
/*nand regs set*/
|
|
info->dma_regs.cmd = 0; //下一个描述符地址寄存器
|
|
info->nand_regs.cmd = 0; //
|
|
#if 0
|
|
info->nand_regs.addrh = SPARE_ADDRH(page_addr); //读、写、擦除操作起始地址高8位
|
|
info->nand_regs.addrl = SPARE_ADDRL(page_addr) + mtd->writesize; //读、写、擦除操作起始地址低32位
|
|
#else
|
|
info->nand_regs.addrh = page_addr; //读、写、擦除操作起始地址高8位
|
|
info->nand_regs.addrl = 0x800; //读、写、擦除操作起始地址低32位
|
|
#endif
|
|
info->nand_regs.op_num = info->buf_count; //NAND读写操作Byte数;擦除为块数
|
|
/*nand cmd set */
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->read = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->op_spare = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
/*dma regs config*/
|
|
info->dma_regs.length = ALIGN_DMA(info->buf_count); //传输数据长度寄存器 代表一块被搬运内容的长度,单位是字
|
|
((struct ls1g_nand_dma_cmd *)&(info->dma_regs.cmd))->dma_int_mask = 0;
|
|
/*dma GO set*/
|
|
nand_setup(NAND_ADDRL|NAND_ADDRH|NAND_OP_NUM|NAND_CMD, info);
|
|
dma_setup(DMA_LENGTH|DMA_CMD, info);
|
|
//printf("first sync dma\n");
|
|
sync_dma(info);
|
|
//show_dma_regs((void *)(info->data_ask),1); // by xqch
|
|
break;
|
|
#if 0
|
|
case NAND_CMD_READOOB:
|
|
info->state = STATE_BUSY;
|
|
info->buf_count = mtd->oobsize - column;
|
|
info->buf_start = 0;
|
|
if(info->buf_count <=0 )
|
|
break;
|
|
/*nand regs set*/
|
|
info->nand_regs.addrh = page_addr >> (32 - PAGE_SHIFT);
|
|
|
|
info->nand_regs.addrl = (page_addr << PAGE_SHIFT) + column + 2048;
|
|
info->nand_regs.op_num = info->buf_count;
|
|
/*nand cmd set */
|
|
info->nand_regs.cmd=0;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->read = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->op_spare = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
/*dma regs config*/
|
|
info->dma_regs.length =0;
|
|
info->dma_regs.cmd = 0;
|
|
info->dma_regs.length = (info->buf_count + 3)/4;
|
|
((struct ls1g_nand_dma_cmd *)&(info->dma_regs.cmd))->dma_int_mask = 0;
|
|
/*dma GO set*/
|
|
dma_setup(DMA_LENGTH|DMA_CMD,info);
|
|
nand_setup(NAND_ADDRL|NAND_ADDRH|NAND_OP_NUM|NAND_CMD,info);
|
|
// printf("\ncmdfunc\n");
|
|
sync_dma(info);
|
|
break;
|
|
#endif
|
|
case NAND_CMD_READ0:
|
|
//printf("NAND_CMD_READ0: page = %08x\n", page_addr);
|
|
if(info->state == STATE_BUSY){
|
|
printk("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
info->buf_start = 0 ;
|
|
info->cac_size = info->buf_count;
|
|
if(info->buf_count <=0 )
|
|
break;
|
|
#if 0
|
|
info->buf_count = mtd->oobsize + mtd->writesize ;
|
|
info->nand_regs.addrh = SPARE_ADDRH(page_addr);
|
|
info->nand_regs.addrl = SPARE_ADDRL(page_addr);
|
|
#else
|
|
//info->buf_count = mtd->writesize ;
|
|
info->buf_count = mtd->oobsize + mtd->writesize ;
|
|
info->nand_regs.addrh = page_addr;
|
|
info->nand_regs.addrl = 0x000;
|
|
#endif
|
|
info->nand_regs.op_num = info->buf_count;
|
|
/*nand cmd set */
|
|
info->nand_regs.cmd = 0;
|
|
info->dma_regs.cmd = 0;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->read = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->op_spare = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->op_main = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
/*dma regs config*/
|
|
info->dma_regs.length = ALIGN_DMA(info->buf_count);
|
|
((struct ls1g_nand_dma_cmd *)&(info->dma_regs.cmd))->dma_int_mask = 0;
|
|
dma_setup(DMA_LENGTH|DMA_CMD,info);
|
|
nand_setup(NAND_ADDRL|NAND_ADDRH|NAND_OP_NUM|NAND_CMD,info);
|
|
|
|
//show_dma_regs((void *)(info->data_ask),1); // by xqch
|
|
sync_dma(info);
|
|
break;
|
|
case NAND_CMD_SEQIN:
|
|
if(info->state == STATE_BUSY){
|
|
printk("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
info->buf_count = mtd->oobsize + mtd->writesize - column;
|
|
info->buf_start = 0;
|
|
info->seqin_column = column;
|
|
|
|
info->seqin_page_addr = page_addr;
|
|
// complete(&info->cmd_complete);
|
|
//printf("NAND_CMD_SEQIN: column =%08x, page_addr = %08x\n", column, page_addr);
|
|
break;
|
|
case NAND_CMD_PAGEPROG:
|
|
//printf("NAND_CMD_PAGEPROG: column =%08x, page_addr = %08x\n", column, page_addr);
|
|
if(info->state == STATE_BUSY){
|
|
printf("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
if(cmd_prev != NAND_CMD_SEQIN){
|
|
printf("Prev cmd don't complete...\n");
|
|
break;
|
|
}
|
|
if(info->buf_count <= 0 )
|
|
break;
|
|
|
|
if(((info->num)++) % 512 == 0){
|
|
printk("nand have write : %d M\n",(info->size)++);
|
|
}
|
|
|
|
/*nand regs set*/
|
|
|
|
//info->buf_count = mtd->writesize ;
|
|
//printf("NAND_CMD_PAGEPROG: column = %08x\n", column);
|
|
//info->buf_count = mtd->oobsize + mtd->writesize - column;
|
|
info->buf_count = mtd->oobsize + mtd->writesize;
|
|
//printf("NAND_CMD_PAGEPROG: buf_count = %08x\n", info->buf_count);
|
|
#if 0
|
|
info->nand_regs.addrh = SPARE_ADDRH(info->seqin_page_addr);
|
|
info->nand_regs.addrl = SPARE_ADDRL(info->seqin_page_addr) + info->seqin_column;
|
|
#else
|
|
info->nand_regs.addrl = info->seqin_column;
|
|
info->nand_regs.addrh = info->seqin_page_addr;
|
|
#endif
|
|
info->nand_regs.op_num = info->buf_start;
|
|
|
|
/*nand cmd set */
|
|
info->nand_regs.cmd = 0;
|
|
info->dma_regs.cmd = 0;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->write = 1;
|
|
if(info->seqin_column < mtd->writesize)
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->op_main = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->op_spare = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
|
|
/*dma regs config*/
|
|
info->dma_regs.length = ALIGN_DMA(info->buf_count);
|
|
((struct ls1g_nand_dma_cmd *)&(info->dma_regs.cmd))->dma_int_mask = 0;
|
|
((struct ls1g_nand_dma_cmd *)&(info->dma_regs.cmd))->dma_r_w = 1;
|
|
nand_setup(NAND_ADDRL|NAND_ADDRH|NAND_OP_NUM|NAND_CMD,info);
|
|
dma_setup(DMA_LENGTH|DMA_CMD,info);
|
|
sync_dma(info);
|
|
break;
|
|
case NAND_CMD_RESET:
|
|
info->state = STATE_BUSY;
|
|
/*nand cmd set */
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->reset = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
nand_setup(NAND_CMD,info);
|
|
status_time = STATUS_TIME_LOOP_R;
|
|
while(!ls1g_nand_status(info)){
|
|
/*
|
|
if(!(status_time--)){
|
|
write_z_cmd;
|
|
break;
|
|
}
|
|
*/
|
|
udelay(50);
|
|
}
|
|
|
|
info->state = STATE_READY;
|
|
// complete(&info->cmd_complete);
|
|
break;
|
|
case NAND_CMD_ERASE1:
|
|
//printf("NAND_CMD_ERASE1: page = %08x\n", page_addr);
|
|
info->state = STATE_BUSY;
|
|
/*nand regs set*/
|
|
info->nand_regs.addrl = 0x00;
|
|
info->nand_regs.addrh = page_addr ;
|
|
/*nand cmd set */
|
|
info->nand_regs.cmd = 0;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->erase_one = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
nand_setup(NAND_ADDRL|NAND_ADDRH|NAND_OP_NUM|NAND_CMD,info);
|
|
status_time = STATUS_TIME_LOOP_E;
|
|
udelay(300);
|
|
while(!ls1g_nand_status(info)){
|
|
/*
|
|
if(!(status_time--)){
|
|
write_z_cmd;
|
|
break;
|
|
}
|
|
*/
|
|
udelay(50);
|
|
}
|
|
|
|
info->state = STATE_READY;
|
|
// complete(&info->cmd_complete);
|
|
break;
|
|
case NAND_CMD_STATUS:
|
|
info->buf_count = 0x1;
|
|
info->buf_start = 0x0;
|
|
*(unsigned char *)info->data_buff=ls1g_nand_status(info) | 0x80;
|
|
// complete(&info->cmd_complete);
|
|
break;
|
|
case NAND_CMD_READID:
|
|
if(info->state == STATE_BUSY){
|
|
printf("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
info->buf_count = 0x4;
|
|
info->buf_start = 0;
|
|
{
|
|
#define _NAND_IDL ( *((volatile unsigned int*)(0xbbee0010)))
|
|
#define _NAND_IDH (*((volatile unsigned int*)(0xbbee0014)))
|
|
#define _NAND_BASE 0xbbee0000
|
|
#define _NAND_SET_REG(x,y) do{*((volatile unsigned int*)(_NAND_BASE+x)) = (y);}while(0)
|
|
#define _NAND_READ_REG(x,y) do{(y) = *((volatile unsigned int*)(_NAND_BASE+x));}while(0)
|
|
|
|
unsigned int id_val_l=0,id_val_h=0;
|
|
unsigned int timing = 0;
|
|
unsigned char *data = (unsigned char *)(info->data_buff);
|
|
_NAND_READ_REG(0xc,timing);
|
|
_NAND_SET_REG(0xc,0x30f0);
|
|
_NAND_SET_REG(0x0,0x21);
|
|
|
|
while(((id_val_l |= _NAND_IDL) & 0xff) == 0){
|
|
id_val_h = _NAND_IDH;
|
|
}
|
|
|
|
//printk("id_val_l=0x%08x\nid_val_h=0x%08x\n",id_val_l,id_val_h);
|
|
_NAND_SET_REG(0xc,timing);
|
|
data[0] = (id_val_h & 0xff);
|
|
data[1] = (id_val_l & 0xff000000)>>24;
|
|
data[2] = (id_val_l & 0x00ff0000)>>16;
|
|
data[3] = (id_val_l & 0x0000ff00)>>8;
|
|
//printk(KERN_ERR "IDS=============================0x%x\n",*((int *)(info->data_buff)));
|
|
|
|
}
|
|
|
|
#if 0
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->read_id = 1;
|
|
((struct ls1g_nand_cmdset*)&(info->nand_regs.cmd))->cmd_valid = 1;
|
|
nand_setup(NAND_CMD,info);
|
|
status_time = STATUS_TIME_LOOP_R;
|
|
while(!ls1g_nand_status(info)){
|
|
if(!(status_time--)){
|
|
write_z_cmd;
|
|
break;
|
|
}
|
|
//udelay(30);
|
|
}
|
|
#endif
|
|
break;
|
|
case NAND_CMD_ERASE2:
|
|
case NAND_CMD_READ1:
|
|
break;
|
|
default :
|
|
printf(KERN_ERR "non-supported command.\n");
|
|
break;
|
|
}
|
|
|
|
if(info->cmd == NAND_CMD_READ0 || info->cmd == NAND_CMD_READOOB ){
|
|
//nand_cache_inv((unsigned long)(info->data_buff),info->cac_size);
|
|
}
|
|
|
|
info->state = STATE_READY;
|
|
}
|
|
|
|
int ls1g_nand_detect(struct mtd_info *mtd)
|
|
{
|
|
printf("NANDFlash info:\nerasesize\t%d B\nwritesize\t%d B\noobsize \t%d B\n",mtd->erasesize, mtd->writesize,mtd->oobsize );
|
|
return (mtd->erasesize != 1<<17 || mtd->writesize != 1<<11 || mtd->oobsize != 1<<6);
|
|
|
|
}
|
|
static void ls1g_nand_init_info(struct ls1g_nand_info *info)
|
|
{
|
|
//*((volatile unsigned int *)0xbbee0018) = 0x30000; //外部颗粒容量大小
|
|
info->num=0;
|
|
info->size=0;
|
|
info->cac_size = 0;
|
|
info->state = STATE_READY;
|
|
|
|
info->cmd_prev = -1;
|
|
info->page_addr = -1;
|
|
info->nand_addrl = 0x0;
|
|
info->nand_addrh = 0x0;
|
|
//info->nand_timing = 0x4<<8 | 0x12;
|
|
info->nand_op_num = 0x0;
|
|
info->nand_cs_rdy_map = 0x00000000;
|
|
info->nand_cmd = 0;
|
|
|
|
info->dma_orderad = 0x80811000;
|
|
info->dma_saddr = info->data_buff_phys;
|
|
info->dma_daddr = DMA_ACCESS_ADDR;
|
|
info->dma_length = 0x0;
|
|
info->dma_step_length = 0x0;
|
|
info->dma_step_times = 0x1;
|
|
info->dma_cmd = 0x0;
|
|
|
|
info->order_reg_addr = ORDER_REG_ADDR;
|
|
}
|
|
|
|
int ls1g_nand_pmon_info_init(struct ls1g_nand_info *info, struct mtd_info *mtd)
|
|
{
|
|
info->drcmr_dat = ((unsigned int)(malloc(sizeof(struct ls1g_nand_dma_desc),M_DMAMAP,M_WAITOK))&0x1fffffff) | 0x80000000;
|
|
info->drcmr_dat = 0x82000000; //DMA描述符地址
|
|
info->drcmr_dat = (unsigned int)((info->drcmr_dat + 15) & ~0xff); //DMA描述符地址
|
|
if(info->drcmr_dat == NULL)
|
|
return -1;
|
|
info->drcmr_dat_phys = ((info->drcmr_dat) & 0x1fffffff) | 0x80000000; //DMA描述符物理地址
|
|
|
|
info->mmio_base = 0x1bee0000 | 0xa0000000; //NAND寄存器基地址
|
|
|
|
//info->data_buff = ((unsigned int)(malloc(MAX_BUFF_SIZE,M_DMAMAP,M_WAITOK))&0x1fffffff)|0x80000000;
|
|
info->data_buff = 0x8d000000;
|
|
if(info->data_buff == NULL)
|
|
return -1;
|
|
printf("data_buff==0x%08x\n",info->data_buff);
|
|
info->data_buff_phys = ((unsigned int)(info->data_buff) & 0x1fffffff) | 0x80000000; //DAM数据缓存物理地址
|
|
printf("data_buff_phys==0x%08x\n",info->data_buff_phys);
|
|
|
|
// info->data_ask = ((unsigned int)(malloc(sizeof(struct ls1g_nand_ask_regs),M_DMAMAP,M_WAITOK))&0x1fffffff)|0xa0000000;
|
|
info->data_ask = 0x82400000;//malloc(sizeof(struct ls1g_nand_ask_regs),M_DMAMAP,M_WAITOK); // address for dma descriptor
|
|
if(info->data_ask ==NULL)
|
|
return -1;
|
|
info->data_ask_phys = ( info->data_ask & 0x1fffffff) |0x80000000;
|
|
|
|
ls1g_nand_init_info(info);
|
|
/*
|
|
if(ls1g_nand_detect(mtd)){
|
|
printk(KERN_ERR "PMON driver don't support the NANDFlash!\n");
|
|
return -1;
|
|
}
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
static void find_good_part(struct mtd_info *ls1g_soc_mtd)
|
|
{
|
|
int offs;
|
|
int start=-1;
|
|
char name[20];
|
|
int idx=0;
|
|
|
|
for(offs=0;offs< ls1g_soc_mtd->size;offs+=ls1g_soc_mtd->erasesize){
|
|
if(ls1g_soc_mtd->block_isbad(ls1g_soc_mtd,offs)&& start>=0){
|
|
sprintf(name,"g%d",idx++);
|
|
add_mtd_device(ls1g_soc_mtd,start,offs-start,name);
|
|
start=-1;
|
|
}
|
|
else if(start<0){
|
|
start=offs;
|
|
}
|
|
}
|
|
|
|
if(start>=0){
|
|
sprintf(name,"g%d",idx++);
|
|
add_mtd_device(ls1g_soc_mtd,start,offs-start,name);
|
|
}
|
|
}
|
|
|
|
#define __ww(addr,val) *((volatile unsigned int*)(addr)) = (val)
|
|
#define __rw(addr,val) val = *((volatile unsigned int*)(addr))
|
|
#define __display(addr,num) do{for(i=0;i<num;i++){printf("0x%08x:0x%08x\n",(addr+i*sizeof(int)),*((volatile unsigned int*)(addr+i*sizeof(int))));}}while(0)
|
|
#define NAND_REG_BASE 0xbbee0000
|
|
#define NAND_DEV 0x1bee0040
|
|
|
|
#define STATUS_TIME 100
|
|
|
|
#define cmd_to_zero do{ \
|
|
__ww(NAND_REG_BASE,0); \
|
|
__ww(NAND_REG_BASE,0); \
|
|
__ww(NAND_REG_BASE,0x400); \
|
|
}while(0)
|
|
|
|
//验证擦除
|
|
unsigned int verify_erase(unsigned int addr,int all)
|
|
{
|
|
int i=0,flag=0;
|
|
volatile unsigned int *base = (unsigned int*)DDR_ADDR;
|
|
unsigned int val=0;
|
|
|
|
//#define DMA_DESP 0xa0800000
|
|
__ww(DMA_DESP+0, 0); //下一个描述符地址寄存器 这里设置为无效
|
|
__ww(DMA_DESP+0x4, DDR_PHY); //内存地址寄存器
|
|
__ww(DMA_DESP+0x8, NAND_DEV); //设备地址寄存器
|
|
__ww(DMA_DESP+0xc, 0x8400); //长度寄存器 代表一块被搬运内容的长度,单位是字
|
|
__ww(DMA_DESP+0x10, 0x0); //间隔长度寄存器 间隔长度说明两块被搬运内存数据块之间的长度,前一个step的结束地址与后一个step的开始地址之间的间隔
|
|
__ww(DMA_DESP+0x14, 0x1); //循环次数寄存器 循环次数说明在一次DMA操作中需要搬运的块的数目
|
|
__ww(DMA_DESP+0x18, 0x0); //控制寄存器
|
|
__ww(ORDER_REG_ADDR, DMA_DESP_ORDER); //DMA模块控制寄存器位 在confreg模块,存放第一个DMA描述符地址寄存器
|
|
//#define DMA_DESP_ORDER 0x00800008
|
|
//可以开始读描述符链的第一个DMA描述符
|
|
|
|
__ww(NAND_REG_BASE+0x0, 0x0);
|
|
__ww(NAND_REG_BASE+0x4, addr<<(12+6));//读、写、擦除操作起始地址低32位
|
|
__ww(NAND_REG_BASE+0x1c, 0x21000);//main + spare NAND读写操作Byte数;擦除为块数
|
|
__ww(NAND_REG_BASE+0x0, 0x300); //操作发生在NAND的SPARE区 操作发生在NAND的MAIN区
|
|
__ww(NAND_REG_BASE+0x0, 0x303); //读操作 命令有效
|
|
|
|
//udelay(5000);
|
|
while(1){
|
|
//#define DMA_ASK_ORDER 0x00800004
|
|
__ww(ORDER_REG_ADDR, DMA_ASK_ORDER);//用户请求将当前DMA操作的相关信息写回到指定的内存地址
|
|
__rw(DMA_DESP+0x4, val); //读取DMA内存的值到val中
|
|
if(val == (0x21000+DDR_PHY)){break;}
|
|
if((*((volatile unsigned int *)(NAND_REG_BASE)) & 0x400) != 0) break;
|
|
|
|
//udelay(400);
|
|
}
|
|
for(;i<0x8400;i++){
|
|
if(*(base+i) != 0xffffffff){printf("\naddr 0x%08x: 0x%08x isn't 0xFFFFFFFF",(base+i),*(base+i));flag=1;if(all){return flag;}}
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
void nandwrite(int argc,char **argv)/*cmd addr(L,page num) timing op_num(byte) */
|
|
{
|
|
|
|
unsigned int cmd, val, pagenum, column;
|
|
unsigned int buf;
|
|
unsigned int addr; // base on byte
|
|
|
|
cmd = 0x05;
|
|
addr = strtoul(argv[1],0,0);
|
|
val = strtoul(argv[2],0,0);
|
|
printf("addr : %08lx, val = %08x\n", addr, val);
|
|
pagenum = addr/2048;
|
|
column = addr % 2048;
|
|
|
|
__ww(NAND_REG_BASE+0x0,0x0);
|
|
__ww(NAND_REG_BASE+0x4,column); // set address in a page; no use in erase operation
|
|
__ww(NAND_REG_BASE+0x8,pagenum); // set address index pages
|
|
__ww(NAND_REG_BASE+0x1c,4); // set block number(erase unit) (204,188)
|
|
// __ww(NAND_REG_BASE+0x0,cmd);
|
|
|
|
/* dma configure */
|
|
__ww(DDR_PHYADDR,val);
|
|
__ww(DMA_DESP,0xa0081100); // means next descriptor address, not used
|
|
__ww(DMA_DESP+0x4,DDR_PHYADDR);
|
|
__ww(DMA_DESP+0x8,NAND_DEV);
|
|
__ww(DMA_DESP+0xc,1);
|
|
__ww(DMA_DESP+0x10,0x0);
|
|
__ww(DMA_DESP+0x14,0x1);
|
|
__ww(DMA_DESP+0x18,0x1000); // bit 12 == 1'b1: read ddr to write dev;bit 12 = 1'b0, read dev to write ddr
|
|
__ww(NAND_REG_BASE+0x0,cmd);
|
|
__ww(ORDER_REG_ADDR,DMA_DESP_ORDER); // start dma
|
|
|
|
buf = 0xaabbccdd;
|
|
val = 0x0;
|
|
__rw(NAND_REG_BASE+0x0,val);
|
|
while( (val & 0x400) != 0x400 ) // operation done
|
|
{
|
|
//__rw(NAND_REG_BASE+0x0040,buf);
|
|
//__rw(DDR_PHYADDR,buf);
|
|
__rw(NAND_REG_BASE,val);
|
|
//printf("cmd: %08x, buf data :%08x\n", val, buf);
|
|
}
|
|
//__rw(DDR_PHYADDR,buf);
|
|
//__rw(NAND_REG_BASE,val);
|
|
printf("nand address: %08lx @ page %08x: %08x\n", addr, pagenum,column);
|
|
|
|
}
|
|
|
|
void nandread(int argc,char **argv)/*cmd addr(L,page num) timing op_num(byte) */
|
|
{
|
|
|
|
unsigned int cmd, val, pagenum, column;
|
|
unsigned int buf;
|
|
unsigned int addr; // base on byte
|
|
|
|
cmd = 0x03;
|
|
addr = strtoul(argv[1],0,0);
|
|
printf("addr : %08lx\n", addr);
|
|
pagenum = addr/2048;
|
|
column = addr % 2048;
|
|
|
|
__ww(NAND_REG_BASE+0x0,0x0);
|
|
__ww(NAND_REG_BASE+0x4,column); // set address in a page; no use in erase operation
|
|
__ww(NAND_REG_BASE+0x8,pagenum); // set address index pages
|
|
__ww(NAND_REG_BASE+0x1c,4); // set block number(erase unit) (204,188)
|
|
// __ww(NAND_REG_BASE+0x0,cmd);
|
|
|
|
/* dma configure */
|
|
__ww(DMA_DESP,0xa0081100); // means next descriptor address, not used
|
|
__ww(DMA_DESP+0x4,DDR_PHYADDR);
|
|
__ww(DMA_DESP+0x8,NAND_DEV);
|
|
__ww(DMA_DESP+0xc,1);
|
|
__ww(DMA_DESP+0x10,0x0);
|
|
__ww(DMA_DESP+0x14,0x1);
|
|
__ww(DMA_DESP+0x18,0x0000); // bit 12 == 1'b1: read ddr write dev;bit 12 = 1'b0, read dev to write ddr
|
|
__ww(NAND_REG_BASE+0x0,cmd);
|
|
__ww(ORDER_REG_ADDR,DMA_DESP_ORDER); // start dma
|
|
|
|
buf = 0xaabbccdd;
|
|
val = 0x0;
|
|
__rw(NAND_REG_BASE+0x0,val);
|
|
while( (val & 0x400) != 0x400 ) // operation done
|
|
{
|
|
//__rw(NAND_REG_BASE+0x0040,buf);
|
|
__rw(DDR_PHYADDR,buf);
|
|
__rw(NAND_REG_BASE,val);
|
|
//printf("cmd: %08x, buf data :%08x\n", val, buf);
|
|
}
|
|
__rw(DDR_PHYADDR,buf);
|
|
__rw(NAND_REG_BASE,val);
|
|
printf("nand address: %08lx @ page %08x: %08x ====== %08x\n", addr, pagenum,column, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nanderase_verify(int argc, char ** argv)
|
|
{
|
|
int i=0, flag=0;
|
|
int status_time;
|
|
char *detail;
|
|
// all =strtoul(argv[1],0,0);
|
|
detail = argv[1];
|
|
flag = strncmp(detail, "detail", 6);
|
|
// __ww(0xbfd00420,0x0a000000); //lxy
|
|
__ww(NAND_REG_BASE+0x0, 0x0);
|
|
__ww(NAND_REG_BASE+0x0, 0x41); //NAND复位,命令有效
|
|
__ww(NAND_REG_BASE+0x4, 0x00); //读、写、擦除操作起始地址低32位
|
|
status_time = STATUS_TIME;
|
|
|
|
printf("erase blockaddr: 0x%08x\n", i);
|
|
for(i=0; i<1024; i++){
|
|
printf("\b\b\b\b\b\b\b\b");
|
|
printf("%08x", i<<(11+6));
|
|
__ww(NAND_REG_BASE+0x4, i<<(11+6)); //128K?
|
|
__ww(NAND_REG_BASE+0x0, 0x8); //擦除操作
|
|
__ww(NAND_REG_BASE+0x0, 0x9); //擦除操作 命令有效
|
|
//udelay(2000);
|
|
//外部NAND芯片RDY情况
|
|
//while((*((volatile unsigned int *)(NAND_REG_BASE)) & 0x1<<16) == 0){
|
|
while((*((volatile unsigned int *)(NAND_REG_BASE)) & 0x400) == 0){
|
|
/*
|
|
if(!(status_time--)){
|
|
cmd_to_zero; //操作完成
|
|
status_time = STATUS_TIME;
|
|
break;
|
|
}
|
|
*/
|
|
//udelay(80);
|
|
}
|
|
//验证擦除
|
|
if(verify_erase(i,flag)){printf("BLOCK:%d,addr:0x%08x,some error or bad block\n", i, i<<(11+6));}
|
|
}
|
|
printf("\nerase all nandflash ok...\n");
|
|
|
|
#if 0
|
|
// __ww(NAND_REG_BASE+0x0,0x9);
|
|
// //udelay(1000000);
|
|
// __display(NAND_REG_BASE,0x10);
|
|
// return 0;
|
|
|
|
// __ww(NAND_REG_BASE+0x0,0x0);
|
|
// __display(0xbfd00420,0x10);
|
|
// //udelay(1000000);
|
|
// __display(NAND_REG_BASE,0x1);
|
|
// __ww(NAND_REG_BASE+0x0,0x0);
|
|
// __display(NAND_REG_BASE,0x1);
|
|
|
|
__ww(DMA_DESP,0xa0081100);
|
|
__ww(DMA_DESP+0x4,DDR_PHY);
|
|
__ww(DMA_DESP+0x8,NAND_DEV);
|
|
__ww(DMA_DESP+0xc,0x10);
|
|
__ww(DMA_DESP+0x10,0x0);
|
|
__ww(DMA_DESP+0x14,0x1);
|
|
__ww(DMA_DESP+0x18,0x0);
|
|
// //udelay(1000000);
|
|
__ww(ORDER_REG_ADDR,DMA_DESP_ORDER);
|
|
// //udelay(1000000);
|
|
__display(DMA_DESP,0x10);
|
|
__display(ORDER_REG_ADDR,0x1);
|
|
// //udelay(1000000);
|
|
__ww(NAND_REG_BASE+0x4,0x800);
|
|
__ww(NAND_REG_BASE+0x1c,0x40);
|
|
// //udelay(1000000);
|
|
__display(NAND_REG_BASE,0X4);
|
|
__ww(NAND_REG_BASE,0x203);
|
|
// //udelay(1000000);
|
|
__display(NAND_REG_BASE,0X4);
|
|
__ww(ORDER_REG_ADDR,0x8000004);
|
|
__display(0xa8000000,0x8);
|
|
__display(DDR_ADDR,0x10);
|
|
|
|
// while(1);
|
|
#endif
|
|
}
|
|
|
|
void nanderase(void)
|
|
{
|
|
int i=0;
|
|
int status_time ;
|
|
|
|
// __ww(0xbfd00420,0x0a000000); //lxy
|
|
__ww(NAND_REG_BASE+0x0, 0x0);
|
|
__ww(NAND_REG_BASE+0x0, 0x41); //NAND复位,命令有效
|
|
__ww(NAND_REG_BASE+0x4, 0x00); //读、写、擦除操作起始地址低32位
|
|
|
|
status_time = STATUS_TIME;
|
|
|
|
printf("erase blockaddr: 0x%08x", i);
|
|
for(i=0;i<1024;i++){
|
|
printf("\b\b\b\b\b\b\b\b");
|
|
printf("%08x", i<<(11+6));
|
|
__ww(NAND_REG_BASE+0x4, 0x0); //128K?
|
|
//__ww(NAND_REG_BASE+0x8, i<<(11+6)); //128K?
|
|
__ww(NAND_REG_BASE+0x8, i<<(6)); //128K?
|
|
__ww(NAND_REG_BASE+0x0, 0x8); //擦除操作
|
|
__ww(NAND_REG_BASE+0x0, 0x9); //擦除操作 命令有效
|
|
//udelay(2000);
|
|
//外部NAND芯片RDY情况
|
|
//while((*((volatile unsigned int *)(NAND_REG_BASE)) & 0x1<<16) == 0){
|
|
while((*((volatile unsigned int *)(NAND_REG_BASE)) & (0x1<<10)) == 0){
|
|
//udelay(80);
|
|
}
|
|
}
|
|
printf("\nerase all nandflash ok...\n");
|
|
}
|
|
|
|
//#include "nand_gpio.c"
|
|
|
|
static int nand_read_id(void)
|
|
{
|
|
unsigned int cmd_id = 0x21;
|
|
unsigned int cmd_reg = 0xbbee0000;
|
|
unsigned int id_reg = 0xbbee0010;
|
|
unsigned int id_val = 0;
|
|
unsigned int id_val_h = 0;
|
|
int i, dev_id, maf_idx, busw;
|
|
struct mtd_info *mtd = ls1g_soc_mtd;
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_flash_dev *type = NULL;
|
|
|
|
*((volatile unsigned int *)(0xbbee000c)) = 0x30f0; //NAND命令有效需等待的周期数 NAND一次读写所需总时钟周期数
|
|
*((volatile unsigned int *)(0xbbee0000)) = 0x21; //读ID操作 命令有效
|
|
#define IDL *((volatile unsigned int*)(0xbbee0010))
|
|
#define IDH *((volatile unsigned int*)(0xbbee0014))
|
|
while(1){
|
|
while(((id_val |= IDL) & 0xff) == 0){
|
|
id_val_h = IDH;
|
|
}
|
|
break;
|
|
}
|
|
/*
|
|
__asm__ __volatile__(
|
|
".set mips3\n"
|
|
"\tlw $4,%1\n"
|
|
"\tlw $5,%2\n"
|
|
"\tsw $4,0($5)\n"
|
|
// "\tla,a0,%0i"
|
|
"1:\tlw $4,0(%3)\n"
|
|
"\tbeqz $4,1f\n"
|
|
"\tmove %0,$4\n"
|
|
:"=r"(id_val)
|
|
:"r"(cmd_id),"r"(cmd_reg),"r"(id_reg)
|
|
);
|
|
*/
|
|
|
|
dev_id = (id_val >> 24);
|
|
|
|
/* Lookup the flash id */
|
|
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
|
if (dev_id == nand_flash_ids[i].id) {
|
|
type = &nand_flash_ids[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!type){
|
|
printf("No NAND device found!!!\n");
|
|
return 1;
|
|
}
|
|
|
|
/* Try to identify manufacturer */
|
|
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
|
|
if (nand_manuf_ids[maf_idx].id == (unsigned char)id_val_h)
|
|
break;
|
|
}
|
|
|
|
busw = type->options & NAND_BUSWIDTH_16;
|
|
|
|
// printf("read_id_l:0x%08x\nread_id_h:0x%08x\n", id_val, id_val_h);
|
|
printf("NAND device: Manufacturer ID:"
|
|
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", id_val_h,
|
|
(id_val >> 24), nand_manuf_ids[maf_idx].name, mtd->name);
|
|
printf("NAND bus width %d instead %d bit\n",
|
|
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
|
busw ? 16 : 8);
|
|
return 0;
|
|
}
|
|
|
|
#define TIMING 0xc
|
|
#define OP_NUM 0x1c
|
|
#define ADDR 0x4
|
|
#define CMD 0x0
|
|
static unsigned int nand_num=0;
|
|
|
|
static void nandwrite_test(int argc,char **argv)/*cmd addr(L,page num) timing op_num(byte) */
|
|
{
|
|
unsigned int cmd = 0,addr=0,timing=0x412,op_num=0,dma_num=0;
|
|
unsigned int pages=0,i,val,timeout;
|
|
|
|
if(argc != 5){
|
|
printf("\nnandwrite_test : cmd addr(start write page_num) timing op_num(write pages)\n");
|
|
printf("EXAMPLE:nandwrite_test : 0x203 0x10 0x412 0x5\n\n");
|
|
return;
|
|
}
|
|
nand_num = 0;
|
|
// __ww(0xbfd00420,0x0a000000); //lxy
|
|
cmd = strtoul(argv[1],0,0);
|
|
addr = strtoul(argv[2],0,0);
|
|
timing = strtoul(argv[3],0,0);
|
|
op_num = strtoul(argv[4],0,0);
|
|
// dma_num = strtoul(argv[5],0,0);
|
|
if(cmd & 0x9){addr <<= 12;}
|
|
else{addr <<= 11;}
|
|
// pages = (op_num>>11)+1;
|
|
pages = op_num;
|
|
for(i=0;i<pages;i++){
|
|
__ww(NAND_REG_BASE+TIMING, timing); //0xbbee000c NAND命令有效需等待的周期数 NAND一次读写所需总时钟周期数
|
|
__ww(NAND_REG_BASE+OP_NUM, 0x840); //0xbbee001c NAND读写操作Byte数;擦除为块数
|
|
__ww(NAND_REG_BASE+ADDR, addr); //读、写、擦除操作起始地址低32位
|
|
__ww(DDR_ADDR, 0xffffffff);
|
|
/*dma configure*/
|
|
__ww(DMA_DESP, 0x80081100); //下一个描述符地址寄存器 这里设置为无效
|
|
__ww(DMA_DESP+0x4, DDR_PHY); //内存地址寄存器
|
|
__ww(DMA_DESP+0x8, NAND_DEV); //设备地址寄存器
|
|
__ww(DMA_DESP+0xc, 0x210); //长度寄存器 代表一块被搬运内容的长度,单位是字
|
|
__ww(DMA_DESP+0x10, 0x0); //间隔长度寄存器 间隔长度说明两块被搬运内存数据块之间的长度,前一个step的结束地址与后一个step的开始地址之间的间隔
|
|
__ww(DMA_DESP+0x14, 0x1); //循环次数寄存器 循环次数说明在一次DMA操作中需要搬运的块的数目
|
|
__ww(DMA_DESP+0x18, 0x1000); //控制寄存器 DMA操作类型,“1”为读ddr2写设备,“0”为读设备写ddr2
|
|
__ww(ORDER_REG_ADDR, DMA_DESP_ORDER); //DMA模块控制寄存器位 在confreg模块,存放第一个DMA描述符地址寄存器
|
|
//#define DMA_DESP_ORDER 0x00800008
|
|
//可以开始读描述符链的第一个DMA描述符
|
|
/*send cmd*/
|
|
__ww(NAND_REG_BASE+CMD, cmd&0x200); //操作发生在NAND的SPARE(备用)区
|
|
__ww(NAND_REG_BASE+CMD, cmd);
|
|
//udelay(100);
|
|
while(1){
|
|
__ww(ORDER_REG_ADDR,DMA_ASK_ORDER);
|
|
__rw(DMA_DESP+0x4,val);
|
|
if(val == (0x210+DDR_PHY)){break;}
|
|
//udelay(20);
|
|
}
|
|
timeout=30;
|
|
//udelay(300);
|
|
__rw(NAND_REG_BASE+CMD,val);
|
|
while(!(val&0x1<<20)){
|
|
//udelay(20);
|
|
if(!(timeout--)){nand_num++;break;}
|
|
__rw(NAND_REG_BASE+CMD,val);
|
|
}
|
|
printf("nand_num===0x%08x\n", nand_num);
|
|
}
|
|
}
|
|
|
|
//int ls1g_soc_nand_init(void)
|
|
void ls1g_soc_nand_init(int argc,char **argv)/*cmd addr(L,page num) timing op_num(byte) */
|
|
{
|
|
// nand_gpio_read_id();
|
|
// return 0;
|
|
int val;
|
|
struct nand_chip *this;
|
|
printf("\nNAND DETE\n");
|
|
// nand_test();
|
|
// if(__rw(0xbfd00420,val) != 0x0a000000) //lxy
|
|
// __ww(0xbfd00420,0x0a000000); //lxy
|
|
|
|
|
|
/* Allocate memory for MTD device structure and private data */
|
|
/* 为MTD设备结构体和nand_chip分配内存 */
|
|
ls1g_soc_mtd = malloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), M_DEVBUF, M_WAITOK);
|
|
if (!ls1g_soc_mtd) {
|
|
printk("Unable to allocate fcr_soc NAND MTD device structure.\n");
|
|
return -ENOMEM;
|
|
}
|
|
/* Get pointer to private data 获得私有数据(nand_chip)指针*/
|
|
this = (struct nand_chip *)(&ls1g_soc_mtd[1]);/*分配内存后的nand_chip结构体指针*/
|
|
/*由于soc_soc_mtd是mtd_info结构体指针 在kmalloc内存分配时加上了+ sizeof(struct nand_chip) 所以&soc_soc_mtd[1]地址相当于指向nand_chip结构体*/
|
|
|
|
/* Initialize structures 初始化结构体 清零 */
|
|
memset(ls1g_soc_mtd, 0, sizeof(struct mtd_info));
|
|
memset(this, 0, sizeof(struct nand_chip));
|
|
|
|
/* Link(连接) the private(私有的) data with the MTD structure */
|
|
/*将nand_chip赋予mtd_info私有指针*/
|
|
ls1g_soc_mtd->priv = this;
|
|
/* 15 us command delay time 从数据手册获知命令延迟时间 */
|
|
this->chip_delay = 15;
|
|
if(ls1g_nand_init(ls1g_soc_mtd)){
|
|
printf("\n\nerror: PMON nandflash driver have some error!\n\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Scan to find existence of the device 扫描以确定器件的存在*/
|
|
if (nand_scan(ls1g_soc_mtd, 1)) {
|
|
free(ls1g_soc_mtd,M_DEVBUF); /*扫描失败 soc_soc_mtd可以释放*/
|
|
return -ENXIO;
|
|
}
|
|
if(ls1g_nand_detect(ls1g_soc_mtd)){
|
|
printf("error: PMON driver don't support the NANDFlash!\n ");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Register the partitions */
|
|
// add_mtd_partitions(fcr_soc_mtd, partition_info, NUM_PARTITIONS);
|
|
//
|
|
// add_mtd_device(mtd_info,offset,size,name)
|
|
// add_mtd_device(ls1g_soc_mtd,0,0,"total");
|
|
|
|
#if 1
|
|
add_mtd_device(ls1g_soc_mtd,0,0x01000000,"kernel");
|
|
add_mtd_device(ls1g_soc_mtd,0x01000000,0x07000000,"os");
|
|
// add_mtd_device(ls1g_soc_mtd,0x07500000,0x00b00000,"data");
|
|
#else
|
|
add_mtd_device(ls1g_soc_mtd,0,0x00700000,"kernel");
|
|
add_mtd_device(ls1g_soc_mtd,0x00700000,0x06e00000,"os");
|
|
add_mtd_device(ls1g_soc_mtd,0x07500000,0x00b00000,"data");
|
|
#endif
|
|
|
|
// find_good_part(ls1g_soc_mtd);
|
|
/* Return happy */
|
|
return 0;
|
|
}
|
|
|
|
static const Cmd Cmds[] =
|
|
{
|
|
{"MyCmds"},
|
|
{"nandread","val",0,"nand read addr",nandread,0,99,CMD_REPEAT},
|
|
{"nandwrite","val",0,"nand write addr val",nandwrite,0,99,CMD_REPEAT},
|
|
{"nanderase_verify","val",0,"hardware test",nanderase_verify,0,99,CMD_REPEAT},
|
|
{"nanderase","val",0,"hardware test",nanderase,0,99,CMD_REPEAT},
|
|
// {"nandreadid_gpio","val",0,"hardware test",nand_gpio_read_id,0,99,CMD_REPEAT},
|
|
{"nandreadid","val",0,"hardware test",nand_read_id,0,99,CMD_REPEAT},
|
|
{"nandwrite_test","val",0,"hardware test",nandwrite_test,0,99,CMD_REPEAT},
|
|
{"nandinit","val",0,"hardware test",ls1g_soc_nand_init,0,99,CMD_REPEAT},
|
|
{0, 0}
|
|
};
|
|
|
|
static void init_cmd __P((void)) __attribute__ ((constructor));
|
|
static void init_cmd()
|
|
{
|
|
cmdlist_expand(Cmds, 1);
|
|
}
|
|
|
|
|
|
|