前言
之前使用 IMX6ULL 开发板时遇到过 nfs 挂载不上的问题,当时是通过更换官方新版 kernel 解决的,参考《挂载 nfs 文件系统》。
今天,使用自己编译的 kernel 又遇到了该问题,第二次遇到了,该正面解决了。
NFS 挂载失败
kernel 启动 log
Starting kernel ...
Booting Linux on physical CPU 0x0
Linux version 4.1.15 (liyongjun@Box) (gcc version 4.9.4 (Linaro GCC 4.9-2017.01) ) #15 SMP PREEMPT Tue Jun 6 02:03:54 CST 2023
CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
Machine model: Freescale i.MX6 ULL 14x14 EVK Board
...
CAN device driver interface
flexcan 2090000.can: device registered (reg_base=a09e8000, irq=27)
flexcan 2094000.can: device registered (reg_base=a09f0000, irq=28)
20b4000.ethernet supply phy not found, using dummy regulator
pps pps0: new PPS source ptp0
libphy: fec_enet_mii_bus: probed
fec 20b4000.ethernet eth0: registered PHC device 0
2188000.ethernet supply phy not found, using dummy regulator
pps pps1: new PPS source ptp1
fec 2188000.ethernet eth1: registered PHC device 1
PPP generic driver version 2.4.2
PPP BSD Compression module registered
PPP Deflate Compression module registered
PPP MPPE Compression module registered
NET: Registered protocol family 24
...
lib80211: common routines for IEEE802.11 drivers
Key type dns_resolver registered
mmc1: MAN_BKOPS_EN bit is not set
Registering SWP/SWPB emulation handler
mmc1: new DDR MMC card at address 0001
mmcblk1: mmc1:0001 8GTF4R 7.28 GiB
snvs_rtc 20cc000.snvs:snvs-rtc-lp: setting system clock to 2023-06-05 18:05:19 UTC (1685988319)
mmcblk1boot0: mmc1:0001 8GTF4R partition 1 4.00 MiB
mmcblk1boot1: mmc1:0001 8GTF4R partition 2 4.00 MiB
mmcblk1rpmb: mmc1:0001 8GTF4R partition 3 512 KiB
usb 1-1.1: new full-speed USB device number 3 using ci_hdrc
IP-Config: Failed to open eth0
IP-Config: Device `eth0' not found
问题定位
从启动 log 看到,没有找到 eth0,说明要么网卡硬件出问题了,要么网卡驱动出问题了。开发板硬件应该没什么问题,大概率是驱动出了点问题。
以太网硬件设施包括两部分:MAC 芯片 + PYH 芯片。
IMX6ULL 这颗 SOC 集成了 MAC 芯片,芯片外接了一颗 LAN8720 PHY 芯片。
MAC 是 SOC 内部的,内核代码又是 NXP 厂家提供的,所以 MAC 芯片及其驱动应该没什么问题,大概率是 PHY 芯片驱动的问题。
资料收集
PHY 硬件原理图(LAN8720A)
软件引脚配置
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 0x4001b031
>;
};
来个一一对应
可以看到,原理图中芯片使用到的引脚,除了 ENET2_INT_TREE#
和 ENET2_RST
,在 DTS pinctrl 中都有配置,那就重点看下这两个不在 pinctrl 中的引脚有没有正确使用。
ENET2_INT_TREE#
为中断引脚,从下面 DTS 配置可以看出该引脚是有在使用的。
fec2: ethernet@020b4000 {
compatible = "fsl,imx6ul-fec", "fsl,imx6q-fec";
reg = <0x020b4000 0x4000>;
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>, // ENET2 interrupt IRQ=152
<GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ENET>,
<&clks IMX6UL_CLK_ENET_AHB>,
<&clks IMX6UL_CLK_ENET_PTP>,
<&clks IMX6UL_CLK_ENET2_REF_125M>,
<&clks IMX6UL_CLK_ENET2_REF_125M>;
clock-names = "ipg", "ahb", "ptp",
"enet_clk_ref", "enet_out";
stop-mode = <&gpr 0x10 4>;
fsl,num-tx-queues=<1>;
fsl,num-rx-queues=<1>;
fsl,magic-packet;
fsl,wakeup_irq = <0>;
status = "disabled";
};
再来看下 ENET2_RST
,在 DTS 中找了半天,也没看到该引脚的配置。
&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 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <2>;
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
};
};
};
会不会不需要该引脚呢,上电后没必要 reset?
先不管,先看看代码中有没有使用该引脚吧。
驱动源码
在驱动 probe() 函数中发现了 fec_reset_phy()
函数,
static int
fec_probe(struct platform_device *pdev)
{
...
fec_reset_phy(pdev);
...
}
static void fec_reset_phy(struct platform_device *pdev)
{
int err, phy_reset;
int msec = 1;
struct device_node *np = pdev->dev.of_node;
if (!np)
return;
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); // 从设备树中获取 "phy-reset-gpios" 引脚
if (!gpio_is_valid(phy_reset))
return;
err = devm_gpio_request_one(&pdev->dev, phy_reset,
GPIOF_OUT_INIT_LOW, "phy-reset");
if (err) {
dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
return;
}
msleep(msec);
gpio_set_value(phy_reset, 1);
}
在该函数的实现中,赫然看到 of_get_named_gpio(np, "phy-reset-gpios", 0);
,该 of 函数想要从设备树中获取 “phy-reset-gpios” 引脚,很显然这里是获取不到的,因为设备树中没有配置。
尝试修复
在 DTS 中添加 reset 引脚的配置
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; // add: GPIO5 IO08 低电平复位
phy-reset-duration = <200>; // add: 低电平保持时间
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@2 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <2>;
};
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
};
};
};
编译设备树,烧录,验证
snvs_rtc 20cc000.snvs:snvs-rtc-lp: setting system clock to 2023-06-06 12:50:53 UTC (1686055853)
fec 20b4000.ethernet eth0: Freescale FEC PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=20b4000.ethernet:01, irq=-1)
IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
fec 20b4000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
IP-Config: Complete:
device=eth0, hwaddr=00:04:9f:04:d2:35, ipaddr=192.168.31.224, mask=255.255.255.0, gw=192.168.31.1
host=192.168.31.224, domain=, nis-domain=(none)
bootserver=192.168.31.223, rootserver=192.168.31.223, rootpath=
gpio_dvfs: disabling
can-3v3: disabling
ALSA device list:
#0: wm8960-audio
VFS: Mounted root (nfs filesystem) on device 0:14.
devtmpfs: mounted
Freeing unused kernel memory: 424K (809a4000 - 80a0e000)
INIT: version 2.88 booting
/etc/rcS.d/S00psplash.sh: line 28: /usr/bin/psplash: No such file or directory
Starting udev
random: nonblocking pool is initialized
udev: Not using udev cache because of changes detected in the following files:
udev: /proc/version /proc/cmdline /proc/devices
udev: lib/udev/rules.d/* etc/udev/rules.d/*
udev: The udev cache will be regenerated. To identify the detected changes,
udev: compare the cached sysconf at /etc/udev/cache.data
udev: against the current sysconf at /dev/shm/udev.cache
udevd[94]: starting version 3.1.5
kjournald starting. Commit interval 5 seconds
EXT3-fs (mmcblk1p2): using internal journal
EXT3-fs (mmcblk1p2): recovery complete
EXT3-fs (mmcblk1p2): mounted filesystem with ordered data mode
FAT-fs (mmcblk1p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
mmc0: Timeout waiting for hardware interrupt.
mmcblk0: error -84 sending status command, retrying
mmcblk0: error -110 sending stop command, original cmd response 0x0, card status 0x900
mmcblk0: error -110 transferring data, sector 15564672, nr 8, cmd response 0x0, card status 0x0
mmc0: tried to reset card
kjournald starting. Commit interval 5 seconds
EXT3-fs (mmcblk0p2): using internal journal
EXT3-fs (mmcblk0p2): recovery complete
EXT3-fs (mmcblk0p2): mounted filesystem with ordered data mode
FAT-fs (mmcblk0p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
bootlogd: cannot allocate pseudo tty: No such file or directory
Populating dev cache
ALSA: Restoring mixer settings...
/usr/sbin/alsactl: set_control:1461: Cannot write control '2:0:0:Playback Volume:0' : Input/output error
/usr/sbin/alsactl: set_control:1461: Cannot write control '2:0:0:Speaker Playback Volume:0' : Input/output error
INIT: Entering runlevel: 5
Configuring network interfaces... ifup skipped for nfsroot interface eth0
run-parts: /etc/network/if-pre-up.d/nfsroot: exit status 1
Starting system message bus: Starting Xserver
dbus.
Starting Connection Manager
Starting Dropbear SSH server: dropbear.
Starting rpcbind daemon...done.
Starting watchdog: [ OK ]
starting statd: done
Starting advanced power management daemon: No APM support in kernel
(failed.)
Starting atd: OK
exportfs: can't open /etc/exports for reading
NFS daemon support not enabled in kernel
Starting system log daemon...0
Starting kernel log daemon...0
* Starting Avahi mDNS/DNS-SD Daemon: avahi-daemon [ ok ]
Starting Telephony daemon
Starting Linux NFC daemon
* starting FTP Server: vsftpd... done.
Starting crond: OK
Starting nginx: nginx.
Running local boot scripts (/etc/rc.local).
root@ATK-IMX6U:~# ^C
root@ATK-IMX6U:~# ifconfig
eth0 Link encap:Ethernet HWaddr 00:04:9f:04:d2:35
inet addr:192.168.31.224 Bcast:192.168.31.255 Mask:255.255.255.0
inet6 addr: fe80::204:9fff:fe04:d235/64 Scope:Link
inet6 addr: 240e:3a6:1806:55f0:204:9fff:fe04:d235/64 Scope:Global
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:21428 errors:0 dropped:50 overruns:0 frame:0
TX packets:17993 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:21492516 (20.4 MiB) TX bytes:2510506 (2.3 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:140 (140.0 B) TX bytes:140 (140.0 B)
LAN8720 驱动成功,eth0 link ready,nfs 挂载成功,系统引导成功。