十四.linux开发之uboot移植(十四)——从UBOOT官方下载uboot移植详解

版权声明:本文为博主原创文章,允许转载请注明。谢谢! https://blog.csdn.net/wangweijundeqq/article/details/82390587

官方uboot2013.10版本的移植流程图(包括有道云笔记地址)

http://note.youdao.com/noteshare?id=d78fd7030db099c8a1bed0f6b948bf75&sub=1287D105BC284AD887BC4E2A2A097C87

  1. uboot官方版本下载地址:ftp://ftp.denx.de/pub/u-boot/
  2. uboot下载版本:u-boot-2013.10.tar.bz2

一.选择官方原版uboot

1、官方原版uboot的版本

(1)版本号。刚开始是1.3.4,后来变成2009.08

(2)新版和旧版的差别。uboot的架构很早就定下来了,然后里面普遍公用的东西(common目录下、drivers目录下、fs目录下等···)在各个版本之间几乎是完全一样的。差别最大的是board和cpu目录,这两个目录正是单板(开发板)相关的。越新的uboot版本支持越多的开发板(CPU),所以越新的uboot越庞大。

(3)我们选择uboot 2013.10版本进行实验移植

2、注意:实践工作中一般是从SoC厂商的uboot出发移植的

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

3.先初步浏览官方原版uboot

前提:使用SourceInsight 建立工程及添加文件

工程项目文件和工程中管理的源代码文件目录可以不同,但是我一般习惯放在一起。放在:E:\Linux\winshare\u-boot-2013.10\u-boot-2013.10\SI_Proj

SI_Proj是自己建的工程目录

4、主Makefile浏览及boards.cfg文件

(1)2013.10版本的uboot的Makefile中使用了boards.cfg文件,因此在配置uboot时make xxx_config,这个xxx要到boards.cfg文件中查找。

(2)其实就相当于把以前的版本的uboot中各种开发板的配置部分规则抽离出来写到了Makefile中,然后把配置信息部分写到了一个独立文件boards.cfg。

(1)参照物开发板为:55p_goni

(2)配置对应的cpu、board文件夹分别为:

cpu: u-boot-2013.10\arch\arm\cpu\armv7

board: u-boot-2013.10\board\samsung\goni

拷贝UBOOT官方修改后的u-boot-2013.10.zip到ubuntu

使用tar命令解压包出现问题

原因是因为对只有一个压缩内容的文件来解压的时候才用tar, 而如果压缩包里有多个文件被压缩了,tar命令不能继续工作。可以采用unzip命令去解压。

使用命令:unzip u-boot-2013.10.zip ,解压成功

二、主mkconfig脚本分析

1、脚本功能浏览

(1)首先我们在命令行配置uboot时,是:make s5p_goni_config,对应Makefile中的一个目标。其中 make s5p_goni_config是 s5pc1xx这个单板的一个目标名称

要针对某个单板进行配置,需要执行: makeNAME_config,其中 NAME 在 boards.cfg 中列出。

(2)新版本的Makefile中:

%_config:: unconfig

@$(MKCONFIG) -A $(@:_config=)

从这里分析得出结论,实际配置时是调用mkconfig脚本,然后传参2个:-A和s5p_goni

(3)

到了mkconfig脚本中,如果参数个数等于 2,而且第 1 个参数等于“ -A”,则执行:

line=`awk '($0 !~ /^#/ && $7 ~ /^'"$2"'$/) { print $1, $2, $3, $4, $5, $6, $7, $8 }' $srctree/boards.cfg`

awk 是一个非常强大的文本处理工具, $srctree 被替换为 u-boot-2014.04,即 u-boot 源码树目录, awk

会读取 u-boot-2014.04/boards.cfg 中的每一行,如果和前面的表达式($0 !~ /^#/ && $7 ~ /^'"$2"'$/)相匹配,

则执行{ print $1, $2, $3, $4, $5, $6, $7, $8 }

boards.cfg 保存了各种单板的相关信息,其格式为:

Active  arm         armv7          s5pc1xx     samsung    smdkc100    smdkc100          -              Minkyu Kang           <[email protected]>

awk 在执行过程中, $0 代表当前整行, 同时将第一个字段存入$1,将第 2 个字段存入$2, 依此类推,

awk 缺省按空格分段,可以通过-F 指定分隔符。

若果当前行不以#开头,而且第 7 个字段和 mkconfig 传进来的第 2 个参数( s5p_goni) 相等,则分别将字段 1~字段 8 输出到 line 中保存,最终得到:

line= Active    arm   armv7    s5pc1xx    samsung    goni  s5p_goni   –

(4)注意在解析完boards.cfg之后,$1到$8就有了新的值。

$#=8

$1=Active          状态

$2=arm              架构

$3=armv7          CPU

$4=s5pc1xx       SOC

$5=samsung      厂商

$6=goni     单板名称

$7=s5p_goni   配置目标

$8=-                    选项

(5)

${7%_config}表示去掉${7}的后缀_config,这里${7}=s5p_goni ,得到

CONFIG_NAME=s5p_goni

BOARD_NAME=s5p_goni

2、几个传参和其含义

(1)几个很重要的变量

arch=arm    体系架构

cpu=armv7   cpu 类型

vendor=samsung 厂商名称

soc=s5pc1xx     片上系统

然后在ubuntu中,make s5p_goni_config

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文件。

4、Makefile中添加交叉编译工具链

(1)官方原版的uboot中CROSS_COMPLIE是没有定义的,需要自己去定义。如果没定义就直接去编译,就会用gcc编译。

(2)在Makefile中添加一行:

CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-

5、配置编译测试

(1)编译过程:

make distclean

make s5p_goni_config

make

(2)结果:得到u-boot.bin即可

三.先解决官方版本uboot的烧录运行

1.在linux下用烧录脚本来烧录(实质是dd命令进行sd卡扇区写入)

(1)移植原来的版本的uboot中的sd_fusing文件夹到官方uboot版本中,使用这个文件夹中的sd_fusing.sh脚本来进行烧录。烧录启动开发板,串口显示如下

2、分析:为什么烧录运行不正确?

(1)串口接串口2,串口有输出。但是这个串口输出不是uboot输出的,而是内部iROM中的BL0运行时输出的。

(2)输出错误信息分析:

第一个SD checksum Error:是第一顺序启动设备SD0(iNand)启动时校验和失败打印出来的;

第二个SD checksum Error:是第二顺序启动设备SD2(外部SD卡)启动时校验和失败打印出来的;

剩下的是串口启动和usb启动的东西,可以不管。

总结:从两个SD checksum Error,可以看出:外部SD卡校验和失败了。

分析:SD卡烧录出错了,导致SD卡校验和会失败

3、解决方案分析

(1)为什么SD卡烧录会出错?可能原因:烧录方法错误、烧录原材料错误。

(2)经过分析,sd_fusing这个文件夹下的mkbl1这个程序肯定没错,上一层目录下的u-boot.bin是存在的,校验和失败不失败和u-boot.bin无关。

(3)经过分析和查找,发现是mkbl1程序和start.S中前16个字节校验和的处理上面不匹配造成的,解决方法是在start.S最前面加上16个字节的占位。(查看之前移植三星uboot文件中的start.S中是有16个字节的占位的这里拷贝到我们的源码中即可

4、代码实践

(1)重新编译烧录运行(还是使用老办法,在windows中的Source iN.中修改,在linux下新建一个cp.sh脚本拷贝编译即可,

发现结果只显示一个SD checksum Error。

这一个就是内部SD0通道的inand启动校验和失败打印出来的。剩下的没有了说明外部SD卡校验和成功了,只是SD卡上的uboot是错误的,没有串口输出内容,所以没有输出了。

四.start.S文件分析与移植1

1.start.S流程分析

(1)#define CONFIG_SYS_TEXT_BASE  0x34800000  可以看出我们的uboot的连接地址是在0x34800000位置。

(2)cpu_init_cp15这个函数功能是设置MMU、cache等。这个版本的uboot中未使用虚拟地址,因此MMU在这里直接关掉。

(3)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、添加开发板制锁和串口打印字符"O"

(1)我们为了调试uboot的第一阶段,就要看到现象。为了看到现象,我们向lowlevel_init函数中添加2个代码,一个是开发板制锁,一个是串口打印"O"

(2)这两段代码可以直接从ARM裸机全集课程中的代码中来。

(3)实践添加。

3、实践结果及分析

(1)实验结果是:没看到开发板制锁,串口也没有输出任何东西。实验失败。

(2)结论:因为开发板制锁没有成功,所以我们判定,在开发板制锁代码运行之前uboot就已经挂掉了。下面就是去跟踪代码运行,然后判定问题点再去解决。

五.start.S文件分析与移植2

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)经过判断开发板LED的闪速情况,即只亮了两个LED,即

我们发现:start.S中工作一切正常,但是函数一旦放到lowlevel_init.S中就完全不工作了。通过分析得出结论:b lowlevel_init这句代码出了问题。

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重复定义了。

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文件的处理技巧,解决了这个问题。

4、实践验证。

结果是开发板制锁和串口输出'O'都成功了。

六.添加DDR初始化

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、分析DDR初始化代码移植思路

(1)如果本来uboot中有DDR初始化代码,那我们可以就着这些代码来修改。但是问题是这个uboot2013.10中根本没有DDR初始化,所以我们需要完全从头去另外添加DDR初始化代码。

(2)我们的思路就是从三星版本的uboot中直接移植DDR初始化代码过来即可。三星版本的uboot中DDR初始化函数在cpu/s5pc11x/s5pc110/cpu_init.S文件中,直接将这个文件移植过来即可。

3、动手移植

(1)添加cpu_init.S文件到uboot2013.10中。放在E:\Linux\winshare\u-boot-2013.10\u-boot-2013.10\board\samsung\goni

注意,这里的代码必须保证在前8kb内,所以必须和lowlevel_init.S文件一样的链接处理。

主要是在board/samsung/goni/Makefile中和arch/arm/cpu/u-boot.lds文件中做修改添加。

(2)分析添加的cpu_init.S,可知,没有包含#include <s5pc110.h>

将上节三星的头文件s5pc110.h添加到include目录下。

即E:\Linux\winshare\u-boot-2013.10\u-boot-2013.10\include

(3)对cpu_init.S文件代码进行修整,把一些无用的代码去掉,把一些相关的条件编译人工处理一下。

(4)在SourceInsigt工程中添加入这两个文件。然后重新解析一遍。然后对新添加的代码进行分析修整,把里面一些明显的宏定义缺失给补上。

4、移植必要的宏定义(对cpu_init.S文件代码进行修整)

(1)DDR配置参数,从三星版本的smdkv210single.h中复制到s5p_goni.h中。

(2)s5pc110.h中进行修整。

思路就是:先看该工程文件中黑色函数或者宏定义直接注释掉,先不管,后面如果报错再仔细排查,不报错就说明与显示黑色的无关!如果太大不好注释,就直接删除,后面需要的话,再添加。

往下继续看:发现,引用了大量这个__REG这个宏,(虽然显示绿色)而该文件中开头却没有包含文件,说明这里工程其他文件中的该定义并不是我们需要的。

故从上节三星的工程追踪添加该宏

#define __REG(x) (*(vu_long *)(x))

#define __REGl(x) (*(vu_long *)(x))

#define __REGw(x) (*(vu_short *)(x))

#define __REGb(x) (*(vu_char *)(x))

#define __REG2(x,y) (*(vu_long *)((x) + (y)))

5、代码同步、编译成功

cp.sh脚本中添加同步命令

6、添加调试信息,验证DDR初始化完成。

(1)调试信息有LED点亮和串口输出两种。优先选用串口调试的方法。

(2)在DDR初始化完成后,添加串口输出字符"K",这样启动时如果看到了"OK"就说明DDR已经被成功初始化了。

在lowlevel_init.S中添加一下代码

(3)结果:看到了"OK"标志,说明DDR添加实验成功。

七.添加uboot第二阶段重定位

1、在重定位代码前加调试信息定位

(1)逻辑上来说,重定位部分代码应该在DDR初始化之后和uboot第二阶段来临前之间。

(2)uboot的第一阶段和第二阶段的划分并不是绝对的,唯一必须遵循的原则就是第一阶段不能大于8KB。所以uboot的第一阶段最少要完成DDR初始化和重定位,最多不能超过8KB。在满足这些条件时,第一阶段和第二阶段的接点可以随便挑。

(3)找到合适的地方来写重定位代码,重定位之后远跳转到第二阶段的入口。

2、重定位代码移植(参考上节三星的重定位代码,选择性cp 修改)

3、清bss段移植

4、movi_bl2_copy函数移植

(1)从三星版本的uboot中复制movi.c和movi.h到uboot2013.10中。

movi.c复制到\board\samsung\goni目录下

movi.h复制到include/目录下

(2)改makefile和u-boot.lds。

5.添加uboot第二阶段重定位2

1、_mian函数中基本处理

(1)主要就是把里面的重定位代码部分给删除掉。剩下就是:设置栈、调用board_init_f函数和board_init_r函数。

2、代码同步及编译

(1)主要是crt0.S和movi.c。

在我们三星的uboot中CFG_PHY_UBOOT_BASE就等于0x33e00000

而官方uboot的值如下:

/* Text Base */

#define CONFIG_SYS_TEXT_BASE  0x34800000

即CFG_PHY_UBOOT_BASE就等价于CONFIG_SYS_TEXT_BASE

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中有一个检查重定位的规则,屏蔽掉这个规则后编译连接成功。

4、结果验证及下阶段展望

(1)看到了uboot启动打印出来的一系列信息,但是uboot没有进入命令行。

(2)这说明uboot中的DDR初始化和重定位功能都已经完美实现,后面就是第二阶段的继续移植了。

八.CPU时钟信息显示移植

 

1.时钟信息函数print_cpuinfo函数

跟踪进去,修改为如下;

继续跟踪代码1

继续跟踪代码2

samsung_get_base_clock();相当于 调用的#define S5PC110_CLOCK_BASE 0xE0100000

以上,就是我们需要分析的时钟有关代码

 

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中添加上时钟初始化的代码即可。(添加的方法就是从三星的uboot版本中选择性移植时钟代码)

2、时钟初始化函数的添加

(1)在上节三星的uboot版本中的lowlevel_init.S中移植system_clock_init函数,并且在本版本中的s5p_goni.h文件中添加相关的宏定义参数,然后在lowlevel_init.S文件中中调用system_clock_init函数。

最后,同步编译代码

报错:internal_relocation (type: OFFSET_IMM) not fixed up

原因是没有包含头文件,在lowlevel_init.S中添加#include <s5pc110.h>,即编译成功,下载到sd卡启动开发板

3、总结

时钟代码的移植还是参考了三星版本的uboot,通过分析三星时钟代码,从时钟初始化代码开始,选择性移植,最主要的调试方法还是通过串口打印调试信息。

八.board和DDR配置显示移植

1、board名称更改

2、DDR配置值修改

从上一直往下分析到这里,才需要更改。

3、MACH_TYPE定义

4、DDR打印信息更改

至此DDR的修改后,board_init_f函数就完结了

5、同步代码、编译烧录代码,启动开发板 显示如下

 

1、关于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、去掉oneNand支持

2.1去掉该宏定义后,从新同步编译会报错如下:

解决方法就是:我们本身就不需要env_onenand.c的编译,故想办法从编译中排除掉

查看相关的Makefile ,我们只需要屏蔽掉CONFIG_ENV_IS_IN_ONENAND这个宏即可

该宏在S5p_goni.h中,我们直接将与onenand相关的这几个宏全部注释,再根据报错修改源码

2.2继续同步编译,报错没有ENV环境变量

添加ENV环境变量  #define CONFIG_ENV_SIZE CFG_ENV_SIZE    //0x4000

3.添加SD/MMC支持

3.1继续同步编译,报错,意思是没有定义 CONFIG_ENV_IS_IN这个环境变量

到底指向的是谁,我们当然是MMC

添加ENV环境变量指定到MMC   #define  CONFIG_ENV_IS_IN_MMC     1

3.2续同步编译,报错,根据报错 在env_mmc.c文件中没有定义CONFIG_SYS_MMC_ENV_DEV这个宏

添加该宏的定义:

根据上节三星MMC的分析,这个宏就是设备编号,0表示inand 1表示SD卡

续同步编译,报错,根据报错显示是onenand.c 我们不需要onenand,故找到对应的Makefile 屏蔽掉,不参加编译即可

onenand.c 在我们的\board\samsung\goni中,打开goni文件中的Makefile

3.3继续同步编译,编译通过 烧录启动

 至此,我们通过去掉oneNand支持的源码,随着编译报错修改这个方向

一直到添加SD/MMC支持到这,已经能启动命令行了

下面就从MMC的初始化代码开化寺分析

九.uboot2013.10中SD/MMC驱动浏览

1、从初始化代码开始浏览

drivers/mmc/mmc.c、

2、相关函数和文件

(1)board/samsung/goni/goni.c

arch/arm/include/asm/arch-s5pc1xx/mmc.h

drivers/mmc/S5p_sdhci.c

(2)对当前的board/samsung/goni/goni.c文件中的board_mmc_init函数中的通道口2进行修改,因为原函数的通道2源码是找不到的。所以串口MMC信息中没有通道2

屏蔽掉他的通道2的源码,直接将图中红色部分的源码添加到通道0的源码后面即可。

串口打印MMC信息这两个错误,就来自与mmc_start_init函数

3、当前错误路径定位及解决方案分析

(1)错误发生路径定位(一层一层的追踪)

board_init_r 函数            目录:(\arch\arm\lib\Board.c)

mmc_initialize            目录:(\drivers\mmc\Mmc.c)

do_preinit                 目录:(\drivers\mmc\Mmc.c)

mmc_start_init      目录:(\drivers\mmc\Mmc.c)

mmc_go_idle      目录:(\drivers\mmc\Mmc.c)

mmc_send_cmd   目录:(\drivers\mmc\Mmc.c)

                                sdhci_send_command 目录:(\drivers\mmc\Sdhci.c)

      

   

 

sdhci_transfer_data错误在这个函数中  目录:(\drivers\mmc\Sdhci.c)

(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中的驱动实现。

我们选择第二种方法,即移植原来三星版本的SD/MMC驱动替换掉本版本的MMC驱动

十.三星版本SD卡驱动移植到uboot官方版本

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、复制必要的文件并修改相应Makefile

(1)首先解决drivers/mmc目录下的文件替换。将三星版本下的drivers/mmc/mmc.c

drivers/mmc/sdhci.c

替换到uboot版本下

修改drivers\mmc下的Makefile,让uboot版本的mmc文件不再编译,替换的三星mmc文件添加进Makefile  实现编译

(2)修改初始化代码。

将\u-boot-samsung-dev\cpu\s5pc11x\文件下的setup_hsmmc.c复制到

\u-boot-2013.10\board\samsung\goni\文件下

将三星mmc初始化的代码复制到uboot2013版本中的mmc初始化处

在goni\文件中的Makefile中添加对setup_hsmmc.c编译的支持

3、代码浏览及修补

(1)按照代码运行时的流程来逐步浏览代码,看哪里需要修补。

从这里我们自己修改的MMC初始化开始,跟踪这几个函数,结合三星版本的uboot工程,查看黑色的(没有定义),把需要添加的一些定义或者函数cp过来,或者是找到uboot版本中提供的相同功能的函数等。

例如,进入setup_hsmmc_clock函数,发现get_MPLL_CLK()黑色,即在本工程中获取时钟频率的函数不是这个。

结合三星版本的工程,发现uboot版本的时钟Clock.c文件中有获取时钟频率的函数,将其修改即可。(如果在uboot版本中没有相同功能的函数,则需要采取的方法就是直接将三星官方的uboot对应函数全部cp过来,移植修改)

下面,按照这种方法,依次追踪分析MMC初始化的另外两个函数,发现没有黑色高亮,或者是有黑色但是分析三星版本的,发现都不需要更改。

即mmc初始化里面这3个函数就修改完成了。

4.继续修补驱动代码

(即从拷贝过去的mmc.c和s3c_hsmmc.csetup_hsmmc.c入手分析)

(1)、include/mmc.h

从mmc.c从上往下分析中,发现黑色没有定义,

跟踪原三星版本代码,可知来自mmc.h,故将三星的include/mmc.h替换到uboot版本

替换后,继续往下分析驱动文件mmc.c,发现都有定义了,即没有黑色的。

即mmc.c就暂时修补完了。

(2)、include/s3c_hsmmc.h

接下来分析s3c_hsmmc.c,同理从上往下分析中,发现黑色没有定义,

跟踪原三星版本代码,可知来自s3c_hsmmc.h,将三星的include/s3c_hsmmc.h添加到uboot版本的include/目录下,继续往下分析驱动文件mmc.c,发现都有定义了,即没有黑色的。

即s3c_hsmmc.h就暂时修补完了。

(3)、setup_hsmmc.c从上往下分析,发现没有问题。

(4)、同步及编译、问题解决

(1)出错1:cmd_mmc.c中出错。

原因是cmd_mmc.c和mmc驱动密切相关,所以改了驱动后这个实现文件也要跟着改,解决方法是从三星版本的直接将cmd_mmc.c文件复制过来替换,然后在cp.sh脚本中中同步该文件。

(2)修改后继续编译,出错2:drivers/mmc/mmc_write.c编译出错。

原因是这个文件和本来版本中的mmc.c文件相关,但是mmc.c被替换掉了所以这个文件编译报错。解决方案就是修改drivers/mmc/下的makefile去掉这个文件的依赖,让他不被编译。然后在cp.sh脚本中中同步该makefile。

(3)修改后继续编译,出错3:没有定义#include<regs.h>

解决方法:在s3c_hsmmc.csetup_hsmmc.c中都将#include<regs.h>注释掉,然后添加#include <s5pc110.h>

(4)修改后继续编译,出错4:setup_hsmmc.c没有添加Clk.h

解决方法:在setup_hsmmc.c中添加  #include <asm/arch/clk.h>

(注意看上面Clk.h的文件路径,是如何添加的)

然后同步编译,这次就没有问题了。

(5)、烧写到SD卡,启动开发板,效果测试

(1)读写测试均成功

 

(6)、解决每次编译时间都很长的问题。

(1)每次编译脚本cp.sh执行时都会先cp同步代码,然后make distclean···所以每次都会清空后从头编译,这就很费时间了。

(2)但是实际上有时候是不会make distclean的,只需要先cp然后直接make即可(当更改没有涉及到配置头文件s5p_goni.h,没有涉及到makefile文件,或者其他项目配置文件,也就是说我们的更改只是普通代码文件的更改时)

如何执行脚本时只make,不make distclean??

解决方法:在cp.sh脚本最后添加如下:

当我们只需要执行make时,执行cp.sh 脚本命令,在source cp.sh 后面随便添加一个参数即可。例如 source cp.sh ddd  ,则只会执行make命令。

十一.ENV环境变量的移植

1、iNand分区表检查-env究竟应该放在哪

(1)测试环境变量是否可以保存,通过开机set设置环境变量然后save,然后关机后重启来测试环境变量的保存是否成功。测试结果是成功的!

(2)我们的环境变量究竟保存到哪里去了?这个就要去分析代码中的分区表。

(3)环境变量应该被放在哪里?虽然无法确定ENV一定要放在哪里,但是有一些地方肯定是不能放的,否则将来会出问题。原则是同一个SD卡扇区只能放一种东西,不能叠加,否则就会被覆盖掉。

uboot烧录时使用的扇区数是:SD2的扇区1-16和49-x(x-49大于等于uboot的大小)

从sd_fusing.sh脚本中分析得到的。

(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个扇区。0x4000就是16k

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冲突了。BL1为扇区1-16

解决方案是改变这个CONFIG_ENV_OFFSET的值,将ENV写到别的空闲扇区去。

参考Movi.h里面三星官方对SD卡的分区方式

(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扇区起始的地方即可。

即将CONFIG_ENV_OFFSET这个宏定义修改为17*512即可,编译时会自动包含

修改好保存同步编译,下载到开发板。

3.环境变量的测试和配置移植

(1)、如何测试环境变量的保存是否正确

(1)程序修改重新编译后启动,启动后要注意iNand中本来有没有环境变量。为了保险起见对iNand的前49个扇区进行擦除,然后就可以确保里面没有之前保存过的环境变量了。

使用命令:mmc write 0 30000000 0# 49来擦除SD0的扇区0-48,保证以前的环境变量都没有了。

(2)重新开机后先set随便改一个环境变量作为标记然后save保存然后重启。例如设置bootdelay为5

(3)测试方法是,使用:mmc read 0 30000000 17# 32命令将iNand的17开始的32个扇区读出来到内存30000000处

然后md 0x30000000 100 查看。找到显示区域里面的各个环境变量,看读出来的和自己刚才修改的值是否一样。

可以看到bootdelay=5

(2)、常用环境变量的配置移植

uboot提供的环境变量看着太繁杂了,我们修改源码将不需要的环境变量全部注释掉

(1)我们将S5p_goni.h中的环境变量注释部分不需要的,再将我们三星官方uboot版本中的部分环境变量添加到本文件中

同步编译烧录后,我们需要先对iNand中ENV的17到32这个扇区范围进行擦除,然后就可以确保里面没有之前保存过的环境变量了。

使用命令mmc write 0 30000000 17# 32来擦除ENV的扇区17-32,保证以前的环境变量都没有了。

可以看出,与我们在S5p_goni.h中修改后的源码一致

到这里,环境变量的移植就完成了。

十二.网卡驱动的移植

1、添加网络支持

(1)uboot中对各种功能也是一个条件编译可以配置可以裁剪的设计(从linux内核学来的),默认情况下我们的uboot没有选择支持网络。

(2)在配置头文件中添加一行 #define CONFIG_CMD_NET

(3)添加了网络支持宏之后,在uboot初始化时就会执行eth_initialize函数,从而网络相关代码初始化就会被执行,将来网络就有可能能用。

2、添加ping和tftp命令

(1)在linux系统中网络底层驱动被上层应用调用的接口是socket,是一个典型的分层结构,底层和上层是完全被socket接口隔离的。

(2)但是在uboot中网络底层驱动和上层应用是黏在一起的,不分层。意思就是上层网络的每一个应用都是自己去调用底层驱动中的操作硬件的代码来实现的。

(3)uboot中有很多预先设计的需要用到网络的命令,和我们直接相关的就是ping和tftp这两个命令。这两个命令在uboot中也是需要用相应的宏开关来打开或者关闭的。

(4)使用搜索关键字(do_pingtftp)经过代码检查,发现ping命令开关宏为CONFIG_CMD_PING,而tftp命令的开关为CONFIG_CMD_NET,确认添加。

3、代码实践

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

4、移植网卡初始化代码

我们直接移植三星版本的uboot的网卡初始化函数到uboot版本中,以下是三星Smdkc110.c文件中的网卡初始化部分

添加到uboot中的board_init函数中,还需要添加网卡的一些参数

添加完成后,同步编译成功,烧录进去。还是和上面没有添加网卡初始化函数一样的结果,是什么原因呐??

5、实验现象分析

从eth_initialize函数开始分析,找到打印上面红色部分信息的地方

(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.”

6、DM9000驱动浏览

(1)想解决这个问题,就是要在305行之前去注册网卡驱动。注册网卡驱动的代码不能随便乱写,一定要遵守linux网卡驱动架构的要求。这一块的代码一般属于网卡驱动的一部分,像这里就在dm9000x.c中。

(2)dm9000x.c中的最后一个函数int dm9000_initialize(bd_t *bis),这个函数就是用来注册dm9000网卡驱动的。

7、问题修复

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

添加后,同步编译报错:错误如下:board_eth_init重复定义了。

解决方法:将board_eth_init函数的弱声明注释掉,从新同步编译,就可以了。

下载到开发板,看到如下,能ping通ubuntu,然后通过TFTP

  1. 使用tftp 0x30008000 zImage-qt;

  1. bootm 0x30008000

遇到的问题,内核启动失败,原因是该uboot的内核引导程序有问题,后面学习了内核后,再来处理这个问题。

通过对三星uboot和uboot官方这两个版本的移植,让我们对uboot的移植过程有了更深入的了解和掌握。

最后,可以得出:这里对uboot官方版本的移植是成功的。

猜你喜欢

转载自blog.csdn.net/wangweijundeqq/article/details/82390587