3.3 nand_scan_ident扫描识别nand控制器
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. 参考文件
NANDFLASH原理分析_大江的专栏-CSDN博客
Linux NAND FLASH驱动框架分析---mtd-linuxDOS-ChinaUnix博客