Linux内核4.14版本——Nand子系统(1)——hisi504_nand.c分析

1. 简介

2. DTS

3. hisi_nfc_probe函数

3.1 获取dts中的资源

3.2 设置nand-chip结构体中必要的字段

3.3 nand_scan_ident扫描识别nand控制器

3.4 设置DMA相关的东西

3.5 ecc相关的配置

3.6 构造mtd结构体,并注册

4. nand_scan_tail

6. 参考文件


1. 简介

      我们以drivers\mtd\nand\hisi504_nand.c来分析nand flash的驱动。

      通过前文spi-nor子系统的研究,我们struct spi_nor结构体代表一个spi-nor的控制器设备。在nand的研究中,Linux使用struct nand_chip结构体来抽象nand control。

/**
 * struct nand_chip - NAND Private Flash Chip Data
 * @mtd:		MTD device registered to the MTD framework
 * @IO_ADDR_R:		[BOARDSPECIFIC] address to read the 8 I/O lines of the
 *			flash device
 * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the
 *			flash device.
 * @read_byte:		[REPLACEABLE] read one byte from the chip
 * @read_word:		[REPLACEABLE] read one word from the chip
 * @write_byte:		[REPLACEABLE] write a single byte to the chip on the
 *			low 8 I/O lines
 * @write_buf:		[REPLACEABLE] write data from the buffer to the chip
 * @read_buf:		[REPLACEABLE] read data from the chip into the buffer
 * @select_chip:	[REPLACEABLE] select chip nr
 * @block_bad:		[REPLACEABLE] check if a block is bad, using OOB markers
 * @block_markbad:	[REPLACEABLE] mark a block bad
 * @cmd_ctrl:		[BOARDSPECIFIC] hardwarespecific function for controlling
 *			ALE/CLE/nCE. Also used to write command and address
 * @dev_ready:		[BOARDSPECIFIC] hardwarespecific function for accessing
 *			device ready/busy line. If set to NULL no access to
 *			ready/busy is available and the ready/busy information
 *			is read from the chip status register.
 * @cmdfunc:		[REPLACEABLE] hardwarespecific function for writing
 *			commands to the chip.
 * @waitfunc:		[REPLACEABLE] hardwarespecific function for wait on
 *			ready.
 * @setup_read_retry:	[FLASHSPECIFIC] flash (vendor) specific function for
 *			setting the read-retry mode. Mostly needed for MLC NAND.
 * @ecc:		[BOARDSPECIFIC] ECC control structure
 * @buffers:		buffer structure for read/write
 * @buf_align:		minimum buffer alignment required by a platform
 * @hwcontrol:		platform-specific hardware control structure
 * @erase:		[REPLACEABLE] erase function
 * @scan_bbt:		[REPLACEABLE] function to scan bad block table
 * @chip_delay:		[BOARDSPECIFIC] chip dependent delay for transferring
 *			data from array to read regs (tR).
 * @state:		[INTERN] the current state of the NAND device
 * @oob_poi:		"poison value buffer," used for laying out OOB data
 *			before writing
 * @page_shift:		[INTERN] number of address bits in a page (column
 *			address bits).
 * @phys_erase_shift:	[INTERN] number of address bits in a physical eraseblock
 * @bbt_erase_shift:	[INTERN] number of address bits in a bbt entry
 * @chip_shift:		[INTERN] number of address bits in one chip
 * @options:		[BOARDSPECIFIC] various chip options. They can partly
 *			be set to inform nand_scan about special functionality.
 *			See the defines for further explanation.
 * @bbt_options:	[INTERN] bad block specific options. All options used
 *			here must come from bbm.h. By default, these options
 *			will be copied to the appropriate nand_bbt_descr's.
 * @badblockpos:	[INTERN] position of the bad block marker in the oob
 *			area.
 * @badblockbits:	[INTERN] minimum number of set bits in a good block's
 *			bad block marker position; i.e., BBM == 11110111b is
 *			not bad when badblockbits == 7
 * @bits_per_cell:	[INTERN] number of bits per cell. i.e., 1 means SLC.
 * @ecc_strength_ds:	[INTERN] ECC correctability from the datasheet.
 *			Minimum amount of bit errors per @ecc_step_ds guaranteed
 *			to be correctable. If unknown, set to zero.
 * @ecc_step_ds:	[INTERN] ECC step required by the @ecc_strength_ds,
 *			also from the datasheet. It is the recommended ECC step
 *			size, if known; if unknown, set to zero.
 * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
 *			      set to the actually used ONFI mode if the chip is
 *			      ONFI compliant or deduced from the datasheet if
 *			      the NAND chip is not ONFI compliant.
 * @numchips:		[INTERN] number of physical chips
 * @chipsize:		[INTERN] the size of one chip for multichip arrays
 * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
 * @pagebuf:		[INTERN] holds the pagenumber which is currently in
 *			data_buf.
 * @pagebuf_bitflips:	[INTERN] holds the bitflip count for the page which is
 *			currently in data_buf.
 * @subpagesize:	[INTERN] holds the subpagesize
 * @id:			[INTERN] holds NAND ID
 * @onfi_version:	[INTERN] holds the chip ONFI version (BCD encoded),
 *			non 0 if ONFI supported.
 * @jedec_version:	[INTERN] holds the chip JEDEC version (BCD encoded),
 *			non 0 if JEDEC supported.
 * @onfi_params:	[INTERN] holds the ONFI page parameter when ONFI is
 *			supported, 0 otherwise.
 * @jedec_params:	[INTERN] holds the JEDEC parameter page when JEDEC is
 *			supported, 0 otherwise.
 * @max_bb_per_die:	[INTERN] the max number of bad blocks each die of a
 *			this nand device will encounter their life times.
 * @blocks_per_die:	[INTERN] The number of PEBs in a die
 * @data_interface:	[INTERN] NAND interface timing information
 * @read_retries:	[INTERN] the number of read retry modes supported
 * @onfi_set_features:	[REPLACEABLE] set the features for ONFI nand
 * @onfi_get_features:	[REPLACEABLE] get the features for ONFI nand
 * @setup_data_interface: [OPTIONAL] setup the data interface and timing. If
 *			  chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this
 *			  means the configuration should not be applied but
 *			  only checked.
 * @bbt:		[INTERN] bad block table pointer
 * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash
 *			lookup.
 * @bbt_md:		[REPLACEABLE] bad block table mirror descriptor
 * @badblock_pattern:	[REPLACEABLE] bad block scan pattern used for initial
 *			bad block scan.
 * @controller:		[REPLACEABLE] a pointer to a hardware controller
 *			structure which is shared among multiple independent
 *			devices.
 * @priv:		[OPTIONAL] pointer to private chip data
 * @manufacturer:	[INTERN] Contains manufacturer information
 */

struct nand_chip {
	struct mtd_info mtd;
	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_byte)(struct mtd_info *mtd, uint8_t byte);
	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);
	void (*select_chip)(struct mtd_info *mtd, int chip);
	int (*block_bad)(struct mtd_info *mtd, loff_t ofs);
	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
	void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
	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);
	int (*erase)(struct mtd_info *mtd, int page);
	int (*scan_bbt)(struct mtd_info *mtd);
	int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
			int feature_addr, uint8_t *subfeature_para);
	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
			int feature_addr, uint8_t *subfeature_para);
	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
	int (*setup_data_interface)(struct mtd_info *mtd, int chipnr,
				    const struct nand_data_interface *conf);


	int chip_delay;
	unsigned int options;
	unsigned int bbt_options;

	int page_shift;
	int phys_erase_shift;
	int bbt_erase_shift;
	int chip_shift;
	int numchips;
	uint64_t chipsize;
	int pagemask;
	int pagebuf;
	unsigned int pagebuf_bitflips;
	int subpagesize;
	uint8_t bits_per_cell;
	uint16_t ecc_strength_ds;
	uint16_t ecc_step_ds;
	int onfi_timing_mode_default;
	int badblockpos;
	int badblockbits;

	struct nand_id id;
	int onfi_version;
	int jedec_version;
	union {
		struct nand_onfi_params	onfi_params;
		struct nand_jedec_params jedec_params;
	};
	u16 max_bb_per_die;
	u32 blocks_per_die;

	struct nand_data_interface *data_interface;

	int read_retries;

	flstate_t state;

	uint8_t *oob_poi;
	struct nand_hw_control *controller;

	struct nand_ecc_ctrl ecc;
	struct nand_buffers *buffers;
	unsigned long buf_align;
	struct nand_hw_control hwcontrol;

	uint8_t *bbt;
	struct nand_bbt_descr *bbt_td;
	struct nand_bbt_descr *bbt_md;

	struct nand_bbt_descr *badblock_pattern;

	void *priv;

	struct {
		const struct nand_manufacturer *desc;
		void *priv;
	} manufacturer;
};

2. DTS

	nand: nand@4020000 { compatible = "hisilicon,504-nfc";
		reg = <0x4020000 0x10000>, <0x5000000 0x1000>;
		interrupts = <0 379 4>;
		nand-bus-width = <8>;
		nand-ecc-mode = "hw";
		nand-ecc-strength = <16>;
		nand-ecc-step-size = <1024>;
		#address-cells = <1>;
		#size-cells = <1>;
		partition@0 { label = "nand_text";
			reg = <0x00000000 0x00400000>; };
		...
	}; 

3. hisi_nfc_probe函数

3.1 获取dts中的资源

static int hisi_nfc_probe(struct platform_device *pdev)
{
	int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
	struct device *dev = &pdev->dev;
	struct hinfc_host *host;
	struct nand_chip  *chip;
	struct mtd_info   *mtd;
	struct resource	  *res;
	struct device_node *np = dev->of_node;

	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
	if (!host)
		return -ENOMEM;
	host->dev = dev;

	platform_set_drvdata(pdev, host);
	chip = &host->chip;
	mtd  = nand_to_mtd(chip);

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(dev, "no IRQ resource defined\n");
		ret = -ENXIO;
		goto err_res;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	host->iobase = devm_ioremap_resource(dev, res);
	if (IS_ERR(host->iobase)) {
		ret = PTR_ERR(host->iobase);
		goto err_res;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	host->mmio = devm_ioremap_resource(dev, res);
	if (IS_ERR(host->mmio)) {
		ret = PTR_ERR(host->mmio);
		dev_err(dev, "devm_ioremap_resource[1] fail\n");
		goto err_res;
	}

	.......
}

3.2 设置nand-chip结构体中必要的字段

static int hisi_nfc_probe(struct platform_device *pdev)
{
	int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
	struct device *dev = &pdev->dev;
	struct hinfc_host *host;
	struct nand_chip  *chip;
	......

	nand_set_controller_data(chip, host);
	nand_set_flash_node(chip, np);
	chip->cmdfunc		= hisi_nfc_cmdfunc;
	chip->select_chip	= hisi_nfc_select_chip;
	chip->read_byte		= hisi_nfc_read_byte;
	chip->read_word		= hisi_nfc_read_word;
	chip->write_buf		= hisi_nfc_write_buf;
	chip->read_buf		= hisi_nfc_read_buf;
	chip->chip_delay	= HINFC504_CHIP_DELAY;
	chip->onfi_set_features	= nand_onfi_get_set_features_notsupp;
	chip->onfi_get_features	= nand_onfi_get_set_features_notsupp;

	hisi_nfc_host_init(host);

    ..........
}

      设置nand-chip结构体中必要的字段,hisi_nfc_host_init初始化nand flash控制器。

3.3 nand_scan_ident扫描识别nand控制器

static int hisi_nfc_probe(struct platform_device *pdev)
{
	.............
	ret = nand_scan_ident(mtd, max_chips, NULL);
	if (ret)
		goto err_res;

	............
}

3.4 设置DMA相关的东西

static int hisi_nfc_probe(struct platform_device *pdev)
{
	.............
	ret = nand_scan_ident(mtd, max_chips, NULL);
	if (ret)
		goto err_res;

	host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
		&host->dma_buffer, GFP_KERNEL);
	if (!host->buffer) {
		ret = -ENOMEM;
		goto err_res;
	}

	host->dma_oob = host->dma_buffer + mtd->writesize;
	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
	................
}

3.5 ecc相关的配置

static int hisi_nfc_probe(struct platform_device *pdev)
{
	..........
	if (chip->ecc.mode == NAND_ECC_HW)
		hisi_nfc_ecc_probe(host);
	..........
}

