

《原子嵌入式Linux驱动开发详解》图书前半部分详细解读了Linux内核顶层Makefile和Linux内核的启动流程,本文我们就来学习如何将NXP官方提供的Linux内核移植到正点原子的I.MX6U-ALPHA开发板上。通过本文的学习,我们将掌握如何将半导体厂商提供的Linux BSP包移植到自己的平台上。
更多嵌入式Linux驱动开发好内容都在《原子嵌入式Linux驱动开发详解》图书中,扫描下图即可优惠购买。

长按识别扫码优惠购买
内容简介
创建VSCode工程
这里我们使用NXP官方提供的Linux源码,将其移植到正点原子I.MX6U-ALPHA开发板上。NXP官方原版Linux源码已经放到了开发板光盘中,路径为:1、例程源码->4、NXP官方原版Uboot和Linux->linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。使用FileZilla将其发送到Ubuntu中并解压,得到名为linux-imx-rel_imx_4.1.15_2.1.0_ga的目录,为了和NXP官方的名字区分,可以使用“mv”命令对其重命名,我这里将其重命名为“linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek”,命令如下:
mv linux-imx-rel_imx_4.1.15_2.1.0_ga linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
完成以后创建VSCode工程,步骤和Windows下一样,重点是.vscode/settings.json这个文件。
NXP官方开发板Linux内核编译
NXP提供的Linux源码肯定是可以在自己的I.MX6ULL EVK开发板上运行下去的,所以我们肯定是以I.MX6ULL EVK开发板为参考,然后将Linux内核移植到I.MX6U-ALPHA开发板上的。
1. 修改顶层Makefile
修改顶层Makefile,直接在顶层Makefile文件里面定义ARCH和CROSS_COMPILE这两个的变量值为arm和arm-linux-gnueabihf-,结果如图所示:

■ 图34-1 修改顶层Makefile
图34-1中第252和253行分别设置了ARCH和CROSS_COMPILE这两个变量的值,这样在编译的时候就不用输入很长的命令了。
2. 配置并编译Linux内核
和uboot一样,在编译Linux内核之前要先配置Linux内核。每个板子都有其对应的默认配置文件,这些默认配置文件保存在arch/arm/configs目录中。imx_v7_defconfig和imx_v7_mfg_defconfig都可作为I.MX6ULL EVK开发板所使用的默认配置文件。但是这里建议使用imx_v7_mfg_defconfig这个默认配置文件,首先此配置文件默认支持I.MX6UL这款芯片,而且重要的一点就是此文件编译出来的zImage可以通过NXP官方提供的MfgTool工具烧写!!imx_v7_mfg_defconfig中的“mfg”的意思就是MfgTool。
进入到Ubuntu中的Linux源码根目录下,执行如下命令配置Linux内核:
make clean //第一次编译Linux内核之前先清理一下
make imx_v7_mfg_defconfig //配置Linux内核
配置完成以后如图34-2所示:

■ 图34-2 配置Linux内核
配置完成以后就可以编译了,使用如下命令编译Linux内核:
make -j16 //编译Linux内核
等待编译完成,结果如图34-3所示:

■ 图34-3 Linux编译完成
Linux内核编译完成以后会在arch/arm/boot目录下生成zImage镜像文件,如果使用设备树的话还会在arch/arm/boot/dts目录下开发板对应的.dtb(设备树)文件,比如imx6ull-14x14-evk.dtb就是NXP官方的I.MX6ULL EVK开发板对应的设备树文件。至此我们得到两个文件:
1)、Linux内核镜像文件:zImage。
2)、NXP官方I.MX6ULL EVK开发板对应的设备树文件:imx6ull-14x14-evk.dtb。
3. Linux内核启动测试
在上一小节我们已经得到了NXP官方I.MX6ULL EVK开发板对应的zImage和imx6ull-14x14-evk.dtb这两个文件。这两个文件能不能在正点原子的I.MX6U-ALPHA EMMC版开发板上启动呢?测试一下不就知道了,在测试之前确保uboot中的环境变量bootargs内容如下:
console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw
将上一小节编译出来的zImage和imx6ull-14x14-evk.dtb复制到Ubuntu中的tftp目录下,因为我们要在uboot中使用tftp命令将其下载到开发板中,拷贝命令如下:
cp arch/arm/boot/zImage home/zuozhongkai/linux/tftpboot/ -f
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb home/zuozhongkai/linux/tftpboot/ -f
拷贝完成以后就可以测试了,启动开发板,进入uboot命令行模式,然后输入如下命令将zImage和imx6ull-14x14-evk.dtb下载到开发板中并启动:
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000
结果图34-4所示:

■ 图34-4 启动Linux内核
从图34-4可以看出,此时Linux内核已经启动了,如果EMMC中的根文件系统存在,我们就可以进入到Linux系统里面使用命令进行操作如图34-5所示:

■ 图34-5 进入Linux根文件系统
4. 根文件系统缺失错误
Linux内核启动以后是需要根文件系统的,根文件系统存在哪里是由uboot的bootargs环境变量指定,bootargs会传递给Linux内核作为命令行参数。比如上一小节设置root=/dev/mmcblk1p2,也就是说根文件系统存储在/dev/mmcblk1p2中,也就是EMMC的分区2中。这是因为正点原子的EMMC版本开发板出厂的时候已经EMMC的分区2中烧写好了根文件系统,所以设置root=/dev/mmcblk1p2。如果我们不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?这个问题是很常见的,我们在实际的工作中开发一个产品,这个产品的第一版硬件出来以后我们是没有对应的根文件系统可用的,必须要自己做根文件系统。在构建出对应的根文件系统之前Linux内核是没有根文件系统可用的,此时Linux内核启动以后会出现什么问题呢?带着这个问题,我们将uboot中的bootargs环境变量改为“console=ttymxc0,115200”,也就是不填写root的内容了,命令如下:
setenv bootargs 'console=ttymxc0,115200' //设置bootargs
saveenv //保存
修改完成以后重新从网络启动,启动以后会有如图34-6所示错误:

■ 图34-6 根文件系统缺失错误
在图34-6中最后会有下面这一行:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
也就是提示内核崩溃,VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。即使根文件系统目录存在,如果根文件系统目录里面是空的依旧会提示内核崩溃。这个就是根文件系统缺失导致的内核崩溃,但是内核是启动了的,只是根文件系统不存在而已。
在Linux中添加自己的开发板
在34.2小节中我们通过编译NXP官方I.MX6ULL EVK开发板对应的Linux内核,发现其可以在正点原子的EMMC版本开发板启动,所以我们就参考I.MX6ULL EVK开发板的设置,在Linux内核中添加正点原子的I.MX6U-ALPHA开发板。
1. 添加开发板默认配置文件
将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份,命名为imx_alientek_emmc_defconfig,命令如下:
cd arch/arm/configs
cp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig
以后imx_alientek_emmc_defconfig就是正点原子的EMMC版开发板默认配置文件了。完成以后如图34-7所示:

■ 图34-7 新添加的默认配置文件
make imx_alientek_emmc_defconfig
2. 添加开发板对应的设备树文件
添加适合正点原子EMMC版开发板的设备树文件,进入目录arch/arm/boot/dts中,复制一份imx6ull-14x14-evk.dts,然后将其重命名为imx6ull-alientek-emmc.dts,命令如下:
cd arch/arm/boot/dts
cp imx6ull-14x14-evk.dts imx6ull-alientek-emmc.dts
.dts是设备树源码文件,编译Linux的时候会将其编译为.dtb文件。imx6ull-alientek-emmc.dts创建好以后我们还需要修改文件arch/arm/boot/dts/Makefile,找到“dtb-$(CONFIG_SOC_IMX6ULL)”配置项,在此配置项中加入“imx6ull-alientek-emmc.dtb” ,如下所示:
示例代码34-1 arch/arm/boot/dts/Makefile代码段
400 dtb-$(CONFIG_SOC_IMX6ULL) += \
401 imx6ull-14x14-ddr3-arm2.dtb \
......
417 imx6ull-14x14-evk.dtb \
418 imx6ull-14x14-evk-btwifi.dtb \
419 imx6ull-14x14-evk-emmc.dtb \
420 imx6ull-14x14-evk-gpmi-weim.dtb \
421 imx6ull-14x14-evk-usb-certi.dtb \
422 imx6ull-alientek-emmc.dtb \
423 imx6ull-9x9-evk.dtb \
424 imx6ull-9x9-evk-btwifi.dtb \
425 imx6ull-9x9-evk-ldo.dtb
第422行为“imx6ull-alientek-emmc.dtb”,这样编译Linux的时候就可以从imx6ull-alientek-emmc.dts编译出imx6ull-alientek-emmc.dtb文件了。
3. 编译测试
经过34.3.1和34.3.2两个小节,Linux内核里面已经添加了正点原子I.MX6UL-ALIPHA EMMC版开发板了,接下接编译测试一下,我们可以创建一个编译脚本,imx6ull_alientek_emmc.sh,脚本内容如下:
示例代码34-2 imx6ull_alientek_emmc.sh编译脚本
1 #!/bin/sh
2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_emmc_defconfig
4 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
5 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
第2行,清理工程。
第3行,使用默认配置文件imx_alientek_emmc_defconfig来配置Linux内核。
第4行,打开Linux的图形配置界面,如果不需要每次都打开图形配置界面可以删除此行。
第5行,编译Linux。
执行shell脚本imx6ull_alientek_emmc.sh编译Linux内核,命令如下:
chmod 777 imx6ull_alientek_emmc.sh //给予可执行权限
./imx6ull_alientek_emmc.sh //执行shell脚本编译内核
tftp 80800000 zImage
tftp 83000000 imx6ull-alientek-emmc.dtb
bootz 80800000 - 83000000
只要出现如图34-8所示内容就表示Linux内核启动成功:

■ 图34-8 Linux内核启动
Linux内核启动成功,说明我们已经在NXP提供的Linux内核源码中添加了正点原子I.MX6UL-ALPHA开发板。
主频和网络驱动修改
1. CPU主频修改
正点原子I.MX6U-ALPHA开发板所使用的I.MX6ULL芯片主频都是792MHz的,也就是NXP官方宣传的800MHz版本,本节教程也就以792MHz的核心板为例讲解。
确保EMMC中的根文件系统可用!然后重新启动开发板,进入终端(可以输入命令),如图34-9所示:

■ 图34-9 进入命令行
进入图34-9所示的命令行以后输入如下命令查看cpu信息:
cat /proc/cpuinfo
结果如图34-10所示:

■ 图34-10 cpu信息
在图34-10中有BogoMIPS这一条,此时BogoMIPS为3.00,BogoMIPS是Linux系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,BogoMIPS值就越大。BogoMIPS只是粗略的计算CPU性能,并不十分准确。但是我们可以通过BogoMIPS值来大致的判断当前处理器的性能。在图34-10中并没有看到当前CPU的工作频率,那我们就转变另一种方法查看当前CPU的工作频率。进入到目录/sys/bus/cpu/devices/cpu0/cpufreq中,此目录下会有很多文件,如图34-11所示:

■ 图34-11 cpufreq目录
此目录中记录了CPU频率等信息,这些文件的含义如下:
cpuinfo_cur_freq:当前cpu工作频率,从CPU寄存器读取到的工作频率。
cpuinfo_max_freq:处理器所能运行的最高工作频率(单位: KHz)。
cpuinfo_min_freq :处理器所能运行的最低工作频率(单位: KHz)。
cpuinfo_transition_latency:处理器切换频率所需要的时间(单位:ns)。
scaling_available_frequencies:处理器支持的主频率列表(单位: KHz)。
scaling_available_governors:当前内核中支持的所有governor(调频)类型。
scaling_cur_freq:保存着cpufreq模块缓存的当前CPU频率,不会对CPU硬件寄存器进行检查。
scaling_driver:该文件保存当前CPU所使用的调频驱动。
scaling_governor:governor(调频)策略,Linux内核一共有5中调频策略,
1)、Performance,最高性能,直接用最高频率,不考虑耗电。
2)、Interactive,一开始直接用最高频率,然后根据CPU负载慢慢降低。
3)、Powersave,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!
4)、Userspace,可以在用户空间手动调节频率。
5)、Ondemand,定时检查负载,然后根据负载来调节频率。负载低的时候降低CPU频率,这样省电,负载高的时候提高CPU频率,增加性能。
scaling_max_freq:governor(调频)可以调节的最高频率。
cpuinfo_min_freq:governor(调频)可以调节的最低频率。
stats目录下给出了CPU各种运行频率的统计情况,比如CPU在各频率下的运行时间以及变频次数。
使用如下命令查看当前CPU频率:
cat cpuinfo_cur_freq
结果如图34-12所示:

■ 图34-12 当前CPU频率
从图34-12可以看出,当前CPU频率为396MHz,工作频率很低!其他的值如下:
cpuinfo_cur_freq = 396000
cpuinfo_max_freq = 792000
cpuinfo_min_freq = 198000
scaling_cur_freq = 198000
scaling_max_freq = 792000
cat scaling_min_freq = 198000
scaling_available_frequencies = 198000 396000 528000 792000
cat scaling_governor = ondemand
可以看出,当前CPU支持198MHz、396MHz、528MHz和792MHz四种频率切换,其中调频策略为ondemand,也就是定期检查负载,然后根据负载情况调节CPU频率。因为当前我们开发板并没有做什么工作,因此CPU频率降低为396MHz以省电。如果开发板做一些高负载的工作,比如播放视频等操作那么CPU频率就会提升上去。查看stats目录下的time_in_state文件可以看到CPU在各频率下的工作时间,命令如下:
cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state
结果如图34-13所示:

■ 图34-13 CPU运行频率统计
从图34-13中可以看出,CPU在198MHz、396MHz、528MHz和792MHz都工作过,其中198MHz的工作时间最长!假如我们想让CPU一直工作在792MHz那该怎么办?很简单,配置Linux内核,将调频策略选择为performance。或者修改imx_alientek_emmc_defconfig文件,此文件中有下面几行:
示例代码34-3 调频策略
41 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y
43 CONFIG_CPU_FREQ_GOV_USERSPACE=y
44 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
第41行,配置ondemand为默认调频策略。
第42行,使能powersave策略。
第43行,使能userspace策略。
第44行,使能interactive策略。
将示例代码34-3中的第41行屏蔽掉,然后在44行后面添加:
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
结果下所示:
示例代码34-4 修改调频策略
41 #CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y
43 CONFIG_CPU_FREQ_GOV_USERSPACE=y
44 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
45 CONFIG_CPU_FREQ_GOV_ONDEMAND=y
修改完成以后重新编译Linux内核,编译之前先清理一下工程!因为我们重新修改过默认配置文件了,编译完成以后使用新的zImage镜像文件重新启动Linux。再次查看/sys/devices/system/cpu/cpu0/cpufreq/ cpuinfo_cur_freq文件的值,如图34-14所示:

■ 图34-14 当前CPU频率
从图34-14可以看出,当前CPU频率为792MHz了。查看scaling_governor文件看一下当前的调频策略,如图34-15所示:

■ 图34-15 调频策略
从图34-15可以看出当前的CPU调频策略为preformance,也就是高性能模式,一直以最高主频运行。
我们再来看一下如何通过图形化界面配置Linux内核的CPU调频策略,输入“make menuconfig”打开Linux内核的图形化配置界面,如图34-16所示:

■ 图34-16 Linux内核图形化配置界面
进入如下路径:
CPU Power Management
-> CPU Frequency scaling
-> Default CPUFreq governor
打开默认调频策略选择界面,选择“performance”,如图34-17所示:

■ 图34-17 默认调频策略选择
在图34-17中选择“performance”即可,选择以后退出图形化配置界面,然后编译Linux内核,一定不要清理工程!否则的话我们刚刚的设置就会被清理掉。编译完成以后使用新的zImage重启Linux,查看当前CPU的工作频率和调频策略。
我们学习的时候为了高性能,大家可以使用performance模式。但是在以后的实际产品开发中,从省电的角度考虑,建议大家使用ondemand模式,一来可以省电,二来可以减少发热。
2. 使能8线EMMC驱动
正点原子EMMC版本核心板上的EMMC采用的8位数据线,原理图如图34-18所示:

■ 图34-18 EMMC原理图
Linux内核驱动里面EMMC默认是4线模式的,4线模式肯定没有8线模式的速度快,所以本节我们将EMMC的驱动修改为8线模式。修改方法很简单,直接修改设备树即可,打开文件imx6ull-alientek-emmc.dts,找到如下所示内容:
示例代码34-5 imx6ull-alientek-emmc.dts代码段
734 &usdhc2 {
735 pinctrl-names = "default";
736 pinctrl-0 = <&pinctrl_usdhc2>;
737 non-removable;
738 status = "okay";
739 };
关于设备树的原理以及内容我们后面会有专门的章节讲解,示例代码34-5中的代码含义我们现在不去纠结,只需要将其改为如下代码即可:
示例代码34-6 imx6ull-alientek-emmc.dts代码段
734 &usdhc2 {
735 pinctrl-names = "default", "state_100mhz", "state_200mhz";
736 pinctrl-0 = <&pinctrl_usdhc2_8bit>;
737 pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
738 pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
739 bus-width = <8>;
740 non-removable;
741 status = "okay";
742 };
修改完成以后保存一下imx6ull-alientek-emmc.dts,然后使用命令“make dtbs”重新编译一下设备树,编译完成以后使用新的设备树重启Linux系统即可。












