单片机
返回首页

3.移植uboot-使板卡支持nor、nand

2021-07-30 来源:eefocus

在上一章,我们添加了nor,nand启动后,uboot启动出如下图所示:

上面的Flash: *** failed *** 是属于uboot第二阶段函数board_init_r()里的代码, 代码如下所示(位于arch/arm/lib/board.c):


/*第二阶段*/

void board_init_r(gd_t *id, ulong dest_addr)        //gd    uboot重定位地址

{

     ... ...

  puts('Flash: ');                        //打印flash:

  flash_size = flash_init();                    //初始化nor_flash

  if (flash_size > 0)

  {

       ... ...

       print_size(flash_size, 'n');            //打印nor_flash的大小

  }

  else

  {

    puts(failed);                //打印数组failed[]='*** failed ***n';

    hang();                        //进入while中,并打印: ### ERROR ### Please RESET the board ###     

  }

#if defined(CONFIG_CMD_NAND)

       puts('NAND:  ');                                      //打印NAND:

       nand_init();                                               //初始化nand_flah

... ...

}


从上面代码看出, board_init_r()会来初始化nor,由于新的uboot不支持nor,所以flash_init()初始失败,然后打印一串错误代码后,等待复位.


由于2440在nand启动时,会自动装载nand的前4k内容,所以不支持norflash,因为nor的前4k内容被nand占用.


所以修改上面代码,避免nand启动一直卡住,将:


else

{

  puts(failed);                //打印数组failed[]='*** failed ***n';

  hang();                        //进入while中,并打印: ### ERROR ### Please RESET the board ###     

}


改为:


else

{

  puts('0  KBrn');                //打印0 KB

}


1.接下来,下章便来修改代码,使uboot支持读写norflash


1.1首先在include/common.h中添加:


#define DEBUG     //调试模式


然后使用nor启动新的uboot,打印出调试信息:

打印出norflash的厂家ID=0xC2,设备ID=0x2249,显然uboot匹配读出的ID没有成功.


搜索JEDEC PROBE字段,找到位于board_init_r()->flash_init()->flash_detect_legacy():

如上图所示,该函数会进入board_init_r()->flash_init()->flash_detect_legacy()->jedec_flash_match(),里面会通过两个ID来匹配jedec_table[].


1.2接下来向jedec_table[]里添加norflash:MT29LV160DB(位于drivers/mtd/jedec_flash.c)


代码如下:


     /*MX29LV160DB*/

       {

       .mfr_id         = (u16)MX_MANUFACT,        //厂家ID0x00C200C2 (读nor,便是0xc2)

       .dev_id         = 0x2249,                          //设备ID

       .name           = 'MXIC MX29LV160DB',

       .uaddr          = {

           [1] = MTD_UADDR_0x0555_0x02AA /* 数组[1]表示是16位nor,解锁地址为:0x555,0x2AA */

        },

       .DevSize        = SIZE_2MiB,

       .CmdSet         = P_ID_AMD_STD,

       .NumEraseRegions= 4,                      //4种不同的扇区规格

       .regions        = {

       ERASEINFO(16*1024, 1),

       ERASEINFO(8*1024, 2),

       ERASEINFO(32*1024, 1),

       ERASEINFO(64*1024, 31),

                          }

           },


重新烧写看打印信息,出现这么一段ERROR:


ERROR:too many flash sectors


说flash的扇区太多了,搜索找到位于drivers/mtd/jedec_flash.c中:

显然是CONFIG_SYS_MAX_FLASH_SECT宏小于我们flash的扇区,所以打印ERROR。


所以修改CONFIG_SYS_MAX_FLASH_SECT宏定义(位于include/configs/smdk2440.h),并去掉之前定义的DEBUG调试宏(位于include/common.h)


1.3然后重新烧写


输入flinfo命令(flash info),就能查看flash的信息了:

然后通过uboot命令,检测nor的读写是否正确:


protect off all 

erase 80000 +7ffff              

cp.b 30000000 80000 1000             //烧写在另一个位置

cmp.b 30000000 80000 1000              //比较,是否读写正确


2.接下来继续修改代码,使uboot支持NandFlash


2410的NandFlash位于drivers/mtd/nand/s3c2410_nand.c


2.1 首先复制s3c2410_nand.c,改为s3c2440_nand.c


改Makefile,如下图所示:

2.2 在上一章分析过CONFIG_NAND_S3C2410宏,位于include/configs/smdk2440.h:

如上图所示,其中CONFIG_CMD_NAND宏:表示uboot是否支持nand,在上章里,我们把它屏蔽了,接下来便取消屏蔽CONFIG_CMD_NAND宏。


2.3继续添加对CONFIG_NAND_S3C2440宏的支持,将:


#ifdef CONFIG_CMD_NAND

#define CONFIG_NAND_S3C2410

#define CONFIG_SYS_S3C2410_NAND_HWECC

#define CONFIG_SYS_MAX_NAND_DEVICE  1

#define CONFIG_SYS_NAND_BASE              0x4E000000

#endif


#ifdef CONFIG_CMD_NAND


 

#ifdef CONFIG_S3C2410          

#define CONFIG_NAND_S3C2410

#define CONFIG_SYS_S3C2410_NAND_HWECC

#else                                                                   // CONFIG_S3C2440      

#define CONFIG_NAND_S3C2440    

#define CONFIG_SYS_S3C2440_NAND_HWECC

#endif


#define CONFIG_SYS_MAX_NAND_DEVICE      1

#define CONFIG_SYS_NAND_BASE            0x4E000000

#endif


由于smdk2410.h中定义的是CONFIG_S3C2410,而smdk2440.h中定义的是CONFIG_S3C2440,所以便会根据上面的#ifdef来动态定义宏


 


2.4 然后来看看nand的流程(和linux的nand驱动有很多相似的地方):


1)uboot重定位后进入第二阶段board_init_r():


void board_init_r(gd_t *id, ulong dest_addr)        //gd    uboot重定位地址

{

  ... ...

#if defined(CONFIG_CMD_NAND)                      //需要定义CONFIG_CMD_NAND宏

       puts('NAND:  ');

       nand_init();            /* go init the NAND */

#endif

 ... ...

}


2)进入nand_init():


void nand_init(void)

{

... ...

for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)      //在2.3小节里,该宏为1

nand_init_chip(i);                     

printf('%lu MiBn', total_nand_size / 1024);

... ...

}


3)进入nand_init()->nand_init_chip(0):


static void nand_init_chip(int i)

{

    struct mtd_info *mtd = &nand_info[i];      //mtd_info属于软件的一部分,实现用户层读写等操作

    struct nand_chip *nand = &nand_chip[i];     //属于底层,保存对nand的硬件相关操作,它是mtd_info结构体的priv私有成员

    ulong base_addr = base_address[i];           //获取nand寄存器基地址,等于0x4E000000

    int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;


    if (maxchips < 1)

           maxchips = 1;


    mtd->priv = nand;     //设置私有成员nand_chip

    ... ...

    if (board_nand_init(nand))                   //位于s3c2440_nand.c,该函数会设置nand_chip结构体的成员

           return;


    if (nand_scan(mtd, maxchips)) //通过mtd->priv来开启nand片选,来获取nand的型号,类型等.并填充mtd结构体下其它的成员.

           return;


    nand_register(i);     //注册nand,使uboot支持对nand的读写操作

}


这个nand_chip结构体和我们之前学的linux下的nand驱动章节里的nand_chip一摸一样,流程也非常相似.


由于在2.1小节里,该函数所在的文件s3c2440_nand.c是从s3c2410_nand.c复制过来的,所以接下来便修改s3c2440_nand.c (位于drivers/mtd/nand目录下)


2.5 修改s3c2440_nand.c(参考2410数据手册和2440数据手册)


1)首先将所有带2410字的变量都替换为2440


2)修改board_nand_init()


参考以前写的nand驱动,将


  tacls = 4;

    twrph0 = 8;

    twrph1 = 8;

    cfg = S3C2440_NFCONF_EN;                       //启动nand控制器

    cfg |= S3C2440_NFCONF_TACLS(tacls - 1);

    cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

    cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

    writel(cfg, &nand_reg->nfconf);


改为:


    tacls = 0;                      //10ns    

    twrph0 = 1;                  //20ns

    twrph1 = 0;                  //10ns


    nand_reg->nfconf = (tacls<<12) | (twrph0<<8) | (twrph1<<4); //设置时序

    nand_reg->nfcont=(1<<1)|(1<<0); // bit1:关闭片选(),       bit0:开启nand flash 控制器


2)添加nand_chip结构体成员


nand->select_chip=s3c2440_select_chip;             //设置CE


然后并写一个s3c2440_select_chip()函数


/*nand flash  :CE */


static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)

{

        if(chipnr==-1)          //CE Disable

       {

        my_regs->nfcont|=(0x01<<1);               //bit1置1

       }

        else                         //CE Enable

       {

        my_regs->nfcont&=~(0x01<<1);        //bit1置0 

       }           


}


3)屏蔽带硬件ECC的相关操作


将:


#ifdef CONFIG_S3C2410_NAND_HWECC

nand->ecc.hwctl = s3c2440_nand_enable_hwecc;

nand->ecc.calculate = s3c2440_nand_calculate_ecc;

nand->ecc.correct = s3c2440_nand_correct_data;

nand->ecc.mode = NAND_ECC_HW;

nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;

nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;

#else

nand->ecc.mode = NAND_ECC_SOFT;

#endif


改为:


nand->ecc.mode = NAND_ECC_SOFT;               //使用软件ECC


2.6 修改s3c2440_hwcontrol()函数


改为:


static void s3c2440_hwcontrol(struct mtd_info *mtd,int cmd,unsigned int ctrl)

{

    struct nand_chip *chip = mtd->priv;        

    struct s3c2440_nand *nand = s3c2440_get_base_nand();   //获取nand寄存器地址

     

    if (ctrl & NAND_CLE)                 // 传输的是命令

        nand->nfcmd=cmd;  

    else                                // 传输的是地址

        nand->nfaddr=cmd;  

}


s3c2440_hwcontrol()函数的ctrl是个标志位:


bit[1]==1: 表示要发送的dat是命令

bit[2]==1: 表示要发送的dat是地址

bit[0]==1:表示使能nand , ==0:表示禁止nand

(PS:具体可以参考内核的nand_command_lp()函数,它会调用这个cmd_crtl函数来实现功能 )


2.7编译烧写


如下图所示,可以看到已支持Nand Flash:

试验nand是否能读写:


nand erase 0 2000                      //擦除

mw.b 30000000 0x55 2000

nand write 30000000 0 2000        //将0x55写入nand

nand dump 0 2000        //打印


如下图所示, 可以看到读写nand都没问题

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 红外线探测报警器

  • 短波AM发射器电路设计图

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 开关电源的基本组成及工作原理

  • 带有短路保护系统的5V直流稳压电源电路图

    相关电子头条文章