admin管理员组

文章数量:1658610

<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 = <&ethphy0>;
	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 = <&ethphy1>;
	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 <linux/of_gpio.h> 
#include <linux/io.h> 

以上就是在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开发板配套资料,如有不足,遗漏,错误之处,可联系作者,进一步改善。
如若获取更详细的资料可到正点原子官方网址下载。

本文标签: 内核第二部分过程详细系统