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;
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
- Popular Resources
- Popular amplifiers
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- Is there any software that can edit 16*16 dot matrix modeling?
- What does it mean to use series resistors for isolation? Can series resistors also play an isolating role?
- Introduction to RF EDA simulation software (including algorithms and principles)
- How to convert OUT file to hexadecimal format?
- [National Technology N32WB452 Review] III. Hardware touch debugging and DVP image acquisition
- Common mode inductor location
- Sub-library: SensorTile.Box development platform
- 51 single chip microcomputer simulates the phenomenon of water droplets growing from small to large and then dripping (with C language source code)
- The 8th China International Smart Card Expo was grandly held
- Share the solution to listen to music while charging an Android phone (listen and play while charging)