Analysis of nandflash driver under Linux (1)——Based on s3c6410 platform

Publisher:茶叶侠Latest update time:2022-06-15 Source: eefocusKeywords:linux Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

The source code is mainly in the S3c_nand.c (linux2.6.28driversmtdnand) file.


1. Module loading and unloading


module_init(s3c_nand_init);

module_exit(s3c_nand_exit);


static void __exit s3c_nand_exit(void)

{

platform_driver_unregister(&s3c2450_nand_driver);

platform_driver_unregister(&s3c6400_nand_driver);

platform_driver_unregister(&s3c6410_nand_driver);

platform_driver_unregister(&s5pc100_nand_driver);

}


static int __init s3c_nand_init(void)

{

printk("S3C NAND Driver, (c) 2008 Samsung Electronicsn");


platform_driver_register(&s3c2450_nand_driver);

platform_driver_register(&s3c6400_nand_driver);

        platform_driver_register(&s3c6410_nand_driver);Our related part

        return platform_driver_register(&s5pc100_nand_driver);

}


The corresponding platform_device is as follows:


static struct resource s3c_nand_resource[] = {

[0] = {

.start = S3C64XX_PA_NAND,

.end   = S3C64XX_PA_NAND + S3C64XX_SZ_NAND - 1,

.flags = IORESOURCE_MEM,

}

};


struct platform_device s3c_device_nand = {

.name  = "s3c-nand",

.id  = -1,

.num_resources = ARRAY_SIZE(s3c_nand_resource),

.resource   = s3c_nand_resource,

};


The corresponding platform_driver is as follows:


static struct platform_driver s3c6410_nand_driver = {

.probe  = s3c6410_nand_probe,

.remove  = s3c_nand_remove,

.suspend  = s3c_nand_suspend,

.resume  = s3c_nand_resume,

.driver  = {

.name = "s3c6410-nand",

.owner  = THIS_MODULE,

},

};


Do you feel that there is something wrong with the above? The driver name and device name of the platform device should be the same, but now they are different. However, in the following function: it has been corrected.


static void __init smdk6410_map_io(void)

{

s3c_device_nand.name = "s3c6410-nand";


        ...............


}


2. Now let's look at the probe function: the s3c6410_nand_probe function source code is as follows:


static int s3c6410_nand_probe(struct platform_device *dev)

{

return s3c_nand_probe(dev, TYPE_S3C6410);

}


There are definitions:


enum s3c_cpu_type {

TYPE_S3C2450,  /* including s3c2416 */

TYPE_S3C6400,

TYPE_S3C6410,  /* including s3c6430/31 */

        TYPE_S5PC100,

};


2.1, then look at the s3c_nand_probe function, the source code is as follows:


/* s3c_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 calls the

 * nand layer to look for devices

 */

static int s3c_nand_probe(struct platform_device *pdev,enum s3c_cpu_type cpu_type)

struct s3c_nand_mtd_info *plat_info = pdev->dev.platform_data; Where is this assigned?


In the Mach-smdk6410.c (linux2.6.28archarmmach-s3c6410) file, there is a function:


static void __init smdk6410_machine_init(void) is in the first two lines of this function:


s3c_device_nand.dev.platform_data = &s3c_nand_mtd_part_info;  nand有关

s3c_device_onenand.dev.platform_data = &s3c_onenand_data;   onenand有关


The s3c_nand_mtd_part_info structure is a global variable:


struct s3c_nand_mtd_info s3c_nand_mtd_part_info = {

.chip_nr = 1,

.mtd_part_nr = ARRAY_SIZE(s3c_partition_info),

.partition = s3c_partition_info,

};



struct mtd_partition *partition_info = (struct mtd_partition *)plat_info->partition;

struct nand_chip *nand;

struct resource *res;

int err = 0;

int ret = 0;

int i, j, size;



#if defined(CONFIG_MTD_NAND_S3C_HWECC) is related to the mode of generating ECC

struct nand_flash_dev *type = NULL;

u_char tmp;

u_char dev_id;

#endif



/* get the clock source and enable it */

s3c_nand.clk = clk_get(&pdev->dev, "nand");

if (IS_ERR(s3c_nand.clk)) {

dev_err(&pdev->dev, "failed to get clock");

err = -ENOENT;

goto exit_error;

}

clk_enable(s3c_nand.clk);



/* allocate and map the resource */

/* currently we assume we have the one resource */

res = pdev->resource; Get I/O memory resources

size = res->end - res->start + 1;



s3c_nand.area = request_mem_region(res->start, size, pdev->name); I/O memory resource request


if (s3c_nand.area == NULL) {

dev_err(&pdev->dev, "cannot reserve register regionn");

err = -ENOENT;

goto exit_error;

}



s3c_nand.cpu_type   = cpu_type;

s3c_nand.device     = &pdev->dev;

s3c_nand.regs       = ioremap(res->start, size);


