天天看點

MTD裝置驅動--NAND flash

前面的文章MTD裝置驅動(http://blog.csdn.net/paomadi/article/details/9262307)講了mtd裝置的架構組織

其中講述了調用int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts) 

該函數一個作用是mtd_info slave來繼承mtd_info master的屬性方法

那麼mtd_info master的屬性方法在哪裡設定?

針對nand flash則通過以下步驟來設定

一.配置設定一個nand_chip,并初始化其成員函數

struct nand_chip {
	void __iomem *IO_ADDR_R;
	void __iomem *IO_ADDR_W;
	uint8_t (*read_byte)(struct mtd_info *mtd);	//讀一個位元組
	u16 (*read_word)(struct mtd_info *mtd);		//讀一個字
	void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);	//寫緩沖區
	void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);			//讀緩沖區
	int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);	//校驗緩沖區
	void (*select_chip)(struct mtd_info *mtd, int chip);					//選擇晶片
	int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);		//壞塊
	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);					//标記壞塊
	void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);		//控制指令
	int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,u8 *id_data);	//初始化大小
	int (*dev_ready)(struct mtd_info *mtd);	//裝置準備好
	void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,int page_addr);
	int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
	void (*erase_cmd)(struct mtd_info *mtd, int page);	//擦除,指令
	int (*scan_bbt)(struct mtd_info *mtd);	//掃描bbt區
	int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,int status, int page);
	int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);	//頁寫
	int chip_delay;
	unsigned int options;	//選項
	int page_shift;
	int phys_erase_shift;
	int bbt_erase_shift;
	int chip_shift;
	int numchips;	//晶片個數
	uint64_t chipsize;	//晶片容量
	int pagemask;
	int pagebuf;
	int subpagesize;
	uint8_t cellinfo;
	int badblockpos;
	int badblockbits;
	int onfi_version;
	struct nand_onfi_params	onfi_params;
	flstate_t state;
	uint8_t *oob_poi;
	struct nand_hw_control *controller;
	struct nand_ecclayout *ecclayout;
	struct nand_ecc_ctrl ecc;
	struct nand_buffers *buffers;
	struct nand_hw_control hwcontrol;
	struct mtd_oob_ops ops;
	uint8_t *bbt;
	struct nand_bbt_descr *bbt_td;
	struct nand_bbt_descr *bbt_md;
	struct nand_bbt_descr *badblock_pattern;
	void *priv;
};
           

二.配置設定mtd_info結構體

初始化幾個變量一般是owner子產品所有者(THIS_MODULE),priv私有資料(一般選擇nand_chip對象或包含nand_chip對象結構體對象),name(名字)

三.調用nand_scan函數,将第二步配置設定的mtd_info對象和nand flash的晶片個數傳遞進來

nand_scan

int nand_scan(struct mtd_info *mtd, int maxchips)
{
	int ret;

	if (!mtd->owner && caller_is_module()) {
		printk(KERN_CRIT "%s called with NULL mtd->owner!\n",__func__);
		BUG();
	}
	ret = nand_scan_ident(mtd, maxchips, NULL);	//讀取flash ID并建立MTD相應字段
	if (!ret)
		ret = nand_scan_tail(mtd);	//填充未初始化的功能函數預設函數并掃描壞塊表
	return ret;
}
EXPORT_SYMBOL(nand_scan);
           

nand_scan函數分成兩大塊來完成nand_scan_ident和nand_scan_tail

1.nand_scan_ident讀取flash ID并建立MTD相應字段

