spi nand driver代码流程分析

硬件环境

    主芯片:bcm63xx,      spi nand:Winbond W25N01GV

    内核:linux-4.1.27

代码环境

    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层代码结构

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的SPI NAND Flash驱动代码的示例,基于Linux内核的MTD框架: ``` #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/spi/spi.h> #define SPI_NAND_CMD_READ 0x03 #define SPI_NAND_CMD_READID 0x9F #define SPI_NAND_CMD_RESET 0xFF #define SPI_NAND_PAGE_SIZE 2048 #define SPI_NAND_BLOCK_SIZE (64 * 1024) #define SPI_NAND_CHIP_SIZE (1024 * 1024 * 8) struct spi_nand_chip { struct mtd_info mtd; struct spi_device *spi; u8 *buf; }; static int spi_nand_read_buf(struct spi_nand_chip *chip, u32 addr, u8 *buf, u32 len) { u8 cmd[4]; int ret; cmd[0] = SPI_NAND_CMD_READ; cmd[1] = addr >> 16; cmd[2] = addr >> 8; cmd[3] = addr; ret = spi_write_then_read(chip->spi, cmd, sizeof(cmd), buf, len); if (ret < 0) { dev_err(&chip->spi->dev, "SPI NAND read error: %d\n", ret); return ret; } return 0; } static int spi_nand_read_id(struct spi_nand_chip *chip) { u8 cmd = SPI_NAND_CMD_READID; u8 id[5]; int ret; ret = spi_write_then_read(chip->spi, &cmd, sizeof(cmd), id, sizeof(id)); if (ret < 0) { dev_err(&chip->spi->dev, "SPI NAND read ID error: %d\n", ret); return ret; } dev_info(&chip->spi->dev, "SPI NAND ID: %02x %02x %02x %02x %02x\n", id[0], id[1], id[2], id[3], id[4]); return 0; } static int spi_nand_probe(struct spi_device *spi) { struct spi_nand_chip *chip; struct mtd_info *mtd; int ret; chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; chip->buf = devm_kmalloc(&spi->dev, SPI_NAND_PAGE_SIZE, GFP_KERNEL); if (!chip->buf) return -ENOMEM; mtd = &chip->mtd; mtd->name = "spi-nand"; mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->writesize = SPI_NAND_PAGE_SIZE; mtd->erasesize = SPI_NAND_BLOCK_SIZE; mtd->size = SPI_NAND_CHIP_SIZE; mtd->_erase = nand_erase; mtd->_read = nand_read; ret = spi_setup(spi); if (ret) return ret; chip->spi = spi; ret = spi_nand_read_id(chip); if (ret) return ret; return mtd_device_register(mtd, NULL, 0); } static int spi_nand_remove(struct spi_device *spi) { struct mtd_info *mtd = spi_get_drvdata(spi); mtd_device_unregister(m

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值