mini2440之nand flash 分析(1)

来源:互联网 发布:淘宝全屏详情页 编辑:程序博客网 时间:2024/05/16 01:51

1:nand falsh 分区信息

可以查看mini2440上面的文件/proc/mtd,/proc/partitions ,/dev

[@mini2440 /proc]#cat mtd
dev:    size   erasesize  name
mtd0: 00040000 00020000 "supervivi"
mtd1: 00020000 00020000 "param"
mtd2: 00500000 00020000 "Kernel"
mtd3: 07aa0000 00020000 "root"
mtd4: 08000000 00020000 "nand"

[@mini2440 /proc]#cat partitions
major minor  #blocks  name

  31        0        256 mtdblock0
  31        1        128 mtdblock1
  31        2       5120 mtdblock2
  31        3     125568 mtdblock3
  31        4     131072 mtdblock4

[@mini2440 /dev]#ls -al

brw-rw----    1 0        0          31,   0 Feb 29 18:24 mtdblock0
brw-rw----    1 0        0          31,   1 Feb 29 18:24 mtdblock1
brw-rw----    1 0        0          31,   2 Feb 29 18:24 mtdblock2
brw-rw----    1 0        0          31,   3 Feb 29 18:24 mtdblock3
brw-rw----    1 0        0          31,   4 Feb 29 18:24 mtdblock4

上面的分区信息有下面的代码所决定:

static struct mtd_partition friendly_arm_default_nand_part[] = { [0] = {  .name = "supervivi",  .size = 0x00040000,  .offset = 0, }, [1] = {  .name = "param",  .offset = 0x00040000,  .size = 0x00020000, }, [2] = {  .name = "Kernel",  .offset = 0x00060000,  .size = 0x00500000, }, [3] = {  .name = "root",  .offset = 0x00560000,  .size = 1024 * 1024 * 1024, // }, [4] = {  .name = "nand",  .offset = 0x00000000,  .size = 1024 * 1024 * 1024, // }};

在linux内核中nand flash 是以平台设备的方式注册到内核中去的。平台设备的定义如下:

struct platform_device s3c_device_nand = { .name    = "s3c2410-nand", .id    = -1, .num_resources   = ARRAY_SIZE(s3c_nand_resource), .resource   = s3c_nand_resource,};代码中resource 定义如下:static struct resource s3c_nand_resource[] = { [0] = {  .start = S3C_PA_NAND, //#define S3C2410_PA_NAND    (0x4E000000)  .end   = S3C_PA_NAND + SZ_1M,  .flags = IORESOURCE_MEM, //定义为内存资源,主要用来映射到nand flash 对应的寄存器地址。 }};

 

其系统中定义的平台设备的信息,可以在/sys目录中找到。

[@mini2440 /sys/devices/platform]#ll
total 0
drwxr-xr-x   18 0        0                0 Feb 29 20:20 .
drwxr-xr-x    5 0        0                0 Feb 29 20:20 ..
drwxr-xr-x    3 0        0                0 Feb 29 20:20 dm9000
drwxr-xr-x    2 0        0                0 Feb 29 20:22 s3c2410-iis
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2410-lcd
drwxr-xr-x    3 0        0                0 Feb 29 20:22 s3c2410-ohci
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2410-rtc
drwxr-xr-x    2 0        0                0 Feb 29 20:22 s3c2410-wdt
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2440-i2c
drwxr-xr-x    2 0        0                0 Feb 29 20:22 s3c2440-nand
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2440-sdi
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2440-uart.0
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2440-uart.1
drwxr-xr-x    3 0        0                0 Feb 29 20:20 s3c2440-uart.2
drwxr-xr-x    2 0        0                0 Feb 29 20:22 s3c2440-usbgadget
drwxr-xr-x    2 0        0                0 Feb 29 20:22 s3c24xx_uda134x.0
drwxr-xr-x    3 0        0                0 Feb 29 20:20 soc-audio
drwxr-xr-x    2 0        0                0 Feb 29 20:22 test_ts
-rw-r--r--    1 0        0             4096 Feb 29 20:22 uevent

上面目中显示的为注册到内核中的平台设备的信息。

2:重要的数据结构

struct s3c2410_platform_nand {/* timing information for controller, all times in nanoseconds */inttacls;/* time for active CLE/ALE to nWE/nOE */inttwrph0;/* active time for nWE/nOE */inttwrph1;/* time for release CLE/ALE from nWE/nOE inactive */unsigned intignore_unset_ecc:1;intnr_sets;struct s3c2410_nand_set *sets; //定义系统中有几个flash,在mini2440中我们知道,只有一个128M的Nand flashvoid (*select_chip)(struct s3c2410_nand_set *,int chip);};
struct s3c2410_nand_set {unsigned intdisable_ecc:1;unsigned intflash_bbt:1;intnr_chips;intnr_partitions;char*name;int*nr_map;struct mtd_partition*partitions; //分区信息,struct nand_ecclayout*ecc_layout;};
对每一个分区数据结构的定义。
struct mtd_partition {char *name;/* identifier string */uint64_t size;/* partition size */uint64_t offset;/* offset within the master MTD space */uint32_t mask_flags;/* master MTD flags to mask out for this partition */struct nand_ecclayout *ecclayout;/* out of band layout for this partition (NAND only)*/};
flash 中的ECC和OBB相关的数据结构。
struct nand_oobfree {__u32 offset;__u32 length;};#define MTD_MAX_OOBFREE_ENTRIES8/* * ECC layout control structure. Exported to userspace for * diagnosis and to allow creation of raw images */struct nand_ecclayout {__u32 eccbytes;__u32 eccpos[64];__u32 oobavail;struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];};

内核中对partition的定义如下:

static struct s3c2410_nand_set friendly_arm_nand_sets[] = {[0] = {.name= "NAND",.nr_chips= 1,.nr_partitions= ARRAY_SIZE(friendly_arm_default_nand_part),.partitions= friendly_arm_default_nand_part,},};

其中partitions 就是上面提到的flash的分区信息。

3:mini2440 flash 分析

2440上面的flash 驱动使用的是2410上面的代码,故研究的文件为: linux/drivers/mtd/nand/s3c2410.c

上面提到过nand flash 是以平台设备的方式注册到linux内核中的,故可以找到平台设备和平台驱动。定义及初始化如下:

struct platform_device s3c_device_nand = {.name  = "s3c2410-nand", //名字与platform_driver 中的相同.id  = -1,.num_resources  = ARRAY_SIZE(s3c_nand_resource),.resource  = s3c_nand_resource,};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);}static void __exit s3c2410_nand_exit(void){platform_driver_unregister(&s3c24xx_nand_driver);}

参考博客

在static struct platform_driver平台驱动中可以看到,常见的函数 probe,remove,resume,suspend。

上面提到过nand flash 是以平台设备的方式注册到内核中的,故在系统初始化的时候会在平台设备和平台驱动之间建立起联系,这种联系的建立就是在probe函数中实现。

现在看看函数s3c24xx_nand_probe的定义:

在此之前先看看s3c24xx_nand_probe中涉及到的数据结构: enum s3c_cpu_type cpu_type; struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd;

enum s3c_cpu_type {
 TYPE_S3C2410,
 TYPE_S3C2412,
 TYPE_S3C2440,
};定义可3sc系列cpu的类型。

struct s3c2410_nand_info {
 /* mtd info */
 struct nand_hw_control  controller;
 struct s3c2410_nand_mtd  *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;
 unsigned long   save_sel;
 unsigned long   clk_rate;

