环境
选项内容
编译主机UbuntuLTS 16.04
目标板ATK I.MX6ULL- Mini (512MB DDR3 + 8GB EMMC)
移植的u-boot版本2021.01 [下载地址]
交叉编译工具arm-linux-gnueabihf-gcc 6.5.0 [下载地址]

注:如果移植过程有不懂的步骤可以先看这篇文章:[点击跳转]


1、先使用官方默认配置编译一遍

查看configs目录下有2个关于mx6ull的单板配置:

mx6ull_14x14_evk_defconfig
mx6ull_14x14_evk_plugin_defconfig

这里选用mx6ull_14x14_evk_defconfig,所以u-boot整个编译步骤如下:

tar xjvf packet/u-boot-2021.01.tar.bz2
cd u-boot-2021.01/
make mx6ull_14x14_evk_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

编译后得到u-boot-dtb.imx文件,将它烧写查看是否能正常使用,烧录的方法有多种,其中就包含使用官方的Mfgtools工具和u-boot的自我更新。这里推荐使用官方工具,因为如果移植的uboot网卡没有配置好的话就用不了tftp等下载命令。

对于imx6ull,烧录的脚本是mfgtool2-yocto-mx-evk-emmc.vbs,其中烧录的文件对应于mfgtools\Profiles\Linux\OS Firmware\files目录下的几个文件:

u-boot-imx6ull14x14evk_emmc.imx
zImage
zImage-imx6ull-14x14-evk-emmc.dtb
rootfs_nogpu.tar.bz2

这里将编译后得到的u-boot-dtb.imx文件重命名为u-boot-imx6ull14x14evk_emmc.imx替换进去即可烧录。

烧录后查看现象:

U-Boot 2021.01-ge963177 (Mar 16 2021 - 21:52:11 +0800)

CPU:   Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU:   Industrial temperature grade (-40C to 105C) at 44C
Reset cause: POR
Model: Freescale i.MX6 UltraLiteLite 14x14 EVK Board
Board: MX6ULL 14x14 EVK
DRAM:  512 MiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
Loading Environment from MMC... *** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   Could not get PHY for FEC1: addr 1
Could not get PHY for FEC1: addr 1
Could not get PHY for FEC0: addr 2
Could not get PHY for FEC0: addr 2
No ethernet found.

Hit any key to stop autoboot:  0
switch to partitions #0, OK
mmc1(part 0) is current device
switch to partitions #0, OK
mmc1(part 0) is current device
Failed to load 'boot.scr'
5582616 bytes read in 239 ms (22.3 MiB/s)
Booting from mmc ...
38376 bytes read in 4 ms (9.1 MiB/s)
Kernel image @ 0x82000000 [ 0x000000 - 0x552f18 ]
## Flattened Device Tree blob at 83000000
   Booting using the fdt blob at 0x83000000
   Using Device Tree in place at 83000000, end 8300c5e7

Starting kernel ...
// ....
Welcome to imx6ull!
imx6ull login: root
Password:
[root@imx6ull]:~#

可以看到CPU、Board、DRAM、MMC都能正确识别了,而且最最重要的——启动内核也是正常的。如果看到Starting kernel ...字样之后就没有往下走,那首先知道的一点就是u-boot是可以启动内核的了,只是环境变量可能没有设置正确,参考的设置如下:

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv bootcmd 'fatload mmc 1:1 80800000 zImage;fatload mmc 1:1 83000000 imx6ull-14x14-evk.dtb;bootz 80800000 - 83000000'
saveenv

uboot具体命令参考:u-boot:常用命令解释


2、其他外设检验

复位重新进入u-boot检验一下设备:

2.1 MMC设备的检验
=> mmc rescan
=>
=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1 (eMMC)
=> mmc dev 0
switch to partitions #0, OK
mmc0 is current device
=>
=> mmc info
Device: FSL_SDHC
Manufacturer ID: 83
OEM: 4e43
Name: NCard
Bus Speed: 50000000
Mode: SD High Speed (50MHz)
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 14.9 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes
=>
=> mmc dev 1
switch to partitions #0, OK
mmc1(part 0) is current device
=>
=> mmc info
Device: FSL_SDHC
Manufacturer ID: 15
OEM: 100
Name: 8GTF4
Bus Speed: 52000000
Mode: MMC High Speed (52MHz)
Rd Block Len: 512
MMC version 5.1
High Capacity: Yes
Capacity: 7.3 GiB
Bus Width: 4-bit
Erase Group Size: 512 KiB
HC WP Group Size: 8 MiB
User Capacity: 7.3 GiB WRREL
Boot Capacity: 4 MiB ENH
RPMB Capacity: 512 KiB ENH
Boot area 0 is not write protected
Boot area 1 is not write protected
=>

