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

时间:2022-02-20 02:18:55

源码主要在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,看下图(摘于用户手册)

linux下的nandflash驱动分析(1)——基于s3c6410平台
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;
}