[ARM Linux系统移植] Linux 内核移植
本章暂且先跳过 linux 内核代码的说明,因为这又将耗费“一本书”的内容。我们直接开始进行 linux 内核的移植工作。
主要的移植内容就是网络驱动,在 u-boot 移植的相关章节中我们也了解到,当前开发板和 i.Mx 使用了不一样的网络芯片。网络驱动的移植工作很重要,因为后续 linux 驱动学习的调试手段都需要用到网络。
1. 添加自己的开发板
和 u-boot 的移植工作类似,为了便于管理,我们为当前开发板引入“专有”的文件:配置文件和设备树文件。
添加配置文件
添加自己的配置文件,直接使用原本的 imx_v7_mfg_defconfig 文件。执行如下命令:
- > cd arch/arm/configs
- > cp imx_v7_mfg_defconfig imx_my_emmc_defconfig
添加设备树文件
添加的设备树文件基于原先的 imx6ull-14x14-evk.dts,后续再在其内容上进行修改。执行如下命令:
- > cd arch/arm/boot/dts
- > cp imx6ull-14x14-evk.dts imx6ull-my-emmc.dts
如下粗体指示,之后需要在 arch/arm/boot/dts/Makefile 文件内添加 imx6ull-my-emmc.dtb。这样就可以根据 imx6ull-my-emmc.dts 文件编译生成想要的 imx6ull-my-emmc.dtb 文件。
- dtb-$(CONFIG_SOC_IMX6ULL) += \
- imx6ull-14x14-ddr3-arm2.dtb \
- ……
- imx6ull-14x14-evk-usb-certi.dtb \
- imx6ull-my-emmc.dtb \
在添加了配置文件和设备树文件之后,可以尝试编译验证一下。执行如下命令,需要注意的是其中的配置文件变成了我们添加的配置文件:
- > 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- all -j16
编译生成之后,将 zImage 和 imx6ull-my-emmc.dtb 文件放在 tftp 服务器上。同时还需要改一下 u-boot 的 bootcmd 环境变量,让它读取现在添加的设备树文件:
- > setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-my-emmc.dtb; bootz 80800000 - 83000000'
开发板启动之后,我们可以明显地看到网络未准备的提示,后续还需要进行网络驱动的移植。
2. 驱动修改
在添加好自己的配置文件和设备树文件之后,需要修改 emmc 驱动和网络驱动。
2.1 8 线 emmc 驱动
当前默认是使用 4 线的 emmc 驱动,开发板是使用 8 线的 emmc,速度也比 4 线的快。修改相应的设备树文件就可以让内核使用 8 线的 emmc 驱动。我们将设备树内的 &usdhc2 节点修改如下:
- &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";
- };
修改之后,使用
- > make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
命令,可以直接重新编译生成 dtb 文件,而不用整体编译。
2.2 网络驱动
网络驱动需要修改的地方较多,这边先依葫芦画瓢按照文档中的步骤进行操作。
2.2.1 修改复位引脚和网络时钟引脚
首先按如下代码所示,注释或删除掉粗体标注的地方,它们和管脚 GPIO5_IO07 以及 GPIO5_IO08 相关。这两个管脚现在用于网络芯片的复位引脚,所以需要将之前用到这两个管脚的地方删除掉,以防止干扰到复位引脚。
- pinctrl_spi4: spi4grp {
- fsl,pins = <
- MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
- MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1
- /*MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1*/
- /*MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000*/
- >;
- };
- spi4 {
- compatible = "spi-gpio";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_spi4>;
- /*pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;*/
- status = "okay";
- gpio-sck = <&gpio5 11 0>;
- gpio-mosi = <&gpio5 10 0>;
- /*cs-gpios = <&gpio5 7 0>;*/
接着按如下代码所示,增加两个复位引脚的信息。
- &iomuxc_snvs {
- pinctrl-names = "default_snvs";
- pinctrl-0 = <&pinctrl_hog_2>;
- imx6ul-evk {
- …………
- pinctrl_enet1_reset: enet1resetgrp {
- fsl,pins = <
- /* used for enet1 reset */
- MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
- >;
- };
- pinctrl_enet2_reset: enet2resetgrp {
- fsl,pins = <
- /* used for enet2 reset */
- MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
- >;
- };
- };
接着按如下代码所示,修改网络时钟引脚的电气属性:
- pinctrl_enet1: enet1grp {
- fsl,pins = <
- MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
- MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
- MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
- MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
- MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
- MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
- MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
- MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009
- >;
- };
- pinctrl_enet2: enet2grp {
- fsl,pins = <
- MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0
- MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
- MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
- MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
- MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
- MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
- MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
- MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
- MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
- MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009
- >;
- };
至此,复位引脚和网络时钟引脚的相关内容就修改完毕了。
2.2.2 修改 fec1 和 fec2 节点
按如下代码所示,修改 fec1 和 fec2 节点的 pinctrl-0 属性,添加 2.2.1 中的复位引脚内容。
- &fec1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet1
- pinctrl_enet1_reset>;
- phy-mode = "rmii";
- phy-handle = <ðphy0>;
- status = "okay";
- };
- &fec2 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet2
- pinctrl_enet2_reset>;
2.2.3 修改器件地址
在 u-boot 移植实验中已经了解到网口的器件地址也发生了变化,ENET1 为 0x0,ENET2 为 0x1。如下代码中的第 196/199 行、第 202/205 行定义了新的器件地址。
第 177 和 178 行设置了 ENET1 网络复位引脚所使用的 IO,且低电平有效,持续时间为 200 ms。第 188 和 189 行设置 ENET2 的内容同理。
第 198 和 204 行表明芯片是 SMSC 公司的,这样内核就会找到所需的 LAN8720A 驱动。
- &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>;
- };
- };
- };
2.2.4 修改 fec_main.c
在 drivers/net/ethernet/freescale/fec_main.c 文件中设置两个网络时钟引脚 IO 寄存器的 SION 位为 1。
- static int
- fec_probe(struct platform_device *pdev)
- {
- struct fec_enet_private *fep;
- struct fec_platform_data *pdata;
- struct net_device *ndev;
- int i, irq, ret = 0;
- struct resource *r;
- const struct of_device_id *of_id;
- static int dev_id;
- struct device_node *np = pdev->dev.of_node, *phy_node;
- int num_tx_qs;
- int num_rx_qs;
- 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);
2.2.5 修改 smsc.c
在之前 u-boot 的移植过程中,了解到需要强制软复位一下网络芯片,否则会有一些错误发生。在 linux 中也是如此,如下代码把软复位操作移到判断外面,无论什么情况都强制软复位一下。
- static int smsc_phy_reset(struct phy_device *phydev)
- {
- 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);
- }
- int timeout = 50000;
- 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;
- }
2.2.6 修改 LCD 引脚
这一节的内容文档上没有。自己在实验的过程中,如果没有修改以下复用引脚的配置,网络则会反复重启。最后在论坛上找到了相应的解决方案,虽然目前还不知道为什么 lcd 相关引脚会影响到网络。
- pinctrl_lcdif_dat: lcdifdatgrp {
- fsl,pins = <
- MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x49
- MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x49
- MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x49
- MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x49
- MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x49
- MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x49
- MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x49
- MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x51
- MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x49
- MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x49
- MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x49
- MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x49
- MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x49
- MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x49
- MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x49
- MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x51
- MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x49
- MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x49
- MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x49
- MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x49
- MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x49
- MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x49
- MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x49
- MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x51
- >;
- };
- pinctrl_lcdif_ctrl: lcdifctrlgrp {
- fsl,pins = <
- MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x49
- MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x49
- MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x49
- MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x49
- >;
- };
2.2.7 使能 LAN8720 驱动
输入以下命令:
- > make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
如下图所示,按路径 Device Drivers - Network device support - PHY Device support and infrastructure,选择开启 Drivers for SMSC PHYs,即可编译 LAN8720 驱动。

使能后,我们可以核对一下 .config 文件,可以找到如下内容:
- CONFIG_SMSC_PHY=y
为了防止后续 make distclean 后,还需要重新使能,可以将现在生成的 .config 重命名覆盖我们的配置文件:
- > cp .config arch/arm/configs/imx_my_emmc_defconfig
2.2.8 网络驱动测试
可以手动给网卡配置 ip 地址:
- > ifconfig eth0 169.254.155.84
配置完之后,如果 ping 得通主机,则代表网络驱动移植成功。
- > ping 169.254.155.83