int nand_scan_ident(struct mtd_info *mtd, int maxchips,struct nand_flash_dev *table)
{
	int i, busw, nand_maf_id, nand_dev_id;
	struct nand_chip *chip = mtd->priv;	//私有資料中擷取nand_chip
	struct nand_flash_dev *type;

	busw = chip->options & NAND_BUSWIDTH_16;	//擷取總線寬度
	nand_set_defaults(chip, busw);	//設定nand_chip預設方法函數
	type = nand_get_flash_type(mtd, chip, busw,&nand_maf_id, &nand_dev_id, table);	//擷取nand flash裝置類型
	if (IS_ERR(type)) {
		if (!(chip->options & NAND_SCAN_SILENT_NODEV))
			printk(KERN_WARNING "No NAND device found.\n");
		chip->select_chip(mtd, -1);
		return PTR_ERR(type);
	}
	for (i = 1; i < maxchips; i++) {	//晶片個數
		chip->select_chip(mtd, i);	//調用select_chip方法選中晶片
		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);	//調用NAND_CMD_RESET指令重新開機裝置
		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);	//調用NAND_CMD_READID指令讀取裝置ID
		if (nand_maf_id != chip->read_byte(mtd) || nand_dev_id != chip->read_byte(mtd))	//讀廠商ID和裝置ID
			break;
	}
	if (i > 1)
		printk(KERN_INFO "%d NAND chips detected\n", i);
	chip->numchips = i;	//晶片數
	mtd->size = i * chip->chipsize;	//MTD裝置總容量
	return 0;
}
EXPORT_SYMBOL(nand_scan_ident);
           

1.1nand_set_defaults設定一些預設方法

static void nand_set_defaults(struct nand_chip *chip, int busw)
{
	if (!chip->chip_delay)
		chip->chip_delay = 20;
	if (chip->cmdfunc == NULL)
		chip->cmdfunc = nand_command;									//nand指令
	if (chip->waitfunc == NULL)
		chip->waitfunc = nand_wait;										//等待指令執行完
	if (!chip->select_chip)
		chip->select_chip = nand_select_chip;							//片選
	if (!chip->read_byte)
		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;		//讀位元組
	if (!chip->read_word)
		chip->read_word = nand_read_word;								//讀字
	if (!chip->block_bad)
		chip->block_bad = nand_block_bad;								//壞塊查找
	if (!chip->block_markbad)
		chip->block_markbad = nand_default_block_markbad;				//标記壞塊
	if (!chip->write_buf)
		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;		//寫緩沖區
	if (!chip->read_buf)
		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;		//讀緩沖區
	if (!chip->verify_buf)
		chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;	//校驗緩沖區
	if (!chip->scan_bbt)
		chip->scan_bbt = nand_default_bbt;								//掃描bbt
	if (!chip->controller) {
		chip->controller = &chip->hwcontrol;
		spin_lock_init(&chip->controller->lock);
		init_waitqueue_head(&chip->controller->wq);
	}
}
           