if (s3c_nand.regs == NULL) {

dev_err(&pdev->dev, "cannot reserve register regionn");

err = -EIO;

goto exit_error;

}

The above lines of code are all about s3c_nand initialization. s3c_nand is a s3c_nand_info structure with the following definition:


static struct s3c_nand_info s3c_nand;


struct s3c_nand_info {

/* mtd info */

struct nand_hw_controlcontroller;

struct s3c_nand_mtd_info*mtds;

struct s3c2410_platform_nand*platform;



/* device info */

struct device*device;

struct resource*area;

struct clk*clk;

void __iomem*regs;

void __iomem  *sel_reg;

int sel_bit;

int mtd_count;



enum s3c_cpu_typecpu_type;

};


Just now, we initialized these items.



/* allocate memory for MTD device structure and private data */ Apply for memory of the size of struct mtd_info structure and struct nand_chip structure. Why do we need to apply for it here?


         Defined: static struct mtd_info *s3c_mtd = NULL;

s3c_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);



if (!s3c_mtd) {

printk("Unable to allocate NAND MTD dev structure.n");

return -ENOMEM;

}



/* Get pointer to private data */ How do we get this? Look carefully at s3c_mtd[1] below. Do you understand?

nand = (struct nand_chip *) (&s3c_mtd[1]);



/* Initialize structures */ Initialize to 0

memset((char *) s3c_mtd, 0, sizeof(struct mtd_info));

memset((char *) nand, 0, sizeof(struct nand_chip));



/* Link the private data with the MTD structure */ The pointer in mtd_info points to the struct nand_chip structure

s3c_mtd->priv = nand;



for (i = 0; i < plat_info->chip_nr; i++){ This loop continues until the end of this function, which can be said to be the main body of the probe function.


It says that its main work is to allocate and initialize the I/O memory in nand_chip according to the special situation of the target board NAND controller, ECC check, hwcontrol(), dev_ready()correct_data(), read_byte(), write_byte() and other member functions (if no value is assigned, the default function in nand_base.c will be used).


Then, call the nand_scan() function with mtd_info as the parameter to detect the existence of NAND Flash.


Finally, if partitioning is required, add_mtd_partitions() is called with mtd_info and mtd_partition as parameters to add partition information.




Here we involve a structure plat_info which is an instance of the struct s3c_nand_mtd_info structure:


struct s3c_nand_mtd_info {


uint chip_nr;

uint mtd_part_nr;

struct mtd_partition *partition;

};


In the Partition.h (linux2.6.28archarmplat-s3cincludeplat) file, it is defined:


struct s3c_nand_mtd_info s3c_nand_mtd_part_info = {

.chip_nr = 1,

.mtd_part_nr = ARRAY_SIZE(s3c_partition_info),

.partition = s3c_partition_info,

};


Because s3c6410 supports dual-chip NAND, see the following figure (extracted from the user manual)


nand->IO_ADDR_R= (char *)(s3c_nand.regs + S3C_NFDATA);

nand->IO_ADDR_W= (char *)(s3c_nand.regs + S3C_NFDATA);

nand->cmd_ctrl= s3c_nand_hwcontrol;

nand->dev_ready= s3c_nand_device_ready;

nand->scan_bbt= s3c_nand_scan_bbt;

nand->options= 0;



#if defined(CONFIG_MTD_NAND_S3C_CACHEDPROG)   

nand->options|= NAND_CACHEPRG;

#endif



#if defined(CONFIG_MTD_NAND_S3C_HWECC)        硬件ECC

nand->ecc.mode= NAND_ECC_HW;

nand->ecc.hwctl= s3c_nand_enable_hwecc;

nand->ecc.calculate= s3c_nand_calculate_ecc;

nand->ecc.correct= s3c_nand_correct_data;


This is also a structure struct nand_ecc_ctrl ecc; you can see it is related to ECC


s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);

s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);

s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);

s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

s3c_nand_device_ready(0);



tmp = readb(nand->IO_ADDR_R); /* Maf. ID */ Manufacturer ID

dev_id = tmp = readb(nand->IO_ADDR_R); /* Device ID */  设备ID



for (j = 0; nand_flash_ids[j].name != NULL; j++) {

if (tmp == nand_flash_ids[j].id) {

type = &nand_flash_ids[j];

break;

}

}


There are two structures involved here, which are about the device and manufacturer information: as shown below:


/**

 * struct nand_flash_dev - NAND Flash Device ID Structure

 * @name: Identify the device type

 * @id: device ID code

 * @pagesize: Pagesize in bytes. Either 256 or 512 or 0

 * If the pagesize is 0, then the real pagesize

 * and the eraseize are determined from the

 * extended id bytes in the chip

 * @erasesize: Size of an erase block in the flash device.

 * @chipsize: Total chipsize in Mega Bytes

 * @options: Bitfield to store chip relevant options

 */

struct nand_flash_dev {

char *name;

int id;

unsigned long pagesize;

unsigned long chipsize;

unsigned long erasesize;

unsigned long options;

};



