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.
846 lines
23 KiB
846 lines
23 KiB
/*
|
|
* Author:mengtianfang
|
|
* email:mengtianfang@loongson.cn
|
|
* nandinit, nanderase, nandreadid, nandread, nandwrite, nand_load functions can be used.
|
|
* These functions are modified according to the source file sys/dev/nand/ls1g-annd.c.
|
|
*/
|
|
|
|
#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>
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifndef __iomem
|
|
#define __iomem
|
|
#endif
|
|
|
|
#define DMA_ACCESS_ADDR 0x40
|
|
#define ORDER_REG_ADDR 0xb2d01160
|
|
#define DDR_PHYADDR 0xad000000
|
|
#define NAND_PAGE_SIZE 2048
|
|
#define NAND_PAGE_PER_BLOCK 0x40
|
|
|
|
#define nand_write(val, addr) (*(volatile unsigned int*)(addr) = (val))
|
|
#define nand_read(addr) (*(volatile unsigned int*)(addr))
|
|
|
|
#define ALIGN_DMA(x) (((x) + 3)/4)
|
|
#define STATUS_TIME_LOOP_R 1000
|
|
#define STATUS_TIME_LOOP_E 1000
|
|
|
|
/* add for 2g1a */
|
|
#define NAND_REG_BASE 0xb2e78000
|
|
|
|
#define NAND_REG_CMD (NAND_REG_BASE + 0x00)
|
|
#define NAND_REG_ADDR_C (NAND_REG_BASE + 0x04)
|
|
#define NAND_REG_ADDR_R (NAND_REG_BASE + 0x08)
|
|
#define NAND_REG_TIMING (NAND_REG_BASE + 0x0c)
|
|
#define NAND_REG_IDL (NAND_REG_BASE + 0x10)
|
|
#define NAND_REG_STATUS (NAND_REG_BASE + 0x14)
|
|
#define NAND_REG_IDH (NAND_REG_BASE + 0x14)
|
|
#define NAND_REG_PARAMETER (NAND_REG_BASE + 0x18)
|
|
#define NAND_REG_OP_NUM (NAND_REG_BASE + 0x1c)
|
|
#define NAND_REG_CS_RDY_MAP (NAND_REG_BASE + 0x20)
|
|
#define NAND_REG_DMA_AC_ADDR (NAND_REG_BASE + 0x40)
|
|
|
|
#define NAND_CMD_VALID (1 << 0)
|
|
#define NAND_CMD_OP_RD (1 << 1)
|
|
#define NAND_CMD_OP_WR (1 << 2)
|
|
#define NAND_CMD_OP_ER (1 << 3)
|
|
#define NAND_CMD_BLK_ER (1 << 4)
|
|
#define NAND_CMD_RD_ID (1 << 5)
|
|
#define NAND_CMD_OP_RST (1 << 6)
|
|
#define NAND_CMD_RD_STATUS (1 << 7)
|
|
#define NAND_CMD_MAIN (1 << 8)
|
|
#define NAND_CMD_SPARE (1 << 9)
|
|
#define NAND_CMD_DONE (1 << 10)
|
|
|
|
enum{
|
|
STATE_READY = 0,
|
|
STATE_BUSY ,
|
|
};
|
|
|
|
struct ls2g1a_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 ls2g1a_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 ls2g1a_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 ls2g1a_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 ls2g1a_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 ls2g1a_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 ls2g1a_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 ls2g1a_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 *ls2g1a_soc_mtd = NULL;
|
|
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} }
|
|
};
|
|
|
|
static int ls2g1a_nand_ecc_calculate(struct mtd_info *mtd,
|
|
const uint8_t *dat, uint8_t *ecc_code)
|
|
{
|
|
return 0;
|
|
}
|
|
static int ls2g1a_nand_ecc_correct(struct mtd_info *mtd,
|
|
uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
|
|
{
|
|
/*
|
|
* 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 ls2g1a_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static int ls2g1a_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
|
|
{
|
|
udelay(50);
|
|
return 0;
|
|
}
|
|
|
|
static void ls2g1a_nand_select_chip(struct mtd_info *mtd, int chip)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static int ls2g1a_nand_dev_ready(struct mtd_info *mtd)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void ls2g1a_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
{
|
|
struct ls2g1a_nand_info *info = mtd->priv;
|
|
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
|
|
|
|
memcpy(buf, info->data_buff + info->buf_start, real_len);
|
|
info->buf_start += real_len;
|
|
}
|
|
|
|
static u16 ls2g1a_nand_read_word(struct mtd_info *mtd)
|
|
{
|
|
struct ls2g1a_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 ls2g1a_nand_read_byte(struct mtd_info *mtd)
|
|
{
|
|
struct ls2g1a_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)++];
|
|
return retval;
|
|
}
|
|
|
|
static void ls2g1a_nand_write_buf(struct mtd_info *mtd,const uint8_t *buf, int len)
|
|
{
|
|
struct ls2g1a_nand_info *info = mtd->priv;
|
|
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
|
|
|
|
memcpy(info->data_buff + info->buf_start, buf, real_len);
|
|
info->buf_start += real_len;
|
|
}
|
|
|
|
static int ls2g1a_nand_verify_buf(struct mtd_info *mtd,const uint8_t *buf, int len)
|
|
{
|
|
int i = 0;
|
|
while(len--){
|
|
if(buf[i++] != ls2g1a_nand_read_byte(mtd) ){
|
|
printf("verify error...\n\n");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nand_read_id(void);
|
|
static void ls2g1a_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int column, int page_addr);
|
|
static void ls2g1a_nand_init_mtd(struct mtd_info *mtd,struct ls2g1a_nand_info *info);
|
|
|
|
int ls2g1a_nand_init(struct mtd_info *mtd)
|
|
{
|
|
int ret = 0;
|
|
ret = ls2g1a_nand_pmon_info_init((struct ls2g1a_nand_info *)(mtd->priv), mtd);//data buff, desc buff, reg
|
|
ls2g1a_nand_init_mtd(mtd, (struct ls2g1a_nand_info *)(mtd->priv));//func
|
|
return ret;
|
|
}
|
|
|
|
static void ls2g1a_nand_init_mtd(struct mtd_info *mtd, struct ls2g1a_nand_info *info)
|
|
{
|
|
struct nand_chip *this = &info->nand_chip;
|
|
|
|
this->options = 8;
|
|
|
|
this->waitfunc = ls2g1a_nand_waitfunc; /*等待设备准备好 硬件相关函数*/
|
|
this->select_chip = ls2g1a_nand_select_chip; /*控制CE信号*/
|
|
this->dev_ready = ls2g1a_nand_dev_ready; /*板特定的设备ready/busy信息*/
|
|
this->cmdfunc = ls2g1a_nand_cmdfunc; /*命令处理函数*/
|
|
this->read_word = ls2g1a_nand_read_word; /*从芯片读一个字*/
|
|
this->read_byte = ls2g1a_nand_read_byte; /*从芯片读一个字节*/
|
|
this->read_buf = ls2g1a_nand_read_buf; /*将芯片数据读到缓冲区*/
|
|
this->write_buf = ls2g1a_nand_write_buf; /*将缓冲区内容写入芯片*/
|
|
this->verify_buf = ls2g1a_nand_verify_buf; /*验证芯片和写入缓冲区中的数据*/
|
|
|
|
// this->ecc.mode = NAND_ECC_NONE;
|
|
this->ecc.mode = NAND_ECC_SOFT; /*ECC模式 这里是软件模式*/
|
|
// this->ecc.hwctl = ls2g1a_nand_ecc_hwctl;
|
|
// this->ecc.calculate = ls2g1a_nand_ecc_calculate;
|
|
// this->ecc.correct = ls2g1a_nand_ecc_correct;
|
|
// this->ecc.size = 2048;
|
|
// this->ecc.bytes = 24;
|
|
|
|
// this->ecc.layout = &hw_largepage_ecclayout;
|
|
// mtd->owner = THIS_MODULE;
|
|
}
|
|
|
|
static void wait_nand_done(struct ls2g1a_nand_info *info, int timeout)
|
|
{
|
|
int t, status_times = timeout;
|
|
|
|
do {
|
|
t = nand_read(NAND_REG_CMD) & NAND_CMD_DONE;
|
|
if (!(status_times--)) {
|
|
printf("nand time out\n");
|
|
nand_write(0x0, NAND_REG_CMD);
|
|
nand_write(0x0, NAND_REG_CMD);
|
|
nand_write(NAND_CMD_OP_RST | NAND_CMD_VALID, NAND_REG_CMD);
|
|
break;
|
|
}
|
|
udelay(50);
|
|
} while(t == 0);
|
|
|
|
nand_write(0x0, NAND_REG_CMD);
|
|
}
|
|
|
|
static void dma_setup(struct ls2g1a_nand_info *info, int dma_cmd, int dma_cnt)
|
|
{
|
|
int t;
|
|
struct ls2g1a_nand_dma_desc *dma_base = (volatile struct ls2g1a_nand_dma_desc *)(info->drcmr_dat);
|
|
|
|
dma_base->orderad = 0;
|
|
dma_base->saddr = info->data_buff_phys;
|
|
dma_base->daddr = DMA_ACCESS_ADDR;
|
|
dma_base->length = dma_cnt;
|
|
dma_base->step_length = 0;
|
|
dma_base->step_times = 0x1;
|
|
dma_base->cmd = dma_cmd;
|
|
|
|
/* dma start */
|
|
*(volatile unsigned int *)(info->order_reg_addr) = ((unsigned int )info->drcmr_dat_phys) | (0x1 << 3);
|
|
*(volatile unsigned int *)(info->order_reg_addr) = (0x1 << 2) | (info->data_ask_phys);
|
|
t = STATUS_TIME_LOOP_R;
|
|
while (*(volatile unsigned int *)(info->order_reg_addr) & (0x1 << 3) && t) {
|
|
t--;
|
|
udelay(50);
|
|
}
|
|
if (t == 0)
|
|
printf("dma timeout!\n\n");
|
|
wait_nand_done(info, STATUS_TIME_LOOP_R);
|
|
}
|
|
|
|
static void nand_setup(unsigned int cmd, int addr_c, int addr_r, int op_num)
|
|
{
|
|
nand_write(op_num, NAND_REG_OP_NUM);
|
|
nand_write(addr_c, NAND_REG_ADDR_C);
|
|
nand_write(addr_r, NAND_REG_ADDR_R);
|
|
nand_write(0, NAND_REG_CMD);
|
|
nand_write(0, NAND_REG_CMD);
|
|
nand_write(cmd, NAND_REG_CMD);
|
|
}
|
|
|
|
static void ls2g1a_nand_cmdfunc(struct mtd_info *mtd, unsigned command,int column, int page_addr)
|
|
{
|
|
struct ls2g1a_nand_info *info = mtd->priv;
|
|
|
|
unsigned int cmd = 0;
|
|
int addrc, addrr, op_num;
|
|
int dma_cmd, dma_cnt;
|
|
|
|
info->cmd = command;
|
|
info->page_addr = page_addr;
|
|
switch(command){
|
|
case NAND_CMD_READOOB:
|
|
if(info->state == STATE_BUSY){
|
|
printf("read oob 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 */
|
|
addrr = page_addr;
|
|
addrc = mtd->writesize;
|
|
op_num = info->buf_count;
|
|
cmd = (NAND_CMD_OP_RD | NAND_CMD_SPARE | NAND_CMD_VALID);
|
|
nand_setup(cmd, addrc, addrr, op_num);
|
|
|
|
/* dma regs config */
|
|
dma_cnt = ALIGN_DMA(info->buf_count);
|
|
dma_cmd = 1;//dma_int_mask
|
|
dma_setup(info, dma_cmd, dma_cnt);
|
|
break;
|
|
case NAND_CMD_READ0:
|
|
if(info->state == STATE_BUSY){
|
|
printf("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
|
|
cmd = NAND_CMD_MAIN | NAND_CMD_SPARE | NAND_CMD_OP_RD | NAND_CMD_VALID;
|
|
addrr = page_addr;
|
|
addrc = 0;
|
|
op_num = mtd->oobsize + mtd->writesize;
|
|
|
|
info->buf_count = op_num;
|
|
info->buf_start = 0;
|
|
info->cac_size = info->buf_count;
|
|
|
|
nand_setup(cmd, addrc, addrr, op_num);
|
|
/* dma regs config */
|
|
dma_cmd = 0x1;
|
|
dma_cnt = ALIGN_DMA(op_num);
|
|
dma_setup(info, dma_cmd, dma_cnt);
|
|
break;
|
|
case NAND_CMD_SEQIN:
|
|
if(info->state == STATE_BUSY){
|
|
printf("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;
|
|
break;
|
|
case NAND_CMD_PAGEPROG:
|
|
if(info->state == STATE_BUSY){
|
|
printf("nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
if(info->buf_count <= 0 )
|
|
break;
|
|
|
|
if(((info->num)++) % 512 == 0){
|
|
printf("nand have write : %d M\n",(info->size)++);
|
|
}
|
|
|
|
addrc = info->seqin_column;
|
|
addrr = info->seqin_page_addr;
|
|
op_num = info->buf_start;
|
|
cmd = NAND_CMD_SPARE | NAND_CMD_OP_WR | NAND_CMD_VALID;
|
|
if (addrc < mtd->writesize)
|
|
cmd |= NAND_CMD_MAIN;
|
|
nand_setup(cmd, addrc, addrr, op_num);
|
|
|
|
dma_cmd = 0x1 | (1 << 12);
|
|
dma_cnt = ALIGN_DMA(op_num);
|
|
dma_setup(info, dma_cmd, dma_cnt);
|
|
break;
|
|
case NAND_CMD_RESET:
|
|
info->state = STATE_BUSY;
|
|
cmd = (NAND_CMD_OP_RST | NAND_CMD_VALID);
|
|
nand_setup(cmd, 0, 0, 0);
|
|
wait_nand_done(info, 30);
|
|
info->state = STATE_READY;
|
|
break;
|
|
case NAND_CMD_ERASE1:
|
|
info->state = STATE_BUSY;
|
|
/* nand regs set */
|
|
addrr = page_addr;
|
|
addrc = 0x0;
|
|
op_num = 0x0;
|
|
cmd = NAND_CMD_OP_ER | NAND_CMD_VALID;
|
|
nand_setup(cmd, addrc, addrr, op_num);
|
|
wait_nand_done(info, STATUS_TIME_LOOP_E);
|
|
info->state = STATE_READY;
|
|
break;
|
|
case NAND_CMD_STATUS:
|
|
info->buf_count = 0x1;
|
|
info->buf_start = 0x0;
|
|
*(unsigned char *)info->data_buff =
|
|
(nand_read(NAND_REG_CMD) & (NAND_CMD_DONE)) | (NAND_CMD_RD_STATUS);
|
|
break;
|
|
case NAND_CMD_READID:
|
|
if(info->state == STATE_BUSY){
|
|
printf(" read id nandflash chip if busy...\n");
|
|
return;
|
|
}
|
|
info->state = STATE_BUSY;
|
|
info->buf_count = 0x4;
|
|
info->buf_start = 0;
|
|
nand_read_id();
|
|
|
|
break;
|
|
case NAND_CMD_ERASE2:
|
|
case NAND_CMD_READ1:
|
|
break;
|
|
default :
|
|
printf("non-supported command.\n");
|
|
break;
|
|
}
|
|
|
|
info->state = STATE_READY;
|
|
}
|
|
|
|
int ls2g1a_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 ls2g1a_nand_init_info(struct ls2g1a_nand_info *info)
|
|
{
|
|
info->num=0;
|
|
info->size=0;
|
|
info->cac_size = 0;
|
|
info->state = STATE_READY;
|
|
|
|
info->cmd_prev = -1;
|
|
info->page_addr = -1;
|
|
|
|
nand_write(0x412, NAND_REG_TIMING);
|
|
nand_write(0, NAND_REG_CS_RDY_MAP);
|
|
|
|
info->dma_orderad = 0x0;
|
|
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 ls2g1a_nand_pmon_info_init(struct ls2g1a_nand_info *info, struct mtd_info *mtd)
|
|
{
|
|
info->drcmr_dat = 0xa2000000; //DMA desc_addr a2000000
|
|
info->drcmr_dat = (unsigned int)((info->drcmr_dat + 15) & ~0xff);
|
|
info->drcmr_dat_phys = ((info->drcmr_dat) & 0x1fffffff) | 0x80000000;
|
|
printf("desc addr 0x%x\n", info->drcmr_dat);
|
|
printf("desc addr phys 0x%x\n", info->drcmr_dat_phys);
|
|
|
|
info->mmio_base = NAND_REG_BASE;
|
|
printf("nand reg base 0x%x\n", info->mmio_base);
|
|
|
|
info->data_buff = (unsigned char *)DDR_PHYADDR; //for saddr//ad000000
|
|
memset(info->data_buff, 0, sizeof(unsigned int));
|
|
info->data_buff_phys = ((unsigned int)(info->data_buff) & 0x1fffffff) | 0x80000000;
|
|
printf("data_buff==0x%08x\n",info->data_buff);
|
|
printf("data_buff_phys==0x%08x\n",info->data_buff_phys);
|
|
|
|
info->data_ask = 0xa2000000;
|
|
if(info->data_ask == NULL)
|
|
return -1;
|
|
info->data_ask_phys = ( info->data_ask & 0x1fffffff) |0x80000000;
|
|
|
|
ls2g1a_nand_init_info(info);//nand reg, dma reg
|
|
return 0;
|
|
}
|
|
|
|
static int nandwrite(int argc,char **argv)
|
|
{
|
|
unsigned int cmd, val, addrr, addrc, op_num;
|
|
struct ls2g1a_nand_info *info;
|
|
unsigned int dma_cmd, dma_cnt;
|
|
unsigned int addr; // base on byte
|
|
|
|
if (argc != 3) {
|
|
printf("nandwrite nand_addr val\n");
|
|
return -1;
|
|
}
|
|
addr = strtoul(argv[1], 0, 0);
|
|
val = strtoul(argv[2], 0, 0);
|
|
info = ls2g1a_soc_mtd->priv;
|
|
nand_write(val, info->data_buff);
|
|
|
|
cmd = NAND_CMD_MAIN | NAND_CMD_SPARE | NAND_CMD_OP_WR | NAND_CMD_VALID;
|
|
addrr = addr / NAND_PAGE_SIZE;
|
|
addrc = addr % NAND_PAGE_SIZE;
|
|
op_num = 4;
|
|
nand_setup(cmd, addrc, addrr, op_num);
|
|
|
|
/* dma configure */
|
|
dma_cmd = 0x1 | (0x1 << 12);
|
|
dma_cnt = ALIGN_DMA(op_num);
|
|
dma_setup(info, dma_cmd, dma_cnt);
|
|
|
|
printf("nand address: %08lx @ page %08x: %08x, op_num 0x%x\n",
|
|
addr, addrr, addrc, op_num);
|
|
return 0;
|
|
}
|
|
|
|
static int nandread(int argc,char **argv)
|
|
{
|
|
unsigned int cmd, addrr, addrc;
|
|
struct ls2g1a_nand_info *info;
|
|
unsigned int buf;
|
|
unsigned int addr;
|
|
int op_num, dma_cmd, dma_cnt;
|
|
|
|
if (argc != 2) {
|
|
printf("nandread nand_addr\n");
|
|
return -1;
|
|
}
|
|
memset((void *)DDR_PHYADDR, 0, sizeof(unsigned int));
|
|
addr = strtoul(argv[1], 0, 0);
|
|
|
|
cmd = NAND_CMD_OP_RD | NAND_CMD_VALID;
|
|
addrr = addr / NAND_PAGE_SIZE;
|
|
addrc = addr % NAND_PAGE_SIZE;
|
|
op_num = ls2g1a_soc_mtd->oobsize + ls2g1a_soc_mtd->writesize;
|
|
nand_setup(cmd, addrc, addrr, op_num);
|
|
|
|
/* dma configure */
|
|
info = ls2g1a_soc_mtd->priv;
|
|
dma_cmd = 0x1;
|
|
dma_cnt = ALIGN_DMA(op_num);
|
|
dma_setup(info, dma_cmd, dma_cnt);
|
|
|
|
buf = nand_read(DDR_PHYADDR);
|
|
printf("nand address: %08lx @ page %08x: %08x ====== %08x\n",
|
|
addr, addrr, addrc, buf);
|
|
return 0;
|
|
}
|
|
|
|
static void nanderase(void)
|
|
{
|
|
int i=0;
|
|
|
|
nand_write(0x0, NAND_REG_CMD);
|
|
nand_write(0x0, NAND_REG_CMD);
|
|
nand_write(NAND_CMD_OP_RST | NAND_CMD_VALID, NAND_REG_CMD);
|
|
nand_write(0x0, NAND_REG_ADDR_C);
|
|
|
|
for(i = 0; i < 1024; i++){ // 1024 blocks
|
|
printf("erase blockaddr: 0x%08x\n", i << (11 + 6));
|
|
nand_write(0x0, NAND_REG_ADDR_C); // 128K/block
|
|
nand_write(i << 6, NAND_REG_ADDR_R); //128K
|
|
nand_write(NAND_CMD_OP_ER, NAND_REG_CMD);
|
|
nand_write(NAND_CMD_OP_ER | NAND_CMD_VALID, NAND_REG_CMD);
|
|
while((nand_read(NAND_REG_CMD) & NAND_CMD_DONE) == 0){
|
|
udelay(80);
|
|
}
|
|
nand_write(0x0, NAND_REG_CMD);
|
|
}
|
|
printf("\nerase all nandflash ok...\n");
|
|
}
|
|
|
|
static int nand_read_id(void)
|
|
{
|
|
unsigned char *data;
|
|
unsigned int id_val_l = 0;
|
|
unsigned int id_val_h = 0;
|
|
int i, dev_id, maf_idx, busw;
|
|
struct mtd_info *mtd = ls2g1a_soc_mtd;
|
|
struct nand_chip *chip = mtd->priv;
|
|
struct nand_flash_dev *type = NULL;
|
|
unsigned int timing = 0;
|
|
|
|
timing = nand_read(NAND_REG_TIMING);
|
|
nand_write(0x30f0, NAND_REG_TIMING);
|
|
nand_write(NAND_CMD_RD_ID | NAND_CMD_VALID, NAND_REG_CMD);
|
|
#define IDL *((volatile unsigned int*)(NAND_REG_IDL))
|
|
#define IDH *((volatile unsigned int*)(NAND_REG_IDH))
|
|
while(1){
|
|
while(((id_val_l |= IDL) & 0xff) == 0){
|
|
id_val_h = IDH;
|
|
}
|
|
while (((id_val_h = IDH) & 0xff) == 0);
|
|
break;
|
|
}
|
|
|
|
dev_id = (id_val_l >> 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("readid, NAND device: Manufacturer ID:"
|
|
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", id_val_h,
|
|
(id_val_l >> 24), nand_manuf_ids[maf_idx].name, mtd->name);
|
|
printf("readid, NAND bus width %d instead %d bit\n",
|
|
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
|
busw ? 16 : 8);
|
|
|
|
data = (unsigned char *)(((struct ls2g1a_nand_info *)(mtd->priv))->data_buff);
|
|
nand_write(timing, NAND_REG_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;
|
|
return 0;
|
|
}
|
|
|
|
int ls2g1a_soc_nand_init(void)
|
|
{
|
|
struct ls2g1a_nand_info *info;
|
|
struct nand_chip *this;
|
|
|
|
ls2g1a_soc_mtd = malloc(sizeof(struct mtd_info) + sizeof(struct ls2g1a_nand_info), M_DEVBUF, M_WAITOK);
|
|
if (!ls2g1a_soc_mtd) {
|
|
printf("Unable to allocate fcr_soc NAND MTD device structure.\n");
|
|
return -ENOMEM;
|
|
}
|
|
memset(ls2g1a_soc_mtd, 0, sizeof(struct mtd_info) + sizeof(struct ls2g1a_nand_info));
|
|
info = (struct ls2g1a_nand_info *)(&ls2g1a_soc_mtd[1]);
|
|
|
|
this = &info->nand_chip;
|
|
memset(this, 0, sizeof(struct nand_chip));
|
|
|
|
ls2g1a_soc_mtd->priv = info;
|
|
|
|
/* 15 us command delay time 从数据手册获知命令延迟时间 */
|
|
this->chip_delay = 15;
|
|
|
|
if(ls2g1a_nand_init(ls2g1a_soc_mtd)){//init mtd func, init desc, init mmio, reg
|
|
printf("\n\nerror: PMON nandflash driver have some error!\n\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
if (nand_scan(ls2g1a_soc_mtd, 1)) {
|
|
free(ls2g1a_soc_mtd, M_DEVBUF);
|
|
return -ENXIO;
|
|
}
|
|
if(ls2g1a_nand_detect(ls2g1a_soc_mtd)){
|
|
printf("error: PMON driver don't support the NANDFlash!\n ");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Register the partitions */
|
|
add_mtd_device(ls2g1a_soc_mtd, 0, 0x02000000, "kernel");
|
|
add_mtd_device(ls2g1a_soc_mtd, 0x02000000, 0x06000000, "os");
|
|
printf("init nand ok !\n");
|
|
return 0;
|
|
}
|
|
|
|
static int nand_load(int argc, char *av[])
|
|
{
|
|
int fd_r, fd_w, ret_r, ret_w, count;
|
|
unsigned char *load_buf;
|
|
|
|
if (argc != 2) {
|
|
printf("input error ! must be : nand_load xxx.xxx.xxx.xxx/xxx (load file)\n");
|
|
return -1;
|
|
}
|
|
|
|
load_buf = malloc(NAND_PAGE_PER_BLOCK * NAND_PAGE_SIZE, M_DEVBUF, M_WAITOK);
|
|
if(load_buf == NULL){
|
|
printf("malloc error !\n");
|
|
return -1;
|
|
}
|
|
|
|
fd_r = open(av[1], O_RDONLY);
|
|
if(fd_r < 0){
|
|
printf("src open error !\n");
|
|
return -1;
|
|
}
|
|
|
|
fd_w = open("/dev/fs/yaffs2@mtd0/vmlinux1", O_RDWR | O_CREAT | O_TRUNC);
|
|
if(fd_w < 0){
|
|
close(fd_r);
|
|
printf("dist vmlinux1 open error !\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("open write ok\n");
|
|
count = 0;
|
|
|
|
while(1){
|
|
ret_r = read(fd_r, load_buf, NAND_PAGE_PER_BLOCK * NAND_PAGE_SIZE);
|
|
if(ret_r < 0){
|
|
printf("read error !\n");
|
|
close(fd_r);
|
|
close(fd_w);
|
|
return -1;
|
|
}
|
|
|
|
ret_w = write(fd_w, load_buf, ret_r);
|
|
if(ret_w < 0){
|
|
printf("write vmlinux1 error !\n");
|
|
close(fd_r);
|
|
close(fd_w);
|
|
return -1;
|
|
}
|
|
|
|
count += ret_r;
|
|
printf("%d", count);
|
|
|
|
if(ret_r < NAND_PAGE_PER_BLOCK * NAND_PAGE_SIZE){
|
|
break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
free(load_buf, M_DEVBUF);
|
|
close(fd_r);
|
|
close(fd_w);
|
|
|
|
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", "val", 0, "hardware test", nanderase, 0, 99, CMD_REPEAT},
|
|
{"nandreadid", "val", 0, "hardware test", nand_read_id, 0, 99, CMD_REPEAT},
|
|
{"nandinit", "val", 0, "hardware test", ls2g1a_soc_nand_init, 0, 99, CMD_REPEAT},
|
|
{"nand_load", "path", 0, "spi_nand_load from ", nand_load, 1, 99, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
static void init_cmd __P((void)) __attribute__ ((constructor));
|
|
static void init_cmd()
|
|
{
|
|
cmdlist_expand(Cmds, 1);
|
|
}
|
|
|