《12.uboot的移植2-从uboot官方标准uboot开始移植》

《12.uboot的移植2-从uboot官方标准uboot开始移植》

第一部分、章节目录
2.12.1.选择合适的官方原版uboot
2.12.2.先初步浏览官方原版uboot
2.12.3.mkconfig脚本分析
2.12.4.先解决官方版本uboot的烧录运行
2.12.5.start.S文件分析与移植1
2.12.6.start.S文件分析与移植2
2.12.7.添加DDR初始化1
2.12.8.添加DDR初始化2
2.12.9.添加uboot第二阶段重定位1
2.12.10.添加uboot第二阶段重定位2
2.12.11.CPU时钟信息显示移植1
2.12.12.CPU时钟信息显示移植2
2.12.13.CPU时钟信息显示移植3
2.12.14.board和DDR配置显示移植
2.12.15.board_init_r移植
2.12.16.uboot2013.10中SD/MMC驱动浏览
2.12.17.SD卡驱动移植1
2.12.18.SD卡驱动移植2
2.12.19.SD卡驱动移植3
2.12.20.环境变量的移植
2.12.21.环境变量的测试和配置移植
2.12.22.网卡驱动的移植1
2.12.23.网卡驱动的移植2

第二部分、章节介绍
2.12.1.选择合适的官方原版uboot
本节课讲解uboot的版本差异,并且最终选择一个合适的官方uboot版本作为我们移植工作的起点。
2.12.2.先初步浏览官方原版uboot
本节对官方uboot进行大概浏览和结构分析、工程建立等,并将该uboot和我们之前分析过的移植好的uboot进行大概对比分析。
2.12.3.mkconfig脚本分析
本节分析2013.10版本的uboot的mkconfig,重点解析了8个传参与符号连接创建等,这些在后续分析代码时都会有一定帮助。
2.12.4.先解决官方版本uboot的烧录运行
本节首先移植sd_fusing文件夹,然后分析并修改代码使编译得到的u-boot.bin能够在SD卡中运行起来。
2.12.5.start.S文件分析与移植1
本节分析start.S文件流程,并且添加开发板置锁和串口输出字符的代码。
2.12.6.start.S文件分析与移植2
本节接上节来排除问题,讲解了如何使用LED点亮方式进行调试,并最终解决问题,成功输出字符"O"
2.12.7.添加DDR初始化1
本节开始添加DDR初始化代码,主要是分析了DDR初始化代码添加的位置、相关文件的移植等。
2.12.8.添加DDR初始化2
本节继续解决DDR初始化部分功能,添加调试信息打印以验证DDR初始化是否成功。
2.12.9.添加uboot第二阶段重定位1
本节首先分析uboot代码流程,找到应该添加uboot重定位功能的代码段,然后分析代码重定位的思路。
2.12.10.添加uboot第二阶段重定位2
本节接上节继续添加重定位代码,并且解决编译中的各种问题,成功实现重定位,并且看到了uboot启动第二阶段的信息。
2.12.11.CPU时钟信息显示移植1
本节分析uboot第二阶段的cpu信息输出,并且进行代码移植和更改,解决主频显示不正确的问题。
2.12.12.CPU时钟信息显示移植2
本节接上节继续解决主频显示不正确的问题。
2.12.13.CPU时钟信息显示移植3
本节最终解决了主频显示不正确的问题,并且更正了以前一些不正确的认识,对210的iROM中时钟设置更加明确。
2.12.14.board和DDR配置显示移植
本节主要解决开发板名称、DDR配置值的初始化等修改,这些都是init_sequences中的东西。
2.12.15.board_init_r移植
本节开始移植board_init_r中的一些函数,主要内容是去掉原来的oneNand支持,加上SD/MMC的支持并将环境变量修改到SD/MMC中。
2.12.16.uboot2013.10中SD/MMC驱动浏览
本节进行SD/MMC驱动的浏览和错误问题定位分析,最终分析得到解决方案思路。
2.12.17.SD卡驱动移植1
本节开始移植SD卡驱动,主要进行驱动文件的逐个分析、文件关系的对应复制Makefile的修改等。
2.12.18.SD卡驱动移植2
本节继续进行SD卡驱动移植实验,将移植的驱动文件整理然后进行编译、修改等使之可以通过编译。
2.12.19.SD卡驱动移植3
本节继续进行SD卡驱动移植实验,主要在编译脚本cp.sh中添加代码使之选择性清理配置编译,这样可以提升效率。
2.12.20.环境变量的移植
本节分析环境变量分区的问题,通过代码分区确认了当前环境变量被放在何处,并且通过修改代码将环境变量放在合适的位置。
2.12.21.环境变量的测试和配置移植
本节对环境变量进行测试和效果确认,并且将uboot中默认的环境变量表重新设置以使该uboot更方便使用。
2.12.22.网卡驱动的移植1
本节开始移植网卡驱动,主要是分析网卡驱动初始化代码,然后进行网卡初始化的添加、代码实践、效果查看。
2.12.23.网卡驱动的移植2
本节接上节继续进行网卡驱动的移植。主要是分析网卡驱动不工作的原因并且进行解决方案分析、实践及效果确认。