/**

 * struct nand_manufacturers - NAND Flash Manufacturer ID Structure

 * @name: Manufacturer name

 * @id: manufacturer ID code of device.

*/

struct nand_manufacturers {

int id;

char * name;

};


Their instances are defined in the Nand_ids.c (linux2.6.28driversmtdnand) file


You can check it yourself in the struct nand_flash_dev nand_flash_ids[] and struct nand_manufacturers nand_manuf_ids[] arrays.







if (!type) {

printk("Unknown NAND Device.n");

goto exit_error;

}


nand->cellinfo = readb(nand->IO_ADDR_R); /* the 3rd byte */

tmp = readb(nand->IO_ADDR_R);/*  the 4th byte */



if (!type->pagesize) {

if (((nand->cellinfo >> 2) & 0x3) == 0) {   SLC

nand_type = S3C_NAND_TYPE_SLC;

nand->ecc.size = 512;

nand->ecc.bytes= 4;



if ((1024 << (tmp & 0x3)) > 512) {

nand->ecc.read_page = s3c_nand_read_page_1bit;

nand->ecc.write_page = s3c_nand_write_page_1bit;

nand->ecc.read_oob = s3c_nand_read_oob_1bit;

nand->ecc.write_oob = s3c_nand_write_oob_1bit;

nand->ecc.layout = &s3c_nand_oob_64;

} else {

nand->ecc.layout = &s3c_nand_oob_16;

}

} else {

nand_type = S3C_NAND_TYPE_MLC;       MLC

nand->options |= NAND_NO_SUBPAGE_WRITE;/* NOP = 1 if MLC */

nand->ecc.read_page = s3c_nand_read_page_4bit;

nand->ecc.write_page = s3c_nand_write_page_4bit;

nand->ecc.size = 512;

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

Previous article:Linux serial port driver - s3c6410 platform (I)
Next article:Arm-linux memory management (4)

Recommended ReadingLatest update time:2024-11-23 10:35

S3C6410 Embedded Application Platform Construction (VI) - Linux-3.14.4 Ported to OK6410 (Yaffs2 File Production)
This article mainly describes how to use the yaffs2 tool and busybox to create a yaffs2 file system image. Most of them are referenced from the Internet, the purpose is to record learning, not for any purpose. 1. Make the mkyaffs2image tool Enter the utils directory under the yaffs2 source dire
[Microcontroller]
Design of LCD driver terminal based on ARM9 & Linux
1 Introduction The LCD driver terminal integrates the LCD controller, microcontroller, etc., and encapsulates the functions of LCD control and graphical interface display through software. It opens the operation interface to the user and shields the control details of the LCD display, so that the user can contro
[Microcontroller]
Design of LCD driver terminal based on ARM9 & Linux
[S3C6410-00] Embedded Linux development environment construction
This article is a summary of the previous one, which involves the most basic environment construction issues in embedded Linux development. I will put my summary online later. Let's get started. When developing embedded Linux, the most basic tools are Host-Windows system, VisualMachine-VMWare and Board. T
[Microcontroller]
Compiled U-boot and arm-linux-gcc compiler used
To be honest, compiling U-boot is quite tiring. There are not many modifications to be made, but a lot of effort is spent on the compiler, and various strange problems are often encountered. Below is the compiled U-boot and the corresponding gcc compiler GCC download address: http://download.csdn.net/detail/king_m
[Microcontroller]
Compiled U-boot and arm-linux-gcc compiler used
Installation of arm linux cross-compilation tool
(1) Downloading the tool  http://www.codesourcery.com/sgpp/lite/arm/portal/release1293 or IA32 GNU/Linux TAR. The current tool name is arm-2010q1-202-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 (2) Copy the downloaded compressed package to Ubuntu. For example, the address I copied is /share/toolchain, and thi
[Microcontroller]
__read_mostly variable in linux kernel
Kernel version: 2.6.14 When reading the socket source code, there is the following sentence (net/socket.c):       view plain   copy     print ? static struct vfsmount *sock_mnt __read_mostly;       I felt that __read_mostly was very strange, so I analyzed it in depth. The __read_mostly
[Microcontroller]
AT91RM9200 Linux porting notes (Part 3) - porting Linux kernel 2.6.17
The board I have originally came with a 2.4.19 kernel. I plan to port the new 2.6 kernel and download the 2.6.17 kernel from the Internet. The download address is: http://www.kernel.org/pub/linux/kernel/v2.6/ Modify the PATH environment variable or the CROSS_COMPILE macro of the Makefile file, add the cross-compilatio
[Microcontroller]
How to learn Linux device drivers in the face of constantly upgraded kernels
  Faced with the constantly upgraded Linux kernel, GNU development tools, and various graphics libraries in the Linux environment, many Linux application developers and Linux device driver developers are both excited and annoyed. The excitement is that the new software and tools provide me with more powerful functions
[Microcontroller]
Latest Microcontroller Articles
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号