1.2nand_get_flash_type擷取nand flash類型

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,struct nand_chip *chip,int busw,int *maf_id, int *dev_id,struct nand_flash_dev *type)
{
	int i, maf_idx;
	u8 id_data[8];
	int ret;

	chip->select_chip(mtd, 0);	//片選晶片
	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);	//重新開機晶片
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);	//讀晶片ID
	*maf_id = chip->read_byte(mtd);		//讀工廠id
	*dev_id = chip->read_byte(mtd);		//讀裝置id
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);	//讀晶片ID
	for (i = 0; i < 2; i++)
		id_data[i] = chip->read_byte(mtd);		//讀取晶片ID
	if (id_data[0] != *maf_id || id_data[1] != *dev_id) {	//比較ID值
		printk(KERN_INFO "%s: second ID read did not match %02x,%02x against %02x,%02x\n", __func__,*maf_id, *dev_id, id_data[0], id_data[1]);
		return ERR_PTR(-ENODEV);
	}
	if (!type)
		type = nand_flash_ids;
	for (; type->name != NULL; type++)	//擷取比對的裝置
		if (*dev_id == type->id)
			break;
	chip->onfi_version = 0;
	if (!type->name || !type->pagesize) {	//檢測是否onfi接口标準
		ret = nand_flash_detect_onfi(mtd, chip, busw);
		if (ret)
			goto ident_done;
	}
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);	//讀晶片ID
	for (i = 0; i < 8; i++)	//讀取全部id資訊
		id_data[i] = chip->read_byte(mtd);
	if (!type->name)
		return ERR_PTR(-ENODEV);
	if (!mtd->name)
		mtd->name = type->name;	//設定名字
	chip->chipsize = (uint64_t)type->chipsize << 20;	//擷取晶片容量
	if (!type->pagesize && chip->init_size) {
		busw = chip->init_size(mtd, chip, id_data);	//初始化晶片容量
	} 
	else if (!type->pagesize) {
		int extid;
		chip->cellinfo = id_data[2];
		extid = id_data[3];
		if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && id_data[0] == NAND_MFR_SAMSUNG 
			&& (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && id_data[5] != 0x00) {
			mtd->writesize = 2048 << (extid & 0x03);
			extid >>= 2;
			switch (extid & 0x03) {
			case 1:
				mtd->oobsize = 128;
				break;
			case 2:
				mtd->oobsize = 218;
				break;
			case 3:
				mtd->oobsize = 400;
				break;
			default:
				mtd->oobsize = 436;
				break;
			}
			extid >>= 2;
			mtd->erasesize = (128 * 1024) << (((extid >> 1) & 0x04) | (extid & 0x03));
			busw = 0;
		}
		else {
			mtd->writesize = 1024 << (extid & 0x03);
			extid >>= 2;
			mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
			extid >>= 2;
			mtd->erasesize = (64 * 1024) << (extid & 0x03);
			extid >>= 2;
			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
		}
	}
	else {
		mtd->erasesize = type->erasesize;	//擦除尺寸
		mtd->writesize = type->pagesize;	//頁尺寸
		mtd->oobsize = mtd->writesize / 32;	//oob區尺寸
		busw = type->options & NAND_BUSWIDTH_16;	//資料寬度
		if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 && id_data[6] == 0x00 && id_data[7] == 0x00 && mtd->writesize == 512) {
			mtd->erasesize = 128 * 1024;
			mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
		}
	}
	chip->options &= ~NAND_CHIPOPTIONS_MSK;
	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
	chip->options |= NAND_NO_AUTOINCR;
	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
		if (nand_manuf_ids[maf_idx].id == *maf_id)
			break;
	}
	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
		printk(KERN_INFO "NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,*dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,busw ? 16 : 8);
		return ERR_PTR(-EINVAL);
	}
	chip->page_shift = ffs(mtd->writesize) - 1;
	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;

	chip->bbt_erase_shift = chip->phys_erase_shift =
		ffs(mtd->erasesize) - 1;
	if (chip->chipsize & 0xffffffff)
		chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
	else {
		chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
		chip->chip_shift += 32 - 1;
	}
	chip->badblockbits = 8;
	if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))	//設定壞塊位置
		chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
	else
		chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
	if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX))
		chip->options |= NAND_BBT_SCANLASTPAGE;
	else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX 
		|| *maf_id == NAND_MFR_TOSHIBA || *maf_id == NAND_MFR_AMD)) || (mtd->writesize == 2048 && *maf_id == NAND_MFR_MICRON))
		chip->options |= NAND_BBT_SCAN2NDPAGE;
	if (!(busw & NAND_BUSWIDTH_16) && *maf_id == NAND_MFR_STMICRO && mtd->writesize == 2048) {
		chip->options |= NAND_BBT_SCANBYTE1AND6;
		chip->badblockpos = 0;
	}
	if (chip->options & NAND_4PAGE_ARRAY)
		chip->erase_cmd = multi_erase_cmd;
	else
		chip->erase_cmd = single_erase_cmd;
	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
		chip->cmdfunc = nand_command_lp;
	printk(KERN_INFO "NAND device: Maf ID: 0x%02x,Chip ID: 0x%02x (%s, %s)\n erasesize: 0x%x, writesize: %d, oobsize: %d\n", *maf_id, *dev_id,
		nand_manuf_ids[maf_idx].name, chip->onfi_version ? type->name : chip->onfi_params.model,mtd->erasesize, mtd->writesize, mtd->oobsize);
	return type;
}
           