第三部分、随堂记录
2.12.1.选择合适的官方原版uboot
2.12.1.1、官方原版uboot的版本
(1)版本号。刚开始是1.3.4,后来变成2009.08
(2)新版和旧版的差别。uboot的架构很早就定下来了,然后里面普遍公用的东西(common目录下、drivers目录下、fs目录下等···)在各个版本之间几乎是完全一样的。差别最大的是board和cpu目录,这两个目录正是单板(开发板)相关的。越新的uboot版本支持越多的开发板(CPU),所以越新的uboot越庞大。
(3)并不是越新的版本就越好。越新的uboot中会多出更多的开发板的支持代码,如果我们的开发板并不是很新,就没必要去用很新版本的uboot。因为多出来的代码自己也用不到而且还会成为累赘。

2.12.1.2、官方原版uboot的来源
(1)从uboot官方网站ftp下载
(2)从一些镜像网站下载

2.12.1.3、新版uboot配置体系的改变
(1)在最新的uboot版本(准确的说是2013.10到2014.10中的某个版本)中,uboot的文件体系发生了一个很大的变化。这个变化就是uboot引入了linux kernel的配置体系(Kbuild、Kconfig、menuconfig),从而让我们可以在图形界面下,像配置内核一样配置uboot。
(2)所以新版本的uboot配置时和我们之前的课程讲的就不同了。我们移植时不能选择这种配置方式更改之后的uboot版本。我们要选择更改之前的。
(3)新版本的配置方式本质上和linux kernel一样的,所以在学完linux kernel移植后自己就能看懂,因此不用担心。

2.12.1.4、结论:选择合适的官方原版uboot进行移植
(1)结合以上,选择2013.10版本进行实验移植是比较合理的。

2.12.1.5、注意:实践工作中一般是从SoC厂商的uboot出发移植的
(1)在工作中一般是不需要从uboot官方版本出发去做移植的,而是从SoC厂商提供的开发板配套的uboot去做移植的。

2.12.2.先初步浏览官方原版uboot
2.12.2.1、文件夹结构浏览
(1)文件夹结构分析、主要文件检视
总的来说,文件夹结构和以前基本一样。不同的主要是lib,以前是lib_arm和lib_generic,现在是arch和lib。arch目录下放的是和cpu架构有关的东西。
总的来说,2013.10版本的uboot在结构上和1.3.4版本的uboot还是有所不同的。
(2)参照物开发板的选择
我们开发板使用的CPU是S5PV210,所以要找uboot中针对S5PV210或者S5PC110进行移植的作为参考。
根据规律,我们应该参考include/configs/s5p_goni.h,对应的board在uboot/board/samsung/goni这个目录。

(3)删除无关文件和文件夹
其实不删除也可以,但是删除更好。

2.12.2.2、建立SI工程并预解析

2.12.2.3、主Makefile浏览及boards.cfg文件
(1)2013.10版本的uboot的Makefile中使用了boards.cfg文件,因此在配置uboot时make xxx_config,这个xxx要到boards.cfg文件中查找。
(2)其实就相当于把以前的版本的uboot中各种开发板的配置部分规则抽离出来写到了Makefile中,然后把配置信息部分写到了一个独立文件boards.cfg。

2.12.2.4、mkconfig脚本浏览及符号连接的分析
(1)下节课详细分析,给出结论。
2.12.2.5、结论:
(1)参照物开发板为:55p_goni
(2)配置对应的cpu、board文件夹分别为:
cpu: u-boot-2013.10\arch\arm\cpu\armv7
board: u-boot-2013.10\board\samsung\goni

2.12.3.mkconfig脚本分析
2.12.3.1、脚本功能浏览
(1)首先我们在命令行配置uboot时,是:make s5p_goni_config,对应Makefile中的一个目标。
(2)新版本的Makefile中:
%_config:: unconfig
@$(MKCONFIG) -A $(@:_config=)
从这里分析得出结论,实际配置时是调用mkconfig脚本,然后传参2个:-A和s5p_goni
(3)到了mkconfig脚本中了。在24到35行中使用awk正则表达式将boards.cfg中与刚才$1(s5p_goni)能够匹配上的那一行截取出来赋值给变量line,然后将line的内容以空格为间隔依次分开,分别赋值给$1、$2···$8。
(4)注意在解析完boards.cfg之后,$1到$8就有了新的值。
$1 = Active
$2 = arm
$3 = armv7
$4 = s5pc1xx
$5 = samsung
$6 = goni
$7 = s5p_goni
$8 = -

2.12.3.2、几个传参和其含义
(1)几个很重要的变量
arch=arm
cpu=armv7
vendor=samsung
soc=s5pc1xx

2.12.3.3、符号链接
(1)include/asm -> arch/arm/include/asm
(2)include/asm/arch -> include/asm/arch-s5pc1xx
(3)include/asm/proc -> include/asm/proc-armv

最后创建了include/config.h文件。

2.12.3.4、Makefile中添加交叉编译工具链
(1)官方原版的uboot中CROSS_COMPLIE是没有定义的,需要自己去定义。如果没定义就直接去编译,就会用gcc编译。
(2)添加一行:
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-

2.12.3.5、配置编译测试
(1)编译过程:
make distclean
make s5p_goni_config
make
(2)结果:得到u-boot.bin即可

2.12.4.先解决官方版本uboot的烧录运行
2.12.4.1、如何烧录uboot
(1)烧录u-boot.bin到SD卡中有2种方法:windows下用烧录软件;linux下用dd命令烧录脚本来烧录。因为windows下的工具不开源,出了问题没法调试,所以不推荐。推荐linux下用烧录脚本来烧录(实质是dd命令进行sd卡扇区写入)
(2)移植原来的版本的uboot中的sd_fusing文件夹到官方uboot版本中,使用这个文件夹中的sd_fusing.sh脚本来进行烧录。

2.12.4.2、分析:为什么烧录运行不正确?
(1)串口接串口2,串口有输出。但是这个串口输出不是uboot输出的,而是内部iROM中的BL0运行时输出的。
(2)输出错误信息分析:
第一个SD checksum Error:是第一顺序启动设备SD0(iNand)启动时校验和失败打印出来的;
第二个SD checksum Error:是第二顺序启动设备SD2(外部SD卡)启动时校验和失败打印出来的;
剩下的是串口启动和usb启动的东西,可以不管。
总结:从两个SD checksum Error,可以看出:外部SD卡校验和失败了。
分析:SD卡烧录出错了,导致SD卡校验和会失败。

2.12.4.3、解决方案分析
(1)为什么SD卡烧录会出错?可能原因:烧录方法错误、烧录原材料错误。
(2)经过分析,sd_fusing这个文件夹下的mkbl1这个程序肯定没错,上一层目录下的u-boot.bin是存在的,校验和失败不失败和u-boot.bin无关。
(3)经过分析和查找,发现是mkbl1程序和start.S中前16个字节校验和的处理上面不匹配造成的,解决方法是在start.S最前面加上16个字节的占位。

2.12.4.4、代码实践
(1)重新编译烧录运行,发现结果只显示一个SD checksum Error。这一个就是内部SD0通道的inand启动校验和失败打印出来的。剩下的没有了说明外部SD卡校验和成功了,只是SD卡上的uboot是错误的,没有串口输出内容,所以没有输出了。

2.12.5.start.S文件分析与移植1
2.12.5.1、start.S流程分析
(1)#define CONFIG_SYS_TEXT_BASE 0x34800000 可以看出我们的uboot的连接地址是在0x34800000位置。
(2)save_boot_params是个空函数,里面直接返回的。
(3)cpu_init_cp15这个函数功能是设置MMU、cache等。这个版本的uboot中未使用虚拟地址,因此MMU在这里直接关掉。
(4)cpu_init_crit,这个函数里只有一句跳转指令,短跳转到lowlevel_init函数。
注意:uboot中有2个lowlevel_init.S文件(文件中还都有lowlevel_init函数),凭一般分析无法断定2个中哪个才是我们想要的。通过分析两个文件所在文件夹下面的Makefile可以判定board/samsung/goni目录下的才是真正包含进来的,arch/arm/cpu/armv7目录下的并没有被包含进来。
还可以通过实践验证的方法来辅助判断。通过查看之前已经编译过的uboot源码目录,看哪个被编程为.o文件了,就知道哪个是真正被使用的了。
(5)lowlevel_init函数在board/samsung/goni目录下,主要作用是时钟设置、串口设置、复位状态判断···这个函数是S5PC100和S5PC110两个CPU共用的。
(6)经过浏览,发现lowlevel_init函数中做的有意义的事情有:关看门狗、调用uart_asm_init来初始化串口、并没有做时钟初始化(下面有时钟初始化的函数,但是实际没调用。如果uboot中没有初始化时钟,那么时钟就是iROM中初始化的那种配置)
2.12.5.2、添加开发板制锁和串口打印字符"O"
(1)我们为了调试uboot的第一阶段,就要看到现象。为了看到现象,我们向lowlevel_init函数中添加2个代码,一个是开发板制锁,一个是串口打印"O"
(2)这两段代码可以直接从ARM裸机全集课程中的代码中来。其实也可以从三星移植版本的uboot中来,但是因为三星移植版本中用到了很多寄存器定义,涉及到头文件的,所以移植过来不方便。
(3)实践添加。
2.12.5.3、实践结果及分析
(1)实验结果是:没看到开发板制锁,串口也没有输出任何东西。实验失败。
(2)结论:因为开发板制锁没有成功,所以我们判定,在开发板制锁代码运行之前uboot就已经挂掉了。下面就是去跟踪代码运行,然后判定问题点再去解决。

2.12.6.start.S文件分析与移植2
2.12.6.1、添加LED点亮代码跟踪程序运行
(1)在基础代码阶段,串口还没有运行,串口调试工具还无法使用时,使用LED点亮的方式来调试程序就是一个有力的手段。
(2)有些情况下可以用Jlink等调试工具来调试这种基础代码。
(3)从程序的基本运行路径端出发,隔一段给他添加一个LED点亮代码,然后运行时根据现象来观察,判定哪里执行了哪里没执行。从而去定位问题。
(4)从以前的裸机代码中组织出一个标准的LED点亮然后延时一段的一个标准代码段:
ldr r0, =0x11111111
ldr r1, =0xE0200240
str r0, [r1]
ldr r0, =((1<<3) | (0<<4) | (1<<5)) // 1是灭,0是亮
ldr r1, =0xE0200244
str r0, [r1]

ldr r2, =9000000
ldr r3, =0x0

delay_loop:
sub r2, r2, #1
cmp r2, r3
bne delay_loop

(5)之前做实验时发现一个现象:我们的uboot运行时按住电源开关时所有4颗LED都是亮的。所以我们做实验时给LED点亮是看不到现象的,所以我们的代码关键是要熄灭某些LED来判断。
(6)我们将熄灭LED的函数在start.S中隔一段的关键部位放上1个,然后运行时通过观察LED的点亮熄灭状态,就知道程序运行到哪里了。
(7)经过判断我们发现:start.S中工作一切正常,但是函数一旦放到lowlevel_init.S中就完全不工作了。通过分析得出结论:b lowlevel_init这句代码出了问题。

2.12.6.2、修改u-boot.lds将lowlevel_init.S放到前部
(1)问题分析:跳转代码出了问题。分析问题出在代码的连接上。
(2)三星S5PV210要求BL1大小为8KB,因此uboot第一阶段代码必须在整个uboot镜像的前8KB内,否则跳转不到。
(3)对比三星移植版本的uboot的u-boot.lds和官方版本uboot的连接脚本u-boot.lds(注意这两个版本的uboot的连接脚本的位置是不同的),就发现lowlevel_init.S的代码段没有被放在前面。
(4)在u-boot.lds中start.o后面添加board/samsung/goni/lowlevel_init.o (.text*),这个就保证了lowlevel_init函数被连接到前面8kb中去。
(5)报错,lowlevel_init重复定义了。

2.12.6.3、修改board/samsung/goni/Makefile解决编译问题
(1)问题分析:为什么会重复定义。因为lowlevel_init这个函数被连接时连接了2次。一次是board/samsung/goni这个目录下生成libgoni.o时连接了1次,第2次是连接脚本最终在连接生成u-boot时又连接了一次,所以重复定义了。
(2)这个错误如何解决?思路是在libgoni.o中不要让他连接进lowlevel_init,让他只在最终连接u-boot时用1次,就可以避免重复定义。
(3)参考当前版本的uboot的start.S文件的处理技巧,解决了这个问题。

2.12.6.4、实践验证。
结果是开发板制锁和串口输出’O’都成功了。

2.12.7.添加DDR初始化1
2.12.7.1、分析下一步移植路线
(1)cpu_init_crit函数成功初始化串口、时钟后,转入_main函数,函数在arch/arm/lib/crt0.S文件中。
(2)在crt0.S中首先设置栈,将sp指向DDR中的栈地址;然后调用board_init_f函数进行板级初始化。函数在arch/arm/lib/board.c中。
(3)在这个版本的uboot中,把以前uboot的第二阶段start_armboot函数分成了2部分:board_init_f和board_init_r。所以在这里就和以前版本的uboot接轨上了,推测board_init_f中肯定是做了板级初始化,board_init_r中进入了uboot的命令行。
(4)分析到这里,在uboot2013.10版本中思路已经很清晰了:uboot的第二阶段就在crt0.S文件中,第二阶段的入口就是_main函数。第一阶段工作主要就是cpu_init_crit函数,所以我们要在cpu_init_crit函数中添加DDR初始化和uboot的重定位。
(5)分析到这里,下一步工作方向就确定了。我们要先在cpu_init_crit函数中添加DDR初始化,然后在start.S中bl _main之前添加uboot的重定位,然后将bl _main改成ldr pc, __main(__main: .word _main)长跳转。然后在crt0.S中board_init_f后删除那些重定位代码,至此uboot的第二阶段就应该能启动起来了。后续的移植就是第二阶段了。

2.12.7.2、分析DDR初始化代码移植思路
(1)如果本来uboot中有DDR初始化代码,那我们可以就着这些代码来修改。但是问题是这个uboot2013.10中根本没有DDR初始化,所以我们需要完全从头去另外添加DDR初始化代码。
(2)我们的思路就是从三星版本的uboot中直接移植DDR初始化代码过来即可。三星版本的uboot中DDR初始化函数在cpu/s5pc11x/s5pc110/cpu_init.S文件中,直接将这个文件移植过来即可。

2.12.7.3、动手移植
(1)添加cpu_init.S文件到uboot2013.10中。注意,这里的代码必须保证在前8kb内,所以必须和lowlevel_init.S文件一样的链接处理。主要是在board/samsung/goni/Makefile中和arch/arm/cpu/u-boot.lds文件中做修改添加。
(2)添加头文件s5pc110.h到include目录下。
(3)对cpu_init.S文件代码进行修整,把一些无用的代码去掉,把一些相关的条件编译人工处理一下。
(4)在SourceInsigt工程中添加入这两个文件。然后重新解析一遍。然后对新添加的代码进行分析修整,把里面一些明显的宏定义缺失给补上。

2.12.8.添加DDR初始化2
2.12.8.1、移植必要的宏定义
(1)DDR配置参数,从三星版本的smdkv210single.h中复制到s5p_goni.h中。
(2)s5pc110.h中进行修整。

2.12.8.2、代码同步、编译、再修整

2.12.8.3、添加调试信息,验证DDR初始化完成。
(1)调试信息有LED点亮和串口输出两种。优先选用串口调试的方法。
(2)在DDR初始化完成后,添加串口输出字符"K",这样启动时如果看到了"OK"就说明DDR已经被成功初始化了。
(3)结果:看到了"OK"标志,说明DDR添加实验成功。

2.12.9.添加uboot第二阶段重定位1
2.12.9.1、在重定位代码前加调试信息定位
(1)逻辑上来说,重定位部分代码应该在DDR初始化之后和uboot第二阶段来临前之间。
(2)uboot的第一阶段和第二阶段的划分并不是绝对的,唯一必须遵循的原则就是第一阶段不能大于8KB。所以uboot的第一阶段最少要完成DDR初始化和重定位,最多不能超过8KB。在满足这些条件时,第一阶段和第二阶段的接点可以随便挑。
(3)找到合适的地方来写重定位代码,重定位之后远跳转到第二阶段的入口。
(4)
2.12.9.2、重定位代码移植
2.12.9.3、清bss段移植
2.12.9.4、movi_bl2_copy函数移植
(1)从三星版本的uboot中赋值movi.c和movi.h到uboot2013.10中。
(2)改makefile和u-boot.lds。

2.12.10.添加uboot第二阶段重定位2
2.12.10.1、_mian函数中基本处理
(1)主要就是把里面的重定位代码部分给删除掉。剩下就是:设置栈、调用board_init_f函数和board_init_r函数。

2.12.10.2、代码同步及编译
(1)主要是crt0.S和movi.h。

2.12.10.3、编译中出现问题解决
(1)movi.h中宏定义出错,最后在s5p_goni.h中添加了 CONFIG_EVT1这个宏解决了
(2)连接错误:u-boot contains relocations other than R_ARM_RELATIVE
在uboot下用grep “R_ARM_RELATIVE” -nR *搜索,发现Makefile中有一个检查重定位的规则,屏蔽掉这个规则后编译连接成功。

2.12.10.4、结果验证及下阶段展望
(1)看到了uboot启动打印出来的一系列信息,但是uboot没有进入命令行。
(2)这说明uboot中的DDR初始化和重定位功能都已经完美实现,后面就是第二阶段的继续移植了。

2.12.11.CPU时钟信息显示移植1
2.12.11.1、小问题:banner信息补全
2.12.11.2、CPU ID的确定
2.12.11.3、CPU各种频率的自动计算
2.12.11.4、代码实践
(1)arch/arm/include/asm/arch-s5pc1xx/cpu.h,和arch/arm/cpu/armv7/s5p-common/cpu_info.c文件同步一下

2.12.12.CPU时钟信息显示移植2

2.12.13.CPU时钟信息显示移植3
2.12.13.1、问题分析
(1)时钟显示ARMCLK是400MHz。
(2)调试,把m、p、s和apll_ratio打印出来后,发现这几个值的设置和之前的uboot的设置是不同的。原因在于我们当前版本的uboot中并未对SoC的时钟进行过设置,当前uboot中的时钟是iROM代码默认设置的。
(3)我自己之前一直认为iROM中把210的时钟设置为了1000MHz,然后三星版本的uboot中设置的时钟也是按照这个数据手册356页推荐的这个最佳性能配置时钟设置的。所以以前认为uboot中可以没有时钟设置也是一样的。
(4)但是实际上不是这样的,实际上内部iROM中设置的时钟APLL输出是800MHz,ARMCLK是400MHz。如果uboot中不做时钟的设置实际得到的就是这个时钟。所以我们之前代码得到的结果是400MHz。
(5)所以要解决这个时钟不对的问题,要在lowlevel_init.S中添加上时钟初始化的代码即可。

2.12.13.2、时钟初始化函数的添加
(1)在lowlevel_init.S中移植system_clock_init函数,并且在s5p_goni.h中添加相关的宏定义参数,然后在lowlevel_init函数中调用system_clock_init函数。
2.12.13.1、总结和感悟

2.12.14.board和DDR配置显示移植
2.12.14.1、board名称更改
2.12.14.2、DDR配置值修改
2.12.14.3、MACH_TYPE定义
2.12.14.4、DDR打印信息更改
2.12.14.5、代码实践
2.12.14.6、关于MACH_TYPE的定义问题。
(1)在uboot2013.10中和uboot1.3.4中设计有所不同。在uboot1.3.4中这个东西是分散定义在各个配置头文件当中的。但是在uboot2013.10中我们把MACH_TYPE集中定义在一个文件arch/arm/include/asm/mach-types.h中了。
(2)集中定义其实是uboot从linux内核中学来的。在linux kernel中MACH_TYPE就是在文件中集中定义的。集中定义的好处是方便查阅,不容易定义重复。
(3)这个MACH_TYPE是和开发板绑定的,原则上每一个开发板型号都有一个MACH_TYPE,这个机器码由linux内核管理者来分配的,如果需要应该向这些人申请。

2.12.15.board_init_r移植
2.12.15.1、去掉oneNand支持
2.12.15.2、添加SD/MMC支持

2.12.16.uboot2013.10中SD/MMC驱动浏览
2.12.16.1、从初始化代码开始浏览
(1)
2.12.16.2、相关函数和文件
(1)
drivers/mmc/mmc.c、
drivers/mmc/sdhci.c
board/samsung/goni/goni.c
arch/arm/include/asm/arch-s5pc1xx/mmc.h

2.12.16.3、当前错误定位及解决方案分析
(1)错误发生路径定位
board_init_r
mmc_initialize
do_preinit
mmc_start_init
mmc_go_idle
mmc_send_cmd
sdhci_send_command
sdhci_transfer_data 错误在这个函数中
(2)错误原因分析
sdhic.c中的所有函数构成了三星210CPU的SD/MMC控制器的驱动。这里面的函数是三星公司的工程师写的,内容就是用来控制210CPU的内部的SD/MMC控制器和外部的SD卡通信的。这就是所谓的驱动。
sdhci_transfer_data函数出错,说明是SoC的SD/MMC控制器和外部SD卡(其实现在用的是SD0的iNand)的数据传输出了问题。(细节分析发现是控制器内部有一个中断状态错误标志被置位了。)
(3)解决方案分析:
两条思路:第一是去逐行的分析SD卡驱动实现(分析中要对SD卡通信协议和210这个SoC的SD控制器非常熟悉),然后发现错误所在,然后修改代码解决问题;第二个是投机取巧的方法,就是把原来三星移植版本的uboot中的SD/MMC驱动整个移植过来替换掉uboot2013.10中的MMC驱动。其实还有第三条折中思路,就是综合第一种和第二种,譬如参考三星移植版本的uboot中的驱动实现来修补uboot2013.10中的驱动实现。

2.12.17.SD卡驱动移植1
2.12.17.1、分析两个版本的uboot中SD卡驱动差异
(1)uboot2013.10中:驱动相关的文件主要有:
drivers/mmc/mmc.c
drivers/mmc/sdhci.c
drivers/mmc/s5p_sdhci.c
board/samsung/goni/goni.c
(2)三星移植版本中,驱动相关的文件主要有:
drivers/mmc/mmc.c
drivers/mmc/s3c_hsmmc.c
cpu/s5pc11x/cpu.c
cpu/s5pc11x/setup_hsmmc.c
(3)经过分析发现:SD卡驱动要工作要包含2部分内容,一部分是drivers/mmc目录下的是驱动,另外一部分是uboot自己提供的初始化代码(譬如GPIO初始化、时钟初始化)
2.12.17.2、复制必要的文件并修改相应Makefile
(1)首先解决drivers/mmc目录下的文件替换。
(2)修改初始化代码。
2.12.17.3、代码浏览及修补
(1)按照代码运行时的流程来逐步浏览代码,看哪里需要修补。

2.12.18.SD卡驱动移植2
2.12.18.1、继续修补驱动代码
(1)include/mmc.h
(2)include/s3c_hsmmc.h
2.12.18.2、同步及编译、问题解决
(1)出错1:cmd_mmc.c中出错。原因是cmd_mmc.c和mmc驱动密切相关,所以改了驱动后这个实现文件也要跟着改,解决方法是从三星版本的直接同名文件复制过来替换
(2)出错2:drivers/mmc/mmc_write.c编译出错。原因是这个文件和本来版本中的mmc.c文件相关,但是mmc.c被替换掉了所以这个文件编译报错。解决方案就是修改makefile去掉这个文件的依赖,让他不被编译。
(3)出错3:#include<regs.h>注释掉,然后添加#include <s5pc110.h>

2.12.19.SD卡驱动移植3
2.12.19.1、解决每次编译时间都很长的问题。
(1)每次编译脚本cp.sh执行时都会先cp同步代码,然后make distclean···所以每次都会清空后从头编译,这就很费时间了。
(2)但是实际上有时候是不会make distclean的,只需要先cp然后直接make即可(当更改没有涉及到配置头文件s5p_goni.h,没有涉及到makefile文件,或者其他项目配置文件,也就是说我们的更改只是普通代码文件的更改时)

2.12.19.2、效果测试
(1)读写测试均成功

2.12.20.环境变量的移植
2.12.20.1、iNand分区表检查-env究竟应该放在哪
(1)测试环境变量是否可以保存,通过开机set设置环境变量然后save,然后关机后重启来测试环境变量的保存是否成功。
(2)我们的环境变量究竟保存到哪里去了?这个就要去分析代码中的分区表。
(3)环境变量应该被放在哪里?虽然无法确定ENV一定要放在哪里,但是有一些地方肯定是不能放的,否则将来会出问题。原则是同一个SD卡扇区只能放一种东西,不能叠加,否则就会被覆盖掉。uboot烧录时使用的扇区数是:SD2的扇区1-16和49-x(x-49大于等于uboot的大小)
(3)从uboot的烧录情况来看,SD2的扇区0空闲,扇区1-16被uboot的BL1占用,扇区17-48空闲,扇区49-x被uboot的BL2占用。再往后就是内核、rootfs等镜像的分区了。系统移植工程师可以根据kernel镜像大小、rootfs大小等来自由给SD分区。
(4)从uboot的分区情况来看,ENV不能往扇区1-16或者49-x中来放置,其他地方都可以商量。ENV的大小是16K字节也就是32个扇区。

2.12.20.2、环境变量相关代码浏览
(1)目前情况是uboot在SD2中,而ENV在SD0中,所以现在ENV不管放在哪个扇区都能工作,不会有问题。但是我们还是得找到ENV分区所在并且改到不会和uboot冲突,因为将来部署系统时我们会将uboot和kernel、rootfs等都烧录到iNnand中去,那时候也要确保不会冲突。
(2)static inline int write_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
类似于这种函数,在代码分析中,关键是弄明白各种参数的意义。mmc表示要写的mmc设备,size表示要写的大小,offset表示要写到SD卡的哪个扇区去,buffer是要写的内容。
(3)CONFIG_ENV_OFFSET这个宏决定了我们的ENV在SD卡中相对SD卡扇区0的偏移量,也就是ENV写到SD卡的哪里去了。经过分析发现这个宏的值为0.所以我们的ENV
被写到了0扇区开始的32个扇区中。
(4)写到这里肯定不行,因为和uboot的BL1冲突了。解决方案是改变这个CONFIG_ENV_OFFSET的值,将ENV写到别的空闲扇区去。
(5)#define MOVI_BL2_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT) 后面这三个其实分别是1+16+32=49
其中的1就是扇区0(空闲的),16是就是扇区1-16(uboot的BL1),32就是扇区17-48(存放ENV的),49自然就是uboot的BL2开始扇区了。这种安排是三星移植的uboot版本中推荐的SD卡的分区方式,不一定是唯一的。
(6)我们参考这个设计,即可实现环境变量不冲突。所以只要将ENV放到17扇区起始的地方即可。

2.12.21.环境变量的测试和配置移植
2.12.21.1、如何测试环境变量的保存是否正确
(1)程序修改重新编译后启动,启动后要注意iNand中本来有没有环境变量。为了保险起见对iNand的前49个扇区进行擦除,然后就可以确保里面没有之前保存过的环境变量了。使用命令:mmc write 0 30000000 0# 49来擦除SD0的扇区0-48,保证以前的环境变量都没有了。
(2)重新开机后先set随便改一个环境变量作为标记然后saveenv然后重启。
(3)测试方法是,使用:mmc read 0 30000000 17# 32命令将iNand的17开始的32个扇区读出来到内存30000000处,然后md查看。找到显示区域里面的各个环境变量,看读出来的和自己刚才修改的值是否一样。

2.12.21.2、常用环境变量的配置移植
(1)常用的环境变量就是网络相关的那几个,和CONFIG_BOOTCOMMAND、CONFIG_BOOTARGS等。

2.12.22.网卡驱动的移植1
2.12.22.1、添加网络支持
(1)uboot中对各种功能也是一个条件编译可以配置可以裁剪的设计(从linux内核学来的),默认情况下我们的uboot没有选择支持网络。
(2)在配置头文件中添加一行 #define CONFIG_CMD_NET
(3)添加了网络支持宏之后,在uboot初始化时就会执行eth_initialize函数,从而网络相关代码初始化就会被执行,将来网络就有可能能用。

2.12.22.2、添加ping和tftp命令
(1)在linux系统中网络底层驱动被上层应用调用的接口是socket,是一个典型的分层结构,底层和上层是完全被socket接口隔离的。
(2)但是在uboot中网络底层驱动和上层应用是黏在一起的,不分层。意思就是上层网络的每一个应用都是自己去调用底层驱动中的操作硬件的代码来实现的。
(3)uboot中有很多预先设计的需要用到网络的命令,和我们直接相关的就是ping和tftp这两个命令。这两个命令在uboot中也是需要用相应的宏开关来打开或者关闭的。
(4)经过代码检查,发现ping命令开关宏为CONFIG_CMD_PING,而tftp命令的开关为CONFIG_CMD_NET,确认添加。

2.12.22.3、代码实践。结果是ping和tftp命令都被识别了,但是都提示no ethernet found`````网络不通。为什么不通?因为还没做初始化等移植

2.12.22.4、移植网卡初始化代码

2.12.23.网卡驱动的移植2
2.12.23.1、实验现象分析
(1)因为我们没有自定义的网卡初始化函数(board_eth_init或者cpu_eth_init),所以uboot启动时初始化网卡时打印:Net: Net Initialization Skipped
(2)eth.c中有2个很重要的全局变量:eth_devices(用来指向一个链表,这个链表中保存了当前系统中所有的网卡信息)和eth_current(eth_current指针指向当前我们正在操作的那个网卡)。
(3)在linux的网卡驱动体系中,有一个数据结构(struct eth_device)用来表示(封装)一个网卡的所有信息,系统中注册一个网卡时就是要建立一个这个结构体的实例,然后填充这个实例中的各个元素,最后将这个结构体实例加入到eth_devices这个链表上,就完成了注册。了解了这些之后,你就明白了网卡驱动在初始化时必须负责将自己注册到系统的网卡驱动体系中(其实就是把自己的eth_device结构体实例添加到eth_devices链表中)。如果你不做这个过程就会出现:网卡找不到的错误。
(4)分析当前的问题是:在305行判断eth_devices是否为NULL之前没有去做网卡驱动的注册,所以这里为NULL,所以打印出了“No ethernet found.”

2.12.23.2、DM9000驱动浏览
(1)想解决这个问题,就是要在305行之前去注册网卡驱动。注册网卡驱动的代码不能随便乱写,一定要遵守linux网卡驱动架构的要求。这一块的代码一般属于网卡驱动的一部分,像这里就在dm9000x.c中。
(2)dm9000x.c中的最后一个函数int dm9000_initialize(bd_t *bis),这个函数就是用来注册dm9000网卡驱动的。

2.12.24.3、问题修复
(1)根据之前分析uboot函数,发现前面有2个函数预留的可以用来放网卡初始化函数的,经过对比感觉board_eth_init函数稍微合适点,于是乎去添加。
2.12.24.4、对比和总结

2.12.23.uboot启动内核的移植

猜你喜欢

转载自blog.csdn.net/qq_40083589/article/details/84555352