Chinaunix首页 | 论坛 | 博客
  • 博客访问: 381208
  • 博文数量: 87
  • 博客积分: 983
  • 博客等级: 准尉
  • 技术积分: 685
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-25 07:20
文章分类

全部博文(87)

文章存档

2016年(1)

2015年(3)

2014年(55)

2013年(13)

2012年(15)

分类: 嵌入式

2014-03-20 23:46:03

nandflash应该算是研究了很长时间了,但是还是有的地方没搞懂,有知道的大侠还望不吝键盘。
我用的是K9F2G08U0A,貌似第三节已经介绍过了。在此不费口舌。直接切入重点难点。
在此移植过程中,犯了一个低级的错误,导致我花了半年的时间捣鼓nand(有点夸张哈),马虎害死人啊。
s3c2440和s3c2410的nand寄存器不一样,需要拿两者的芯片对照修改。寄存器不一样导致了底层寄存器操作不一样。需要修改其底层函数。主要修改的初始化函数和读写 控制 函数。
初始化函数:对于下面的代码,需要注意的是局部变量cfg的初始化,因为cfg要被写入NFCONF寄存器中,若不初始化,根据c语言知识,他会是一个不确定的数,写入寄存器的话会干扰寄存器其他位。

点击(此处)折叠或打开

  1. int board_nand_init(struct nand_chip *nand)
  2. {
  3.     u_int32_t cfg = 0;
  4.     u_int8_t tacls, twrph0, twrph1;
  5.     struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
  6.     struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();

  7.     debugX(1, "board_nand_init()\n");

  8.     writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);

  9.     /* initialize hardware */
  10.     twrph0 = 4;
  11.     twrph1 = 2;
  12.     tacls = 2;

  13.     writel((1<<4)|(0<<1)|(1<<0), &nand_reg->NFCONT);
  14.     
  15.     cfg |= S3C2440_NFCONF_TACLS(tacls - 1);
  16.     cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
  17.     cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
  18.     writel(cfg, &nand_reg->NFCONF);

  19.     /* initialize nand_chip data structure */
  20.     nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;

  21.     nand->select_chip = nand_select;
  22.     ... ...
  23. }

读写控制函数:其核心代码如下(修改后):

点击(此处)折叠或打开

  1. static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
  2. {
  3.     //struct nand_chip *chip = mtd->priv;
  4.     struct s3c2440_nand *nand = s3c2440_get_base_nand();

  5.     debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
  6.     
  7.     //IO_ADDR_W = (ulong)nand;
  8.     if (ctrl & NAND_CTRL_CHANGE) {
  9.         //ulong IO_ADDR_W = (ulong)nand;
  10.         IO_ADDR_W = (ulong)nand;
  11.         if (ctrl & NAND_CLE)
  12.             IO_ADDR_W |= S3C2440_ADDR_NCLE;
  13.         if (ctrl & NAND_ALE)
  14.             IO_ADDR_W |= S3C2440_ADDR_NALE;

  15.         //chip->IO_ADDR_W = (void *)IO_ADDR_W;

  16.         if (ctrl & NAND_NCE)
  17.             writel(readl(&nand->NFCONT) & ~S3C2440_NFCONT_nFCE,
  18.              &nand->NFCONT);
  19.         else
  20.             writel(readl(&nand->NFCONT) | S3C2440_NFCONT_nFCE,
  21.              &nand->NFCONT);
  22.     }

  23.     if (cmd != NAND_CMD_NONE)
  24.         writeb(cmd, (void *)IO_ADDR_W);
  25. }
稍加分析,非常简单,但越是觉得简单就越想不到会在这里出错。代码写的少,导致的后果就是运用其规则的不熟练。
2410的原始代码是这样的:

点击(此处)折叠或打开

  1. static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
  2. {
  3.     struct nand_chip *chip = mtd->priv;
  4.     struct s3c2410_nand *nand = s3c2410_get_base_nand();

  5.     debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);

  6.     if (ctrl & NAND_CTRL_CHANGE) {
  7.         ulong IO_ADDR_W = (ulong)nand;

  8.         if (!(ctrl & NAND_CLE))
  9.             IO_ADDR_W |= S3C2410_ADDR_NCLE;
  10.         if (!(ctrl & NAND_ALE))
  11.             IO_ADDR_W |= S3C2410_ADDR_NALE;

  12.         chip->IO_ADDR_W = (void *)IO_ADDR_W;/* BUG!因为涉及到后续的芯片的读写数据,而此处nand的写地址指向的是命令寄存器
  13.                                              *或地址寄存器,而写数据要写到数据缓冲区中,之后写数据可能会写错寄存器*/ 
  14.         if (ctrl & NAND_NCE)
  15.             writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,
  16.              &nand->NFCONF);
  17.         else
  18.             writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,
  19.              &nand->NFCONF);
  20.     }

  21.     if (cmd != NAND_CMD_NONE)
  22.         writeb(cmd, chip->IO_ADDR_W);
  23. }
需要注意的是临时变量IO_ADDR_W = (ulong)nand 的位置。我刚开始就是写到我注释的地方导致的错误。导致的错误可能是全部都是坏块:
Skipping bad block at  0x00000000                                          
Skipping bad block at  0x00020000                                          
Skipping bad block at  0x00040000                                          
Skipping bad block at  0x00060000                                          
Skipping bad block at  0x00080000                                          
Skipping bad block at  0x000a0000                                          
Skipping bad block at  0x000c0000                                          
Skipping bad block at  0x000e0000                                          
Skipping bad block at  0x00100000                                          
Skipping bad block at  0x00120000
... ...

出现此现象的原因还不是很清楚,没有查到什么样的具体操作会导致这个。只能先暂时总结下出现此现象的解决办法了:
1、网上说的,nand scrub。
2、刚才我说的,底层函数不对。
3、环境参数不对。在配置文件smdk2440.h中,有几个关于环境参数的宏,定义了环境参数的保存地址,用于执行saveenv时保存在flash的一块区域中。通过查找 do_saveenv()函数,可知道,如果要环境参数保存在nor中,要定义
#define CONFIG_ENV_IS_IN_FLASH 1 以及相关的宏,如果环境参数要保存在nand中,就定义#define CONFIG_ENV_IS_IN_NAND 1 以及相关函数。
暂时修改为下:

#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x070000) /* addr of environment */
#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
将上述宏改为
#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_CMD_SAVEENV
#define CONFIG_ENV_OFFSET 0X40000
#define CONFIG_ENV_RANGE CONFIG_ENV_OFFSET
#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */

之所以说“暂时”是因为还没有给nand分区,所以还不能执行saveenv命令,因为执行saveenv可能会破坏代码的结构。shell中查看分析信息用mtdparts,u-boot还没此命令,需要在源码里添加,具体的添加过程比较简单,略了。分区信息如下:
256k(u-boot),128k(params),4m(kernel),-(rootfs) 仅供参考。
分区完后再裁剪(这个u-boot已经很小了,也就没裁剪),才能设置环境参数的这几个宏。可以计算得到,存放参数的这块区域起始地址以及大小。

阅读(813) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~