选项 | 内容 |
---|---|
编译主机 | UbuntuLTS 16.04 |
目标板 | ATK I.MX6ULL- Mini (512MB DDR3 + 8GB EMMC) |
移植的u-boot版本 | 2021.01 [下载地址] |
交叉编译工具 | arm-linux-gnueabihf-gcc 6.5.0 [下载地址] |
注:如果移植过程有不懂的步骤可以先看这篇文章:[点击跳转]
查看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:常用命令解释
复位重新进入u-boot检验一下设备:
=> 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存储都能够正常识别。
根据启动的打印可以看到以下几句:
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 = <ðphy0>;
status = "okay";
};
*/
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
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 = <ðphy1>;
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 menuconfig
将CONFIG_PHY_MICREL
和CONFIG_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
=>
大功告成!