 enum s3c_cpu_type  cpu_type;

#ifdef CONFIG_CPU_FREQ
 struct notifier_block freq_transition;
#endif
}
上面中的黄色的结构体定义如下:struct s3c2410_nand_mtd

struct s3c2410_nand_mtd {
struct mtd_info mtd;
struct nand_chip chip;
struct s3c2410_nand_set *set;
struct s3c2410_nand_info *info;
int scan_res;
};
其中 struct mtd_info主要定义了对flash 进行操作的各种函数,类似于file_operation 中的各种操作函数。通知还包括与bad block相关的问题。

struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;

uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);

int chip_delay;
unsigned int options;

int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
uint64_t chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;

nand_state_t state;

uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;

struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;

struct mtd_oob_ops ops;

uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;

struct nand_bbt_descr *badblock_pattern;

void *priv;
};这个结构中包括的信息比较重要,要慢慢的研究。

现在就看看函数static int s3c24xx_nand_probe(struct platform_device *pdev)的定义:

static int s3c24xx_nand_probe(struct platform_device *pdev)
{
 struct s3c2410_platform_nand *plat = to_nand_plat(pdev); //这个数据结构的获取是由: s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;的定义而来的

而其定义如下:friendly_arm_nand_info;

static struct s3c2410_platform_nand friendly_arm_nand_info = {
 .tacls  = 20,
 .twrph0  = 60,
 .twrph1  = 20,
 .nr_sets = ARRAY_SIZE(friendly_arm_nand_sets),
 .sets  = friendly_arm_nand_sets,
 .ignore_unset_ecc = 1,
};


 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;

 pr_debug("s3c2410_nand_probe(%p)\n", pdev); //打印信息,可以将打印条件打开,但是打印的信息的将很多。主要在kernel.h中定义来个宏来实现

/*#define DEBUG 1
#define     KERN_DEBUG KERN_INFO

*/

 info = kmalloc(sizeof(*info), GFP_KERNEL);
 if (info == NULL) {
  dev_err(&pdev->dev, "no memory for flash info\n");
  err = -ENOMEM;
  goto exit_error;
 }

 memset(info, 0, sizeof(*info));
 platform_set_drvdata(pdev, info); //主要所用的事情就是初始化并赋值操作,将info赋值给 dev->p->driver_data指针

 spin_lock_init(&info->controller.lock); //初始化自旋锁
 init_waitqueue_head(&info->controller.wq); //初始化等待队列头

 /* get the clock source and enable it */

 info->clk = clk_get(&pdev->dev, "nand");
 if (IS_ERR(info->clk)) {
  dev_err(&pdev->dev, "failed to get clock\n");
  err = -ENOENT;
  goto exit_error;
 }

 clk_enable(info->clk);

 /* allocate and map the resource */

 /* currently we assume we have the one resource */

//其中此函数中参数的传入是在注册平台设备的时候传入的即:platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));函数将参数出入此函数的。

/*

static struct resource s3c_nand_resource[] = {
 [0] = {
  .start = S3C_PA_NAND,
  .end   = S3C_PA_NAND + SZ_1M,
  .flags = IORESOURCE_MEM,
 }
};

*/
 res  = pdev->resource;
 size = res->end - res->start + 1; //resource的大小,上面中的宏主要定义的是2440的nand flash的物理地址。

 info->area = request_mem_region(res->start, size, pdev->name); //申请内存区域,为后面的ioremap()的使用申请空间。

 if (info->area == NULL) {
  dev_err(&pdev->dev, "cannot reserve register region\n");
  err = -ENOENT;
  goto exit_error;
 }

 info->device     = &pdev->dev;
 info->platform   = plat;
 info->regs       = ioremap(res->start, size); //函数的作用就是:将物理地址转化为虚拟地址,在以后对物理寄存器的访问转化成了对虚拟地址加上一个偏移进行的。
 info->cpu_type   = cpu_type;

 if (info->regs == NULL) {
  dev_err(&pdev->dev, "cannot reserve register region\n");
  err = -EIO;
  goto exit_error;
 }

 dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);