2.nand_scan_tail填充未初始化的功能函數預設函數并掃描壞塊表

int nand_scan_tail(struct mtd_info *mtd)
{
	int i;
	struct nand_chip *chip = mtd->priv;

	if (!(chip->options & NAND_OWN_BUFFERS))
		chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
	if (!chip->buffers)
		return -ENOMEM;
	chip->oob_poi = chip->buffers->databuf + mtd->writesize;	//計算oob位置
	if (!chip->ecc.layout) {	//設定ecc區布局
		switch (mtd->oobsize) {
		case 8:
			chip->ecc.layout = &nand_oob_8;
			break;
		case 16:
			chip->ecc.layout = &nand_oob_16;
			break;
		case 64:
			chip->ecc.layout = &nand_oob_64;
			break;
		case 128:
			chip->ecc.layout = &nand_oob_128;
			break;
		default:
			printk(KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize);
			BUG();
		}
	}
	if (!chip->write_page)	//沒有頁寫方法
		chip->write_page = nand_write_page;	//指定預設的頁寫方法
	switch (chip->ecc.mode) {	
	case NAND_ECC_HW_OOB_FIRST:
		if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) {
			printk(KERN_WARNING "No ECC functions supplied; Hardware ECC not possible\n");
			BUG();
		}
		if (!chip->ecc.read_page)
			chip->ecc.read_page = nand_read_page_hwecc_oob_first;
	case NAND_ECC_HW:
		if (!chip->ecc.read_page)
			chip->ecc.read_page = nand_read_page_hwecc;
		if (!chip->ecc.write_page)
			chip->ecc.write_page = nand_write_page_hwecc;
		if (!chip->ecc.read_page_raw)
			chip->ecc.read_page_raw = nand_read_page_raw;
		if (!chip->ecc.write_page_raw)
			chip->ecc.write_page_raw = nand_write_page_raw;
		if (!chip->ecc.read_oob)
			chip->ecc.read_oob = nand_read_oob_std;
		if (!chip->ecc.write_oob)
			chip->ecc.write_oob = nand_write_oob_std;
	case NAND_ECC_HW_SYNDROME:
		if ((!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) &&
		    (!chip->ecc.read_page || chip->ecc.read_page == nand_read_page_hwecc ||
		     !chip->ecc.write_page || chip->ecc.write_page == nand_write_page_hwecc)) {
			printk(KERN_WARNING "No ECC functions supplied; Hardware ECC not possible\n");
			BUG();
		}
		if (!chip->ecc.read_page)
			chip->ecc.read_page = nand_read_page_syndrome;
		if (!chip->ecc.write_page)
			chip->ecc.write_page = nand_write_page_syndrome;
		if (!chip->ecc.read_page_raw)
			chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
		if (!chip->ecc.write_page_raw)
			chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
		if (!chip->ecc.read_oob)
			chip->ecc.read_oob = nand_read_oob_syndrome;
		if (!chip->ecc.write_oob)
			chip->ecc.write_oob = nand_write_oob_syndrome;
		if (mtd->writesize >= chip->ecc.size)
			break;
		printk(KERN_WARNING "%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",chip->ecc.size, mtd->writesize);
		chip->ecc.mode = NAND_ECC_SOFT;
	case NAND_ECC_SOFT:
		chip->ecc.calculate = nand_calculate_ecc;
		chip->ecc.correct = nand_correct_data;
		chip->ecc.read_page = nand_read_page_swecc;
		chip->ecc.read_subpage = nand_read_subpage;
		chip->ecc.write_page = nand_write_page_swecc;
		chip->ecc.read_page_raw = nand_read_page_raw;
		chip->ecc.write_page_raw = nand_write_page_raw;
		chip->ecc.read_oob = nand_read_oob_std;
		chip->ecc.write_oob = nand_write_oob_std;
		if (!chip->ecc.size)
			chip->ecc.size = 256;
		chip->ecc.bytes = 3;
		break;
	case NAND_ECC_NONE:
		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
		chip->ecc.read_page = nand_read_page_raw;
		chip->ecc.write_page = nand_write_page_raw;
		chip->ecc.read_oob = nand_read_oob_std;
		chip->ecc.read_page_raw = nand_read_page_raw;
		chip->ecc.write_page_raw = nand_write_page_raw;
		chip->ecc.write_oob = nand_write_oob_std;
		chip->ecc.size = mtd->writesize;
		chip->ecc.bytes = 0;
		break;
	default:
		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",chip->ecc.mode);
		BUG();
	}
	chip->ecc.layout->oobavail = 0;
	for (i = 0; chip->ecc.layout->oobfree[i].length && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
		chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length;
	mtd->oobavail = chip->ecc.layout->oobavail;
	chip->ecc.steps = mtd->writesize / chip->ecc.size;
	if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
		printk(KERN_WARNING "Invalid ecc parameters\n");
		BUG();
	}
	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
		switch (chip->ecc.steps) {
		case 2:
			mtd->subpage_sft = 1;
			break;
		case 4:
		case 8:
		case 16:
			mtd->subpage_sft = 2;
			break;
		}
	}
	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
	chip->state = FL_READY;	//初始化狀态
	chip->select_chip(mtd, -1);	//選中晶片
	chip->pagebuf = -1;
	mtd->type = MTD_NANDFLASH;	//MTD裝置類型
	mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :MTD_CAP_NANDFLASH;
	mtd->erase = nand_erase;	//擦除方法
	mtd->point = NULL;
	mtd->unpoint = NULL;
	mtd->read = nand_read;	//讀方法
	mtd->write = nand_write;	//寫方法
	mtd->panic_write = panic_nand_write;
	mtd->read_oob = nand_read_oob;	//讀oob區
	mtd->write_oob = nand_write_oob;	//寫oob區
	mtd->sync = nand_sync;	//同步
	mtd->lock = NULL;
	mtd->unlock = NULL;
	mtd->suspend = nand_suspend;	//挂起
	mtd->resume = nand_resume;	//喚醒
	mtd->block_isbad = nand_block_isbad;	//壞塊判斷
	mtd->block_markbad = nand_block_markbad;	//标記壞塊
	mtd->writebufsize = mtd->writesize;
	mtd->ecclayout = chip->ecc.layout;	//ecc布局
	if (chip->options & NAND_SKIP_BBTSCAN)
		return 0;
	return chip->scan_bbt(mtd);	//掃描,标記壞塊
}
EXPORT_SYMBOL(nand_scan_tail);
           

四.nand_scan完了也就設定好mtd_info master的屬性及方法了,那麼就可以調用add_mtd_partitions添加mtd的分區了

五.關于分區mtd_partition資訊,一般在闆級初始化(MACHINE_START)檔案中配置

例如:

static struct mtd_partition xxx_nand_partitions_bs128[] = {
	/* All the partition sizes are listed in terms of NAND block size */
	{
		.name		= "U-Boot",
		.offset		= 0,	/* Offset = 0x0 */
		.size		= 18 * SZ_128K,
		.mask_flags	= MTD_WRITEABLE,	/* force read-only */
	},
	{
		.name		= "U-Boot Env",
		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x240000 */
		.size		= 2 * SZ_128K,
	},
	{
		.name		= "U-Boot Logo",
		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x280000 */
		.size		= 24 * SZ_128K,
	},
	{
		.name		= "Kernel",
		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x580000 */
		.size		= 34 * SZ_128K,
	},
	{
		.name		= "File System",
		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x6C0000 */
		.size		= 1601 * SZ_128K,
	},
	{
		.name		= "Reserved",
		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0xCEE0000 */
		.size		= MTDPART_SIZ_FULL,
	},

};