linux内核移植和uboot移植总体上差不多
环境搭建
解压内核文件,这里改名如图一
安装 lzop 库,否则内核编译会失败,提示“ recipe for target ‘arch/arm/boot/compressed/piggy.lzo
sudo apt-get install lzop
一、修改顶层Makefile
创建打开工程目录
直接在顶层 Makefile 文件里面定义 ARCH 和 CROSS_COMPILE 这两个的变量值为
“arm” 和 “arm-linux-gnueabihf-”
修改如图二
二、配置并编译 Linux 内核
和 uboot 一样,在编译 Linux 内核之前要先配置 Linux 内核。每个板子都有其对应的默认
配 置 文 件 , 这 些 默 认 配 置 文 件 保 存 在 arch/arm/configs 目 录 中
这里使用 imx_v7_mfg_defconfig 这个默认配置文件,首先此配置文件默认支持 I.MX6UL 这款芯片,而且重要的一点就是此文件编译出来的 zImage 可以通过 NXP 官方提供的 MfgTool 工具烧写
imx_v7_mfg_defconfig 中的“mfg”的意思就是 MfgTool
Linux 源码根目录下,执行如下命令配置 Linux 内核:
make clean
make imx_v7_mfg_defconfig
第一次编译 Linux 内核之前先清理一下,然后配置,如图三
配置完成以后就可以编译了,使用如下命令编译 Linux 内核
make -j16
(编译没出现错误直接往下看)
若编译出现make[2]: *** [scripts/Makefile.host:100:scripts/dtc/dtc] 错误 1
make[1]: *** [scripts/Makefile.build:403:scripts/dtc] 错误 2
可能是ubantu版本和gcc版本过高,下面修改一下,输入命令,在640行前面加“extern”
sudo vim scripts/dtc/dtc-lexer.lex.c_shipped
保存后重新编译,如图四
Linux 内核编译完成以后会在 arch/arm/boot 目录下生成 zImage 镜像文件
如果使用设备树的话还会在 arch/arm/boot/dts 目录下开发板对应的.dtb(设备树)文件
至此得到两个文件:
①、 Linux 内核镜像文件: zImage。
②、 NXP 官方 I.MX6ULL EVK 开发板对应的设备树文件: imx6ull-14x14-evk.dtb
三、Linux 内核启动测试
得到这两个文件能不能启动呢?得测试。
编译出来的 zImage 和 imx6ull-14x14-evk.dtb 复制到 Ubuntu 中的 tftp 目录下
因为要在 uboot 中使用 tftp 命令将其下载到开发板中,命令如下,如图五
cp arch/arm/boot/zImage /home/ubantu22/tftpboot/ -f
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/ubantu22/tftpboot/ -f
启动开发板,进入 uboot 命令行模式,确保 uboot 中环境变量 bootargs 内容,输入命令如下:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveent
开发板和ubantu可以ping通
然后输入如下命令,将zImage 和 imx6ull-14x14-evk.dtb 下载到开发板中并启动,如图六
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000
没有根文件系统会卡在“Starting kernel ...”
根文件系统缺失错误
Linux 内核启动以后是需要根文件系统的,根文件系统存在哪里是由 uboot 的 bootargs 环境
变 量 指 定 , bootargs 会 传 递 给 Linux 内 核 作 为 命 令 行 参 数。实际的工作中开发一个产品,这个产品的第一版硬件出来以后是没有对应的根文件系统可用的,必须要自己做根文件系统。在构建出对应的根文件系统之前 Linux 内核是没有根文件系统可用的
没有根文件系统,Linux 内核启动后会出现什么问题呢?将 uboot 中 bootargs 环境变量修改如下
setenv bootargs 'console=ttymxc0,115200'
saveent
修改完成以后重新从网络启动,启动以后会有如图七
最后这行,也就是提示内核崩溃,因为 VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。即使根文件系统目录存在,如果根文件系统目录里面是空的依旧会提示内核崩溃。
这个就是根文件系统缺失导致的内核崩溃,但是内核是启动了的,只是根文件系统不存在而已
这个后续会添加进入
四、在 Linux 中添加自己的开发板
1 添加开发板默认配置文件
将 arch/arm/configs 目 录 下 的 imx_v7_mfg_defconfig 重 新 复 制 一 份 ,
命 名 为imx_my_emmc_defconfig,命令如下:
cd arch/arm/configs
cp imx_v7_mfg_defconfig imx_my_emmc_defconfig
以后imx_my_emmc_defconfig 就是开发板默认配置文件,可以使用来配置开发板对应的 Linux 内核
2 添加开发板对应的设备树文件
进入目录 arch/arm/boot/dts 中,复制一份 imx6ull-14x14-evk.dts
然后将其重命名为 imx6ull-my-emmc.dts,命令如下
cd arch/arm/boot/dts
cp imx6ull-14x14-evk.dts imx6ull-my-emmc.dts
.dts 是设备树源码文件,编译 Linux 的时候会将其编译为.dtb 文件。imx6ull-my-emmc.dts创 建 好 还 需 要 修 改 文 件 arch/arm/boot/dts/Makefile ,找 到 “ dtb-$(CONFIG_SOC_IMX6ULL)”配置项,在此配置项422行中加入“imx6ull-my-emmc.dtb”
这样编译 Linux 的时候就可以从 imx6ull-my-emmc.dts 编译出 imx6ull-my-emmc.dtb 文件了
Linux 内核里面已经添加了开 发 板 了 , 接 下 接 编 译 测 试 一 下 , 创 建 一 个 编 译 脚 本 ,
在内核源码根目录下,创建imx6ull_my_emmc.sh,脚本内容如下:
#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_my_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
第 2 行,清理工程。
第 3 行,使用默认配置文件 imx_my_emmc_defconfig 来配置 Linux 内核。
第 4 行,打开 Linux 的图形配置界面,如果不需要每次都打开图形配置界面可以删除此行。
第 5 行,编译 Linux。
执行 shell 脚本 imx6ull_my_emmc.sh 编译 Linux 内核, 命令如下:
chmod 777 imx6ull_my_emmc.sh
./imx6ull_my_emmc.sh
编译完成以后就会在目录 arch/arm/boot 下生成 zImage 镜像文件
在 arch/arm/boot/dts 目录下生成 imx6ull-my-emmc.dtb 文件
使能 8 线 EMMC 驱动,EMMC 版本核心板上的 EMMC 采用的 8 位数据线,原理图如图
Linux 内核驱动里面 EMMC 默认是 4 线模式的, 4 线模式肯定没有 8 线模式的速度快,所
以将 EMMC 的驱动修改为 8 线模式
直接修改设备树即可,打开文件 imx6ull-my-emmc.dts,找到如下所示内容
打开 arch/arm/boot/dts/imx6ull-my-emmc.dts文件,找到下面内容
然后找到同路径下的imx6ull-14x14-evk-emmc.dts
把usdhc2里面12行到18行的内容复制来替换imx6ull-my-emmc.dts文件的内容,修改如下
修改完之后输入如下命令编译一下设备树
make dtbs
将这zImgae和imx6ull-my-emmc.dts两个文件拷贝到 tftp 目录下
重启开发板,在uboot 命令模式中使用 tftp 命令下载这两个文件并启动,命令如下:
tftp 80800000 zImage
tftp 83000000 imx6ull-my-emmc.dtb
bootz 80800000 – 83000000
如果在启动出现”Bad Linux ARM zImage magic!“
重新修改一下bootcmd和bootargs,保存并重启开发板,重新tftp下载文件并启动内核即可
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-my-emmc.dtb; bootz
80800000 - 83000000'
启动内核后可以看到成功了
五、CPU 主频和网络驱动修改
1、cpu主频修改
开发板所使用的 I.MX6ULL 芯片主频都是 792MHz 的,也就是NXP 官方宣传的 800MHz 版本
确保 EMMC 中的根文件系统可用!然后重新启动开发板,进入终端,输入命令查看 cpu 信息:
cat /proc/cpuinfo
BogoMIPS 这一条,此时 BogoMIPS 为 3.00, BogoMIPS 是 Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高, BogoMIPS 值就越大,只是粗略的计算 CPU 性能,并不十分准确,只能判断大致,而且并没有看到当前 CPU 的工作频率,那用另一种方法查看当前 CPU 的工作频率。进入到目录/sys/bus/cpu/devices/cpu0/cpufreq 中,此目录下会有很多文件
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_max_freq: governor(调频)可以调节的最高频率。
cpuinfo_min_freq: governor(调频)可以调节的最低频率。
scaling_governor: governor(调频)策略, Linux 内核一共有 5 中调频策略:
①、 Performance,最高性能,直接用最高频率,不考虑耗电。
②、 Interactive,一开始直接用最高频率,然后根据 CPU 负载慢慢降低。
③、 Powersave,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!
④、 Userspace,可以在用户空间手动调节频率。
⑤、 Ondemand,定时检查负载,然后根据负载来调节频率。负载低的时候降低 CPU 频率,
这样省电,负载高的时候提高 CPU 频率,增加性能。
stats 目录下给出了 CPU 各种运行频率的统计情况,比如 CPU 在各频率下的运行时间以及
变频次数。
使用如下命令查看当前 CPU 频率
cat cpuinfo_cur_freq
当前 CPU 频率为 198MHz,工作频率很低(不一定和这里的频率一样)
当前 CPU 支持 198MHz、 396MHz、 528Mhz 和 792MHz 四种频率切换,其中调频策略为 ondemand,也就是定期检查负载,然后根据负载情况调节 CPU 频率
查看 stats 目录下的 time_in_state 文件可以看到 CPU 在各频率下的工作时间,命令如下:
cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state
CPU 在 198MHz、 396MHz、 528MHz 和 792MHz 都工作过,其中 198MHz 的工作时间最长
下面通过图形化界面方式配置 Linux 内核的 CPU 调频策略,输入“ make menuconfig”打开 Linux 内核的图形化配置界面,进入如下路径:
CPU Power Management-> CPU Frequency scaling-> Default CPUFreq governor
打开默认调频策略选择界面,选择“Ondemand”
选择以后退出图形化配置界面,提示保存就保存,然后编译 Linux内核,一定不要清理工程!
否则的话我们刚刚的设置就会被清理掉。
重新编译一下
make -j16
编译完成以后使用新的zImage 重启 Linux,查看当前 CPU 的工作频率和调频策略
在这个模式下就会自动调整频率了
2、修改网络驱动
开发板的网络和 NXP 官方的网络硬件上不同,网络 PHY 芯片由 KSZ8081 换为了 LAN8720A,两个网络 PHY 芯片的复位 IO 也不同
①修改 LAN8720 的复位以及网络时钟引脚驱动
ENET1 复位引脚 ENET1_RST 连接在 I.M6ULL 的 SNVS_TAMPER7 这个引脚上。
ENET2的复位引脚 ENET2_RST 连接在 I.MX6ULL 的 SNVS_TAMPER8 上。
打开设备树文件 imx6ull-my-emmc.dts,找到如下代码:
将 588 和 589 这两行删除掉!IO被使用了,这不是想要的,所以删除掉!
删除掉以后继续在 imx6ull-alientek-emmc.dts 中找到如下所示代码:
129 行和第 133 行处的代码删除掉!否则会干扰到网络复位引脚!
继续找到名为“iomuxc_snvs”的节点添加网络复位引脚信息,直接在后面添加,如下
/*enet1 reset */
pinctrl_enet1_reset: enet1resetgrp {
fsl,pins = <
/* used for enet1 reset */
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
>;
};
/*enet2 reset*/
pinctrl_enet2_reset: enet2resetgrp {
fsl,pins = <
/* used for enet2 reset */
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
>;
};
继续在 imx6ull-alientekemmc.dts 中找到pinctrl_enet1: enet1grp
如下所示代码,修改如下
这是修改 ENET1 和 ENET2 的网络时钟引脚配置,原来默认值为 0x4001b031。
修改 fec1 和 fec2 节点的 pinctrl-0 属性
在 imx6ull-alientek-emmc.dts 文件中找到名为“fec1”和“fec2”的这两个节点,修改其中的
“pinctrl-0”属性值,修改以后如下所示:
&pinctrl_enet1_reset>;
&pinctrl_enet2_reset>;
添加176行和183行
②修改 LAN8720A 的 PHY 地址
ENET1 的 LAN8720A 地址为 0x0, ENET2 的 LAN8720A地址为 0x1
继续在fec1/2修改,修改如下
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_enet1_reset>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
status = "okay";
};&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2
&pinctrl_enet2_reset>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
phy-reset-duration = <200>;
status = "okay";mdio {
#address-cells = <1>;
#size-cells = <0>;ethphy0: ethernet-phy@2 {
compatible = "ethernet-phy-ieee802.3-c22";
smsc,disable-energy-detect;
reg = <0>;
};ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
smsc,disable-energy-detect;
reg = <1>;
};
};
};
保存、使用“make dtbs”命令重新编译一下设备树
③修改 fec_main.c 文件
要 在 I.MX6ULL 上 使 用 LAN8720A , 需 要 修 改 一 下 Linux 内 核 源 码 , 打 开
drivers/net/ethernet/freescale/fec_main.c,找到函数 fec_probe,在 fec_probe 中加入如下代码:
void __iomem *IMX6U_ENET1_TX_CLK;
void __iomem *IMX6U_ENET2_TX_CLK;IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC, 4);
writel(0X14, IMX6U_ENET1_TX_CLK);IMX6U_ENET2_TX_CLK = ioremap(0X020E00FC, 4);
writel(0X14, IMX6U_ENET2_TX_CLK);
3455-3462就是新加入代码
在 I.MX6ULL 上使用 LAN8720A 就需要设置ENET1 和 ENET2 的 TX_CLK 引脚复位寄存器的 SION 位为 1
④配置 Linux 内核,使能 LAN8720 驱动
在linux内核源码根目录下输入命令“make menuconfig”,打开图形化配置界面
选择使能 LAN8720A 的驱动,路径如下:
Device Drivers-> Network device support-> PHY Device support and infrastructure
-> Drivers for SMSC PHYs
按下 'y' 键,选择将“Drivers for SMSC PHYs”编译到 Linux 内核中,因此“<>”里面变为了“*”。 LAN8720A 是 SMSC 公司出品的,因此勾选这个以后就会编译 LAN8720 驱动,配置好以后退出配置界面,然后重新编译一下 Linux 内核
⑤修改 smsc.c 文件
使能 LAN8720A 驱动以后就直接使用。但是在测试 NFS 挂载文件系统的时候发现文件系统挂载成功率很低!在 uboot 中需要对LAN8720A 进行一次软复位,要设置 LAN8720A 的 BMCR(寄存器地址为 0)寄存器 bit15 为 1。所以在 Linux 中也需要对 LAN8720A 进行一次软复位
LAN8720A 的驱动文件是 drivers/net/phy/smsc.c,在此文件中有个叫做 smsc_phy_reset 的函数,
修改以后的 smsc_phy_reset函数内容如下所示:
static int smsc_phy_reset(struct phy_device *phydev)
{
int err, phy_reset;
int msec = 1;
struct device_node *np;
int timeout = 50000;
if (phydev->addr == 0) /* FEC1 */
{
np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000");
if (np == NULL)
{
return -EINVAL;
}
}if (phydev->addr == 1) /* FEC2 */
{
np = of_find_node_by_path("/soc/aips-bus@02000000/ethernet@020b4000");
if (np == NULL)
{
return -EINVAL;
}
}err = of_property_read_u32(np, "phy-reset-duration", &msec);
/* A sane reset duration should not be longer than 1s */
if (!err && msec > 1000)
msec = 1;
phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
if (!gpio_is_valid(phy_reset))
return;gpio_direction_output(phy_reset, 0);
gpio_set_value(phy_reset, 0);
msleep(msec);
gpio_set_value(phy_reset, 1);
int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
if (rc < 0)
return rc;/* If the SMSC PHY is in power down mode, then set it
* in all capable mode before using it.
*/
if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {/* set "all capable" mode and reset the phy */
rc |= MII_LAN83C185_MODE_ALL;
phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
}
phy_write(phydev, MII_BMCR, BMCR_RESET);/* wait end of reset (max 500 ms) */
do {
udelay(10);
if (timeout-- == 0)
return -1;
rc = phy_read(phydev, MII_BMCR);
} while (rc & BMCR_RESET);
return 0;
}
要添加两个头文件
#include <linux/of_gpio.h>
#include <linux/io.h>
网络驱动测试
修改好设备树和 Linux 内核以后重新编译一下
make -j16
开发板与电脑同一网段下
最后使用新的文件启动 Linux 内核。启动以后命令查看一下当前活动的网卡有哪些
ifconfig -a
eth0 对应于 ENET2, eth1 对应于 ENET1。使用如下命令依次打开 eth0 和 eth1 这两个网卡:
ifconfig eth0 up
ifconfig eth1 up
可以看到“SMSC LAN8710/LAN8720”字样,说明当前网络驱动使用的是前面使能的 SMSC 驱动
输入“ifconfig”命令来查看一下当前活动的网卡
可以看出,此时 eth0 和 eth1 两个网卡都已经打开,并且工作正常,但是这两个网卡都还没
有 IP 地址,所以不能进行 ping 等操作。使用如下命令给两个网卡配置 IP 地址:
ifconfig eth0 192.168.1.221
ifconfig eth1 192.168.1.223
IP 地址选择的合理性,一定要和自己的电脑处于同一个网段内,并且没有被其他的设备占用!设置好以后,使用“ping”命令来 ping 一下自己的主机,如果能 ping 通那说明网络驱动修改成功!
ping 成功,说明网络驱动修改成功!
在后面的构建根文件系统和 Linux 驱动开发中就可以使用网络调试代码
六、保存修改后的图形化配置文件
在修改网络驱动的时候我们通过图形界面使能了 LAN8720A 的驱动,使能以后会在.config中存在代码:“CONFIG_SMSC_PHY=y”
打开 drivers/net/phy/Makefile,有代码“obj-$(CONFIG_SMSC_PHY) += smsc.o”
当 CONFIG_SMSC_PHY=y 的时候就会编译 smsc.c 这个文件, smsc.c 就是 LAN8720A 的驱
动文件。但是当我们执行“make clean”清理工程以后.config 文件就会被删除掉,因此所有的配置内容都会丢失,所以在配置完图形界面以后经过测试没有问题,就必须要保存一下配置文件。
保存配置的方法有两个
1、 直接另存为.config 文件
既然图形化界面配置后的配置项保存在.config 中,那么就简单粗暴,直接将.config 文件另存为 imx_my_emmc_defconfig,然后其复制到 arch/arm/configs 目录下,替换以前的imx_my_emmc_defconfig。这样以后执行“ make imx_my_emmc_defconfig”重新配置
Linux 内核的时候就会使用新的配置文件,默认就会使能 LAN8720A 的驱动。
2、通过图形界面保存配置文件
在图形界面中保存配置文件,在图形界面中会有“< Save >”选项
通过键盘的“→”键,移动到“< Save >”选项,然后按下回车键,打开文件名输入对话框
输入要保存的文件名,可以带路径,一般是相对路径(相对于 Linux 内核源码 根目 录 )。 比如要 将新 的配 置文 件保存 到目 录 arch/arm/configs下,文件名为imx_my_emmc_defconfig,也就是用新的配置文件替换掉老的默认配置文件。
那在图中输入“arch/arm/configs/imx_my_emmc_defconfig”即可
设置好文件名后选择下方的“ < Ok >”按钮,保存文件并退出。退出以后再打开imx_my_emmc_defconfig文件,就会在此文件中找到“CONFIG_SMSC_PHY=y”这一行
关于 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
内核移植成功以后就要开始根文件系统的构建文章来源:https://www.toymoban.com/news/detail-499351.html
rootfs(根文件系统)在下一篇文章文章来源地址https://www.toymoban.com/news/detail-499351.html
到了这里,关于Linux 内核移植的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!