可以看到SD卡和mmc存储都能够正常识别。

2.2 网卡

根据启动的打印可以看到以下几句:

Net:   Could not get PHY for FEC1: addr 1
Could not get PHY for FEC1: addr 1
Could not get PHY for FEC0: addr 2
Could not get PHY for FEC0: addr 2
No ethernet found.

说明网卡还是不能工作。去源码里grep -rn "Could not get PHY for" ./找一下打印位置,发现在drivers/net/phy/phy.c文件的phy_connect函数中,而如果往回追踪它的调用,就会发现它的其中一个调用关系如下:

U_BOOT_DRIVER(fecmxc_gem) 	/* drivers/net/fec_mxc.c */
    .probe	= fecmxc_probe,
		fec_phy_init
			phy_connect 	/* drivers/net/phy/phy.c */
            	...
            	printf("Could not get PHY for %s: addr %d\n", bus->name, addr);

U_BOOT_DRIVER关键字也可以知道它是一个驱动(uboot做得越来越像linux了),具体地说它是imx6ull的MAC驱动(使用的是目前较流行的“内置MAC控制器”+“外置PHY芯片”方案,硬件说明),它同样也是支持dts的方式来匹配的,dts文件就在arch/arm/dts/目录下。

首先先考虑dts节点有没有正确配置,对于Mini开发板的网卡,只引出了一个ENET2,那就参考移植Linux-4.20.9的网卡部分,首先将fec1(ENET1)屏蔽掉:

/*  */
/*
&fec1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet1>;
    phy-mode = "rmii";
    phy-handle = <&ethphy0>;
    status = "okay";
};
*/

&fec2 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet2>;
    phy-mode = "rmii";
    phy-handle = <&ethphy1>;
    status = "okay";

    mdio {
        #address-cells = <1>;
        #size-cells = <0>;

/*
        ethphy0: ethernet-phy@2 {
            reg = <2>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET_REF>;
            clock-names = "rmii-ref";
        };
*/

        ethphy1: ethernet-phy@1 {
            reg = <1>;
            micrel,led-mode = <1>;
            clocks = <&clks IMX6UL_CLK_ENET2_REF>;
            clock-names = "rmii-ref";
        };
    };
};

此时肯定也还启动不了,因为这并不影响驱动程序的流程,这里只是把用不上的ENET1去掉而已。所以还是简单分析probe函数。其中,在获取时钟之后,看到有那么几句话:

#if CONFIG_IS_ENABLED(DM_GPIO)
	fec_gpio_reset(priv);
#endif

在默认配置中也定义了CONFIG_DM_GPIO=y,而fec_gpio_reset(priv);函数里面要用到reset引脚,但是dts中没有配置,所以参考linux内核的配置继续修改:

&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 = <26>; 						/* 复位时间,单位ms */
    status = "okay";
	... // 省略
};


/* pinctrl引脚配置 */
pinctrl_spi4: spi4grp {
    fsl,pins = <
        MX6UL_PAD_BOOT_MODE0__GPIO5_IO10    0x70a1
        MX6UL_PAD_BOOT_MODE1__GPIO5_IO11    0x70a1
        MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07  0x70a1
        //MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08    0x80000000 /* 把重复使用的去掉 */
    >;
};

/* 复位引脚配置 */
pinctrl_enet2_reset: enet2resetgrp {
    fsl,pins = <
        MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08  0x10B0
    >;
};

重新烧写查看现象。这时,关于网络部分的打印发生了变化:

Net:
Error: ethernet@20b4000 address not set.

Error: ethernet@20b4000 address not set.
No ethernet found.