3.6 构造mtd结构体,并注册

static int hisi_nfc_probe(struct platform_device *pdev)
{
	............
	ret = nand_scan_tail(mtd);
	if (ret) {
		dev_err(dev, "nand_scan_tail failed: %d\n", ret);
		goto err_res;
	}

	ret = mtd_device_register(mtd, NULL, 0);
	if (ret) {
		dev_err(dev, "Err MTD partition=%d\n", ret);
		goto err_mtd;
	}

	return 0;

err_mtd:
	nand_release(chip);
err_res:
	return ret;
}

从第3节中,我们知道nand control的主要内容是:

    1. 填充nand-chip结构体。
    2. 初始化nand control控制器。
    3. 识别nand flash。
    4. 设置DMA相关的东西
    5. ecc相关的配置
    6. 填充mtd结构体,并注册mtd结构体。

下面我们主要讲填充mtd结构体的函数nand_scan_tail。

4. nand_scan_tail

我们假设ecc的mode是NAND_ECC_HW来分析这个函数。

/**
 * nand_scan_tail - [NAND Interface] Scan for the NAND device
 * @mtd: MTD device structure
 *
 * This is the second phase of the normal nand_scan() function. It fills out
 * all the uninitialized function pointers with the defaults and scans for a
 * bad block table if appropriate.
 */
int nand_scan_tail(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd_to_nand(mtd);
	struct nand_ecc_ctrl *ecc = &chip->ecc;
	struct nand_buffers *nbuf = NULL;
	...........

	/*
	 * FIXME: some NAND manufacturer drivers expect the first die to be
	 * selected when manufacturer->init() is called. They should be fixed
	 * to explictly select the relevant die when interacting with the NAND
	 * chip.
	 */
	chip->select_chip(mtd, 0);
	ret = nand_manufacturer_init(chip);
	chip->select_chip(mtd, -1);
	if (ret)
		goto err_free_nbuf;

	..........

	/*
	 * Check ECC mode, default to software if 3byte/512byte hardware ECC is
	 * selected and we have 256 byte pagesize fallback to software ECC
	 */MTD_OPS_RAW

	switch (ecc->mode) {
	......
	case NAND_ECC_HW:
		/* Use standard hwecc read page function? */
		if (!ecc->read_page)
			ecc->read_page = nand_read_page_hwecc;
		if (!ecc->write_page)
			ecc->write_page = nand_write_page_hwecc;
		if (!ecc->read_page_raw)
			ecc->read_page_raw = nand_read_page_raw;
		if (!ecc->write_page_raw)
			ecc->write_page_raw = nand_write_page_raw;
		if (!ecc->read_oob)
			ecc->read_oob = nand_read_oob_std;
		if (!ecc->write_oob)
			ecc->write_oob = nand_write_oob_std;
		if (!ecc->read_subpage)
			ecc->read_subpage = nand_read_subpage;
		if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
			ecc->write_subpage = nand_write_subpage_hwecc;
		..........
	}

	...........
	/* Fill in remaining MTD driver data */
	mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
	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;
	mtd->_write_oob = nand_write_oob;
	mtd->_sync = nand_sync;
	mtd->_lock = NULL;
	mtd->_unlock = NULL;
	mtd->_suspend = nand_suspend;
	mtd->_resume = nand_resume;
	mtd->_reboot = nand_shutdown;
	mtd->_block_isreserved = nand_block_isreserved;
	mtd->_block_isbad = nand_block_isbad;
	mtd->_block_markbad = nand_block_markbad;
	mtd->_max_bad_blocks = nand_max_bad_blocks;
	mtd->writebufsize = mtd->writesize;

	......
	return ret;
}

      主要是设置struct nand_chip结构体中eec相关的内容,以及填充mtd结构体。mtd结构体最终被注册。

6. 参考文件

mtd子系统_IT利刃出鞘的博客-CSDN博客

NANDFLASH原理分析_大江的专栏-CSDN博客
Linux NAND FLASH驱动框架分析---mtd-linuxDOS-ChinaUnix博客

【驱动】linux系统下nand flash驱动程序框架 - Leo.cheng - 博客园

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值