主题:针对s3c2440平台实现的Linux-3.4.2内核中nand flash驱动

硬件平台:s3c2440

内核版本:Linux3.4.2

    最近在做s5pv210的nand flash移植时发现以前看过的关于在s3c2440平台下的nand flash驱动框架都忘了,这次准备再次看一遍。从mach-mini2440.c中文件看起。找到这些platform_device数组,实际就每一个外设接口。很容易看到 &s3c_device_nand。

static struct platform_device *mini2440_devices[] __initdata = {
    &s3c_device_ohci,
    &s3c_device_wdt,
    &s3c_device_i2c0,
    &s3c_device_rtc,
    &s3c_device_usbgadget,
    &mini2440_device_eth,
    &mini2440_led1,
    &mini2440_led2,
    &mini2440_led3,
    &mini2440_led4,
    &mini2440_button_device,
    &s3c_device_nand,
    &s3c_device_sdi,
    &s3c_device_iis,
    &uda1340_codec,
    &mini2440_audio,
    &samsung_asoc_dma,
};

然后会在devs.c文件中定位到s3c_device_nand结构体的定义和初始化。可以看到platform_device类型的结构体变量s3c_device_nand 定义如下, 同时在后面还可以看到如何设置nand的硬件参数:s3c_nand_set_platdata(&mini2440_nand_info);此函数是在mach-mini2440.c中mini2440_init()函数被调用。但是mini2440_nand_info是在mach-mini2440.c中定义的, mini2440_nand_info是关于硬件时序和nand flash分区等方面的参数。通过s3c_nand_set_platdata(&mini2440_nand_info)函数将mini2440_nand_info存储在s3c_device_nand.dev.platform_data,如下:

#ifdef CONFIG_S3C_DEV_NAND
static struct resource s3c_nand_resource[] = {
	[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
};

struct platform_device s3c_device_nand = {
	.name		= "s3c2410-nand",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(s3c_nand_resource),
	.resource	= s3c_nand_resource,
};

/*
 * s3c_nand_copy_set() - copy nand set data
 * @set: The new structure, directly copied from the old.
 *
 * Copy all the fields from the NAND set field from what is probably __initdata
 * to new kernel memory. The code returns 0 if the copy happened correctly or
 * an error code for the calling function to display.
 *
 * Note, we currently do not try and look to see if we've already copied the
 * data in a previous set.
 */
static int __init s3c_nand_copy_set(struct s3c2410_nand_set *set)
{
	void *ptr;
	int size;
	size = sizeof(struct mtd_partition) * set->nr_partitions;
	if (size) {
		ptr = kmemdup(set->partitions, size, GFP_KERNEL);
		set->partitions = ptr;

		if (!ptr)
			return -ENOMEM;
	}

	if (set->nr_map && set->nr_chips) {
		size = sizeof(int) * set->nr_chips;
		ptr = kmemdup(set->nr_map, size, GFP_KERNEL);
		set->nr_map = ptr;

		if (!ptr)
			return -ENOMEM;
	}

	if (set->ecc_layout) {
		ptr = kmemdup(set->ecc_layout,
			      sizeof(struct nand_ecclayout), GFP_KERNEL);
		set->ecc_layout = ptr;

		if (!ptr)
			return -ENOMEM;
	}

	return 0;
}

void __init s3c_nand_set_platdata(struct s3c2410_platform_nand *nand)
{
	struct s3c2410_platform_nand *npd;
	int size;
	int ret;

	/* note, if we get a failure in allocation, we simply drop out of the
	 * function. If there is so little memory available at initialisation
	 * time then there is little chance the system is going to run.
	 */

	npd = s3c_set_platdata(nand, sizeof(struct s3c2410_platform_nand),
				&s3c_device_nand);
/************************************************************************/
#if 0
    函数实现,方便看
    void __init *s3c_set_platdata(void *pd, size_t pdsize,
			      struct platform_device *pdev)
    {
	    void *npd;

	    npd = kmemdup(pd, pdsize, GFP_KERNEL);
	    if (!npd) {
	    	printk(KERN_ERR "%s: cannot clone platform data\n", pdev->name);
	    	return NULL;
	    }

    	pdev->dev.platform_data = npd;
	    return npd;
    }
#endif
/************************************************************************/
	if (!npd)
		return;

	/* now see if we need to copy any of the nand set data */

	size = sizeof(struct s3c2410_nand_set) * npd->nr_sets;
	if (size) 
	{
		struct s3c2410_nand_set *from = npd->sets;
		struct s3c2410_nand_set *to;
		int i;

		to = kmemdup(from, size, GFP_KERNEL);
		npd->sets = to;	/* set, even if we failed */

		if (!to) {
			printk(KERN_ERR "%s: no memory for sets\n", __func__);
			return;
		}

		for (i = 0; i < npd->nr_sets; i++) 
		{
			ret = s3c_nand_copy_set(to);
			if (ret) {
				printk(KERN_ERR "%s: failed to copy set %d\n",
				__func__, i);
				return;
			}
			to++;
		}
	}
}
#endif /* CONFIG_S3C_DEV_NAND */


/***********************************************************************************/
/* NAND Flash on MINI2440 board */

static struct mtd_partition mini2440_default_nand_part[] __initdata = {
	[0] = {
		.name	= "u-boot",
		.size	= SZ_256K,
		.offset	= 0,
	},
	[1] = {
		.name	= "u-boot-env",
		.size	= SZ_128K,
		.offset	= SZ_256K,
	},
	[2] = {
		.name	= "kernel",
		/* 5 megabytes, for a kernel with no modules
		 * or a uImage with a ramdisk attached */
		.size	= 0x00500000,
		.offset	= SZ_256K + SZ_128K,
	},
	[3] = {
		.name	= "root",
		.offset	= SZ_256K + SZ_128K + 0x00500000,
		.size	= MTDPART_SIZ_FULL,
	},
};

static struct s3c2410_nand_set mini2440_nand_sets[] __initdata = {
	[0] = {
		.name		= "nand",
		.nr_chips	= 1,
		.nr_partitions	= ARRAY_SIZE(mini2440_default_nand_part),
		.partitions	= mini2440_default_nand_part,
		.flash_bbt 	= 1, /* we use u-boot to create a BBT */
	},
};

static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
	.tacls		= 0,
	.twrph0		= 25,
	.twrph1		= 15,
	.nr_sets	= ARRAY_SIZE(mini2440_nand_sets),
	.sets		= mini2440_nand_sets,
	.ignore_unset_ecc = 1,
};

根据platform_bus可知,这里注册完platform_device之后,还需要有platform_driver的注册,根据platform_device s3c_device_nand中.name  = "s3c2410-nand",可匹配到driver/mtd/nand/s3c2410.c

/* driver device registration */

static struct platform_device_id s3c24xx_driver_ids[] = {
	{
		.name		= "s3c2410-nand",
		.driver_data	= TYPE_S3C2410,
	}, {
		.name		= "s3c2440-nand",
		.driver_data	= TYPE_S3C2440,
	}, {
		.name		= "s3c2412-nand",
		.driver_data	= TYPE_S3C2412,
	}, {
		.name		= "s3c6400-nand",
		.driver_data	= TYPE_S3C2412, /* compatible with 2412 */
	},
};
static struct platform_driver s3c24xx_nand_driver = {
	.probe		= s3c24xx_nand_probe,
	.remove		= s3c24xx_nand_remove,
	.suspend	= s3c24xx_nand_suspend,
	.resume		= s3c24xx_nand_resume,
	.id_table	= s3c24xx_driver_ids,
	.driver		= {
		.name	= "s3c24xx-nand",
		.owner	= THIS_MODULE,
	},
};

static int __init s3c2410_nand_init(void)
{
	printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

	return platform_driver_register(&s3c24xx_nand_driver);
}

当platform_driver变量中的name 和platform_device变量中的name相同时,就会调用paltform_driver中probe函数。即s3c24xx_nand_probe函数,其中完成的内容大致为:

1、nand flash控制器寄存器地址映射。

2、nand flash控制器硬件初始化,主要是在s3c2410_nand_inithw中调用s3c2410_nand_setrate函数来设置硬件时序。

3、获取Soc中nand模块的时钟频率。

4、根据设定的参数来算出有几块nand flash(一般是一块),然后初始化操作nand flash方法。初始化内容为,nand flash的读写方法,片选方法,发送地址,命令方法和硬件ECC校验等。

5、开始识别nand flash型号,在nand_ids.c中有一个全局数组,内部存储了各厂家各种版本的nand flash型号,识别过程是与该全局数组进行一一匹配。成功返回该类型,失败错误码。

6、解析为nand flash设置的分区信息(记录在mach-mini2440.c -> mini2440_default_nand_part数组中)。

 


/* s3c24xx_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 s3c24xx_nand_probe(struct platform_device *pdev)   // pdev = s3c_device_nand
{
/*
	static struct resource s3c_nand_resource[] = {
		[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
	};

	struct platform_device s3c_device_nand = {
		.name		= "s3c2410-nand",
		.id		= -1,
		.num_resources	= ARRAY_SIZE(s3c_nand_resource),
		.resource	= s3c_nand_resource,
	};
*/

