全部博文(108)
分类: 嵌入式
2014-10-29 17:17:54
2、S3C6410 nand_chip 初始化 与 NAND 探测
S3C6410 的 NAND 驱动以 platform 驱动的形式存在,在执行probe() 时,初始化nand_chip 实例并运行 nand_scan 扫描 NAND 设备, 最后调用 add_mtd_partitions() 添加主板中定义的分区表,nand_chip 是nanf flash 驱动的核心数据结构,这个结构体重的成员直接对应这 NAND Flash 的底层操作,针对具体情况的NAND 控制器情况
分析 s3c_nand_probe 函数体对 S3C6410 nand_chip 的初始化和注册: 该文件定义在 drivers/mtd/nand/s3c_nand.c 中,而在基础内核2.6.36.2 中的这个文件为s3c2410.c
所以如果你想在 Linux 2.6.36.2 的内核对 S3C6410 nand flash 控制器进行支持的话,s3c_nand.c 这个文件需要自己添加。
static int s3c_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = pdev->dev.platform_data;
struct s3c2410_nand_set *sets;
struct nand_chip *nand;
struct resource *res;
int err = 0;
int ret = 0;
int nr_sets;
int i, j, size;
#if defined(CONFIG_MTD_NAND_S3C_HWECC)
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;
size = res->end - res->start + 1;
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;
s3c_nand.regs = ioremap(res->start, size);
s3c_nand.platform = plat;
if (s3c_nand.regs == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -EIO;
goto exit_error;
}
sets = (plat != NULL) ? plat->sets : NULL;
nr_sets = (plat != NULL) ? plat->nr_sets : 1;
s3c_nand.mtd_count = nr_sets;
/* 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 */
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 < sets->nr_chips; i++) {
nand->IO_ADDR_R = (char *)(s3c_nand.regs + S3C_NFDATA); // 初始化 IO_ADDR_R
nand->IO_ADDR_W = (char *)(s3c_nand.regs + S3C_NFDATA); // 初始化 IO_ADDR_W
nand->cmd_ctrl = s3c_nand_hwcontrol; // 初始化 cmd_crtl()
nand->dev_ready = s3c_nand_device_ready; // 初始化 dev_read()
nand->scan_bbt = s3c_nand_scan_bbt; // 初始化 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
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;
}
}
if (!type) {
printk("Unknown NAND Device.\n");
goto exit_error;
}
nand->cellinfo = readb(nand->IO_ADDR_R); /* the 3rd byte */ // 第3个字节
tmp = readb(nand->IO_ADDR_R); /* the 4th byte */ // 第4个字节
if (!type->pagesize) {
if (((nand->cellinfo >> 2) & 0x3) == 0) {
nand_type = S3C_NAND_TYPE_MLC_4BIT;
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 */
if ((1024 << (tmp & 0x3)) > 512) {
nand->ecc.layout = &s3c_nand_oob_mlc_64;
} else {
nand->ecc.layout = &s3c_nand_oob_16;
}
/*
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_4BIT;
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;
if(dev_id == 0xd5)
{
printk("dev_id == 0xd5 select s3c_nand_oob_mlc_128\n");
nand_type = S3C_NAND_TYPE_MLC_8BIT;
nand->ecc.read_page = s3c_nand_read_page_8bit;
nand->ecc.write_page = s3c_nand_write_page_8bit;
nand->ecc.size = 512;
nand->ecc.bytes = 13; /* really 7 bytes */
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
}
}
} 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;
printk("S3C NAND Driver is using software ECC.\n");
#endif
if (nand_scan(s3c_mtd, 1)) {
ret = -ENXIO;
goto exit_error;
}
/* Register the partitions */
add_mtd_partitions(s3c_mtd, sets->partitions, sets->nr_partitions); // 注册分区信息
}
pr_debug("initialized ok\n");
return 0;
exit_error:
kfree(s3c_mtd);
return ret;
}
drivers/mtd/nand/s3c_nand.c 是一个platform 驱动, 加入我们需要移植 OK6410 的 NAND Flash 驱动这要在 OK6410 的BSP(板级支持包)中田间针对 NAND 的 platform 设备和 分区信息即可,也就是在 arch/arm/mach-s3c64xx/mach-smdk6410.c 中添加这些信息,就可以将上面的NAND Flash 驱动移植成功。
参考文献:设备驱动开发详解(第2版) 华清远见 宋宝华著
NAND Flash驱动移植方法、可以参考我的上篇文章:
将这篇文章和上一篇文章()一起来阅读,理解 NAND Flash驱动就能容易些了。
本篇文章来源于 Linux公社网站() 原文链接: