Analysis of nandflash driver under Linux (2) - Based on s3c6410 platform

Publisher:喜茶我要七分糖Latest update time:2022-06-16 Source: eefocusKeywords:linux Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

1. In the probe function in the previous article, there appeared a large for loop to judge the NAND manufacturer, device number, and whether it is MLC or SLC. How are these done?


In fact, these are all defined in the NAND chip. We only need to read out this information according to the corresponding timing to make a judgment. See the following figure (extracted from a NAND chip manual):


2. In the previous article, the nand_scan(s3c_mtd, 1) function was not described in detail. This article will describe this function. The source code is as follows:



/**

 * nand_scan - [NAND Interface] Scan for the NAND device

 * @mtd: MTD device structure

 * @maxchips: Number of chips to scan for

 *

 * This fills out all the uninitialized function pointers

 * with the defaults.

 * The flash ID is read and the mtd/chip structures are

 * filled with the appropriate values.

 * The mtd->owner field must be set to the module of the caller

 *

 */

int nand_scan(struct mtd_info *mtd, int maxchips)

{

int ret;



/* Many callers got this wrong, so check for it for a while... */

if (!mtd->owner && caller_is_module()) {

printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!n");

BUG();

}



ret = nand_scan_ident(mtd, maxchips);

if (!ret) If the above function succeeds, this executes the nand_scan_tail function. The analysis of this function is shown below:

ret = nand_scan_tail(mtd);

return right;

}


The main function is nand_scan_ident, the source code is as follows:


/**

 * nand_scan_ident - [NAND Interface] Scan for the NAND device

 * @mtd:     MTD device structure

 * @maxchips:     Number of chips to scan for

 *

 * This is the first phase of the normal nand_scan() function. It

 * reads the flash ID and sets up MTD fields accordingly.

 *

 * The mtd->owner field must be set to the module of the caller.

 */

int nand_scan_ident(struct mtd_info *mtd, int maxchips)

{

int i, busw, nand_maf_id;

struct nand_chip *chip = mtd->priv; get the struct nand_chip structure

struct nand_flash_dev *type;



/* Get buswidth to select the correct functions */

busw = chip->options & NAND_BUSWIDTH_16; is related to the data width, see the following figure:

/* Set the default functions */ Set the default functions according to the data width:

nand_set_defaults(chip, busw);The source code of this function is as follows:


/*

 * Set default functions

 */

static void nand_set_defaults(struct nand_chip *chip, int busw)

{

/* check for proper chip_delay setup, set 20us if not */

if (!chip->chip_delay)

chip->chip_delay = 20;



/* check, if a user supplied command function given */

if (chip->cmdfunc == NULL)

chip->cmdfunc = nand_command;



/* check, if a user supplied wait function given */

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;



if (!chip->controller) {

chip->controller = &chip->hwcontrol;

spin_lock_init(&chip->controller->lock);

init_waitqueue_head(&chip->controller->wq);

}



}



/* Read the flash type */ Read the information of the NAND chip and assign some structure members

type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);



if (IS_ERR(type)) {

printk(KERN_WARNING "No NAND device found!!!n");

chip->select_chip(mtd, -1);

return PTR_ERR(type);

}



/* Check for a chip array */ Related to multiple chips

for (i = 1; i < maxchips; i++) {

chip->select_chip(mtd, i);

/* See comment in nand_get_flash_type for reset */

chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);

/* Send the command for reading device ID */

chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);

/* Read manufacturer and device IDs */

if (nand_maf_id != chip->read_byte(mtd) ||

   type->id != chip->read_byte(mtd))

break;

}

if (i > 1)

printk(KERN_INFO "%d NAND chips detectedn", i);



/* Store the number of chips and calc total size for mtd */

chip->numchips = i;

mtd->size = i * chip->chipsize;



return 0;

}

3. nand_scan_tail function: The source code is as follows:


/**

 * nand_scan_tail - [NAND Interface] Scan for the NAND device

 */


First look at the name of this function. Tail means the end. So what does this function do? Looking at the code below, you can roughly know that it is related to ECC.

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;



/* Set the internal oob buffer location, just after the page data */

chip->oob_poi = chip->buffers->databuf + mtd->writesize;



/*

* If no default placement scheme is given, select an appropriate one

*/

if (!chip->ecc.layout) {

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 %dn", mtd->oobsize);

BUG();

}

This section is related to the OOB layout of the ECC check code. After reading the following definitions, you will understand:


/* Define default oob placement schemes for large and small page devices */

static struct nand_ecclayout nand_oob_8 = {

.eccbytes = 3,

.eccpos = {0, 1, 2},

.oobfree = {

{.offset = 3,

.length = 2},

{.offset = 6,

.length = 2}}

};


static struct nand_ecclayout nand_oob_16 = {

.eccbytes = 6,

.eccpos = {0, 1, 2, 3, 6, 7},

.oobfree = {

{.offset = 8,

. length = 8}}

};


static struct nand_ecclayout nand_oob_64 = {

.eccbytes = 24,

.eccpos = {

  40, 41, 42, 43, 44, 45, 46, 47,

  48, 49, 50, 51, 52, 53, 54, 55,

  56, 57, 58, 59, 60, 61, 62, 63},

.oobfree = {

{.offset = 2,

.length = 38}}

};



