在linux的用户空间中访问flash时要经过三层,
一是上层设备层,核心导出字符设备和块设备;字符设备定义在mtdchar.c(major为90),实现了file_operations,都是调用下层的函数。
块设备定义了mtdblk_dev,每个块设备对应一个分区,即mtd_part(major为31)。
二是中间层原始设备层,用mtd_part表示分区,对应上层的每个次设备号,mtd_info表示一个芯片设备描述,上层的读写函数会
通过核心调用这个结构的相关函数。可以用add_mtd_partitions向核心添加几个新的分区(用mtd_part表示),这需要描述分区
信息的结构mtd_partition(在板文件定义)和整个芯片属性和方法的结构mtd_info(这里面包含了读写和控制函数,会调用下层的函数)。
三是底层芯片级驱动,主要是nand_chip结构体,mtd_info的priv指针指向nand_chip,所以中间层的函数通过这个指针调用nand_chip中的
函数。这个结构体包含了关于nand flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。我们只需要定义nand_chip的
这些成员,因为内核的nand_base.c实现了很多通用代码。
我们要做的就是第三步,先实现nand_chip的有关成员,这与硬件有关,再用nand_scan(mtd_info *mtd ),这会探测芯片,
探测芯片即调用nand_chip中的函数并用nand_chip的成员初始化mtd_info,这是整个芯片的中间层描述,再调用add_mtd_partitions,
即用板文件的分区表mtd_partition数组和前面准备的mtd_info向核心添加整个芯片的所有分区,即是新建几个mtd_part,每个表示一个
分区,这里面包含mtd_info,其中大多成员直接指向整个芯片的mtd_info,因为分区的操作与芯片的操作是一样的。
2. 实现ok6410的nand flash驱动:
首先在板文件定义struct mtd_partition ok6410_nand_part[],即分区信息,struct s3c2410_nand_set ok6410_nand_sets[]
即所有芯片信息,这里就只有一个芯片;struct s3c2410_platform_nand ok6410_nand_info,包含前面信息和芯片属性的
platform_data;最后用s3c_nand_set_platdata(&ok6410_nand_info);形成platform_device,注册到platform总线。
总线会由name找到(match)驱动,即在s3c-nand.c,执行驱动的probe。下面是这个函数:
主要流程为:
a. 得到clk并打开时钟
b. 申请I/O内存并映射到内核虚拟地址
c. 申请nand_chip和mtd_info的内存,根据电路板情况初始化nand_chip的成员
d. 将mtd_info的priv指向nand_chip(下面调用的函数都是nand_chip中的)
e. nand_scan探测nand_flash,这时会读取芯片ID,并用nand_chip初始化mtd_info
f. 调用mtd_add_partitions,用板文件的分区表mtd_partition和上面芯片的mtd_info,新建分区,即几个mtd_part
g. 以后MTD上层访问某个分区,都可以由mtd_part的mtd_info找到nand_flash,并调用其中的函数
点击(此处)折叠或打开
-
/* 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 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);
-
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;
-
-
-
// K9GAG08U0E must add below codes
-
{
-
s3c_nand_hwcontrol(0, NAND_CMD_RESET, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-
s3c_nand_device_wait_ready(0);
-
-
}
-
-
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);
-
-
s3c_nand_device_wait_ready(0);
-
-
u32 manuf=tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
-
-
-
dev_id = tmp = readb(nand->IO_ADDR_R); /* Device ID */
-
-
printk("forlinx nandflash dev_id=%x\n",dev_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;
-
-
printk("forlinx********Nandflash:Type=SLC ChipName:samsung-K9F2G08U0B or hynix-HY27UF082G2B****** \n");
-
-
-
} else
-
{
-
nand->ecc.layout = &s3c_nand_oob_16;
-
}
-
-
} else
-
{
-
// int childType=tmp & 0x03; //Page size
-
int childType = nand->cellinfo;
-
-
if(dev_id == 0xd5)
-
{
-
-
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;
-
-
if(childType==0x94)
-
{
-
//K9GAG08U0D size=2GB type=MLC Page=4K
-
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
-
printk("forlinx****Nandflash:ChipType= MLC ChipName=samsung-K9GAG08U0D************ \n");
-
}
-
else if(childType==0x14)
-
{
-
//K9GAG08U0M size=2GB type=MLC Page=4K
-
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
-
printk("forlinx****Nandflash:ChipType= MLC ChipName=samsung-K9GAG08U0M************ \n");
-
}
-
else if(childType==0x84)
-
{
-
//K9GAG08U0E size=2GB type=MLC Page=8K
-
nand->ecc.layout = &s3c_nand_oob_mlc_232_8bit;
-
printk("forlinx****Nandflash:ChipType= MLC ChipName=samsung-K9GAG08U0E************ \n");
-
-
}
-
-
-
}else if(dev_id == 0xd7)
-
{
-
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;
-
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
-
printk("forlinx****Nandflash:ChipType= MLC ChipName=samsung-K9LBG08U0D************ \n");
-
-
-
}
-
else if(dev_id == 0xd3)
-
{
-
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;
-
-
printk("forlinx****Nandflash:ChipType=MLC ChipName=samsung-K9G8G08U0A************** \n");
-
-
}
-
-
}
-
-
} else if(dev_id == 0xd7)
-
{
-
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;
-
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
-
printk("forlinx****Nandflash:ChipType= MLC ChipName=samsung-K9LBG08U0D************ \n");
-
-
}
-
else if(manuf==0x2c && dev_id ==0x48)
-
{
-
//MT29F16G08ABACAWP size=2GB type=SLC Page=4K
-
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;
-
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
-
-
printk("forlinx******Nandflash:ChipType= SLC ChipName=MT29F16G08ABACAWP\n");
-
-
} else if(manuf==0x2c && dev_id ==0x38)
-
{
-
//MT29F16G08ABACAWP size=1GB type=SLC Page=4K
-
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;
-
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
-
-
printk("forlinx******Nandflash:ChipType= SLC ChipName=MT29F8G08ABABAWP\n");
-
-
}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("forlinx *****Nandflash:ChipType= Unknow\n");
-
-
}
-
-
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;
- }