 /* initialise the hardware */

 err = s3c2410_nand_inithw(info); //函数中就有利用虚拟地址对寄存器进行了访问:writel(S3C2440_NFCONT_ENABLE,info->regs + S3C2440_NFCONT);
 if (err != 0)
  goto exit_error;

 sets = (plat != NULL) ? plat->sets : NULL; //判断结构体是否被赋值

 nr_sets = (plat != NULL) ? plat->nr_sets : 1; //确定nand flash 的个数,在mini2440 中只有一个nand flash 故此值为:1,

信息的确定有下面的定义而得:

/*

static struct s3c2410_nand_set friendly_arm_nand_sets[] = {
 [0] = {
  .name  = "NAND",
  .nr_chips = 1,
  .nr_partitions = ARRAY_SIZE(friendly_arm_default_nand_part),
  .partitions = friendly_arm_default_nand_part,
 },
};

/* choose a set of timings which should suit most 512Mbit
 * chips and beyond.
*/

static struct s3c2410_platform_nand friendly_arm_nand_info = {
 .tacls  = 20,
 .twrph0  = 60,
 .twrph1  = 20,
 .nr_sets = ARRAY_SIZE(friendly_arm_nand_sets),
 .sets  = friendly_arm_nand_sets,
 .ignore_unset_ecc = 1,
};

*/

info->mtd_count = nr_sets;

 /* allocate our information */

 size = nr_sets * sizeof(*info->mtds);
 info->mtds = kmalloc(size, GFP_KERNEL);
 if (info->mtds == NULL) {
  dev_err(&pdev->dev, "failed to allocate mtd storage\n");
  err = -ENOMEM;
  goto exit_error;
 }

 memset(info->mtds, 0, size);

 /* 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->scan_res = nand_scan_ident(&nmtd->mtd,
       (sets) ? sets->nr_chips : 1);

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

 err = s3c2410_nand_cpufreq_register(info);
 if (err < 0) {
  dev_err(&pdev->dev, "failed to init cpufreq support\n");
  goto exit_error;
 }

 if (allow_clk_stop(info)) {
  dev_info(&pdev->dev, "clock idle support enabled\n");
  clk_disable(info->clk);
 }

 pr_debug("initialised ok\n");
 return 0;

 exit_error:
 s3c24xx_nand_remove(pdev);

 if (err == 0)
  err = -EINVAL;
 return err;
}