Read and Write operations by applications on NAND Flash
Next, continue to analyze the NAND Flash Driver:The function for detecting devices in NAND Flash is s3c24xx_nand_probe..
The s3c24xx_nand_probe function first creates a Data Structure struct mtd_info * MTD representing the original MTD device and initializes it. The source code is as follows:
/* S3c24xx_nand_probe
* Called by device layer when it finds a device matching one our driver can handled. This code checks to see if
* It can allocate all necessary resources then callthe NAND layer to look for devices
*/
Static intS3c24xx_nand_probe(Struct platform_device * pdev)
{
Struct s3c2410_platform_nand * plat = to_nand_plat (pdev );
Enum initi_cpu_type cpu_type;
Struct s3c2410_nand_info * Info;
Struct s3c2410_nand_mtd * nmtd;
Struct s3c2410_nand_set * sets;
Struct resource * res;
Int err = 0;
Int size;
Int nr_sets;
Int setno;
Cpu_type = platform_get_device_id (pdev)-> driver_data; // obtain the device ID
Pr_debug ("s3c2410_nand_probe (% P) \ n", pdev );
Info = kmalloc (sizeof (* info), gfp_kernel); // create a struct s3c2410_nand_info * info struct variable representing the NAND Flash Controller
If (Info = NULL ){
Dev_err (& pdev-> Dev, "no memory for flash info \ n ");
Err =-enomem;
Goto exit_error;
}
Memset (Info, 0, sizeof (* info ));
Platform_set_drvdata (pdev, Info );
Spin_lock_init (& info-> controller. Lock );
// Use the spin lock to initialize the waiting queue
Init_waitqueue_head (& info-> controller. WQ );
/* Get the clock source and enable it */
// Initialize the struct s3c2410_nand_info * info struct variable
Info-> CLK = clk_get (& pdev-> Dev, "NAND ");
If (is_err (Info-> CLK )){
Dev_err (& pdev-> Dev, "failed to get clock \ n ");
Err =-enoent;
Goto exit_error;
}
Clk_enable (Info-> CLK );
/* Allocate and map the resource */
/* Currently we assume we have the one resource */
Res = pdev-> resource;
Size = res-> end-res-> Start + 1;
Info-> area = request_mem_region (res-> Start, size, pdev-> name );
If (Info-> area = NULL ){
Dev_err (& pdev-> Dev, "cannot reserve register region \ n ");
Err =-enoent;
Goto exit_error;
}
Info-> device = & pdev-> dev;
Info-> platform = plat;
Info-> regs = ioremap (res-> Start, size );
Info-> cpu_type = cpu_type;
If (Info-> regs = NULL ){
Dev_err (& pdev-> Dev, "cannot reserve register region \ n ");
Err =-EIO;
Goto exit_error;
}
Dev_dbg (& pdev-> Dev, "mapped registers at % P \ n", info-> regs );
/* Initialise the hardware */
Err = s3c2410_nand_inithw (Info );
If (Err! = 0)
Goto exit_error;
Sets = (plat! = NULL )? Plat-> sets: NULL;
Nr_sets = (plat! = NULL )? Plat-> nr_sets: 1;
Info-> mtd_count = nr_sets;
/* Allocate our information */
Size = nr_sets * sizeof (* Info-> MTDs); // create the MTD struct variable struct s3c2410_nand_mtd * MTDS for the driver layer of the NAND Flash Hardware Device
Info-> MTDS = kmalloc (size, gfp_kernel );
If (Info-> MTDS = NULL ){
Dev_err (& pdev-> Dev, "failed to allocate MTD storage \ n ");
Err =-enomem;
Goto exit_error;
}
Memset (Info-> MTDS, 0, size );
/* Initialise all possible chips */
Nmtd = Info-> MTDS;
For (setno = 0; setno <nr_sets; setno ++, nmtd ++ ){
Pr_debug ("initialising set % d (% P, info % P) \ n", setno, nmtd, Info );
S3c2410_nand_init_chip(Info, nmtd, sets); // initializes the interface operation function of the underlying NAND Chip structure of struct nand_chip.
Nmtd-> scan_res =Nand_scan_ident(& Nmtd-> MTD, // read the NAND flash device ID and compare it with the NAND Flash ID table in the drive
(Sets )? Sets-> nr_chips: 1 );
If (nmtd-> scan_res = 0 ){
S3c2410_nand_update_chip (Info, nmtd );
Nand_scan_tail (& nmtd-> MTD );
S3c2410_nand_add_partition(Info, nmtd, sets );
// Add the created and initialized MTD original device to the MTD original device list
}
If (sets! = NULL)
Sets ++;
}
Err = s3c2410_nand_cpufreq_register (Info );
If (ERR <0 ){
Dev_err (& pdev-> Dev, "failed to init cpufreq support \ n ");
Goto exit_error;
}
If (allow_clk_stop (Info )){
Dev_info (& pdev-> Dev, "clock idle support enabled \ n ");
Clk_disable (Info-> CLK );
}
Pr_debug ("initialised OK \ n ");
Return 0;
Exit_error:
S3c24xx_nand_remove(Pdev );
If (ERR = 0)
Err =-einval;
Return err;
}
2. Read and Write Applications to NAND Flash Devices
1. s3c2440_nand_hwcontrol FunctionDirectly operate the command registers and address registers of the S3C2440 NAND flash controller.
Static void s3c2440_nand_hwcontrol (struct mtd_info * MTD, int cmd,
Unsigned int CTRL) // cmd indicates the command or address to be written to the NAND Flash Controller.
// CTRL indicates whether to write commands or addresses
{
Struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo (MTD );
If (cmd = nand_assist_none)
Return;
If (CTRL & nand_cle)
// Command
Writeb (CMD, info-> regs + s3c2440_nfcmd );
Else
Writeb (CMD, info-> regs + s3c2440_nfaddr );
}
2. The specific commands of the nand_command function should refer to the chip manual, including the operation sequence.
/**
* Nand_command-[Default] send command to NAND Device
* @ MTD: MTD device structure
* @ Command: the command to be sent
* @ Column: the column address for this command,-1 if none
* @ Page_addr: The page address for this command,-1 if none
*
* Send command to NAND device. This function is used for small page
* Devices (256/512 bytes per page)
*/
Static void nand_command (struct mtd_info * MTD, unsigned int command,
Int column, int page_addr)
{
Register struct nand_chip * chip = MTD-> priv;
Int CTRL = nand_ctrl_cle | nand_ctrl_change;
/*
* Write out the command to the device.
*/
If (command = nand_cmd_seqin ){
Int readcmd;
If (column> = MTD-> writesize ){
/* OOB area */
Column-= MTD-> writesize;
Readcmd = nand_cmd_readoob;
} Else if (column <256 ){
/* First 256 bytes --> read0 */
Readcmd = nand_assist_read0;
} Else {
Column-= 256;
Readcmd = nand_assist_read1;
}
Chip-> cmd_ctrl (MTD, readcmd, CTRL );
CTRL & = ~ Nand_ctrl_change;
}
Chip-> cmd_ctrl (MTD, command, CTRL );
/*
* Address cycle, when necessary
*/
CTRL = nand_ctrl_ale | nand_ctrl_change;
/* Serially input address */
If (column! =-1 ){
/* Adjust columns for 16 bit buswidth */
If (chip-> options & nand_buswidth_16)
Column >>> = 1;
Chip-> cmd_ctrl (MTD, column, CTRL );
CTRL & = ~ Nand_ctrl_change;
}
If (page_addr! =-1 ){
Chip-> cmd_ctrl (MTD, page_addr, CTRL );
CTRL & = ~ Nand_ctrl_change;
Chip-> cmd_ctrl (MTD, page_addr> 8, CTRL );
/* One More address cycle for devices> 32mib */
If (chip-> chipsize> (32 <20 ))
Chip-> cmd_ctrl (MTD, page_addr> 16, CTRL );
}
Chip-> 1__ctrl (MTD, nand_1__none, nand_nce | nand_ctrl_change );
/*
* Program and erase have their own busy handlers
* Status and sequential in needs no delay
*/
Switch (command ){
Case nand_cmd_pageprog:
Case nand_assist_erase1:
Case nand_assist_erase2:
Case nand_pai_seqin:
Case nand_assist_status:
Return;
Case nand_assist_reset:
If (chip-> dev_ready)
Break;
Udelay (chip-> chip_delay );
Chip-> cmd_ctrl (MTD, nand_cmd_status,
Nand_ctrl_cle | nand_ctrl_change );
Chip-> cmd_ctrl (MTD,
Nand_assist_none, nand_nce | nand_ctrl_change );
While (! (Chip-> read_byte (MTD) & nand_status_ready ));
Return;
/* This applies to read commands */
Default:
/*
* If we don't have access to the busy pin, we apply the given
* Command Delay
*/
If (! Chip-> dev_ready ){
Udelay (chip-> chip_delay );
Return;
}
}
/* Apply this short delay always to ensure that we do wait twb in
* Any case on any machine .*/
Ndelay (100 );
Nand_wait_ready (MTD );
}
3. nand_command_lp Function
The size of the page is 2 Kbytes,Replace the nand_command function with the nand_command_lp Function.
Static void nand_command_lp (struct mtd_info * MTD, unsigned int command,
Int column, int page_addr)
{
Register struct nand_chip * chip = MTD-> priv;
/* Emulate nand_assist_readoob */
If (command = nand_cmd_readoob ){
Column + = MTD-> writesize;
Command = nand_assist_read0;
}
/* Command latch cycle */
Chip-> cmd_ctrl (MTD, command & 0xff,
Nand_nce | nand_cle | nand_ctrl_change );
If (column! =-1 | page_addr! =-1 ){
Int CTRL = nand_ctrl_change | nand_nce | nand_ale;
/* Serially input address */
If (column! =-1 ){
/* Adjust columns for 16 bit buswidth */
If (chip-> options & nand_buswidth_16)
Column >>> = 1;
Chip-> cmd_ctrl (MTD, column, CTRL );
CTRL & = ~ Nand_ctrl_change;
Chip-> 1__ctrl (MTD, column> 8, CTRL );
}
If (page_addr! =-1 ){
Chip-> cmd_ctrl (MTD, page_addr, CTRL );
Chip-> maid (MTD, page_addr> 8,
Nand_nce | nand_ale );
/* One More address cycle for devices> 128mib */
If (chip-> chipsize> (128 <20 ))
Chip-> cmd_ctrl (MTD, page_addr> 16,
Nand_nce | nand_ale );
}
}
Chip-> 1__ctrl (MTD, nand_1__none, nand_nce | nand_ctrl_change );
/*
* Program and erase have their own busy handlers
* Status, sequential in, and deplete1 need no delay
*/
Switch (command ){
Case nand_cmd_cachedprog:
Case nand_cmd_pageprog:
Case nand_assist_erase1:
Case nand_assist_erase2:
Case nand_pai_seqin:
Case nand_assist_rndin:
Case nand_assist_status:
Case nand_assist_deplete1:
Return;
/*
* Read error status commands require only a short delay
*/
Case nand_assist_status_error:
Case nand_1__status_error0:
Case nand_assist_status_error1:
Case nand_assist_status_error2:
Case nand_assist_status_error3:
Udelay (chip-> chip_delay );
Return;
Case nand_assist_reset:
If (chip-> dev_ready)
Break;
Udelay (chip-> chip_delay );
Chip-> cmd_ctrl (MTD, nand_cmd_status,
Nand_nce | nand_cle | nand_ctrl_change );
Chip-> 1__ctrl (MTD, nand_1__none,
Nand_nce | nand_ctrl_change );
While (! (Chip-> read_byte (MTD) & nand_status_ready ));
Return;
Case nand_assist_rndout:
/* No ready/busy check necessary */
Chip-> cmd_ctrl (MTD, nand_cmd_rndoutstart,
Nand_nce | nand_cle | nand_ctrl_change );
Chip-> 1__ctrl (MTD, nand_1__none,
Nand_nce | nand_ctrl_change );
Return;
Case nand_assist_read0:
Chip-> 1__ctrl (MTD, nand_1__readstart,
Nand_nce | nand_cle | nand_ctrl_change );
Chip-> 1__ctrl (MTD, nand_1__none,
Nand_nce | nand_ctrl_change );
/* This applies to read commands */
Default:
/*
* If we don't have access to the busy pin, we apply the given
* Command Delay
*/
If (! Chip-> dev_ready ){
Udelay (chip-> chip_delay );
Return;
}
}
/* Apply this short delay always to ensure that we do wait twb in
* Any case on any machine .*/
Ndelay (100 );
Nand_wait_ready (MTD );
}