其实Freescale的BSP移植文档已经将u-boot的移植步骤讲述的非常详细了,但为了以后方便查阅,还是按照自己的理解记录在这里。
获取源码
根据前一篇文章搭建好LTIB环境后就可以非常方便的导出u-boot源码了。切换到ltib目录,并运行如下指令:
./ltib -m prep -p u-boot
该指令执行需要一些时间,指令执行完成后Freescale维护的u-boot-2009.8就会出现在rpm/BUILD目录下。
为TQIMX6Q开发板创建相应的单板目录,可以参考sabresd相关的目录进行,下面是具体的步骤。
Step1. 创建board目录
创建board目录需要以下几步:
(1) 拷贝mx6q_sabresd目录为mx6q_tqimx6q,作为TQIMX6Q的board目录,指令如下:
cp -R board/freescale/mx6q_sabresd board/freescale/mx6q_tqimx6q
(2) 将该目录下mx6q_sabresd.c改名为mx6q_tqimx6q.c,指令如下:
cp board/freescale/mx6q_sabresd.c board/freescale/mx6q_tqimx6q.c
(3) 修改下该目录下的u-boot.lds文件,将该文件中的sabresd全部替换为tqimx6q。可以使用自己熟悉的文本编辑器完成该操作。
(4) 确认该目录下的Makefile文件。官方的Freescale的BSP移植手册所讲的,需要将Makefile中的sabresd替换为tqimx6q,但实际上Makefile使用的都是环境变量${board},所以不需要修改,如果您的Makefile有直接使用sabresd的话,需要替换为tqimx6q或者${board}。
至此,就完成了board目录的创建。
Step2. 创建单板配置文件
cp include/configs/mx6q_sabresd.h include/configs/mx6q_tqimx6q.h
Step3. 在u-boot根目录下的Makefile文件中添加配置项
-
mx6q_tqimx6q_config : unconfig
-
@$(MKCONFIG) $(@
:_config=) arm arm_cortexa8 mx6q_tqimx6q freescale mx6
Step4. 编译测试
-
export ARCH=arm
-
export CROSS_COMPILE=
/opt/freescale/usr/local/gcc
-4.6
.2-glibc
-2.13-linaro-multilib
-2011.12/fsl-linaro-toolchain/bin/arm-none-linux-gnueabi-
-
make mx6q_tqimx6q_config
-
make -j8
如果以上步骤没有出错的话,此处应该是可以编译通过的。
修改内存参数
不同的开发板的内存参数不同,为了适应这块开发板,需要需改board/freescale/flash-header.S中的DCD参数。要理解这些数据的含义,需要阅读I.MX6Q和内存芯片的用户手册,这里就不一一解释了,具体的修改如下:
-
... ...
-
#else
/* i.MX6Q */
-
dcd_hdr:
.word 0
x40a002D2
/* Tag=0xD2, Len=83*8 + 4 + 4, Ver=0x40 */
-
write_dcd_cmd:
.word 0
x049c02CC
/* Tag=0xCC, Len=83*8 + 4, Param=0x04 */
-
-
/* DCD */
-
-
-
MXC_DCD_ITEM(1,
IOMUXC_BASE_ADDR + 0
x798, 0
x000C0000)
-
MXC_DCD_ITEM(2,
IOMUXC_BASE_ADDR + 0
x758, 0
x00000000)
-
-
MXC_DCD_ITEM(3,
IOMUXC_BASE_ADDR + 0
x588, 0
x00000030)
-
MXC_DCD_ITEM(4,
IOMUXC_BASE_ADDR + 0
x594, 0
x00000030)
-
-
MXC_DCD_ITEM(5,
IOMUXC_BASE_ADDR + 0
x56c, 0
x00000030)
-
MXC_DCD_ITEM(6,
IOMUXC_BASE_ADDR + 0
x578, 0
x00000030)
-
MXC_DCD_ITEM(7,
IOMUXC_BASE_ADDR + 0
x74c, 0
x00000030)
-
-
MXC_DCD_ITEM(8,
IOMUXC_BASE_ADDR + 0
x57c, 0
x00000030)
-
-
MXC_DCD_ITEM(9,
IOMUXC_BASE_ADDR + 0
x58c, 0
x00000000)
-
MXC_DCD_ITEM(10,
IOMUXC_BASE_ADDR + 0
x59c, 0
x00000030)
-
MXC_DCD_ITEM(11,
IOMUXC_BASE_ADDR + 0
x5a0, 0
x00000030)
-
MXC_DCD_ITEM(12,
IOMUXC_BASE_ADDR + 0
x78c, 0
x00000030)
-
-
MXC_DCD_ITEM(13,
IOMUXC_BASE_ADDR + 0
x750, 0
x00020000)
-
-
MXC_DCD_ITEM(14,
IOMUXC_BASE_ADDR + 0
x5a8, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(15,
IOMUXC_BASE_ADDR + 0
x5b0, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(16,
IOMUXC_BASE_ADDR + 0
x524, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(17,
IOMUXC_BASE_ADDR + 0
x51c, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(18,
IOMUXC_BASE_ADDR + 0
x518, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(19,
IOMUXC_BASE_ADDR + 0
x50c, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(20,
IOMUXC_BASE_ADDR + 0
x5b8, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(21,
IOMUXC_BASE_ADDR + 0
x5c0, 0
x00000018)
/* 00000030 */
-
-
MXC_DCD_ITEM(22,
IOMUXC_BASE_ADDR + 0
x774, 0
x00020000)
-
-
MXC_DCD_ITEM(23,
IOMUXC_BASE_ADDR + 0
x784, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(24,
IOMUXC_BASE_ADDR + 0
x788, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(25,
IOMUXC_BASE_ADDR + 0
x794, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(26,
IOMUXC_BASE_ADDR + 0
x79c, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(27,
IOMUXC_BASE_ADDR + 0
x7a0, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(28,
IOMUXC_BASE_ADDR + 0
x7a4, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(29,
IOMUXC_BASE_ADDR + 0
x7a8, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(30,
IOMUXC_BASE_ADDR + 0
x748, 0
x00000018)
/* 00000030 */
-
-
MXC_DCD_ITEM(31,
IOMUXC_BASE_ADDR + 0
x5ac, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(32,
IOMUXC_BASE_ADDR + 0
x5b4, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(33,
IOMUXC_BASE_ADDR + 0
x528, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(34,
IOMUXC_BASE_ADDR + 0
x520, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(35,
IOMUXC_BASE_ADDR + 0
x514, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(36,
IOMUXC_BASE_ADDR + 0
x510, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(37,
IOMUXC_BASE_ADDR + 0
x5bc, 0
x00000018)
/* 00000030 */
-
MXC_DCD_ITEM(38,
IOMUXC_BASE_ADDR + 0
x5c4, 0
x00000018)
/* 00000030 */
-
-
MXC_DCD_ITEM(39,
MMDC_P0_BASE_ADDR + 0
x800, 0
xA1390003)
-
-
MXC_DCD_ITEM(40,
MMDC_P0_BASE_ADDR + 0
x80c, 0
x001F001F)
-
MXC_DCD_ITEM(41,
MMDC_P0_BASE_ADDR + 0
x810, 0
x001F001F)
-
MXC_DCD_ITEM(42,
MMDC_P1_BASE_ADDR + 0
x80c, 0
x001F001F)
-
MXC_DCD_ITEM(43,
MMDC_P1_BASE_ADDR + 0
x810, 0
x001F001F)
-
-
MXC_DCD_ITEM(44,
MMDC_P0_BASE_ADDR + 0
x83c, 0
x4333033F)
-
MXC_DCD_ITEM(45,
MMDC_P0_BASE_ADDR + 0
x840, 0
x032C031D)
-
MXC_DCD_ITEM(46,
MMDC_P1_BASE_ADDR + 0
x83c, 0
x43200332)
-
MXC_DCD_ITEM(47,
MMDC_P1_BASE_ADDR + 0
x840, 0
x031A026A)
-
MXC_DCD_ITEM(48,
MMDC_P0_BASE_ADDR + 0
x848, 0
x4D464746)
-
MXC_DCD_ITEM(49,
MMDC_P1_BASE_ADDR + 0
x848, 0
x47453F4D)
-
MXC_DCD_ITEM(50,
MMDC_P0_BASE_ADDR + 0
x850, 0
x3E434440)
-
MXC_DCD_ITEM(51,
MMDC_P1_BASE_ADDR + 0
x850, 0
x47384839)
-
-
MXC_DCD_ITEM(52,
MMDC_P0_BASE_ADDR + 0
x81c, 0
x33333333)
-
MXC_DCD_ITEM(53,
MMDC_P0_BASE_ADDR + 0
x820, 0
x33333333)
-
MXC_DCD_ITEM(54,
MMDC_P0_BASE_ADDR + 0
x824, 0
x33333333)
-
MXC_DCD_ITEM(55,
MMDC_P0_BASE_ADDR + 0
x828, 0
x33333333)
-
MXC_DCD_ITEM(56,
MMDC_P1_BASE_ADDR + 0
x81c, 0
x33333333)
-
MXC_DCD_ITEM(57,
MMDC_P1_BASE_ADDR + 0
x820, 0
x33333333)
-
MXC_DCD_ITEM(58,
MMDC_P1_BASE_ADDR + 0
x824, 0
x33333333)
-
MXC_DCD_ITEM(59,
MMDC_P1_BASE_ADDR + 0
x828, 0
x33333333)
-
-
MXC_DCD_ITEM(60,
MMDC_P0_BASE_ADDR + 0
x8b8, 0
x00000800)
-
MXC_DCD_ITEM(61,
MMDC_P1_BASE_ADDR + 0
x8b8, 0
x00000800)
-
-
MXC_DCD_ITEM(62,
MMDC_P0_BASE_ADDR + 0
x004, 0
x00020036)
-
MXC_DCD_ITEM(63,
MMDC_P0_BASE_ADDR + 0
x008, 0
x09444040)
-
MXC_DCD_ITEM(64,
MMDC_P0_BASE_ADDR + 0
x00c, 0
x8A8F7955)
/* 555A7975 */
-
MXC_DCD_ITEM(65,
MMDC_P0_BASE_ADDR + 0
x010, 0
xFF328F64)
/* FF328F64 */
-
MXC_DCD_ITEM(66,
MMDC_P0_BASE_ADDR + 0
x014, 0
x01FF00DB)
-
MXC_DCD_ITEM(67,
MMDC_P0_BASE_ADDR + 0
x018, 0
x00001740)
-
-
MXC_DCD_ITEM(68,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x00008000)
-
MXC_DCD_ITEM(69,
MMDC_P0_BASE_ADDR + 0
x02c, 0
x000026D2)
-
MXC_DCD_ITEM(70,
MMDC_P0_BASE_ADDR + 0
x030, 0
x008F1023)
/* 005A1023 */
-
MXC_DCD_ITEM(71,
MMDC_P0_BASE_ADDR + 0
x040, 0
x00000047)
/* 00000027 */
-
-
MXC_DCD_ITEM(72,
MMDC_P0_BASE_ADDR + 0
x000, 0
x841A0000)
/* 831A0000 */
-
-
MXC_DCD_ITEM(73,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x04088032)
-
MXC_DCD_ITEM(74,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x00008033)
-
MXC_DCD_ITEM(75,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x00048031)
-
MXC_DCD_ITEM(76,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x09408030)
-
MXC_DCD_ITEM(77,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x04008040)
-
-
MXC_DCD_ITEM(78,
MMDC_P0_BASE_ADDR + 0
x020, 0
x00005800)
-
-
MXC_DCD_ITEM(79,
MMDC_P0_BASE_ADDR + 0
x818, 0
x00011117)
-
MXC_DCD_ITEM(80,
MMDC_P1_BASE_ADDR + 0
x818, 0
x00011117)
-
-
MXC_DCD_ITEM(81,
MMDC_P0_BASE_ADDR + 0
x004, 0
x00025576)
-
MXC_DCD_ITEM(82,
MMDC_P0_BASE_ADDR + 0
x404, 0
x00011006)
-
MXC_DCD_ITEM(83,
MMDC_P0_BASE_ADDR + 0
x01c, 0
x00000000)
-
-
#endif
-
... ...
修改串口管脚配置
查看TQIMX6Q的原理图可知,TQIMX6Q的UART1的TXD和RXD分别接到了MX6Q_PAD_SD3_DAT7__UART1_TXD和MX6Q_PAD_SD3_DAT6__UART1_RXD两个管脚上,详细如下图:
故,需要修改board/freescale/mx6q_tqimx6q.c中的setup_uart函数,具体的修改如下:
-
static void setup_uart(void)
-
{
-
#if defined CONFIG_MX6Q
-
#if 0
-
/* UART1 TXD */
-
mxc_iomux_v3_setup_pad(MX6Q_PAD_CSI0_DAT10__UART1_TXD);
-
-
/* UART1 RXD */
-
mxc_iomux_v3_setup_pad(MX6Q_PAD_CSI0_DAT11__UART1_RXD);
-
#else
-
/* UART1 TXD */
-
mxc_iomux_v3_setup_pad(MX6Q_PAD_SD3_DAT7__UART1_TXD);
-
// /* UART1 RXD */
-
mxc_iomux_v3_setup_pad(MX6Q_PAD_SD3_DAT6__UART1_RXD);
-
#endif
-
#elif defined CONFIG_MX6DL
-
/* UART1 TXD */
-
mxc_iomux_v3_setup_pad(MX6DL_PAD_CSI0_DAT10__UART1_TXD);
-
-
/* UART1 RXD */
-
mxc_iomux_v3_setup_pad(MX6DL_PAD_CSI0_DAT11__UART1_RXD);
-
#endif
-
}
串口引脚复用冲突
逻辑上讲,按照上面的移植步骤修改之后u-boot就可以正常启动了,但是,实际运行时发现u-boot的log运行到MMC初始化之后就没有了,肯定MMC配置时某些引脚与UART的引脚冲突了。仔细查看原理图及IMX6Q芯片手册可以发现UART1和UART2的TXD和RXD与usdhc3_pads的管脚是复用的,因此,MMC初始化时不能将UART相关的引脚初始化为MMC相关引脚,因此,需要修改board/freescale/mx6q_tqimx6q.c中usdhc3_pads内的引脚信息,具体修改如下:
-
iomux_v3_cfg_t usdhc3_pads[] = {
-
MX6Q_PAD_SD3_CLK__USDHC3_CLK,
-
MX6Q_PAD_SD3_CMD__USDHC3_CMD,
-
MX6Q_PAD_SD3_DAT0__USDHC3_DAT0,
-
MX6Q_PAD_SD3_DAT1__USDHC3_DAT1,
-
MX6Q_PAD_SD3_DAT2__USDHC3_DAT2,
-
MX6Q_PAD_SD3_DAT3__USDHC3_DAT3,
-
// MX6Q_PAD_SD3_DAT4__USDHC3_DAT4,
-
// MX6Q_PAD_SD3_DAT5__USDHC3_DAT5,
-
// MX6Q_PAD_SD3_DAT6__USDHC3_DAT6,
-
// MX6Q_PAD_SD3_DAT7__USDHC3_DAT7,
-
};
实际上就是注释掉DAT4~DAT7这四行,由于这块开发板上没有引出usdhc3,所以不会影响开发板的其它功能。
至此,uboot就可以正常进入终端了。
烧写并运行
将SD卡通过读卡器连接到ubuntu上,确定SD卡对应的设备,本文实验时sd卡识别后对应的设备节点是/dev/sdb(如果不确定可以多拔插几次来确定到底是哪个设备)。值得注意的是正确的指定设备节点是非常重要的,如果指定错误就会破坏掉相应设备上的数据。本文以/dev/sdb为例,具体的设备节点需要修改为自己的,具体的指令如下:
-
sudo dd
if=u-boot.bin
of=
/dev/sdb bs=
512 seek=
2 skip=
2
-
sync
这样就将刚编译好的u-boot.bin(位于uboot的根目录下)烧写到的SD。将烧写有u-boot.bin的SD插到TQIMX6Q开发板,并根据TQIMX6Q的用户手册将拨码开关拨到SD卡启动(拨码状态1000),然后给开发板上电,就可以从串口上看到如下Log信息:
-
U-Boot
2009.08-dirty (
3��月
22
2015 -
00:
53:
32)
-
-
CPU: Freescale i.MX6 family TO1
.2 at
792 MHz
-
Thermal sensor
with ratio =
174
-
Temperature:
23 C, calibration data
0x54e4bb69
-
mx6q pll1:
792MHz
-
mx6q pll2:
528MHz
-
mx6q pll3:
480MHz
-
mx6q pll8:
50MHz
-
ipg clock :
66000000Hz
-
ipg per clock :
66000000Hz
-
uart clock :
80000000Hz
-
cspi clock :
60000000Hz
-
ahb clock :
132000000Hz
-
axi clock :
264000000Hz
-
emi_slow clock:
132000000Hz
-
ddr clock :
528000000Hz
-
usdhc1 clock :
198000000Hz
-
usdhc2 clock :
198000000Hz
-
usdhc3 clock :
198000000Hz
-
usdhc4 clock :
198000000Hz
-
nfc clock :
24000000Hz
-
Board: i.MX6Q-SABRESD: unknown-board Board:
0x63012 [POR ]
-
Boot Device: SD
-
I2C: ready
-
DRAM:
1 GB
-
MMC: FSL_USDHC:
0,FSL_USDHC:
1,FSL_USDHC:
2,FSL_USDHC:
3
-
*** Warning - bad CRC
or MMC,
using
default environment
-
-
In: serial
-
Out: serial
-
Err: serial
-
Net: got MAC address
from IIM:
00:
00:
00:
00:
00:
00
-
FEC0 [PRIME]
-
Hit any
key
to
stop autoboot:
0
-
MX6Q SABRESD U-Boot >
到这里,u-boot的初步移植就算完成了,在接下来文章中,本人将尝试使用移植好的u-boot来启动内核,如果发现问题再来解决问题。
烧写位置的解释
刚开始移植u-boot的时候不确定u-boot.bin应该如何烧写,虽然BSP的文档中介绍了上面的烧写指令,但当时烧写u-boot之后窗口上看不到任何输出,完全不确定u-boot是不是烧写正确,然后就仔细研究了下IMX6Q的芯片手册和flash-header.S的代码,其实为什么这样烧写是可以在手册中找到依据的,u-boot代码也是按照手册中讲述的方式编写的,下面稍作分析。
IMX6Q手册的7.6节讲述了Program Image相关的格式问题,其中,第471页有如下表:
图中标记的部分就是SD卡烧写时为什么要跳过开始的1Kbyte空间。而u-boot的flash-header.S的开头有如下指令:
-
.section "
.text
.flasheader", "
x"
-
b _
start
-
.org
CONFIG_FLASH_HEADER_OFFSET
也就是说,u-boot的第一条指令是b _start,然后接了条.org指令,该指令的英文解释如下:
Following code is inserted at the start of the section plus new-lc .
也就是说,这条之后的代码会放到从当前段(.text.falshheader)开始偏移CONFIG_FLASH_HEADER_OFFSET的位置,并把当前指令之后到OFFSET之前的空间用0填充。而该宏在include/configs/mx6q_tqimx6q.h中定义为0x400,且flash-header会被链接到u-boot.bin的最开头部分(u-boot.lds),所以,Image Vector Table实际上就放到了u-boot.bin开头偏移1Kbyte的位置,跟IMX6Q手册上一致的。理解了这些,就可以理解为什么使用上面的烧写指令了。另外,这个u-boot代码也是可以从开头执行的,但是从开头执行的话会直接跳转到_start,而不会根据Image Vector Table中的DCD配置IMX6Q的各模块。
调试方法
说起调试,最方便的方法恐怕就是使用仿真器了,但是仿真器的价格一般都比较贵,因此,更实用的调试手段是打印log。但是,有些情况下使用串口打印Log的方式也不好用,就如本文遇到的,这种情况下,可以使用开发板上的Led灯来确定u-boot执行到了什么位置。我在调试的时候就使用了这种方式,下面是我根据TQIMX6Q开发板的原理图编写了Led闪动程序:
-
void leds_pika()
-
{
-
struct gpio_control *gpio = (struct gpio_control *)(0x020a4000);
-
gpio->gdir |= (
1 <<
21) | (
1 <<
22) | (
1 <<
23);
-
gpio->imr &= ~((
1 <<
21) | (
1 <<
22) | (
1 <<
23));
-
-
unsigned
long *mux21 = (
unsigned
long *)
0x020E00A4;
-
unsigned
long *mux22 = (
unsigned
long *)
0x020E00A8;
-
unsigned
long *mux23 = (
unsigned
long *)
0x020E00Ac;
-
-
*mux21 =
0x5;
-
*mux22 =
0x5;
-
*mux23 =
0x5;
-
-
-
volatile
int i =
0, j =
0, k =
0;
-
for (i =
0; i !=
20; ++ i) {
-
for (j =
0; j !=
0xFFFF; ++j) {
-
gpio->dr &= ~((
1<<
21)|(
1<<
22)|(
1<<
23));
-
}
-
for (j =
0; j !=
0xFFFF; ++j) {
-
gpio->dr |= (
1<<
21)|(
1<<
22)|(
1<<
23);
-
}
-
}
-
}
查看下TQIMX6Q的原理图和IMX6Q芯片手册中IMUXC和GPIO相关的内容就可以写出如上函数,我将这个函数写在了lib_arm/board.c文件中,用来确定u-boot运行到了start_armboot中的哪个位置,还是很实用的,这里分享给大家。
至此,就完成了u-boot在TQIMX6Q上的移植,在这块开发板上移植好之后我再在TQE9上移植一遍。如有问题,欢迎留言讨论,有时间的话我会尽力回答的。
本文作者:girlkoo