您的位置:首页 > 运维架构 > Linux

linux下的nandflash驱动分析(1)——基于s3c6410平台

2014-05-15 21:02 387 查看
源码主要在S3c_nand.c (linux2.6.28\drivers\mtd\nand)文件中。

1、模块加载和卸载

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 Electronics\n");

platform_driver_register(&s3c2450_nand_driver);

platform_driver_register(&s3c6400_nand_driver);

platform_driver_register(&s3c6410_nand_driver);我们关系的部分

return platform_driver_register(&s5pc100_nand_driver);

}

对应的platform_device如下所示:

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,

};

对应的platform_driver如下所示:

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,

},

};

有没有感觉到上面有什么不对?平台设备的驱动名和设备名应该是一样的,可现在不一样了。不过在下面这个函数中:进行了修正。

static void __init smdk6410_map_io(void)

{

s3c_device_nand.name = "s3c6410-nand";

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

}

2、现在来看probe函数:s3c6410_nand_probe函数源码如下:

static int s3c6410_nand_probe(struct platform_device *dev)

{

return s3c_nand_probe(dev, TYPE_S3C6410);

}

有定义:

enum s3c_cpu_type {

TYPE_S3C2450,
/* including s3c2416 */

TYPE_S3C6400,

TYPE_S3C6410,
/* including s3c6430/31 */

TYPE_S5PC100,

};

2.1、接着看s3c_nand_probe函数,源码如下:

/* 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; 这个在那里赋值的呢?

在Mach-smdk6410.c (linux2.6.28\arch\arm\mach-s3c6410)文件中,有个函数:

static void __init smdk6410_machine_init(void)就是在这个函数的头两行:

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

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

其中s3c_nand_mtd_part_info结构体是个全局量:在下面还要说到

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) 与产生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; 得到I/O内存资源

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

s3c_nand.area = request_mem_region(res->start, size, pdev->name); I/O内存资源申请

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);

if (s3c_nand.regs == NULL) {

dev_err(&pdev->dev, "cannot reserve register region\n");

err = -EIO;

goto exit_error;

}

上面这几行代码都是关于s3c_nand初始化的,s3c_nand是个s3c_nand_info的结构体:与如下定义:

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;

};

刚才就是进行这几项的初始化

/* allocate memory for MTD device structure and private data */申请 struct mtd_info结构体和struct nand_chip结构体大小的内存,这里为什么要这样申请呢?

有定义: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 */还有这是怎么得到的呢?看清下面是s3c_mtd[1],想到了吧?

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

/* Initialize structures */ 初始化为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 */ mtd_info中的指针指向struct nand_chip结构体

s3c_mtd->priv = nand;

for (i = 0; i < plat_info->chip_nr; i++){ 这个循环一直到这个函数的结束,可以说是probe函数的主体。

它说做的主要工作是分配和,根据目标板NAND 控制器的特殊情况初始化nand_chip 中的I/O内存,ECC校验、hwcontrol()、dev_ready()correct_data()、read_byte()、write_byte()等成员函数(如果不赋值会使用nand_base.c中的默认函数)。

然后,以mtd_info为参数调用nand_scan()函数探测NAND Flash的存在。

最后,如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partitions(),添加分区信息。

这里涉及到一个结构体plat_info是struct s3c_nand_mtd_info结构体的实例:

struct s3c_nand_mtd_info {

uint chip_nr;

uint mtd_part_nr;

struct mtd_partition *partition;

};

在Partition.h (linux2.6.28\arch\arm\plat-s3c\include\plat)文件中有定义:

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,

};

因为s3c6410支持双芯片NAND,看下图(摘于用户手册)



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;

这个也是个结构体struct nand_ecc_ctrl ecc; 看就知道和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 */ 制造商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;

}

}

这里涉及到两个结构体,是关于设备和厂商信息的:如下所示:

/**

* 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;

};

他们的实例都定义在Nand_ids.c (linux2.6.28\drivers\mtd\nand)文件的

struct nand_flash_dev nand_flash_ids[]和struct nand_manufacturers nand_manuf_ids[]数组中,可以自己去查看。

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;

nand->ecc.bytes = 8;/* really 7 bytes */

nand->ecc.layout = &s3c_nand_oob_mlc_64;

if((1024 << (tmp & 0x3)) > 2048)

nand->ecc.layout = &s3c_nand_oob_mlc_128;

}

} 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;

nand->ecc.bytes = 8;/* really 7 bytes */

nand->ecc.layout = &s3c_nand_oob_mlc_64;

//jkeqiang

//if((1024 << (tmp & 0x3)) > 2048)

if(dev_id == 0xd5)

{

printk("select s3c_nand_oob_mlc_128\n");

nand->ecc.layout = &s3c_nand_oob_mlc_128;

}

else

{

printk("select s3c_nand_oob_mlc_64\n");

}

}

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 */

add_mtd_partitions(s3c_mtd, partition_info, plat_info->mtd_part_nr); 如果要分区,则以mtd_info和mtd_partition为参 数调用add_mtd_partitions(),添加分区信息

}

pr_debug("initialized ok\n");

return 0;

exit_error:

kfree(s3c_mtd);

return ret;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: