Linux内核移植

2023-11-08

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

创建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 开发板上的。

修改顶层Makefile

修改顶层Makefile,直接在顶层Makefile 文件里面定义ARCH(架构) 和CROSS_COMPILE(交叉编译器) 这两个的变量值为arm 和arm-linux-gnueabihf-,结果如图37.2.1 所示:
在这里插入图片描述
图37.2.1 中第252 和253 行分别设置了ARCH 和CROSS_COMPILE 这两个变量的值,这样在编译的时候就不用输入很长的命令了。

配置并编译Linux内核(生成zImage和.dtb)

和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 所示:
在这里插入图片描述
配置完成以后就可以编译了,使用如下命令编译Linux 内核:

make -j16 //编译Linux 内核

等待编译完成,结果如图37.2.2.2 所示:
在这里插入图片描述
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。

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 内核已经启动了,如果EMMC 中的根文件系统存在,我们就可以进入到Linux 系统里面使用命令进行操作如图37.2.3.2 所示:
在这里插入图片描述

根文件系统缺失错误

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 中最后会有下面这一行:

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

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

在Linux中添加自己的开发板

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

添加开发板默认配置文件(_defconfig)

将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 所示:
在这里插入图片描述
以后就可以使用如下命令来配置正点原子EMMC 版开发板对应的Linux 内核了:

make imx_alientek_emmc_defconfig

添加开发板对应的设备树文件(.dts)

添加适合正点原子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 文件。

修改Makefile

imx6ull-alientek-emmc.dts创建好以后我们还需要修改文件rch/arm/boot/dts/Makefile ,找到“dtb-$(CONFIG_SOC_IMX6ULL)”配置项,在此配置项中加入“imx6ull-alientek-emmc.dtb”,如下所示:

400 dtb-$(CONFIG_SOC_IMX6ULL) += \   //注意\前有空格,\后面不能有空格
401 imx6ull-14x14-ddr3-arm2.dtb \
402 imx6ull-14x14-ddr3-arm2-adc.dtb \
403 imx6ull-14x14-ddr3-arm2-cs42888.dtb \
404 imx6ull-14x14-ddr3-arm2-ecspi.dtb \
405 imx6ull-14x14-ddr3-arm2-emmc.dtb \
406 imx6ull-14x14-ddr3-arm2-epdc.dtb \
407 imx6ull-14x14-ddr3-arm2-flexcan2.dtb \
408 imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \
409 imx6ull-14x14-ddr3-arm2-lcdif.dtb \
410 imx6ull-14x14-ddr3-arm2-ldo.dtb \
411 imx6ull-14x14-ddr3-arm2-qspi.dtb \
412 imx6ull-14x14-ddr3-arm2-qspi-all.dtb \
413 imx6ull-14x14-ddr3-arm2-tsc.dtb \
414 imx6ull-14x14-ddr3-arm2-uart2.dtb \
415 imx6ull-14x14-ddr3-arm2-usb.dtb \
416 imx6ull-14x14-ddr3-arm2-wm8958.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 文件了。

编译测试

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

编译完成以后就会在目录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 8080000083000000

只要出现如图37.3.3.1 所示内容就表示Linux 内核启动成功:
在这里插入图片描述
Linux 内核启动成功,说明我们已经在NXP 提供的Linux 内核源码中添加了正点原子I.MX6UL-ALPHA 开发板。

CPU主频和网络驱动修改(方便以后用网络构建根文件、调试驱动代码)

CPU主频修改

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

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

确保EMMC 中的根文件系统可用!然后重新启动开发板,进入终端(可以输入命令),如图37.4.1.1 所示:
在这里插入图片描述
进入图37.4.1.1 所示的命令行以后输入如下命令查看cpu 信息:

cat /proc/cpuinfo

结果如图37.4.2 所示:
在这里插入图片描述
在图37.4.1.2 中有BogoMIPS 这一条,此时BogoMIPS 为3.00,BogoMIPS 是Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,BogoMIPS 值就越大。

BogoMIPS 只是粗略的计算CPU 性能,并不十分准确。但是我们可以通过BogoMIPS 值来大致的判断当前处理器的性能。在图37.4.1.2 中并没有看到当前CPU 的工作频率,进入到目录/sys/bus/cpu/devices/cpu0/cpufreq 中,此目录下会有很多文件,如图37.4.1.3 所示:
在这里插入图片描述

此目录中记录了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 频率为198MHz,工作频率很低!其他的值如下:

cpuinfo_cur_freq = 198000
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 和792000 四种频率切换,其中调频策略为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 在198MHz、396MHz、528MHz 和792MHz 都工作过,其中198MHz 的工作时间最长!

假如我们想让CPU 一直工作在792MHz 那该怎么办?很简单,配置Linux 内核,将调频策略选择为performance。或者修改imx_alientek_emmc_defconfig 文件,此文件中有下面几行:

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 策略。
将示例代码37.4.1.1 中的第41 行屏蔽掉,然后在44 行后面添加:

CONFIG_CPU_FREQ_GOV_ONDEMAND=y

结果下所示:

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 文件的值,如图37.4.1.6 所示:
在这里插入图片描述
从图37.4.1.6 可以看出,当前CPU 频率为792MHz 了。查看scaling_governor 文件看一下当前的调频策略,如图37.4.1.7 所示:
在这里插入图片描述
从图37.4.1.7 可以看出当前的CPU 调频策略为preformance,也就是高性能模式,一直以最高主频运行。

我们再来看一下如何通过图形化界面配置Linux 内核的CPU 调频策略:

输入“make menuconfig”打开Linux 内核的图形化配置界面,如图37.4.1.8 所示:

在这里插入图片描述
进入如下路径:

CPU Power Management
	-> CPU Frequency scaling
		-> Default CPUFreq governor

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

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

超频至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,找到下面代码:

54 cpu0: cpu@0 {
55 compatible = "arm,cortex-a7";
56 device_type = "cpu";
57 reg = <0>;
58 clock-latency = <61036>; /* two CLK32 periods */
59 operating-points = <
60 /* kHz uV */
61 996000 1275000
62 792000 1225000
63 528000 1175000
64 396000 1025000
65 198000 950000
66 >;
67 fsl,soc-operating-points = <
68 /* KHz uV */
69 996000 1175000
70 792000 1175000
71 528000 1175000
72 396000 1175000
73 198000 1175000
74 >;

示例代码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 的支持,修改以后代码如下:

54 cpu0: cpu@0 {
55 compatible = "arm,cortex-a7";
56 device_type = "cpu";
57 reg = <0>;
58 clock-latency = <61036>; /* two CLK32 periods */
59 operating-points = <
60 /* kHz uV */
61 996000 1275000
62 792000 1225000
63 696000 1225000
64 528000 1175000
65 396000 1025000
66 198000 950000
67 >;
68 fsl,soc-operating-points = <
69 /* KHz uV */
70 996000 1175000
71 792000 1175000
72 696000 1175000
73 528000 1175000
74 396000 1175000
75 198000 1175000
76 >;

第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.11 可以看出,此时支持了696MHz。如果设置调频策略为performance,那么处理器就会一直工作在696MHz。可以对比一下工作在528MHz 和696MHz 下的BogoMIPS 的值,528MHz 主频下的BogoMIPS 值如图37.4.1.12 所示:
在这里插入图片描述
696MHz 主频下的BogoMIPS 值如图37.4.1.13 所示:
在这里插入图片描述
从图37.4.1.12 和图37.2.1.13 中可以看到,528MHz 和696MHz 下的BogoMIPS 值分别为8.00 和10.54,相当于性能提升了(10.54/8)-1=31.75%。

使能8线EMMC驱动

正点原子EMMC 版本核心板上的EMMC 采用的8 位数据线,原理图如图37.4.2.1 所示:
在这里插入图片描述
Linux 内核驱动里面EMMC 默认是4 线模式的,4 线模式肯定没有8 线模式的速度快,所以本节我们将EMMC 的驱动修改为8 线模式。修改方法很简单,直接修改设备树即可,打开文件imx6ull-alientek-emmc.dts,找到如下所示内容(EMMC的外设结点usdhc2):

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

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

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>;//8线
740 non-removable;//不可移除
741 status = "okay";//使能
742 };

修改完成以后保存一下imx6ull-alientek-emmc.dts,然后使用命令

make dtbs

重新编译一下设备树,编译完成以后使用新的设备树重启Linux 系统即可。

修改网络驱动

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

自己的理解:UBOOT需要驱动网络进行一系列的测试,启动完内核后自己的使命就结束了,那网络驱动的任务要移交给内核,所以内核也是需要驱动网络的。

网络驱动在Linux里面是一件很复杂的事情,这里我们只要把它调通就可以了,后面讲驱动会深入学习。

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

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

584 pinctrl_spi4: spi4grp {
585 fsl,pins = <
586 MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
587 MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1
588 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1
589 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000
590 >;
591 };

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

125 spi4 {
126 compatible = "spi-gpio";
127 pinctrl-names = "default";
128 pinctrl-0 = <&pinctrl_spi4>;
129 pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
......
133 cs-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”的节点内容如下:

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

第1 行,imx6ull-alientek-emmc.dts 文件中iomuxc_snvs 节点。
第45~50 行,ENET1 网络复位引脚配置信息。
第53~58 行,ENET2 网络复位引脚配置信息。

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

309 pinctrl_enet1: enet1grp {
310 fsl,pins = <
311 MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
312 MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
313 MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
314 MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
315 MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
316 MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
317 MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
318 MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009
319 >;
320 };
321
322 pinctrl_enet2: enet2grp {
323 fsl,pins = <
324 MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0
325 MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
326 MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
327 MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
328 MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
329 MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
330 MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
331 MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
332 MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
333 MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009
334 >;
335 };

第318 和333 行,分别为ENET1 和ENET2 的网络时钟引脚配置信息,将这两个引脚的电气属性值改为0x4001b009,原来默认值为0x4001b031。
修改完成以后记得保存一下imx6ull-alientek-emmc.dts,网络复位以及时钟引脚驱动就修改好了。

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

1 &fec1 {
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_enet1
4 &pinctrl_enet1_reset>;
5 phy-mode = "rmii";
......
9 status = "okay";
10 };
11
12 &fec2 {
13 pinctrl-names = "default";
14 pinctrl-0 = <&pinctrl_enet2
15 &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 中找到如下代码:

171 &fec1 {
172 pinctrl-names = "default";
......
175 phy-handle = <&ethphy0>;
176 status = "okay";
177 };
178
179 &fec2 {
180 pinctrl-names = "default";
......
183 phy-handle = <&ethphy1>;
184 status = "okay";
185
186 mdio {
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 改为如下内容:

171 &fec1 {
172 pinctrl-names = "default";
173 pinctrl-0 = <&pinctrl_enet1
174 &pinctrl_enet1_reset>;
175 phy-mode = "rmii";
176 phy-handle = <&ethphy0>;
177 phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
178 phy-reset-duration = <200>;
179 status = "okay";
180 };
181
182 &fec2 {
183 pinctrl-names = "default";
184 pinctrl-0 = <&pinctrl_enet2
185 &pinctrl_enet2_reset>;
186 phy-mode = "rmii";
187 phy-handle = <&ethphy1>;
188 phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
189 phy-reset-duration = <200>;
190 status = "okay";
191
192 mdio {
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 中加入如下代码:

3438 static int
3439 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_CLK
3453 * 这两个IO的复用寄存器的SION位为1。
3454 */
3455 void __iomem *IMX6U_ENET1_TX_CLK;
3456 void __iomem *IMX6U_ENET2_TX_CLK;
3457
3458 IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC, 4);
3459 writel(0X14, IMX6U_ENET1_TX_CLK);
3460
3461 IMX6U_ENET2_TX_CLK = ioremap(0X020E00FC, 4);
3462 writel(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 中选择将“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函数内容如下所示:

1 static int smsc_phy_reset(struct phy_device *phydev)
2 {
3 int err, phy_reset;
4 int msec = 1;
5 struct device_node *np;
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 -a”来查看一下开发板中存在的所有网卡,结果如图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 中可以看到“SMSC LAN8710/LAN8720”字样,说明当前的网络驱动使用的就是我们前面使能的SMSC 驱动。
再次输入“ifconfig”命令来查看一下当前活动的网卡,结果如图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 所示:
在这里插入图片描述
可以看出,ping 成功,说明网络驱动修改成功!我们在后面的构建根文件系统和Linux驱动开发中就可以使用网络调试代码啦。

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

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

CONFIG_SMSC_PHY=y

打开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 所示:

在这里插入图片描述
通过键盘的“→”键,移动到“< Save >”选项,然后按下回车键,打开文件名输入对话框,如图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 所示:
在这里插入图片描述
设置好文件名以后选择下方的“< Ok >”按钮,保存文件并退出。退出以后再打开imx_alientek_emmc_defconfig 文件,就会在此文件中找到“CONFIG_SMSC_PHY=y”这一行,如图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内核移植成功以后就要开始根文件系统的构建。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux内核移植 的相关文章

  • 抑制 makefile 中命令调用的回显?

    我为一个作业编写了一个程序 该程序应该将其输出打印到标准输出 分配规范需要创建一个 Makefile 当调用它时make run gt outputFile应该运行该程序并将输出写入一个文件 该文件的 SHA1 指纹与规范中给出的指纹相同
  • 使用 grep 查找包含所有搜索字符串的行

    我有一个文件 其中包含很多与此类似的行 id 2796 some model Profile message type MODEL SAVE fields account 14 address null modification times
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • 如何根据 HTTP 请求使用 Python 和 Flask 执行 shell 命令并流输出?

    下列的这个帖子 https stackoverflow com questions 15092961 how to continuously display python output in a webpage 我能够tail f网页的日志
  • 无法从 jenkins 作为后台进程运行 nohup 命令

    更新 根据下面的讨论 我编辑了我的答案以获得更准确的描述 我正在尝试从詹金斯运行 nohup 命令 完整的命令是 nohup java jar home jar server process 0 35 jar prod gt gt var
  • 从 Python 调用 PARI/GP

    我想打电话PARI GP http pari math u bordeaux fr dochtml gpman html仅从Python计算函数nextprime n 对于不同的n是我定义的 不幸的是我无法得到帕里蟒蛇 http code
  • Linux中的定时器类

    我需要一个计时器来以相对较低的分辨率执行回调 在 Linux 中实现此类 C 计时器类的最佳方法是什么 有我可以使用的库吗 如果您在框架 Glib Qt Wx 内编写 那么您已经拥有一个具有定时回调功能的事件循环 我认为情况并非如此 如果您
  • 使用 sed 更新 xml 属性(Windows + cygwin 和 Linux)?

    我需要使用 sed 命令对 xml 文件进行更新 但我在这方面遇到了麻烦 它需要在 Windows 使用 cygwin 和 Linux 上运行 XML 具有以下元素
  • Linux 中的动态环境变量?

    Linux 中是否可以通过某种方式拥有动态环境变量 我有一个网络服务器 网站遵循以下布局 site qa production 我想要一个环境变量 例如 APPLICATION ENV 当我在 qa 目录中时设置为 qa 当我在生产目录中时
  • 如何在数组中存储包含双引号的命令参数?

    我有一个 Bash 脚本 它生成 存储和修改数组中的值 这些值稍后用作命令的参数 对于 MCVE 我想到了任意命令bash c echo 0 0 echo 1 1 这解释了我的问题 我将用两个参数调用我的命令 option1 without
  • nslookup 报告“无法解析 '(null)': 名称无法解析”,尽管它成功解析了 DNS 名称

    我在 ubuntu 上 并且正在运行 docker 默认桥接网络 我有 Zookeeper kafka 的容器化版本 以及我编写的与 kafka 对话的应用程序 I do a docker exec it
  • Linux 内核标识符中前导和尾随下划线的含义是什么?

    我不断遇到一些小约定 比如 KERNEL Are the 在这种情况下 是内核开发人员使用的命名约定 还是以这种方式命名宏的语法特定原因 整个代码中有很多这样的例子 例如 某些函数和变量以 甚至 这有什么具体原因吗 它似乎被广泛使用 我只需
  • CentOS:无法安装 Chromium 浏览器

    我正在尝试在 centOS 6 i 中安装 chromium 以 root 用户身份运行以下命令 cd etc yum repos d wget http repos fedorapeople org repos spot chromium
  • NPTL 和 POSIX 线程有什么区别?

    NPTL 和 POSIX 线程之间的基本区别是什么 这两者是如何演变的 POSIX 线程 pthread 不是一个实现 它是几个函数的 API 规范 纸上的标准 英文 其名称以pthread 以及定义在
  • Linux 可执行文件与 OS X“兼容”吗?

    如果您在基于 Linux 的平台上用 C 语言编译一个程序 然后将其移植以使用 MacOS 库 它会工作吗 来自编译器的核心机器代码在 Mac 和 Linux 上兼容吗 我问这个问题的原因是因为两者都是 基于 UNIX 的 所以我认为这是真
  • jpegtran 优化而不更改文件名

    我需要优化一些图像 但不更改它们的名称 jpegtran copy none optimize image jpg gt image jpg 但是 这似乎创建了 0 的文件大小 当我对不同的文件名执行此操作时 大小仍然完全相同 怎么样 jp
  • 如何授予 apache 使用 NTFS 分区上的目录的权限?

    我在一台带有 20GB 硬盘的旧机器上运行 Linux Lubutu 12 10 我有一个 1 TB 外部硬盘 上面有一个 NTFS 分区 在该分区上 有一个 www 目录 用于保存我的网页内容 它在启动时自动安装为 media t515
  • 为什么我可以直接从 bash 执行 JAR?

    我是一个长期从事 Java 工作的人 并且知道运行带有主类的 JAR 的方法MANIFEST MFJar 中的文件很简单 java jar theJar jar 我用它来启动 Fabric3 服务器 包含在bin server jar在其标
  • 将 jar 作为 Linux 服务运行 - init.d 脚本在启动应用程序时卡住

    我目前正在致力于在 Linux VM 上实现一个可运行的 jar 作为后台服务 我已经使用了找到的例子here https gist github com shirish4you 5089019作为工作的基础 并将 start 方法修改为
  • 如何更改 Apache 服务器的根目录? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何更改 Apache 服务器的文档根目录 我基本上想要localhost从 来 users spencer projects目录而不是

随机推荐

  • mysql数据库的安装和卸载(windows10)

    数据库安装 官网下载ZIP压缩包 解压到D MySQL mysql 5 6 40 winx64 打开刚刚解压的文件夹 D MySQL mysql 5 6 40 winx64 里面有一个系统自带的配置文件 my default ini 复制该
  • android ndk NEON Support

    NEON Support On this page Using LOCAL ARM NEON Using the neon Suffix Build Requirements Runtime Detection Sample Code Th
  • 从技术的角度Struts1.1与WebWork2的比较

    从技术的角度Struts1 1与WebWork2的比较 标签 action webwork struts 拦截器 验证 从技术的角度Struts1 1与WebWork2的比较 特 征 Struts1 1 WebWork2 Action类 在
  • rootkit模拟木马病毒

    Rootkit是一种特殊的恶意软件 它的功能是在安装目标上隐藏自身及指定的文件 进程和网络链接等信息 比较多见到的是Rootkit一般都和木马 后门等其他恶意程序结合使用 而我们今天要模拟学习的就是与它很像的恶意软件 Rootkit 其中之
  • 【难受】SpirngBoot-Alibaba-nacos跨服务器访问接口的问题

    原想法 我首先准备了 一个网关 2个服务 分别将两个服务部署到不同的远程服务器当中 实现跨服务器访问接口 网关为本地调用 这里就不一一介绍了 问题 利用gateway做路由时出现服务不可用的情况 看日志发现服务调用的IP是172开头的网卡段
  • Sqoop安装与配置

    Sqoop安装与配置 一 了解Sqoop 二 下载Sqoop安装包 三 安装Sqoop 四 配置Sqoop 五 Sqoop基本命令 六 示例 一 了解Sqoop sqoop 是 Hadoop 和关系数据库服务器之间传送数据的工具 主要用于在
  • centos7 kafka安装并安装web界面监控工具

    kafka自带zookeeper 所以不需要下载zookeeper 1 下载 wget http mirrors shu edu cn apache kafka 2 0 0 kafka 2 12 2 0 0 tgz 2 安装 tar zxv
  • ACMP,二维狄洛尼三角剖分

    ACMP cpp std vector
  • python爬虫文字加密_Python爬虫进阶必备

    此次来分析某个小说网站 aHR0cHM6Ly9nLmhvbmdzaHUuY29tL2NvbnRlbnQvOTM0MTYvMTM4Nzc5MTIuaHRtbA node 分析请求 先来看看页面的请求 图1 1 数组 图1 1 通过查看请求 并
  • Error creating bean with name ‘org.apache.cxf.jaxws.spring.NamespaceHandler$SpringServerFactoryBean

    目录 问题描述 解决过程 总结 问题描述 我是在spring整合jaxws 使用webservice的时候报错的 解决过程 这个问题说实话卡了我很久 一直没找着原因 其实但看这个报错就能看出来 有个文件注入不了容器 我一直以为是配置问题 修
  • 强化学习代码练习q-learning-迷宫

    相比上一个demo 这个练习的环境更加复杂 但是就强化学习智能体而言 其整体是一样的 但是既然环境更加复杂 就需要把智能体和环境单独拉出来写 不能再放一个Python文件中 环境类 环境类总结起来就是定义了初始化的参数 构建迷宫 重置函数
  • 设计模式C++学习笔记之一(Strategy策略模式)

    http www cnblogs com wanggary archive 2011 04 07 2008796 html 无意中 从网上下到一本电子书 24种设计模式介绍与6大设计原则 很好奇这里有24种设计模式 印象中GOF写的 设计模
  • CTFSHOW网络迷踪-低碳环保

    记录一个解过的一道OSINT题目 低碳环保 题目来源 CTFshow 题目 解题 先下载附件 得到如图 首先尝试百度识图 但是识别不到 然后我看到右边建筑上方有 奉献清洁能源 几个红字 尝试搜索 搜索到了各种公司 还是没头绪 但是经过观察
  • 租车骑绿岛【C语言】

    租车骑绿岛 部门组织绿岛骑行团建活动 租用公共双人自行车 每辆自行车最多坐两人 最大载重m 给出部门每个人的体重 请问最多需要租用多少双人自行车 输入描述 第一行两个数字m n 分别代表自行车限重 部门总人数 第二行 n个数字 代表每个人的
  • JPA freemaker动态的拼接SQL

    spring data jpa extra https github com slyak spring data jpa extra spring data jpa template 项目地址 https gitee com silentw
  • cJSON解析JSON字符串

    一 为何选择cJSON 我们在使用JSON格式时 如果只是处理简单的协议 可以依据JSON格式 通过对字符串的操作来进行解析与创建 然而随着协议逐渐复杂起来 经常会遇到一些未考虑周全的地方 需要进一步的完善解析方法 此时 使用比较完善的JS
  • <>读书笔记

    lt
  • MySQL+jdbc理论考试【无答案】

    单选 共15题 每题2分 共30分 1 下面关于mysql的说法正确的是 A 默认的端口号是1521 B 默认的端口号是80 C 默认的端口号是3306 D 默认的端口号是443 2 下面排序的说法正确的是 A 默认是升序排序 B asc是
  • Hbuild点击发行,没有反应

    根目录下有 manifest json pages json 等等 才可以打包 换句话说 打开uniapp的文件时 要打开目录下有manifest json pages json的文件 文件上层不要再套一层文件
  • Linux内核移植

    目录 创建VSCode 工程 NXP官方开发板Linux 内核编译 修改顶层Makefile 配置并编译Linux内核 生成zImage和 dtb Linux 内核启动测试 根文件系统缺失错误 在Linux中添加自己的开发板 添加开发板默认