//////////////////////drivers/mtd/nand/s3c_nand.c
module_init(s3c_nand_init);//模块初始化
static int __init s3c_nand_init(void)
{
printk("S3C NAND Driver, (c) 2008 Samsung Electronics\n");
platform_driver_register(&s3c6400_nand_driver);
platform_driver_register(&s3c6410_nand_driver);
}
////////////drivers/mtd/nand/s3c_nand.c
platform平台总线驱动结构体
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,
},
};
////////////drivers/mtd/nand/s3c_nand.c
static int s3c6410_nand_probe(struct platform_device *dev)
{
return s3c_nand_probe(dev, TYPE_S3C6410);
}
///////////////////////////////////////////////////drivers/mtd/nand/s3c_nand.c
/* 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)
{
//在Mach-smdk6410.c (linux2.6.28\arch\arm\mach-s3c6410)文件中,有个函数:smdk6410_machine_init
//s3c_device_nand.dev.platform_data = &s3c_nand_mtd_part_info; nand有关
struct s3c_nand_mtd_info *plat_info = pdev->dev.platform_data;
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)
struct nand_flash_dev *type = NULL;
u_char tmp;
#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 */
//得到I/O内存资源
/* currently we assume we have the one resource */
res = pdev->resource;
size = res->end - res->start + 1;
//
I/O内存资源申请
s3c_nand.area = request_mem_region(res->start, size, pdev->name);
if (s3c_nand.area == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -ENOENT;
goto exit_error;
}
s3c_nand.cpu_type = cpu_type;
s3c_nand.device = &pdev->dev;
//将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问。
s3c_nand.regs = ioremap(res->start, size);
if (s3c_nand.regs == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -EIO;
goto exit_error;
}
/* allocate memory for MTD device structure and private data */
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 */
//kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); 跳过sizeof(struct mtd_info)个字节
nand = (struct nand_chip *) (&s3c_mtd[1]);
/* Initialize structures */
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 */
s3c_mtd->priv = nand;
for (i = 0; i < plat_info->chip_nr; i++) {
//
上面s3c_nand.regs = ioremap(res->start, size); 所以是nand的基地址
////#define S3C_NFDATA S3C2410_NFREG(0x10)
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)
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;
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 */
制造商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;
}
}
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) {
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;
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;
nand->ecc.bytes = 8; /* really 7 bytes */
nand->ecc.layout = &s3c_nand_oob_mlc_64;
}
} else {
nand_type = S3C_NAND_TYPE_SLC;
nand->ecc.size = 512;
nand->cellinfo = 0;
nand->ecc.bytes = 4;
nand->ecc.layout = &s3c_nand_oob_16;
}
printk("S3C NAND Driver is using hardware ECC.\n");
#else
nand->ecc.mode = NAND_ECC_SOFT;
//软件ECC
printk("S3C NAND Driver is using software ECC.\n");
#endif
if (nand_scan(s3c_mtd, 1))
{//以mtd_info为参数调用nand_scan()函数探测NAND Flash的存在
ret = -ENXIO;
goto exit_error;
}
/* Register the partitions */
//如果要分区,则以mtd_info和mtd_partition为参 数调用add_mtd_partitions(),添加分区信息
add_mtd_partitions(s3c_mtd, partition_info, plat_info->mtd_part_nr);
}
pr_debug("initialized ok\n");
return 0;
exit_error:
kfree(s3c_mtd);
return ret;
}
/////////////////////////////drivers/mtd/nand/s3c_nand.c
struct s3c_nand_info {
/* mtd info */
struct nand_hw_control controller;
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_type cpu_type;
};
static struct s3c_nand_info s3c_nand;