硬件环境
主芯片: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层代码结构