那就继续grep -rn "address not set",找到在net/eth-uclass.c文件中的eth_post_probe函数打印的,简单看下函数内容:

static int eth_post_probe(struct udevice *dev)
{
    ...
	/* Check if the device has a valid MAC address in device tree */
	if (!eth_dev_get_mac_address(dev, pdata->enetaddr) ||
	    !is_valid_ethaddr(pdata->enetaddr))
		...

	eth_env_get_enetaddr_by_index("eth", dev->seq, env_enetaddr);
	if (!is_zero_ethaddr(env_enetaddr)) {
		...
		/* Override the ROM MAC address */
		memcpy(pdata->enetaddr, env_enetaddr, ARP_HLEN);
	} else if (is_valid_ethaddr(pdata->enetaddr)) {
		eth_env_set_enetaddr_by_index("eth", dev->seq, pdata->enetaddr);
	} else if (is_zero_ethaddr(pdata->enetaddr) ||
		   !is_valid_ethaddr(pdata->enetaddr)) {
#ifdef CONFIG_NET_RANDOM_ETHADDR
		net_random_ethaddr(pdata->enetaddr);
		printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
		       dev->name, dev->seq, pdata->enetaddr);
#else
		printf("\nError: %s address not set.\n",
		       dev->name);
		return -EINVAL;
#endif
	}

	eth_write_hwaddr(dev);
	...
};

通过上面也可以知道这是由于没有设置MAC地址导致的,其中eth_dev_get_mac_address函数是通过读取dts来获取MAC地址:

static bool eth_dev_get_mac_address(struct udevice *dev, u8 mac[ARP_HLEN])
{
    ...
	p = dev_read_u8_array_ptr(dev, "mac-address", ARP_HLEN);
	if (!p)
		p = dev_read_u8_array_ptr(dev, "local-mac-address", ARP_HLEN);
    ...
}

而其他还可以支持从环境变量中获取、随机生成(需要配置CONFIG_NET_RANDOM_ETHADDR=y)等方式。这里还是在直接在uboot命令行里面设置吧,比较快捷方便:

setenv eth1addr 12:34:56:78:9a:bc # 这里是eth1addr,不是ethaddr,因为后面步骤它打印的是eth1
saveenv

保存之后重启,查看现象:

Net:   eth1: ethernet@20b4000 [PRIME]

能识别出来了,那就配置一下其他网络信息:

setenv ipaddr 192.168.1.123
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
setenv serverip 192.168.1.234
saveenv

然后执行一些ping 192.168.10.234(该IP地址真实存在),但是会发现并不能工作。注意,因为这里使用的PHY芯片是SMSC公司生产的LAN8720A,而uboot默认自带的是Micrel生产的KSZ804,所以根据.config文件去make menuconfigCONFIG_PHY_MICRELCONFIG_PHY_MICREL_KSZ8XXX去掉,将CONFIG_PHY_SMSC配置上去,因为drivers/net/phy/smsc.c中已经包含了LAN8720A的驱动程序,这样才确保uboot能够使用LAN8720A这款PHY芯片(其实uboot里也像linux一样包含了通用的PHY驱动,但不保证能驱动起来)。

配置完成之后重新启动,配置好MAC地址和ip地址之后执行ping测试。啪,打脸了,还是不行…

最后在正点原子的手册里面就说了PHY驱动程序有些问题,将这段代码添加进去,针对于LAN8720进行特殊处理:

--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -224,6 +224,21 @@ int genphy_update_link(struct phy_device *phydev)
 {
        unsigned int mii_reg;

+#ifdef CONFIG_PHY_SMSC
+       static int lan8720_flag = 0;
+       int bmcr_reg = 0;
+
+       if (lan8720_flag == 0) {
+               bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+               phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+               while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) {
+                       udelay(100);
+               }
+               phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg);
+               lan8720_flag = 1;
+       }
+#endif
+
        /*
         * Wait if the link is up, and autonegotiation is in progress
         * (ie - we're capable and it's not done)

重新编译启动,执行ping测试:

=> ping 192.168.1.234
ethernet@20b4000 Waiting for PHY auto negotiation to complete.... done
Using ethernet@20b4000 device
host 192.168.1.234 is alive
=>

大功告成!

Logo

更多推荐