spi nand driver程式碼流程分析
硬體環境
主晶片:bcm63xx, spi nand:Winbond W25N01GV
程式碼環境
linux-4.1.27/drivers/mtd mtd_blkdevs.o mtdblock.o mtdchar.o mtdconcat.o mtdcore.o mtd.o mtdpart.o mtdsuper.o ofpart.o linux-4.1.27/drivers/mtd/maps bcm963xx_mtd.o bcm963xx.o map_funcs.o linux-4.1.27/drivers/mtd/nand bcm63xx_spinand.o
程式碼結構 mtd |---maps/bcm963xx_mtd.c |---module_init(mtd_init); |---bcmspinand_probe |---nand_scan(mtd, 1) |---nand->init_size(mtd, nand, NULL) |---setup_mtd_parts(mtd); |---nand/bcm63xx_spinand.c
讀寫擦除命令
/* Command codes for the flash_command routine */ #define FLASH_PROG 0x02 /* program load data to cache */ #define FLASH_READ 0x03 /* read data from cache */ #define FLASH_WRDI 0x04 /* reset write enable latch */ #define FLASH_WREN 0x06 /* set write enable latch */ #define FLASH_READ_FAST 0x0B /* read data from cache */ #define FLASH_GFEAT 0x0F /* get feature option */ #define FLASH_PEXEC 0x10 /* program cache data to memory array */ #define FLASH_PREAD 0x13 /* read from memory array to cache */ #define FLASH_SFEAT 0x1F /* set feature option */ #define FLASH_SREAD 0x7C /* get Macronix enhanced bad bit */ #define FLASH_PROG_RAN 0x84 /* program load data to cache at offset */ #define FLASH_BERASE 0xD8 /* erase one block in memory array */ #define FLASH_RDID 0x9F /* read manufacturer and product id */ #define FLASH_RESET 0xFF /* reset flash */
暫存器地址和描述
#define FEATURE_STAT_ENH 0x30
#define FEATURE_PROT_ADDR 0xA0 //Protection Register
#define FEATURE_FEAT_ADDR 0xB0 //Configuration Register
#define FEATURE_STAT_ADDR 0xC0 //Status Register-3
#define FEATURE_STAT_AUX 0xF0
spi nand讀寫api對接controller
spiRead(struct spi_transfer *xfer)
struct spi_message message;
spi_message_init(&message);
spi_message_add_tail(xfer, &message);
return(spi_async(pSpiDevice, &message));
spiWrite(unsigned char *msg_buf, int nbytes)
struct spi_message message;
struct spi_transfer xfer;
spi_message_init(&message);
memset(&xfer, 0, (sizeof xfer));
xfer.prepend_cnt = 0;
xfer.len = nbytes;
xfer.speed_hz = pSpiDevice->max_speed_hz;
xfer.rx_buf = NULL;
xfer.tx_buf = msg_buf;
spi_message_add_tail(&xfer, &message);
spi_async(pSpiDevice, &message);
裝置復位
Device Reset (FFh)
FLASH_RESET
unsigned char buf[4];
buf[0] = FLASH_RESET;
spiWrite(buf, 1);
讀狀態暫存器
主要關注讀page時內部ECC校驗狀態bit位;還有裝置狀態bit位,每次操作裝置是否完成都是讀這個bit位;
Read Status Register (0Fh / 05h)
FLASH_GFEAT
spi_nand_ready()
#define STAT_OIP 0x1 /* operation in progress */
//Bit[0]為1表示BUSY,0表示空閒Completed,對應datasheet pg20
return (spi_nand_status()&STAT_OIP) ? 0 : 1;
spi_nand_get_cmd(FLASH_GFEAT, FEATURE_STAT_ADDR);
unsigned char buf[4];
struct spi_transfer xfer;
memset(&xfer, 0, sizeof(struct spi_transfer));
buf[0] = FLASH_GFEAT;
buf[1] = FEATURE_STAT_ADDR;
xfer.tx_buf = buf;
xfer.rx_buf = buf;
xfer.len = 1;
xfer.speed_hz = spi_flash_clock;
xfer.prepend_cnt = 2;
spiRead(&xfer);
return buf[0];
防寫或配置暫存器
Write Protection/Configurations Register (1Fh / 01h)
FLASH_SFEAT
spi_nand_set_feat(unsigned char feat_addr, unsigned char feat_val)
unsigned char buf[3];
buf[0] = FLASH_SFEAT;
buf[1] = feat_addr;
buf[2] = feat_val;
spiWrite(buf, 3);
//init, disable block locking
#define FEAT_DISABLE 0x0
spi_nand_set_feat(FEATURE_PROT_ADDR, FEAT_DISABLE);
//read_page, enable ECC,
#define FEAT_ECC_EN 0x10
spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_ECC_EN);
讀裝置ID
Read JEDEC ID (9Fh)
FLASH_RDID
unsigned char buf[2];
spi_nand_get_device_id(buf, 2);
unsigned char buffer[2];
struct spi_transfer xfer;
memset(&xfer, 0, sizeof(struct spi_transfer));
buffer[0] = FLASH_RDID;
buffer[1] = 0;
xfer.tx_buf = buffer;
xfer.rx_buf = buf;
xfer.len = len;
xfer.speed_hz = spi_flash_clock;
xfer.prepend_cnt = 2;
spiRead(&xfer);
讀page
先發命令13h和行地址等待讀在cache中,獲取ECC校驗狀態,最後再發命令13h和列地址,傳入buffer接收資料;
Page Data Read (13h)
FLASH_PREAD
spi_nand_read_page(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len)
buf[0] = FLASH_PREAD;
spi_nand_row_addr(page_addr, buf+1);
buf[0] = (unsigned char)(page_addr>>(pchip->chip_page_shift+16)); //dummy byte
buf[1] = (unsigned char)(page_addr>>(pchip->chip_page_shift+8));
buf[2] = (unsigned char)(page_addr>>(pchip->chip_page_shift));
先發送高地址
spiWrite(buf, 4);
while(!spi_nand_ready());//等待請求完成
status = spi_nand_ecc();
int status;
status = spi_nand_get_cmd(FLASH_GFEAT, FEATURE_STAT_ADDR);
#define STAT_ECC_MASK1 0x30 /* general, Gigadevice */
status = status & STAT_ECC_MASK1;//取Bit[5:4]位
#define STAT_ECC_GOOD 0x00
if (status == STAT_ECC_GOOD)
return(FLASH_API_OK);
#define STAT_ECC_UNCORR 0x20 /* uncorrectable error 超過4bit無法糾正的錯誤*/
if (status == STAT_ECC_UNCORR){ // correctable errors
printk("nand ecc is uncorr error\n");
return(FLASH_API_ERROR);
}
printk("nand ecc is corr error\n");
return(FLASH_API_CORR); // everything else is correctable
spi_xfr(page_addr, page_offset, buffer, len);
unsigned char buf[4];
struct spi_transfer xfer;
buf[0] = FLASH_READ;
spi_nand_col_addr(page_addr, page_offset, buf+1);
buf[3] = 0; //dummy byte
memset(&xfer, 0, sizeof(struct spi_transfer));
xfer.tx_buf = buf;
xfer.rx_buf = buffer;
xfer.len = maxread;
xfer.speed_hz = spi_flash_clock;
xfer.prepend_cnt = 4;
xfer.addr_len = 3; // length of address field (max 4 bytes)
xfer.addr_offset = 1; // offset of first addr byte in header
xfer.hdr_len = 4; // length of header
xfer.unit_size = 1; // data for each transfer will be divided into multiples of unit_size
spiRead(&xfer);
while (!spi_nand_ready());
寫page
先發命令84h和列地址隨後發資料寫在cache,再發命令10h和行地址寫入flash,最後讀出來進行比較,比較一致表示寫成功;
Random Load Program Data (84h)
FLASH_PROG_RAN
Program Execute (10h)
FLASH_PEXEC
spi_nand_write_page(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len)
unsigned char xfer_buf[pchip->chip_page_size + pchip->chip_spare_size];
spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_ECC_EN); // enable ECC if writing to page
memset(xfer_buf, 0xff, sizeof(xfer_buf));
memcpy(xfer_buf + page_offset, buffer, len);
spi_buf[0] = FLASH_PROG_RAN;
spi_nand_col_addr(page_addr, page_offset, spi_buf + 1);
memcpy(&spi_buf[3], xfer_buf + page_offset, maxwrite);
spi_nand_write_enable();
prot = spi_nand_get_cmd(FLASH_GFEAT, FEATURE_PROT_ADDR);//判斷protect register裡面/WP是否使能為0,否則設定為0
if( prot != 0 )
{
prot = 0;
spi_nand_set_feat(FEATURE_PROT_ADDR, prot);
}
buf[0] = FLASH_WREN;//set write enable latch
spiWrite(buf, 1);
spiWrite(spi_buf, maxwrite + 3);
while(!spi_nand_ready());
spi_nand_write_enable();
spi_buf[0] = FLASH_PEXEC;
spi_nand_row_addr(page_addr, spi_buf + 1);
spiWrite(spi_buf, 4);
while(!spi_nand_ready());
status = spi_nand_status();
spi_nand_write_disable();
spi_nand_read_page(page_addr, 0, buf, pchip->chip_page_size+pchip->chip_spare_size);
memcmp(xfer_buf, buf, pchip->chip_page_size)
擦除block
先判斷是否壞塊,然後發命令D8h進行擦除;
128KB Block Erase (D8h)
FLASH_BERASE
spi_nand_sector_erase_int(unsigned long addr)
spi_nand_is_blk_bad(addr)//判斷是否壞塊
spi_nand_read_page(addr, pchip->chip_page_size, &buf, 1);//讀OOB區第一個位元組
if (0xFF != buf)
spi_nand_write_enable();
buf[0] = FLASH_BERASE;
spi_nand_row_addr(addr, buf+1);
spiWrite(buf, 4);
while(!spi_nand_ready()) ;
status = spi_nand_status();
#define STAT_EFAIL 0x4 /* erase fail */
if( status & STAT_EFAIL )
{
printk("spi_nand_sector_erase_int(): Erase block 0x%lx failed, sts 0x%x\n", addr >> pchip->chip_block_shift, status);
return(FLASH_API_ERROR);
}
spi_nand_write_disable();
寫使能
Write Enable (06h) 每次寫page、擦除block之前必須寫使能
static int spi_nand_write_enable(void)
{
unsigned char buf[4], prot;
/* make sure it is not locked first in protect register*/
prot = spi_nand_get_cmd(FLASH_GFEAT, FEATURE_PROT_ADDR);
if( prot != 0 )
{
prot = 0;
spi_nand_set_feat(FEATURE_PROT_ADDR, prot);
}
/* send write enable cmd and check feature status WEL latch bit */
buf[0] = FLASH_WREN;
spiWrite(buf, 1);
while(!spi_nand_ready());
while(!spi_nand_wel());
return(FLASH_API_OK);
}
關閉寫使能
Write Disable (04h)
每次寫page、擦除block、復位之後需要關閉寫使能;
static int spi_nand_write_disable(void)
{
unsigned char buf[4];
buf[0] = FLASH_WRDI;
spiWrite(buf, 1);
while(!spi_nand_ready());
while(spi_nand_wel());
return(FLASH_API_OK);
}
下一篇
分析mtd層程式碼結構