static struct nand_ecclayout nand_oob_128 = {

.eccbytes = 48,

.eccpos = {

80,81,82,83,84,85,86,87,88,89,

90,91,92,93,94,95,96,97,98,99,

100,101,102,103,104,105,106,107,108,109,

110,111,112,113,114,115,116,117,118,119,

120,121,122,123,124,125,126,127},

.oobfree = {

{.offset = 2,

.length = 78}}

};


The corresponding structure prototype:


/*

 * ECC layout control structure. Exported to userspace for

 * diagnosis and to allow creation of raw images

 */

struct nand_ecclayout {

uint32_t eccbytes; length of the check code

uint32_t eccpos[64]; indicates the storage location of the checksum in the OOB area

uint32_t oobavail;

struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; OOB area available outside the verification code

};



if (!chip->write_page)

chip->write_page = nand_write_page;



/*

* check ECC mode, default to software if 3byte/512byte hardware ECC is

* selected and we have 256 byte pagesize fallback to software ECC

*/

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;



switch (chip->ecc.mode) {

case NAND_ECC_HW:

/* Use standard hwecc read page function ? */

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_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 possiblen");

BUG();

}

/* Use standard syndrome read/write page function ? */

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_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 ECCn",

      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_oob = nand_read_oob_std;

chip->ecc.write_oob = nand_write_oob_std;

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.write_oob = nand_write_oob_std;

chip->ecc.size = mtd->writesize;

chip->ecc.bytes = 0;

break;



default:

printk(KERN_WARNING "Invalid NAND_ECC_MODE %dn",

      chip->ecc.mode);

BUG();

}The above paragraph is assigned to the corresponding function according to the way ECC is generated



/*

* The number of bytes available for a client to place data into

* the out of band area

*/

chip->ecc.layout->oobavail = 0;

for (i = 0; chip->ecc.layout->oobfree[i].length; i++)

chip->ecc.layout->oobavail +=

chip->ecc.layout->oobfree[i].length;

mtd->oobavail = chip->ecc.layout->oobavail;



/*

* Set the number of read / write steps for one page depending on ECC

* mode

*/

chip->ecc.steps = mtd->writesize / chip->ecc.size;

[1] [2]
Keywords:linux Reference address:Analysis of nandflash driver under Linux (2) - Based on s3c6410 platform

Previous article:ARM11 S3C6410 Series Tutorial 2: Serial Port
Next article:S3C6410 Development (1) - Getting Started

Recommended ReadingLatest update time:2024-11-16 11:45

Linux driver to create a hello module
Goal: Execute insmod hello.ko on the development board to print hello init on the console; then execute rmmod to print hello exit on the console The steps to create a hello module are as follows: 1. Create a hello.c file and enter the following code #include linux/module.h #inc
[Microcontroller]
Simple configuration of linux-arm development environment
Simple configuration of the linux-arm development environment is the first step in learning ARM. Many beginners will struggle with this issue for a long time and cannot configure the development environment. I recommend you to watch Wei Dongshan's video, which is very detailed and basically explains the code to you (m
[Microcontroller]
Linux platform device driver - button device driver
A previous blog briefly introduced the platform device driver model (http://www.cnblogs.com/ape-ming/p/5107641.html). Now, based on the template listed in that blog, we will modify the example in the previous blog (http://www.cnblogs.com/ape-ming/p/5110996.html) into the platform device driver model. 1. P
[Microcontroller]
Do the interrupt service routines of various ARM Linux drivers work in ARM's IRQ mode?
As we all know, ARM has various modes such as IRQ, FIQ, USR, SVC, ABORT, etc. When the system receives IRQ, it will enter ARM's IRQ mode. So, do the interrupt service routines of various ARM Linux drivers work in ARM's IRQ mode? the answer is negative. We add a section of assembly to read CPSR: Then we randomly fi
[Microcontroller]
Do the interrupt service routines of various ARM Linux drivers work in ARM's IRQ mode?
Linux2.6.32 kernel porting s3c2440 - DM9000 network card driver porting
reference: http://caiming1987612.blog.163.com/blog/static/118556676200961752714307/ http://blog.chinaunix.net/u1/34474/showart_401078.html http://hi.baidu.com/%D3%F3%C4%E0%C4%EA%B8%E2/blog/item/6256fea7bfceac98d0435819.html Timing diagram and pin connection: http://blog.chinaunix.net/u1/57901/sho
[Microcontroller]
Analysis of make uImage compilation process in Linux transplantation
The command to compile the Linux kernel code that can be run by uboot is make uImage. The following is a detailed description of the process of generating linux-2.6.22.6/arch/arm/boot/uImage: 1. The difference between vmlinux, Image, uImage and zImage 2. Brief introduction to vmlinux generation process
[Microcontroller]
Analysis of make uImage compilation process in Linux transplantation
Changes in power management methods in the new version of Linux system device architecture of linux driver power management
Changes in power management methods in the new version of Linux system device architecture based on linux-2.6.32   1. Power management part of each data structure of the device model   The Linux device model is jointly described through many structures, such as struct device, struct device_type, struct class, struct d
[Microcontroller]
arm driver linux kernel linked list
" Linux kernel linked list" involves five kernel driver functions, one kernel structure, and analyzes two kernel driver functions; there are zero related application templates or kernel driver templates for reference, and one related application template or kernel driver for reference 1. Description   Linked list is
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号