/*
static struct s3c2410_nand_set  mini2440_nand_sets[] __initdata = {
	[0] = {
		.name		= "nand",
		.nr_chips	= 1,
		.nr_partitions	= ARRAY_SIZE(mini2440_default_nand_part),
		.partitions	= mini2440_default_nand_part,
		.flash_bbt 	= 1, // we use u-boot to create a BBT 
	},
};

static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
	.tacls		= 0,
	.twrph0		= 25,
	.twrph1		= 15,
	.nr_sets	= ARRAY_SIZE(mini2440_nand_sets),
	.sets		= mini2440_nand_sets,
	.ignore_unset_ecc = 1,
};
		plat = mini2440_nand_info
*/
	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);// pdev = s3c_device_nand ; 
                                                            //plat = mini2440_nand_info
	enum s3c_cpu_type cpu_type; 
	struct s3c2410_nand_info *info;
	struct s3c2410_nand_mtd *nmtd;
	struct s3c2410_nand_set *sets;
	struct resource *res;
	int err = 0;
	int size;
	int nr_sets;
	int setno;

	cpu_type = platform_get_device_id(pdev)->driver_data;//pdev = s3c_device_nand

	info = kzalloc(sizeof(*info), GFP_KERNEL);

	platform_set_drvdata(pdev, info); //pdev = s3c_device_nand
	                                  //pdev->dev->p->driver_data = info

	/* get the clock source and enable it */
	info->clk = clk_get(&pdev->dev, "nand");

	s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);

	/* allocate and map the resource */
	/* currently we assume we have the one resource */
 /*
    static struct resource s3c_nand_resource[] = {
    	[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
    };

    struct platform_device s3c_device_nand = {
	    .name		= "s3c2410-nand",
	    .id		= -1,
	    .num_resources	= ARRAY_SIZE(s3c_nand_resource),
    	.resource	= s3c_nand_resource,
    };
 */
	res  = pdev->resource;
	size = resource_size(res);

	info->area = request_mem_region(res->start, size, pdev->name);

	info->device     = &pdev->dev;
	info->platform   = plat;      // info->platform = plat,plat = mini2440_nand_info
	info->regs       = ioremap(res->start, size);
	info->cpu_type   = cpu_type;

	/* initialise the hardware */
	err = s3c2410_nand_inithw(info);
	if (err != 0)
		goto exit_error;

/*
	static struct s3c2410_nand_set	mini2440_nand_sets[] __initdata = {
		[0] = {
			.name		= "nand",
			.nr_chips	= 1,
			.nr_partitions	= ARRAY_SIZE(mini2440_default_nand_part),
			.partitions = mini2440_default_nand_part,
			.flash_bbt	= 1, // we use u-boot to create a BBT 
		},
	};
	
	static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
		.tacls		= 0,
		.twrph0 	= 25,
		.twrph1 	= 15,
		.nr_sets	= ARRAY_SIZE(mini2440_nand_sets),
		.sets		= mini2440_nand_sets,
		.ignore_unset_ecc = 1,
	};
		下面的plat = mini2440_nand_info
*/

	sets = (plat != NULL) ? plat->sets : NULL;//plat->sets =mini2440_nand_info->sets = mini2440_nand_sets
	nr_sets = (plat != NULL) ? plat->nr_sets : 1;

	info->mtd_count = nr_sets;

	/* allocate our information */
	size = nr_sets * sizeof(*info->mtds);
	info->mtds = kzalloc(size, GFP_KERNEL);

	/* initialise all possible chips */
	nmtd = info->mtds;

	for (setno = 0; setno < nr_sets; setno++, nmtd++) {
		pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);

		s3c2410_nand_init_chip(info, nmtd, sets);//nmtd = info->mtds;而info->mtds被刚刚动态分配,没有实质性内容

		nmtd->scan_res = nand_scan_ident(&nmtd->mtd,(sets) ? sets->nr_chips : 1, NULL);//成功返回0

		if (nmtd->scan_res == 0) {//上一个函数执行成功,会执行下面的函数
			s3c2410_nand_update_chip(info, nmtd);
			nand_scan_tail(&nmtd->mtd);
			s3c2410_nand_add_partition(info, nmtd, sets);//添加分区信息
		}

		if (sets != NULL)
			sets++;
	}

	return err;
}

根据probe函数中添加的nand flash操作方法,当需要进行读写数据时,可先发地址、命令,然后根据相应的读写函数即可以正常读写nand flash。

顺便把自己看代码时整理的粗糙的流程图贴出来,这样可能看起来会整体框架把握较为清晰。

标题

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值