📄 nand.c
字号:
/*
* drivers/mtd/nand.c
*
* Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
*
* $Id: nand.c,v 1.1.1.1 2004/12/22 10:02:10 zyu Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ids.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/kernel.h>
//#include <linux/mtd/esram-command.h> //GIII
typedef void (* TREAD)( void); //GIII wuer
extern TREAD gfdpread, gfdpwrite, gfdperase;
#undef CONFIG_MTD_NAND_ECC //GIII
#ifdef CONFIG_MTD_NAND_ECC
#include <linux/mtd/nand_ecc.h>
#endif
extern pid_t wait;
/*
* Macros for low-level register control
*/
#define NAND_CTRL (*(volatile unsigned char *) \
((struct nand_chip *) mtd->priv)->CTRL_ADDR)
#define nand_select() ; //{nand_command(mtd, NAND_CMD_RESET, -1, -1); \
//udelay (10);}
#define nand_deselect() //{NAND_CTRL |= 0X00;} //NAND_CTRL |= ~this->NCE;
/*
* NAND low-level MTD interface functions
*/
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf, u_char *ecc_code);
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf,
u_char *ecc_code);
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
unsigned long count, loff_t to, size_t *retlen);
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
static void nand_sync (struct mtd_info *mtd);
/***************************/
int abc(){
int c = 1;
c++;
return c;
}
/*
* Send command to NAND device
*/
static void nand_command (struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
register unsigned long NAND_IO_ADDR = GFD_NAND_COM;
printf("Garfield III: Entering nand_command{} and do 0x%x.\n",(unsigned long)command);
*(RP)(NAND_IO_ADDR) = command;
//printf("nand_command(): jumping out now.\n");
/* Pause for 15us */
udelay (15);
}
/*
* NAND read
*/
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
#ifdef CONFIG_MTD_NAND_ECC
struct nand_chip *this = mtd->priv;
return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf);
#else
return nand_read_ecc (mtd, from, len, retlen, buf, NULL);
#endif
}
/*
* NAND read with ECC
*/
static int nand_read_ecc (U32 from, U32 len, U8 *buf)
{
register int d = 1;
int i,j, col, page, state, retlen;
int erase_state = 0;
U8 *tempbuf;
U8 *tempbuf2;
U8 *tempbuf_head;
tempbuf_head = SysLmalloc (len+1024);
if (!tempbuf_head) {
printf("nand_read_ecc():Unable to allocate NAND data tempbuf_head for Garfield III.\n");
return -1;
}
/* Do not allow reads past end of device */
//printf("nand_read_ecc():Do not allow reads past end of device.\n");
if ((from + len) > 4096*32*512) {
printf("out of device");
SysLfree(tempbuf_head);
return -1;
}
/* First we calculate the starting page */
page = from >> 9; //page_shift SHOULD be 9 in GIII
printf("nand_read_ecc():Now page is %x,\n",page);
/* Get raw starting column */
col = from & 0x1ff; //lowest 9 bits -> column
/* Initialize return value */
retlen = 0;
/* Record address of tempbuf head */
tempbuf = tempbuf_head ;
//*(RP)(0x10003000) &= ~(0x10); //GPT DOWN
//*(RP)(0x10000000) = 0x0000000; //shutdown inters
/* Loop until all data read */
while (retlen < len) {
*(RP)GFD_NAND_ADDR = ((unsigned long)page)<<8;
*(RP)GFD_NAND_CONF = 0x2200aaa;
*(RP)DMACC0SrcAddr = GFD_NAND_DATA; //read source = nand data register
*(RP)DMACC0DestAddr = (unsigned int)tempbuf;
*(RP)DMACC0Control = 0x20249b;
*(RP)DMACC0Configuration = 0x31d; //channel config finished!
*(volatile unsigned int *)0x11000104 = (0x80000000 );
j = *(volatile unsigned int *)0x11000124;
while((j&0x1) != 0x1){
j = *(volatile unsigned int *)0x11000124;
}
retlen = retlen +512; //////////test////
tempbuf += 512;
page++;
}
tempbuf2 = tempbuf_head+col;
for (j=0 ; j < len ; j++){
buf[j] = *(tempbuf2);
//printf("read buffer data[0x%x] is 0x%x.\n",j,buf[j]);
tempbuf2++;
}
//printf("GIII Nand: kfree (tempbuf_head).\n");
SysLfree(tempbuf_head);
//printf("GIII Nand:jumping out of read_ecc Now.\n");
//printf("****************************************************\n");
/* Return happy */
return 0;
}
static int nand_read_page (U32 from, U8 *buf)
{
int j, pagenum;
if ((from + 512) > 4096*32*512)
printf("nand_write_page: Attempted read past end of device\n");
/* First we calculate the starting page */
pagenum = from >> 9;
*(RP)GFD_NAND_ADDR = ((unsigned long)pagenum)<<8;
*(RP)GFD_NAND_CONF = 0x2200aaa;
*(RP)DMACC0SrcAddr = GFD_NAND_DATA;
*(RP)DMACC0DestAddr = (unsigned int)buf;
*(RP)DMACC0Control = 0x20249b;
*(RP)DMACC0Configuration = 0x31d;
*(volatile unsigned int *)0x11000104 = (0x80000000 );
j = *(volatile unsigned int *)0x11000124;
while((j&0x1) != 0x1){
j = *(volatile unsigned int *)0x11000124;
}
return 0;
}
static int nand_read_block (U32 from, U8 *buf)
{
int i;
U8 *tempbuf;
tempbuf = buf;
if ((from + 32*512) > 4096*32*512){
printf("nand_read_block: Attempted read past end of device\n");
return -1;
}
/* First we calculate the starting page */
for(i=0; i<32; i++){
nand_read_page (U32 from, U8 *tempbuf);
from += 512;
tempbuf += 512;
}
return 0;
}
static int nand_write_page (U32 to, U8*buf)
{
int i, j,page;
/* Do not allow write past end of page */
if ((to + len) > 4096*32*512) {
printf("nand_write_page: Attempted write past end of device\n");
return -1;
}
/* Shift to get page and block; one block has 32 pages */
page = ((int) to) >> 9; //page_shift = 9
/* Select the NAND device */
*(RP)GFD_NAND_COM = NAND_CMD_RESET;
*(RP)GFD_NAND_ADDR = ((unsigned long)page)<<8;
*(RP)GFD_NAND_CONF = 0x02200aaa;
*(RP)DMACC0SrcAddr = buf ;
*(RP)DMACC0DestAddr = GFD_NAND_DATA;
*(RP)DMACC0Control = 0x0020149B;
*(RP)DMACC0Configuration = 0x301b;
*(volatile unsigned int *)0x11000104 = (0x80000080);
j = *(volatile unsigned int *)0x11000124;
while((j&0x1) != 0x1){
j = *(volatile unsigned int *)0x11000124;
}
return 0;
}
static int nand_write_block (U32 to, U8*buf)
{
int i,tempto;
U8 *tempbuf;
tempbuf = buf;
if ((to + 32*512) > 4096*32*512){
printf("nand_write_block: Attempted read past end of device\n");
return -1;
}
/*make sure block head address */
tempto = (to >> 14)<<14;
/*we should erase one block before write something into it*/
nand_erase_block(tempto);
for(i=0; i<32; i++){
nand_write_page ( tempto, tempbuf);
tempto+= 512;
tempbuf += 512;
}
return 0;
}
/*
* NAND write
*/
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
#ifdef CONFIG_MTD_NAND_ECC
struct nand_chip *this = mtd->priv;
return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf);
#else
return nand_write_ecc (mtd, to, len, retlen, buf, NULL);
#endif
}
/*
* NAND write with ECC
*/
static int nand_write_ecc (U32 to, U32 len, U8*buf)
{
int i, j;
int first_page, last_page, start_block, end_block, blocknum, col, cnt, relative_addr;
U32 retlen;
U32 *data_buf;
/* Do not allow write past end of page */
if ((to + len) > 4096*32*512) {
printf("nand_write_ecc: Attempted write past end of device\n");
return -1;
}
data_buf = SysLmalloc (32*512); //malloc one block memory for temp use
if (!data_buf) {
printf("nand_read_ecc():Unable to allocate NAND data tempbuf_head for Garfield III.\n");
return -1;
}
/* Shift to get page and block; one block has 32 pages */
first_page = ((int) to) >> 9; //page_shift = 9
start_block = first_page>>5; //block_shift = 5
last_page = ((int)(to + len))>>9;
end_block = last_page>>5;
blocknum = end_block - start_block;
relative_addr = to-start_block*32*512; //count the relative address in one block
col = relative_addr;
retlen = len;
cnt =0;
for(blocknum=start_block; blocknum<=end_block; blocknum++)
{
//把这一块读到data_buf
nand_read_block ((blocknum << 14), data_buf);
if(relative_addr+len<32*512)
{
for(i=0; i<len; i++)
data_buf[col+i] = buf[i];
// write data_buf into NANDFLASH (one block)
nand_write_block ((blocknum << 14), data_buf);
}else{
if(retlen+col>=32*512)
{
for(i=col; i<32*512; i++,cnt++)
data_buf[col+i] = buf[cnt];
// write data_buf into NANDFLASH
nand_write_block ((blocknum << 14), data_buf);
col = 0;
retlen -= 32*512;
}else{
for(i=0; i<retlen; i++,cnt++)
data_buf[i] = buf[cnt];
// write data_buf into NANDFLASH
nand_write_block ((blocknum << 14), data_buf);
}
}
}
printf("****************************************************\n");
printf("nand_write():jumping out of nand_write_ecc().\n");
return 0;
}
/*
* NAND erase a block
*/
static int nand_erase_block(U32 blockhead)
{
int i,realadd;
/* Do not allow erase past end of device */
if (blockhead > 4096*32*512) {
printf ("nand_erase_block: Erase past end of device\n");
return -1;
}
/* Shift to get first page */
realadd = (int) (realadd << 9);
/*erase action now!*/
*(RP)(GFD_NAND_CONF) = 0x00100aaa; // 3 addresses modle
*(RP)GFD_NAND_ADDR = (realadd >> 9 );
/*GPIO PH5 SET TO 1, unprotect write!!!!!!!!!*/
*(RP)0x1000B06C = 0x00000020;
*(RP)0x1000B07C = 0x00000020;
*(RP)0x1000B068 = 0x00000000;
*(RP)0x11000104 = 0x80000060;
i = *(volatile unsigned int *)GFD_NAND_IDLE;
while((i&0x1) != 0x1){
i = *(volatile unsigned int *)GFD_NAND_IDLE;
}
return 0;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cnasic wuer changed");
MODULE_DESCRIPTION("Garfield NAND flash driver code");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -