目录
前言
在上一讲实验里,Linux 内核移植(上),我们先将NXP官方的linux编译了一下,熟悉linux的编译流程;又在linux中添加自己的开发板,也就是正点原子的 I.MX6U-ALPHA 开发板。
本讲实验继续未完成的linux移植工作,也就是对CPU 主频和网络驱动进行修改。
CPU 主频修改
正点原子 I.MX6U-ALPHA 开发板所使用的 I.MX6ULL 芯片主频都是 792MHz 的。
我们需要设置 I.MX6U-ALPHA 开发板工作在 792MHz。
查看CPU频率
首先,确保 EMMC 中的根文件系统可用!然后重新启动开发板,进入终端(可以输入命令),如图:
输入cat 命令查看 cpu 信息:
可以看出:BogoMIPS 为 3.00, BogoMIPS 是 Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高, BogoMIPS 值就越大。
BogoMIPS 只是粗略的计算 CPU 性能,但不能直接看出CPU的频率。
所以进入到目录/sys/bus/cpu/devices/cpu0/cpufreq 中,此目录下会有很多文件,如图:
此目录中记录了 CPU 频率等信息,这些文件的含义如下:
文件/参数 |
描述 |
单位 |
---|---|---|
|
当前 CPU 工作频率(直接从 CPU 寄存器读取) |
KHz |
|
处理器支持的最高工作频率(硬件限制) |
KHz |
|
处理器支持的最低工作频率(硬件限制) |
KHz |
|
处理器切换频率所需的时间 |
纳秒 (ns) |
|
处理器支持的可用频率列表(所有可选的频率值) |
KHz |
|
当前内核支持的所有调频策略(governor)类型 |
无 |
|
cpufreq 模块缓存的当前 CPU 频率(不直接读取硬件寄存器) |
KHz |
|
当前 CPU 使用的调频驱动 |
无 |
|
当前正在使用的调频策略(如 |
无 |
|
当前调频策略(governor)可以调节的最高频率(可能低于 |
KHz |
|
当前调频策略(governor)可以调节的最低频率(可能高于 |
KHz |
如果需要查询或修改这些值,可以使用以下命令:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # 查看当前调频策略
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor # 设置为性能模式
将上面所提到的值都看一遍,如下:
# 当前CPU实际运行频率(直接从硬件寄存器读取)
cpuinfo_cur_freq = 198000 # 当前运行在198MHz(最低频率)
# CPU硬件支持的最大/最小频率范围
cpuinfo_max_freq = 792000 # 硬件支持的最高频率792MHz
cpuinfo_min_freq = 198000 # 硬件支持的最低频率198MHz
# cpufreq子系统当前使用的频率值
scaling_cur_freq = 198000 # 内核记录的当前频率198MHz(与cpuinfo_cur_freq一致)
# 当前调频策略允许的范围
scaling_max_freq = 792000 # 调频策略允许设置的最高频率(与硬件上限一致)
scaling_min_freq = 198000 # 调频策略允许设置的最低频率(与硬件下限一致)
# 该CPU支持的所有可用频率档位
scaling_available_frequencies = 198000 396000 528000 792000
# 可用频率档位:198MHz, 396MHz, 528MHz, 792MHz(共4档)
# 当前使用的调频策略
scaling_governor = ondemand
# 使用"按需调节"策略:
# - 当系统负载升高时自动提升频率
# - 空闲时自动降频到最低档
# - 在性能与功耗间取得平衡
CPU调频策略
Linux 内核一共有 5 种调频策略:
- Performance,最高性能,直接用最高频率,不考虑耗电。
- Interactive,一开始直接用最高频率,然后根据 CPU 负载慢慢降低。
- Powersave,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!
- Userspace,可以在用户空间手动调节频率。
- Ondemand,定时检查负载,然后根据负载来调节频率。负载低的时候降低 CPU 频率,这样省电,负载高的时候提高 CPU 频率,增加性能。
查看CPU各频率工作时间
查看 stats 目录下的 time_in_state 文件可以看到 CPU 在各频率下的工作时间,命令如下:
cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state
结果如图:
可以看出, CPU 在 198MHz、 396MHz、 528MHz 和 792MHz 都工作过,其中 198MHz 的工作时间最长。
当前我们开发板并没有做什么工作,因此 CPU 频率降低为 198MHz 以省电。
配置CPU频率
假如我们想让 CPU 一直工作在 792MHz,可以:
- 配置 Linux 内核,将调频策略选择为 performance。
- 或者修改 imx_alientek_emmc_defconfig 文件。
imx_alientek_emmc_defconfig文件中有下面几行:
# 默认的频率调节策略配置为ondemand(按需调节)
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
# 说明:内核启动时默认使用ondemand策略,该策略会根据CPU负载动态调整频率,负载高时升频,空闲时降频,平衡性能和功耗
# 启用powersave(节能)策略
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
# 说明:该策略会始终让CPU运行在最低频率,最大化节省功耗,适用于对性能要求不高的场景
# 启用userspace(用户空间)策略
CONFIG_CPU_FREQ_GOV_USERSPACE=y
# 说明:允许用户空间程序通过/sys接口直接设置CPU频率,需要配合cpufreqd等守护进程使用
# 启用interactive(交互式)策略
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
# 说明:针对交互式设备(如手机/平板)优化的策略,比ondemand更激进,能更快响应突发负载, 但功耗也相对较高
将这几行代码,修改为如下:
#CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
修改完成以后重新编译 Linux 内核,编译之前先清理一下工程。因为我们重新修改过默认配 置文 件了, 编译完成以后使用新的 zImage 镜像文件重新启动 Linux。
再次查看/sys/devices/system/cpu/cpu0/cpufreq/ cpuinfo_cur_freq 文件的值,如图:
可以看出,当前 CPU 频率为 792MHz 了。
查看 scaling_governor 文件看一下当前的调频策略,如图:
可以看出当前的 CPU 调频策略为 preformance,也就是高性能模式,一直以最高主频运行。
通过图形化界面配置 Linux 内核的 CPU 调频策略,输入“ make menuconfig”打开 Linux 内核的图形化配置界面,如图:
进入如下路径:
CPU Power Management -> CPU Frequency scaling -> Default CPUFreq governor
打开默认调频策略选择界面,选择“performance”,如图:
选择以后退出图形化配置界面,然后编译 Linux内核,记得不要清理工程,否则的话我们刚刚的设置就会被清理掉。
编译完成以后使用新的zImage 重启 Linux,查看当前 CPU 的工作频率和调频策略。
另外,在实际开发中,建议大家使用 ondemand 模式,一来可以省电,二来可以减少发热。
修改 EMMC 驱动
使能 8 线 EMMC 驱动
正点原子 EMMC 版本核心板上的 EMMC 采用的 8 位数据线,原理图如下:
Linux 内核驱动里面 EMMC 默认是 4 线模式的, 4 线模式肯定没有 8 线模式的速度快,所以本讲我们将 EMMC 的驱动修改为 8 线模式。
修改方法很简单,直接修改设备树即可,打开文件 imx6ull-alientek-emmc.dts,找到如下所示内容:
&usdhc2 { // 引用usdhc2节点(在芯片的dtsi中已定义)
pinctrl-names = "default"; // 指定引脚控制状态名称(默认状态)
pinctrl-0 = <&pinctrl_usdhc2>; // 关联具体的引脚配置组(由pinctrl_usdhc2定义)
non-removable; // 标记设备不可热插拔(如eMMC芯片)
status = "okay"; // 启用该控制器
};
将这一段代码修改为:
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz"; // 定义3种引脚状态
pinctrl-0 = <&pinctrl_usdhc2_8bit>; // 默认频率引脚配置
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>; // 100MHz模式引脚配置
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>; // 200MHz模式引脚配置
bus-width = <8>; // 8位数据总线(eMMC典型配置)
non-removable; // 设备不可热插拔(eMMC特性)
status = "okay"; // 启用控制器
};
关闭 EMMC 1.8V 供电选项
由原理图可以看出,此时 EMMC 工作电压是 3.3V 的。
因此我们还需要在usdhc2 节点内容中,关闭 1.8V 这个功能选项。 防止内核在运行的时候用 1.8V 去驱动 EMMC,导致 EMMC 驱动出现问题。
修改后的 usdhc2 节点内容如下:
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz"; // 多频率状态支持
pinctrl-0 = <&pinctrl_usdhc2_8bit>; // 默认模式引脚配置(通常50MHz)
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>; // 100MHz高速模式
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>; // 200MHz超高速模式
bus-width = <8>; // 8位数据总线(eMMC标准)
non-removable; // 非热插拔设备(eMMC特性)
no-1-8-v; // 禁用1.8V低电压模式
status = "okay"; // 启用控制器
};
修改完成以后保存一下 imx6ull-alientek-emmc.dts,然后使用命令“make dtbs”重新编译一下设备树,编译完成以后使用新的设备树重启 Linux 系统即可。
网络驱动修改
在讲解 uboot 移植的时候,U-Boot 移植(下),我们已经知道,正点原子开发板的网络和 NXP 官方的网络硬件上不同:
- 网络 PHY 芯片由 KSZ8081 换为了 SR8201F,
- 两个网络 PHY 芯片的复位 IO 也不同。
所以 Linux 内核自带的网络驱动是驱动不起来 I.MX6U-ALPHA 开发板上的网络的,需要做修改。
修改引脚
ENET1 复位引脚 ENET1_RST 连接在 I.M6ULL 的 SNVS_TAMPER7 这个引脚上。
ENET2的复位引脚 ENET2_RST 连接在 I.MX6ULL 的 SNVS_TAMPER8 上。
打开设备树文件 imx6ull-alientek-emmc.dts,找到如下代码:
将红框中的初始化 SNVS_TAMPER7 和 SNVS_TAMPER8 这两个引脚的代码删除掉。
继续在 imx6ull-alientek-emmc.dts 中找到如下所示代码:
现在我们需要 GPIO5_IO07 和 GPIO5_IO08 分别作为 ENET1 和 ENET2 的复位引脚,而不是 SPI4 的什么功能引脚,所以将红框中的2行代码删除掉,防止干扰到网络复位引脚。
继续在 imx6ull-alientek-emmc.dts 中找到名为“iomuxc_snvs”的节点:
仿照格式在该节点内末尾处,添加网络复位引脚信息:
/*enet1 reset zuozhongkai*/
pinctrl_enet1_reset: enet1resetgrp {
fsl,pins = <
/* used for enet1 reset */
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
>;
};
/*enet2 reset zuozhongkai*/
pinctrl_enet2_reset: enet2resetgrp {
fsl,pins = <
/* used for enet2 reset */
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
>;
};
继续在 imx6ull-alientekemmc.dts 中找到如下所示代码:
红框中的2行代码,分别为 ENET1 和 ENET2 的网络时钟引脚配置信息,将这两个引脚的电气属性值改为 0x4001b031(原来默认值就是 0x4001b031)。
修改完成以后记得保存一下 imx6ull-alientek-emmc.dts,网络复位以及时钟引脚驱动就修改好了。
修改pinctrl-0 属性
在 imx6ull-alientek-emmc.dts 文件中找到名为“fec1”和“fec2”的这两个节点,修改其中的“pinctrl-0”属性值,修改以后如下所示:
修改PHY 地址
我们说过:
- ENET1 的 LAN8720A 地址为 0x2,
- ENET2 的 LAN8720A地址为 0x1。
在 imx6ull-alientek-emmc.dts 中找到如下代码:
像上图一样,添加上这两行代码:
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
同理,在fec2节点中,添加如下代码:
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
另外,继续添加代码:
smsc,disable-energy-detect
最后这段代码如下图:
ENET1 网络复位引脚所使用的 IO 为 GPIO5_IO07,低电平有效。复位低电平信号持续时间为 200ms。
ENET2 网络复位引脚所使用的 IO 为 GPIO5_IO08,同样低电平有效,持续时间同样为 200ms。
“smsc,disable-energy-detect”表明 PHY 芯片是 SMSC 公司的,这样 Linux内核就会找到 SMSC 公司的 PHY 芯片驱动来驱动 LAN8720A。
注意“ethernet-phy@”后面的数字是 PHY 的地址, ENET1 的 PHY 地址为 2,ENET2 的 PHY 地址为 1.
修改 fec_main.c 文件
要在 I.MX6ULL 上使用SR8201F , 需要修改一下 Linux 内核源码 。
打开drivers/net/ethernet/freescale/fec_main.c,找到函数 fec_reset_phy,在 fec_reset_phy 函数中加入如下代码:
根据 SR8201F 收据手册上的要求, SR8201F 在复位结束以后需要等待至少 150ms 才能操作 SR8201F,因此这里添加了一个 200ms 的延时。
网络驱动测试
修改好设备树和 Linux 内核以后重新编译一下,得到新的 zImage 镜像文件和 imx6ullalientek-emmc.dtb 设备树文件:
cd arch/arm/boot/dts进入这个路径下,找到dts文件:
使用网线将 I.MX6U-ALPHA 开发板的两个网口与路由器或者电脑连接起来,最后使用新的文件启动 Linux 内核。
tftp 80800000 zImage
tftp 83000000 imx6ull-alientek-emmc.dtb
bootz 80800000 – 83000000
启动以后,输入命令“ifconfig -a”来查看一下开发板中存在的所有网卡,结果如图:
eth0 和 eth1 是网络接口的网卡,其中 eth0 对应于 ENET2, eth1 对应于 ENET1。
使用如下命令依次打开 eth0 和 eth1 这两个网卡:
ifconfig eth0 up
ifconfig eth1 up
网卡的打开过程如图:
可以看到“Generic PHY”字样,说明当前的网络驱动使用的就是 Linux 内核自带的通用网络 PHY 驱动。
再次输入“ifconfig”命令来查看一下当前活动的网卡,结果如图:
可以看出,此时 eth0 和 eth1 两个网卡都已经打开,并且工作正常,但是这两个网卡都还没有 IP 地址,所以不能进行 ping 等操作。
使用如下命令给两个网卡配置 IP 地址:
ifconfig eth0 192.168.1.251
ifconfig eth1 192.168.1.252
设置好以后,使用“ping”命令来 ping 一下自己的主机,如果能 ping 通那说明网络驱动修改成功:
我们在后面的构建根文件系统和 Linux 驱动开发中就可以使用网络调试代码。
关于 Linux 内核的移植,简单总结一下移植步骤:
- 在 Linux 内核中查找可以参考的板子,一般都是半导体厂商自己做的开发板。
- 编译出参考板子对应的 zImage 和.dtb 文件。
- 使用参考板子的 zImage 文件和.dtb 文件在我们所使用的板子上启动 Linux 内核,看能否启动。
- 如果不能启动,那就需要调试 Linux 内核。启动Linux 内核用到的外设不多,一般就 DRAM(Uboot 都初始化好的)和串口。作为终端使用的串口一般都会参考半导体厂商的 Demo 板。
- 修改相应的驱动。重点是网络驱动,如果是处理器内部 MAC+外部 PHY 这种网络方案的话,只要设置好复位引脚、 PHY 地址信息基本上都可以驱动起来。
- Linux 内核启动以后需要根文件系统,如果没有根文件系统的话肯定会崩溃,所以确定 Linux内核移植成功以后就要开始根文件系统的构建。