<Linux开发>系统移植 -之- linux内核移植过程详细记录(第二部分完结)
前面,第一部分讲解了,NXP官方原厂的Linux直接编译下载到开发板的操作过程,及测试效果。以及过程中设计使用的一些辅助工具。从第一部分,可以熟悉整个Linux编译到下载运行的流程,方便接下来的移植操作。
uboot移植可参考一下:
<Linux开发> -之-系统移植 uboot移植过程详细记录(第一部分)
<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)
<Linux开发> -之-系统移植 uboot移植过程详细记录(第三部分)(uboot移植完结)
Linux内核及设备树移植可参考一下:
<Linux开发>系统移植 -之- linux内核移植过程详细记录(第一部分)
本次为Linux系统移植中Linux移植的第二部分,主要讲解NXP原厂Linux移植到正点原子Linux开发板的过程,并记录。操作流程均参考正点原子官方Linux开发手册,如有讲解不详细、错误、遗漏之处,可联系作者修改补充,也可参考正点原子官方资料。
联系方式QQ:759521350
注:作者采用的是正点原子的IMX6ULL-EMMC版本的Linux开发板。
接下来讲解主要过程记录。
一、在Linux源码内添加使用的开发板型号
1、添加开发板默认配置文件
(1)将之前第一部分解压后的文件夹,复制一份,并重命名为“linux-imx-onefu”
命令:
cp linux-imx-rel_imx_4.1.15_2.1.0_ga_onefu/ linux-imx-onefu -rf
(2)添加所用开发板的默认配置文件
将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份,命名为imx_onefu_emmc_defconfig,命令如下:
cd arch/arm/configs/
cp imx_v7_mfg_defconfig imx_onefu_emmc_defconfig
后续开发中,imx_onefu_emmc_defconfig就做为正点原子Linux-emmc开发板的配置文件。
(3)添加所用开发板的设备树文件
添加开发板的设备树文件,进入目录arch/arm/boot/dts中,复制一份imx6ull-14x14-evk.dts,然后将其重命名为imx6ull-onefu-emmc.dts,命令如下:
cd arch/arm/boot/dts/
cp imx6ull-14x14-evk.dts imx6ull-onefu-emmc.dts
.dts是设备树源码文件,编译Linux的时候会将其编译为.dtb文件。
二、移植文件修改
1、设备树文件修改
使用VScode打开,找到路径下“arch/arm/boot/dts/Makefile”,这个文件,在找到“dtb-$(CONFIG_SOC_IMX6ULL)”配置项。添加一下内容:
imx6ull-onefu-emmc.dtb \
如下图示,此句就是前面添加的“imx6ull-onefu-emmc.dts”设备树文件,编译后生成.dtb的设备树文件,将.dtb设备树文件下载到Linux开发板使用。
注意:要有斜杠…
2、修改脚本文件
测试NXP原厂Linux时写的脚本“imx6ull_onefu_emmc.sh”如下:
移植到开发板后,修改后如下:
3、主频修改
根据正点原子官方文档介绍,正点原子I.MX6U-ALPHA开发板所使用的I.MX6ULL芯片主频都是792MHz的,也就是NXP官方宣传的800MHz版本。
接下来就修改CPU的工作频率。
打开 “arch/arm/configs/imx_onefu_emmc_defconfig”,找到下面几句:
CONFIG_CPU_FREQ_GOV_POWERSAVE=y //使能powersave策略
CONFIG_CPU_FREQ_GOV_USERSPACE=y //使能userspace策略
CONFIG_CPU_FREQ_GOV_ONDEMAND=y //配置ondemand为默认调频策略。
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y//直接上最高频率,然后看CPU负荷慢慢降低
查看是否存在“ CONFIG_CPU_FREQ_GOV_ONDEMAND=y”,如果没有,则添加,否则不做修改。
根据具体打开的文件查看配置,**正点原子官方指导手册此处和上图不一样,**所以需要按照上述添加对应语句。但是本次操作查看,配置已经存在“ CONFIG_CPU_FREQ_GOV_ONDEMAND=y”,所以无需更改。
正点原子手册截图如下:
4、使能8线EMMC驱动
Linux内核驱动里面EMMC默认是4线模式的,4线模式肯定没有8线模式的速度快,由于正点原子的Linux开发板上EMMC采用的是8线模式,所以我们需要将EMMC的驱动修改为8线模式。修改方法很简单,直接修改设备树即可,打开文件imx6ull-onefu-emmc.dts,找到如下所示内容:
&usdhc2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc2>;
non-removable;
status = "okay";
};
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc2_8bit>;
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
bus-width = <8>;
non-removable;
status = "okay";
};
5、修改网络驱动
Linux驱动开发的时候要用到网络调试驱动,所以必须要把网络驱动调试好。在讲解uboot移植的时候就已经说过了,正点原子开发板的网络和NXP官方的网络硬件上不同,网络PHY芯片由KSZ8081换为了LAN8720A,两个网络PHY芯片的复位IO也不同。所以Linux内核自带的网络驱动是驱动不起来I.MX6U-ALPHA开发板上的网络的,需要做修改。
(1)修改LAN8720的复位以及网络时钟引脚驱动
打开“arch/arm/boot/dts/imx6ull-onefu-emmc.dts”
a.删除NXP原厂旧的网络引脚配置
删除下面截图红色标框部分:
b.添加正点原子Linux开发板网络引脚
在imx6ull-onefu-emmc.dts里面找到名为“iomuxc_snvs”的节点(就是直接搜索),然后在此节点下添加网络复位引脚信息,添加完成以后的“iomuxc_snvs”的节点内容如下:
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
imx6ul-evk {
省略..........................
/*enet1 reset water*/
pinctrl_enet1_reset: enet1resetgrp {
fsl,pins = <
/* used for enet1 reset */
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
>;
};
/*enet2 reset water*/
pinctrl_enet2_reset: enet2resetgrp {
fsl,pins = <
/* used for enet12 reset */
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
>;
};
修改一下ENET1和ENET2的网络时钟引脚配置,继续在imx6ull-onefu-emmc.dts中找到,修改后如下所示代码:
(2)修改fec1和fec2节点的pinctrl-0属性
在imx6ull-alientek-emmc.dts文件中找到名为“fec1”和“fec2”的这两个节点,修改其中的“pinctrl-0”属性值,修改后如下:
(3)修改LAN8720A的PHY地址
ENET1的LAN8720A地址为0x0,ENET2的LAN8720A地址为0x1;在imx6ull-alientek-emmc.dts中找到节点fec1和fec2,修改后如下所示:
&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@0 {
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>;
};
};
};
第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”命令重新编译一下设备树。
(4)修改fec_main.c文件
要在I.MX6ULL上使用LAN8720A,需要修改一下Linux内核源码,打开drivers/net/ethernet/freescale/fec_main.c,找到函数fec_probe,在fec_probe中加入如下代码:
/* 设置MX6UL_PAD_ENET1_TX_CLK和MX6UL_PAD_ENET2_TX_CLK
* 这两个IO的复用寄存器的SION位为1。
*/
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。
6、配置Linux内核,使能LAN8720驱动
输入命令“make menuconfig”,打开图形化配置界面,选择使能LAN8720A的驱动,路径如下:
-> Device Drivers
-> Network device support
-> PHY Device support and infrastructure
-> Drivers for SMSC PHYs
选中“Drivers for SMSC PHYs”后,按“y”,按下后,选项的前面中括号内会出现*,LAN8720A是SMSC公司出品的,因此勾选这个以后就会编译LAN8720驱动,配置好以后,通过键盘上的“→”,移动到“Save”,然后按下回车键,在弹出的对话框输入“arch/arm/configs/imx_onefu_emmc_defconfig ”,然后选择“OK”。退出配置界面,然后重新编译一下Linux内核。
7、修改smsc.c文件
需要找到LAN8720A的驱动文件,LAN8720A的驱动文件是drivers/net/phy/smsc.c,在此文件中有个叫做smsc_phy_reset的函数,看名字都知道这是SMSC PHY的复位函数,因此,LAN8720A肯定也会使用到这个复位函数,修改此函数的内容,修改以后的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的输出方向-0:输出*/
gpio_set_value(phy_reset, 0); /*设置GPIO的为0*/
msleep(msec); /*延时1ms*/
gpio_set_value(phy_reset, 1); /*设置GPIO的为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) {
int timeout = 50000;
/* 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;
}
第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
#include
以上就是在NXP官方提供的Linux的基础上移植修改,移植符合正点原子的Linux开发板上的使用,移植修改后,接下来就是编译测试。
三、网络驱动测试
修改好设备树和Linux内核以后重新编译一下,得到新的zImage镜像文件和imx6ull-onefu-emmc.dtb设备树文件,使用网线将I.MX6U-ALPHA开发板的两个网口与路由器或者电脑连接起来,最后使用新的文件启动Linux内核。启动以后使用“ifconfig”命令查看一下当前活动的网卡有哪些,结果如下图所示:
将新得到的zImage 和imx6ull-onefu-emmc.dtb设备树文件放到tftp服务器设置的文件夹内,命令:
cd /home/water/linux/tftpboot //进入到tftp文件夹
cp /home/water/water/kernel/linux-imx-onefu/arch/arm/boot/zImage . //复制镜像
cp /home/water/water/kernel/linux-imx-onefu/arch/arm/boot/dts/imx6ull-onefu-emmc.dtb . //复制设备树
启动开发板进入boot,并下载zImage 和imx6ull-onefu-emmc.dtb设备树文件
输入命令:
tftp 80800000 zImage
tftp 83000000 imx6ull-onefu-emmc.dtb
bootz 80800000 - 83000000
首先确保根文件系统已存在。。。。
使用“ifconfig”命令查看网卡。
如果没有活动的网卡,可使用如下命令查看存在的网卡:
ifconfig -a
然后使用如下命令启动网卡:
ifconfig eth0 up //eth0表示启动的网卡名称
ifconfig eth1 up
使用如下命令设置网卡的IP地址:
ifconfig eth0 192.168.1.145
ifconfig eth1 192.168.1.146
使用"ping"命令ping一下ubuntu主机,命令如下:
ping 192.168.144
可以看出,ping成功,说明网络驱动修改成功!我们在后面的构建根文件系统和Linux驱动开发中就可以使用网络调试代码啦。
关于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开发板配套资料,如有不足,遗漏,错误之处,可联系作者,进一步改善。
如若获取更详细的资料可到正点原子官方网址下载。