700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【正点原子Linux连载】第三十七章 Linux内核移植 -摘自【正点原子】I.MX6U嵌入式Lin

【正点原子Linux连载】第三十七章 Linux内核移植 -摘自【正点原子】I.MX6U嵌入式Lin

时间:2022-03-29 18:38:26

相关推荐

【正点原子Linux连载】第三十七章 Linux内核移植 -摘自【正点原子】I.MX6U嵌入式Lin

1)实验平台:正点原子阿尔法Linux开发板

2)平台购买地址:/item.htm?id=603672744434

2)全套实验源码+手册+视频下载地址:/thread-300792-1-1.html

3)对正点原子Linux感兴趣的同学可以加群讨论:935446741

4)关注正点原子公众号,获取最新资料更新

第三十七章 Linux内核移植

前两章我们简单了解了一下Linux内核顶层Makefile和Linux内核的启动流程,本章我们就来学习一下如何将NXP官方提供的Linux内核移植到正点原子的I.MX6U-ALPHA开发板上。通过本章的学习,我们将掌握如何将半导体厂商提供的Linux BSP包移植到我们自己的平台上。

37.1 创建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这个文件。

37.2 NXP官方开发板Linux内核编译

NXP提供的Linux源码肯定是可以在自己的I.MX6ULL EVK开发板上运行下去的,所以我们肯定是以I.MX6ULL EVK开发板为参考,然后将Linux内核移植到I.MX6U-ALPHA开发板上的。

37.2.1修改顶层Makefile

修改顶层Makefile,直接在顶层Makefile文件里面定义ARCH和CROSS_COMPILE这两个的变量值为arm和arm-linux-gnueabihf-,结果如图37.2.1所示:

图37.2.1 修改顶层Makefile

图37.2.1中第252和253行分别设置了ARCH和CROSS_COMPILE这两个变量的值,这样在编译的时候就不用输入很长的命令了。

37.2.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内核

配置完成以后如图37.2.2.1所示:

图37.2.2.1 配置Linux内核

配置完成以后就可以编译了,使用如下命令编译Linux内核:

make -j16 //编译Linux内核

等待编译完成,结果如图37.2.2.2所示:

图37.2.2.2 Linux编译完成

Linux内核编译完成以后会在arch/arm/boot目录下生成zImage镜像文件,如果使用设备树的话还会在arch/arm/boot/dts目录下开发板对应的.dtb(设备树)文件,比如imx6ull-14x14-evk.dtb就是NXP官方的I.MX6ULL EVK开发板对应的设备树文件。至此我们得到两个文件:

①、Linux内核镜像文件:zImage。

②、NXP官方I.MX6ULL EVK开发板对应的设备树文件:imx6ull-14x14-evk.dtb。

37.2.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

结果图37.2.3.1所示:

图37.2.3.1 启动Linux内核

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

在这里插入图片描述

图37.2.3.2 进入Linux根文件系统

37.2.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 //保存

修改完成以后重新从网络启动,启动以后会有如图37.2.4.1所示错误:

图37.2.4.1 根文件系统缺失错误

在图37.2.4.1中最后会有下面这一行:

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

也就是提示内核崩溃,因为VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。即使根文件系统目录存在,如果根文件系统目录里面是空的依旧会提示内核崩溃。这个就是根文件系统缺失导致的内核崩溃,但是内核是启动了的,只是根文件系统不存在而已。

37.3 在Linux中添加自己的开发板

在37.2小节中我们通过编译NXP官方I.MX6ULL EVK开发板对应的Linux内核,发现其可以在正点原子的EMMC版本开发板启动,所以我们就参考I.MX6ULL EVK开发板的设置,在Linux内核中添加正点原子的I.MX6U-ALPHA开发板。

37.3.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版开发板默认配置文件了。完成以后如图37.3.1.1所示:

图37.3.1.1 新添加的默认配置文件

以后就可以使用如下命令来配置正点原子EMMC版开发板对应的Linux内核了:

make imx_alientek_emmc_defconfig

37.3.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” ,如下所示:

示例代码37.3.2.1 arch/arm/boot/dts/Makefile代码段

400 dtb-$(CONFIG_SOC_IMX6ULL) += \401imx6ull-14x14-ddr3-arm2.dtb \402imx6ull-14x14-ddr3-arm2-adc.dtb \403imx6ull-14x14-ddr3-arm2-cs42888.dtb \404imx6ull-14x14-ddr3-arm2-ecspi.dtb \405imx6ull-14x14-ddr3-arm2-emmc.dtb \406imx6ull-14x14-ddr3-arm2-epdc.dtb \407imx6ull-14x14-ddr3-arm2-flexcan2.dtb \408imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \409imx6ull-14x14-ddr3-arm2-lcdif.dtb \410imx6ull-14x14-ddr3-arm2-ldo.dtb \411imx6ull-14x14-ddr3-arm2-qspi.dtb \412imx6ull-14x14-ddr3-arm2-qspi-all.dtb \413imx6ull-14x14-ddr3-arm2-tsc.dtb \414imx6ull-14x14-ddr3-arm2-uart2.dtb \415imx6ull-14x14-ddr3-arm2-usb.dtb \416imx6ull-14x14-ddr3-arm2-wm8958.dtb \417imx6ull-14x14-evk.dtb \418imx6ull-14x14-evk-btwifi.dtb \419imx6ull-14x14-evk-emmc.dtb \420imx6ull-14x14-evk-gpmi-weim.dtb \421imx6ull-14x14-evk-usb-certi.dtb \422imx6ull-alientek-emmc.dtb \423imx6ull-9x9-evk.dtb \424imx6ull-9x9-evk-btwifi.dtb \425imx6ull-9x9-evk-ldo.dtb

第422行为“imx6ull-alientek-emmc.dtb”,这样编译Linux的时候就可以从imx6ull-alientek-emmc.dts编译出imx6ull-alientek-emmc.dtb文件了。

37.3.3 编译测试

经过37.3.1和37.3.2两个小节,Linux内核里面已经添加了正点原子I.MX6UL-ALIPHA EMMC版开发板了,接下接编译测试一下,我们可以创建一个编译脚本,imx6ull_alientek_emmc.sh,脚本内容如下:

示例代码37.3.2.1 imx6ull_alientek_emmc.sh编译脚本

1 #!/bin/sh2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_emmc_defconfig4 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig5 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脚本编译内核

编译完成以后就会在目录arch/arm/boot下生成zImage镜像文件。在arch/arm/boot/dts目录下生成imx6ull-alientek-emmc.dtb文件。将这两个文件拷贝到tftp目录下,然后重启开发板,在uboot命令模式中使用tftp命令下载这两个文件并启动,命令如下:

tftp 80800000 zImage

tftp 83000000 imx6ull-alientek-emmc.dtb

bootz 80800000 – 83000000

只要出现如图37.3.3.1所示内容就表示Linux内核启动成功:

图37.3.3.1 Linux内核启动

Linux内核启动成功,说明我们已经在NXP提供的Linux内核源码中添加了正点原子I.MX6UL-ALPHA开发板。

37.4 CPU主频和网络驱动修改

37.4.1 CPU主频修改

正点原子I.MX6U-ALPHA开发板所使用的I.MX6ULL芯片主频都是792MHz的,也就是NXP官方宣传的800MHz版本。后续可能会生产528MHz核心板供企业级批量用户,但是开发板搭配的都是792MHz主频的,本节教程也就以792MHz的核心板为例讲解。

1、设置I.MX6U-ALPHA开发板工作在792MHz

确保EMMC中的根文件系统可用!然后重新启动开发板,进入终端(可以输入命令),如图37.4.1.1所示:

图37.4.1.1 进入命令行

进入图37.4.1.1所示的命令行以后输入如下命令查看cpu信息:

cat /proc/cpuinfo

结果如图37.4.2所示:

图37.4.1.2 cpu信息

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

图37.4.1.3 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中调频策略,

①、Performance,最高性能,直接用最高频率,不考虑耗电。

②、Interactive,一开始直接用最高频率,然后根据CPU负载慢慢降低。

③、Powersave,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!

④、Userspace,可以在用户空间手动调节频率。

⑤、Ondemand,定时检查负载,然后根据负载来调节频率。负载低的时候降低CPU频率,这样省电,负载高的时候提高CPU频率,增加性能。

scaling_max_freq:governor(调频)可以调节的最高频率。

cpuinfo_min_freq:governor(调频)可以调节的最低频率。

stats目录下给出了CPU各种运行频率的统计情况,比如CPU在各频率下的运行时间以及变频次数。

使用如下命令查看当前CPU频率:

cat cpuinfo_cur_freq

结果如图37.4.1.4所示:

图37.4.1.4 当前CPU频率

从图37.4.1.4可以看出,当前CPU频率为198MHz,工作频率很低!其他的值如下:

cpuinfo_cur_freq = 198000cpuinfo_max_freq = 792000cpuinfo_min_freq = 198000scaling_cur_freq = 198000scaling_max_freq = 792000cat scaling_min_freq = 198000scaling_available_frequencies = 198000 396000 528000 792000cat scaling_governor = ondemand

可以看出,当前CPU支持198MHz、396MHz、528Mhz和792MHz四种频率切换,其中调频策略为ondemand,也就是定期检查负载,然后根据负载情况调节CPU频率。因为当前我们开发板并没有做什么工作,因此CPU频率降低为198MHz以省电。如果开发板做一些高负载的工作,比如播放视频等操作那么CPU频率就会提升上去。查看stats目录下的time_in_state文件可以看到CPU在各频率下的工作时间,命令如下:

cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state

结果如图37.4.1.5所示:

图37.4.1.5 CPU运行频率统计

从图37.4.1.5中可以看出,CPU在198MHz、396MHz、528MHz和792MHz都工作过,其中198MHz的工作时间最长!假如我们想让CPU一直工作在792MHz那该怎么办?很简单,配置Linux内核,将调频策略选择为performance。或者修改imx_alientek_emmc_defconfig文件,此文件中有下面几行:

示例代码37.4.1.1 调频策略

41 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y43 CONFIG_CPU_FREQ_GOV_USERSPACE=y44 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y第41行,配置ondemand为默认调频策略。第42行,使能powersave策略。第43行,使能userspace策略。第44行,使能interactive策略。

将示例代码37.4.1.1中的第41行屏蔽掉,然后在44行后面添加:

CONFIG_CPU_FREQ_GOV_ONDEMAND=y

结果下所示:

示例代码37.4.1.2 修改调频策略

41 #CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y43 CONFIG_CPU_FREQ_GOV_USERSPACE=y44 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文件的值,如图37.4.1.6所示:

图37.4.1.6 当前CPU频率

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

图37.4.1.7 调频策略

从图37.4.1.7可以看出当前的CPU调频策略为preformance,也就是高性能模式,一直以最高主频运行。

我们再来看一下如何通过图形化界面配置Linux内核的CPU调频策略,输入“make menuconfig”打开Linux内核的图形化配置界面,如图37.4.1.8所示:

图37.4.1.8 Linux内核图形化配置界面

进入如下路径:

CPU Power Management

-> CPU Frequency scaling

-> Default CPUFreq governor

打开默认调频策略选择界面,选择“performance”,如图37.1.4.9所示:

图37.1.4.9 默认调频策略选择

在图37.1.4.9中选择“performance”即可,选择以后退出图形化配置界面,然后编译Linux内核,一定不要清理工程!否则的话我们刚刚的设置就会被清理掉。编译完成以后使用新的zImage重启Linux,查看当前CPU的工作频率和调频策略。

我们学习的时候为了高性能,大家可以使用performance模式。但是在以后的实际产品开发中,从省电的角度考虑,建议大家使用ondemand模式,一来可以省电,二来可以减少发热。

2、超频至700MHz

I.MX6ULL有多种型号,按照工作频率可以分为528MHz、700Mhz(实际696MHz),800MHz(实际792MHz)和900MHz(实际频率未知,应该在900MHz左右)。有些朋友可能用的其他品牌的开发板,其所使用的I.MX6ULL主频可能是528MHz的,虽然芯片标称是528MHz主频,但是其是可以超频的700MHz的(这里的700MHz实际上只有696MHz,但是NXP官方宣传其为700MHz,所以我们就统一称为700MHz吧)。

声明:

对于所用的芯片为528MHz主频但是想体验一下高性能的朋友体验一下超频,笔者测试过528MHz超频到700MHz,还没有出现过超频不稳定的现象发生,但是!毕竟是超频了的,肯定没有工作在528MHz稳定。

如果因为超频带来任何损坏,正点原子不负任何责任!

在实际的产品中,禁止任何超频!务必严格按照I.MX6ULL手册上给出的标准工作频率来运行!!如果想要更高的性能,请购买相应型号的处理器!

看到这里,如果您还是执意要超频,那么就接着往下看,如果要放弃超频,那就跳过本小节,看下一小节。

超频设置其实很简单,修改一下设备树文件arch/arm/boot/dts/imx6ull.dtsi即可,打开imx6ull.dtsi,找到下面代码:

示例代码37.4.1.3 imx6ull.dtsi文件代码段

54 cpu0: cpu@0 {55compatible = "arm,cortex-a7";56device_type = "cpu";57reg = <0>;58clock-latency = <61036>; /* two CLK32 periods */59operating-points = <60/* kHz uV */61996000 127500062792000 122500063528000 117500064396000 102500065198000 95000066>;67 fsl,soc-operating-points = <68 /* KHz uV */69 996000 117500070 792000 117500071 528000 117500072 396000 117500073 198000 117500074 >;

示例代码37.4.1.3就是设置CPU频率的,第61~65行和第69~73行就是I.MX6ULL所支持的频率,单位为KHz,可以看出I.MX6ULL(视具体型号而定)支持996MHz、792MHz、528MHz、396MHz和198MHz。在上一小节中,我们知道Linux内核默认支持198MHz、396MHz、528MHz和792MHz,如果是MCIMX6Y2CVM05AB这颗芯片的话,默认最高只能运行在528MHz,我们在示例代码37.4.2.1中加入针对696MHz的支持,修改以后代码如下:

示例代码37.4.1.4 增加696MHz的支持

54 cpu0: cpu@0 {55compatible = "arm,cortex-a7";56device_type = "cpu";57reg = <0>;58clock-latency = <61036>; /* two CLK32 periods */59operating-points = <60/* kHz uV */61996000 127500062792000 122500063696000 122500064528000 117500065396000 102500066198000 95000067>;68 fsl,soc-operating-points = <69 /* KHz uV */70 996000 117500071 792000 117500072 696000 117500073 528000 117500074 396000 117500075 198000 117500076 >;

第63行,加入了“696000 1225000”,这个就是696MHz的支持。第72行,加入了“696000 1175000”,也是对696MHz的支持。修改好以后保存,并且编译设备树,在Linux内核源码根目录下输入如下命令编译设备树:

make dtbs

命令“make dtbs”只编译设备树文件,也就是将.dts编译为.dtb,编译完成以后使用新的设备树文件imx6ull-alientek_emmc.dtb启动Linux。重启以后查看文件/sys/devices/system/cpu/cpu0/cpufreq/ scaling_available_frequencies的内容,如图37.4.1.10所示:

图37.4.1.10 文件scaling_available_frequencies内容

从图37.4.1.11可以看出,此时支持了696MHz。如果设置调频策略为performance,那么处理器就会一直工作在696MHz。可以对比一下工作在528MHz和696MHz下的BogoMIPS的值,528MHz主频下的BogoMIPS值如图37.4.1.12所示:

图37.4.1.12 528MHz主频下的BogoMIPS

696MHz主频下的BogoMIPS值如图37.4.1.13所示:

图37.4.1.13 696MHz主频下的BogoMIPS

从图37.4.1.12和图37.2.1.13中可以看到,528MHz和696MHz下的BogoMIPS值分别为8.00和10.54,相当于性能提升了(10.54/8)-1=31.75%。

37.4.2 使能8线EMMC驱动

正点原子EMMC版本核心板上的EMMC采用的8位数据线,原理图如图37.4.2.1所示:

图37.4.2.1 EMMC原理图

Linux内核驱动里面EMMC默认是4线模式的,4线模式肯定没有8线模式的速度快,所以本节我们将EMMC的驱动修改为8线模式。修改方法很简单,直接修改设备树即可,打开文件imx6ull-alientek-emmc.dts,找到如下所示内容:

示例代码37.4.2.1 imx6ull-alientek-emmc.dts代码段

734 &usdhc2 {735pinctrl-names = "default";736pinctrl-0 = <&pinctrl_usdhc2>;737non-removable;738status = "okay";739 };

关于设备树的原理以及内容我们后面会有专门的章节讲解,示例代码37.4.2.1中的代码含义我们现在不去纠结,只需要将其改为如下代码即可:

示例代码37.4.2.1 imx6ull-alientek-emmc.dts代码段

734 &usdhc2 {735pinctrl-names = "default", "state_100mhz", "state_200mhz";736pinctrl-0 = <&pinctrl_usdhc2_8bit>;737pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;738pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;739bus-width = <8>;740non-removable;741status = "okay";742 };

修改完成以后保存一下imx6ull-alientek-emmc.dts,然后使用命令“make dtbs”重新编译一下设备树,编译完成以后使用新的设备树重启Linux系统即可。

37.4.3 修改网络驱动

因为在后面学习Linux驱动开发的时候要用到网络调试驱动,所以必须要把网络驱动调试好。在讲解uboot移植的时候就已经说过了,正点原子开发板的网络和NXP官方的网络硬件上不同,网络PHY芯片由KSZ8081换为了LAN8720A,两个网络PHY芯片的复位IO也不同。所以Linux内核自带的网络驱动是驱动不起来I.MX6U-ALPHA开发板上的网络的,需要做修改。

1、修改LAN8720的复位以及网络时钟引脚驱动

ENET1复位引脚ENET1_RST连接在I.M6ULL的SNVS_TAMPER7这个引脚上。ENET2的复位引脚ENET2_RST连接在I.MX6ULL的SNVS_TAMPER8上。打开设备树文件imx6ull-alientek-emmc.dts,找到如下代码:

示例代码37.4.3.1 imx6ull-alientek-emmc.dts代码段

584 pinctrl_spi4: spi4grp {585 fsl,pins = <586 MX6ULL_PAD_BOOT_MODE0__GPIO5_IO100x70a1587 MX6ULL_PAD_BOOT_MODE1__GPIO5_IO110x70a1588 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1589 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000590 >;591 };

示例代码37.4.3.1中第588和589行就是初始化SNVS_TAMPER7和SNVS_TAMPER8这两个引脚的,不过看样子好像是作为了SPI4的IO,这不是我们想要的,所以将588和589这两行删除掉!删除掉以后继续在imx6ull-alientek-emmc.dts中找到如下所示代码:

示例代码37.4.3.2 imx6ull-alientek-emmc.dts代码段

125 spi4 {126compatible = "spi-gpio";127pinctrl-names = "default";128pinctrl-0 = <&pinctrl_spi4>;129pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; ......133cs-gpios = <&gpio5 7 0>;

第129行,设置GPIO5_IO08为SPI4的一个功能引脚(我也不清楚具体作为什么功能用),而GPIO5_IO08就是SNVS_TAMPER8的GPIO功能引脚。第133行,设置GPIO5_IO07作为SPI4的片选引脚,而GPIO5_IO07就是SNVS_TAMPER7的GPIO功能引脚。现在我们需要GPIO5_IO07和GPIO5_IO08分别作为ENET1和ENET2的复位引脚,而不是SPI4的什么功能引脚,因此将示例代码37.4.3.2中的第129行和第133行处的代码删除掉!!否则会干扰到网络复位引脚!在imx6ull-alientek-emmc.dts里面找到名为“iomuxc_snvs”的节点(就是直接搜索),然后在此节点下添加网络复位引脚信息,添加完成以后的“iomuxc_snvs”的节点内容如下:

示例代码37.4.3.3 iomuxc_snvs节点添加网络复位信息

1 &iomuxc_snvs {2 pinctrl-names = "default_snvs";3pinctrl-0 = <&pinctrl_hog_2>;4imx6ul-evk {5....../*省略掉其他*/43 44/*enet1 reset zuozhongkai*/45pinctrl_enet1_reset: enet1resetgrp {46 fsl,pins = <47 /* used for enet1 reset */48 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO070x10B049 >;50 };51 52/*enet2 reset zuozhongkai*/53pinctrl_enet2_reset: enet2resetgrp {54 fsl,pins = <55/* used for enet2 reset */56 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO080x10B0 57 >;58 };59 };

60 };

第1行,imx6ull-alientek-emmc.dts文件中iomuxc_snvs节点。

第45~50行,ENET1网络复位引脚配置信息。

第53~58行,ENET2网络复位引脚配置信息。

最后还需要修改一下ENET1和ENET2的网络时钟引脚配置,继续在imx6ull-alientek-emmc.dts中找到如下所示代码:

示例代码37.4.3.4 imx6ull-alientek-emmc.dts代码段

309 pinctrl_enet1: enet1grp {310fsl,pins = <311 MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0312 MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0313 MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0314 MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0315 MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0316 MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0317 MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0318 MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009319>;320 };321 322 pinctrl_enet2: enet2grp {323fsl,pins = <324 MX6UL_PAD_GPIO1_IO07__ENET2_MDC0x1b0b0325 MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0326 MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0327 MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0328 MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0329 MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0330 MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0331 MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0332 MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0333 MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009334>;335 };

第318和333行,分别为ENET1和ENET2的网络时钟引脚配置信息,将这两个引脚的电气属性值改为0x4001b009,原来默认值为0x4001b031。

修改完成以后记得保存一下imx6ull-alientek-emmc.dts,网络复位以及时钟引脚驱动就修改好了。

2、修改fec1和fec2节点的pinctrl-0属性

在imx6ull-alientek-emmc.dts文件中找到名为“fec1”和“fec2”的这两个节点,修改其中的“pinctrl-0”属性值,修改以后如下所示:

示例代码37.4.3.5 修改fec1和fec2的pinctrl-0属性

1 &fec1 {2 pinctrl-names = "default";3 pinctrl-0 = <&pinctrl_enet14 &pinctrl_enet1_reset>;5 phy-mode = "rmii";......9 status = "okay";10 };11 12 &fec2 {13 pinctrl-names = "default";14 pinctrl-0 = <&pinctrl_enet215 &pinctrl_enet2_reset>;16 phy-mode = "rmii";......36 };

第3~4行,修改后的fec1节点“pinctrl-0”属性值。第14~15行,修改后的fec2节点“pinctrl-0”属性值。

3、修改LAN8720A的PHY地址

在uboot移植章节中,我们说过ENET1的LAN8720A地址为0x0,ENET2的LAN8720A地址为0x1。在imx6ull-alientek-emmc.dts中找到如下代码:

示例代码37.4.3.6 imx6ull-alientek-emmc.dts代码段

171 &fec1 {172pinctrl-names = "default";......175phy-handle = <&ethphy0>;176status = "okay";177 };178 179 &fec2 {180pinctrl-names = "default";......183phy-handle = <&ethphy1>;184status = "okay";185 186mdio {187 #address-cells = <1>;188 #size-cells = <0>;189 190 ethphy0: ethernet-phy@0 {191 compatible = "ethernet-phy-ieee802.3-c22";192 reg = <2>;193 };194 195 ethphy1: ethernet-phy@1 {196 compatible = "ethernet-phy-ieee802.3-c22";197 reg = <1>;198 };199};200 };

第171~177行,ENET1对应的设备树节点。第179~200行,ENET2对应的设备树节点。但是第186~198行的mdio节点描述了ENET1和ENET2的PHY地址信息。将示例代码37.4.3.6改为如下内容:

示例代码37.4.3.7 imx6ull-alientek-emmc.dts代码段

171 &fec1 {172pinctrl-names = "default";173pinctrl-0 = <&pinctrl_enet1174 &pinctrl_enet1_reset>;175phy-mode = "rmii";176phy-handle = <&ethphy0>;177phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;178phy-reset-duration = <200>;179status = "okay";180 };181 182 &fec2 {183pinctrl-names = "default";184pinctrl-0 = <&pinctrl_enet2185 &pinctrl_enet2_reset>;186phy-mode = "rmii";187phy-handle = <&ethphy1>;188phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;189phy-reset-duration = <200>;190status = "okay";191 192mdio {193 #address-cells = <1>;194 #size-cells = <0>;195 196 ethphy0: ethernet-phy@0 {197 compatible = "ethernet-phy-ieee802.3-c22";198 smsc,disable-energy-detect;199 reg = <0>;200};201 202 ethphy1: ethernet-phy@1 {203 compatible = "ethernet-phy-ieee802.3-c22";204 smsc,disable-energy-detect;205 reg = <1>;206 };207};208 };

第177和178行,添加了ENET1网络复位引脚所使用的IO为GPIO5_IO07,低电平有效。复位低电平信号持续时间为200ms。第188和189行,ENET2网络复位引脚所使用的IO为GPIO5_IO08,同样低电平有效,持续时间同样为200ms。

第198和204行,“smsc,disable-energy-detect”表明PHY芯片是SMSC公司的,这样Linux内核就会找到SMSC公司的PHY芯片驱动来驱动LAN8720A。

第196行,注意“ethernet-phy@”后面的数字是PHY的地址,ENET1的PHY地址为0,所以“@”后面是0(默认为2)。

第199行,reg的值也表示PHY地址,ENET1的PHY地址为0,所以reg=0。

第202行,ENET2的PHY地址为1,因此“@”后面为1。

第205行,因为ENET2的PHY地址为1,所以reg=1。

至此,LAN8720A的PHY地址就改好了,保存一下imx6ull-alientek-emmc.dts文件。然后使用“make dtbs”命令重新编译一下设备树。

3、修改fec_main.c文件

要在I.MX6ULL上使用LAN8720A,需要修改一下Linux内核源码,打开drivers/net/ethernet/freescale/fec_main.c,找到函数fec_probe,在fec_probe中加入如下代码:

示例代码37.4.3.8 imx6ull-alientek-emmc.dts代码段

3438 static int3439 fec_probe(struct platform_device *pdev)3440 {3441 struct fec_enet_private *fep;3442 struct fec_platform_data *pdata;3443 struct net_device *ndev;3444 int i, irq, ret = 0;3445 struct resource *r;3446 const struct of_device_id *of_id;3447 static int dev_id;3448 struct device_node *np = pdev->dev.of_node, *phy_node;3449 int num_tx_qs;3450 int num_rx_qs;3451 3452 /* 设置MX6UL_PAD_ENET1_TX_CLK和MX6UL_PAD_ENET2_TX_CLK3453* 这两个IO的复用寄存器的SION位为1。3454*/3455void __iomem *IMX6U_ENET1_TX_CLK;3456void __iomem *IMX6U_ENET2_TX_CLK;3457 3458IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC, 4);3459writel(0X14, IMX6U_ENET1_TX_CLK);3460 3461IMX6U_ENET2_TX_CLK = ioremap(0X020E00FC, 4);3462writel(0X14, IMX6U_ENET2_TX_CLK);3463 ......3656 return ret;3657 }

第3455~3462就是新加入的代码,如果要在I.MX6ULL上使用LAN8720A就需要设置ENET1和ENET2的TX_CLK引脚复位寄存器的SION位为1。

4、配置Linux内核,使能LAN8720驱动

输入命令“make menuconfig”,打开图形化配置界面,选择使能LAN8720A的驱动,路径如下:

-> Device Drivers

-> Network device support

-> PHY Device support and infrastructure

-> Drivers for SMSC PHYs

如图37.4.3.1所示:

图37.4.3.1 使能LAN8720A驱动

图37.4.3.1中选择将“Drivers for SMSC PHYs”编译到Linux内核中,因此“<>”里面变为了“*”。LAN8720A是SMSC公司出品的,因此勾选这个以后就会编译LAN8720驱动,配置好以后退出配置界面,然后重新编译一下Linux内核。

5、修改smsc.c文件

在修改smsc.c文件之前先说点题外话,那就是我是怎么确定要修改smsc.c这个文件的。在写本书之前我并没有修改过smsc.c这个文件,都是使能LAN8720A驱动以后就直接使用。但是我在测试NFS挂载文件系统的时候发现文件系统挂载成功率很低!老是提示NFS服务器找不到,三四次就有一次挂载失败!很折磨人。NFS挂载就是通过网络来挂载文件系统,这样做的好处就是方便我们后续调试Linux驱动。既然老是挂载失败那么可以肯定的是网络驱动有问题,网络驱动分两部分:内部MAC+外部PHY,内部MAC驱动是由NXP提供的,一般不会出问题,否则的话用户早就给NXP反馈了。而且我用NXP官方的开发板测试网络是一直正常的,但是NXP官方的开发板所使用的PHY芯片为KSZ8081。所以只有可能是外部PHY,也就是LAN8720A的驱动可能出问题了。鉴于LAN8720A有“前车之鉴”,那就是在uboot中需要对LAN8720A进行一次软复位,要设置LAN8720A的BMCR(寄存器地址为0)寄存器bit15为1。所以我猜测,在Linux中也需要对LAN8720A进行一次软复位。

首先需要找到LAN8720A的驱动文件,LAN8720A的驱动文件是drivers/net/phy/smsc.c,在此文件中有个叫做smsc_phy_reset的函数,看名字都知道这是SMSC PHY的复位函数,因此,LAN8720A肯定也会使用到这个复位函数,修改此函数的内容,修改以后的smsc_phy_reset函数内容如下所示:

示例代码37.4.3.9 smsc_phy_reset函数

1 static int smsc_phy_reset(struct phy_device *phydev)

2 {

3 int err, phy_reset;

4 int msec = 1;

5 struct device_nodenp;

6 int timeout = 50000;

7 if(phydev->addr == 0) /FEC1/ {

8 np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@

02188000");

9 if(np == NULL) {

10 return -EINVAL;

11 }

12 }

13

14 if(phydev->addr == 1) /FEC2/ {

15 np = of_find_node_by_path("/soc/aips-bus@02000000/ethernet@

020b4000");

16 if(np == NULL) {

17 return -EINVAL;

18 }

19 }

20

21 err = of_property_read_u32(np, “phy-reset-duration”, &msec);

22 /A sane reset duration should not be longer than 1s/

23 if (!err && msec > 1000)

24 msec = 1;

25 phy_reset = of_get_named_gpio(np, “phy-reset-gpios”, 0);

26 if (!gpio_is_valid(phy_reset))

27 return;

28

29 gpio_direction_output(phy_reset, 0);

30 gpio_set_value(phy_reset, 0);

31 msleep(msec);

32 gpio_set_value(phy_reset, 1);

33

34 int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);

35 if (rc < 0)

36 return rc;

37

38 /If the SMSC PHY is in power down mode, then set it

39 * in all capable mode before using it.

40/

41 if ((rc & MII_LAN83C185_MODE_MASK) ==

MII_LAN83C185_MODE_POWERDOWN) {

42

43 /set “all capable” mode and reset the phy/

44 rc |= MII_LAN83C185_MODE_ALL;

45 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);

46 }

47

48 phy_write(phydev, MII_BMCR, BMCR_RESET);

49 /wait end of reset (max 500 ms) */

50

51 do {

52 udelay(10);

53 if (timeout-- == 0)

54 return -1;

55 rc = phy_read(phydev, MII_BMCR);

56 } while (rc & BMCR_RESET);

57 return 0;

58 }

第7~12行,获取FEC1网卡对应的设备节点。

第14~19行,获取FEC2网卡对应的设备节点。

第21行,从设备树中获取“phy-reset-duration”属性信息,也就是复位时间。

第25行,从设备树中获取“phy-reset-gpios”属性信息,也就是复位IO。

第29~32行,设置PHY的复位IO,复位LAN8720A。

第41~48行,以前的smsc_phy_reset函数会判断LAN8720是否处于Powerdown模式,只有处于Powerdown模式的时候才会软复位LAN8720。这里我们将软复位代码移出来,这样每次调用smsc_phy_reset函数LAN8720A都会被软复位。

最后我们还需要在drivers/net/phy/smsc.c文件中添加两个头文件,因为修改后的smsc_phy_reset函数用到了gpio_direction_output和gpio_set_value这两个函数,需要添加的头文件如下所示:

#include <linux/of_gpio.h>

#include <linux/io.h>

6、网络驱动测试

修改好设备树和Linux内核以后重新编译一下,得到新的zImage镜像文件和imx6ull-alientek-emmc.dtb设备树文件,使用网线将I.MX6U-ALPHA开发板的两个网口与路由器或者电脑连接起来,最后使用新的文件启动Linux内核。启动以后使用“ifconfig”命令查看一下当前活动的网卡有哪些,结果如图37.4.3.2所示:

图37.4.3.2 ifconfig命令结果

从图37.4.3.2可以看出,当前没有活动的网卡。输入命令“ifconfig -a”来查看一下开发板中存在的所有网卡,结果如图37.4.3.3所示:

图37.4.3.3 开发板所有网卡

图37.4.3.3中can0和can1为CAN接口的网卡,eth0和eth1才是网络接口的网卡,其中eth0对应于ENET2,eth1对应于ENET1。使用如下命令依次打开eth0和eth1这两个网卡:

ifconfig eth0 up

ifconfig eth1 up

网卡的打开过程如图37.4.3.4所示:

图37.4.3.4 两个网卡打开过程

从图37.4.3.4中可以看到“SMSC LAN8710/LAN8720”字样,说明当前的网络驱动使用的就是我们前面使能的SMSC驱动。

再次输入“ifconfig”命令来查看一下当前活动的网卡,结果如图37.4.3.5所示:

图37.4.3.5 当前活动的网卡。

可以看出,此时eth0和eth1两个网卡都已经打开,并且工作正常,但是这两个网卡都还没有IP地址,所以不能进行ping等操作。使用如下命令给两个网卡配置IP地址:

ifconfig eth0 192.168.1.251

ifconfig eth1 192.168.1.252

上述命令配置eth0和eth1这两个网卡的IP地址分别为192.168.1.251和192.168.1.252,注意IP地址选择的合理性,一定要和自己的电脑处于同一个网段内,并且没有被其他的设备占用!设置好以后,使用“ping”命令来ping一下自己的主机,如果能ping通那说明网络驱动修改成功!比如我的Ubuntu主机IP地址为192.168.1.250,使用如下命令ping一下:

ping 192.168.1.250

结果如图37.4.3.6所示:

图37.4.3.6 ping结果

可以看出,ping成功,说明网络驱动修改成功!我们在后面的构建根文件系统和Linux驱动开发中就可以使用网络调试代码啦。

37.4.4 保存修改后的图形化配置文件

在修改网络驱动的时候我们通过图形界面使能了LAN8720A的驱动,使能以后会在.config中存在如下代码:

CONFIG_SMSC_PHY=y

打开drivers/net/phy/Makefile,有如下代码:

示例代码37.4.4.1 drivers/net/phy/Makefile代码段

11 obj-$(CONFIG_SMSC_PHY) += smsc.o

当CONFIG_SMSC_PHY=y的时候就会编译smsc.c这个文件,smsc.c就是LAN8720A的驱动文件。但是当我们执行“make clean”清理工程以后.config文件就会被删除掉,因此我们所有的配置内容都会丢失,结果就是前功尽弃,一“删”回到解放前!所以我们在配置完图形界面以后经过测试没有问题,就必须要保存一下配置文件。保存配置的方法有两个。

1、直接另存为.config文件

既然图形化界面配置后的配置项保存在.config中,那么就简单粗暴,直接将.config文件另存为imx_alientek_emmc_defconfig,然后其复制到arch/arm/configs目录下,替换以前的imx_alientek_emmc_defconfig。这样以后执行“make imx_alientek_emmc_defconfig”重新配置Linux内核的时候就会使用新的配置文件,默认就会使能LAN8720A的驱动。

2、通过图形界面保存配置文件

相比于第1种直接另存为.config文件,第2种方法就很“文雅”了,在图形界面中保存配置文件,在图形界面中会有“< Save >”选项,如图37.4.4.1所示:

图37.4.4.1 保存配置

通过键盘的“→”键,移动到“< Save >”选项,然后按下回车键,打开文件名输入对话框,如图37.4.4.2所示:

图37.4.4.2 输入文件名

在图37.4.4.2中输入要保存的文件名,可以带路径,一般是相对路径(相对于Linux内核源码根目录)。比如我们要将新的配置文件保存到目录arch/arm/configs下,文件名为imx_alientek_emmc_defconfig,也就是用新的配置文件替换掉老的默认配置文件。那么我们在图37.4.4.2中输入“arch/arm/configs/imx_alientek_emmc_defconfig”即可,如图37.4.4.3所示:

图37.4.4.3 输入文件名

设置好文件名以后选择下方的“< Ok >”按钮,保存文件并退出。退出以后再打开imx_alientek_emmc_defconfig文件,就会在此文件中找到“CONFIG_SMSC_PHY=y”这一行,如图37.4.4.4所示:

图37.4.4.4 新的配置文件

同样的,使用“make imx_alientek_emmc_defconfig”重新配置Linux内核的时候,LAN8720A的驱动就会使能,并被编译进Linux镜像文件zImage中。

关于Linux内核的移植就讲解到这里,简单总结一下移植步骤:

①、在Linux内核中查找可以参考的板子,一般都是半导体厂商自己做的开发板。

②、编译出参考板子对应的zImage和.dtb文件。

③、使用参考板子的zImage文件和.dtb文件在我们所使用的板子上启动Linux内核,看能否启动。

④、如果能启动的话就万事大吉,如果不能启动那就悲剧了,需要调试Linux内核。不过一般都会参考半导体官方的开发板设计自己的硬件,所以大部分情况下都会启动起来。启动Linux内核用到的外设不多,一般就DRAM(Uboot都初始化好的)和串口。作为终端使用的串口一般都会参考半导体厂商的Demo板。

⑤、修改相应的驱动,像NAND Flash、EMMC、SD卡等驱动官方的Linux内核都是已经提供好了,基本不会出问题。重点是网络驱动,因为Linux驱动开发一般都要通过网络调试代码,所以一定要确保网络驱动工作正常。如果是处理器内部MAC+外部PHY这种网络方案的话,一般网络驱动都很好处理,因为在Linux内核中是有外部PHY通用驱动的。只要设置好复位引脚、PHY地址信息基本上都可以驱动起来。

⑥、Linux内核启动以后需要根文件系统,如果没有根文件系统的话肯定会崩溃,所以确定Linux内核移植成功以后就要开始根文件系统的构建。

【正点原子Linux连载】第三十七章 Linux内核移植 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。