1. 程式人生 > >spi nand driver程式碼流程分析

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

 built-in.o  nand_base.o  nand_bbt.o  nand_ecc.o  nand_ids.o  nand.o  nand_timings.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

        |---bcmspinand_probe

讀寫擦除命令

/* 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層程式碼結構