Ambarella 平台KSZ9031网卡芯片协商失败
问题描述:
两个ambarella peanut板子的网口之间用网线直连,会发现不能自动协商成功(网口灯不亮),但是ambarella peanut板子的网口和其他X86 Linux机器网口直连可以协商成功(网口灯亮)。
对于想使用peanut板子网口作LAN口,给其他peanut 板子DHCP分配IP地址会失败。
问题分析
1. 查看ambarella peanut平台驱动 kernel/linux-4.4/drivers/net/ethernet/ambarella/ambarella_eth.c 对网卡芯片 KSZ9031 做了哪些 MDIO 读写,关于MDIO定义看此篇博客:MDC/MDIO接口定义
static int ambhw_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) { int ret_val = 0; struct ambeth_info *lp; int val; int cnt = 0; lp = (struct ambeth_info *)bus->priv; printk("MII Write: reg[0x%.2x], val[0x%.4x].\n", regnum, value); //添加的打印MDIO写了哪些寄存器,写了哪些值 if (netif_msg_hw(lp)) dev_info(&lp->ndev->dev, "MII Write: id[0x%02x], add[0x%02x], val[0x%04x].\n", mii_id, regnum, value); for (cnt = AMBETH_MII_RETRY_CNT; cnt > 0; cnt--) { val = readl_relaxed(lp->regbase + ETH_MAC_GMII_ADDR_OFFSET); if (!(val & ETH_MAC_GMII_ADDR_GB)) break; udelay(10); } if ((cnt <= 0) && netif_msg_hw(lp)) { dev_err(&lp->ndev->dev, "MII Error: Prewrite tmo!\n"); ret_val = -EIO; goto ambhw_mdio_write_exit; } val = value; writel_relaxed(val, lp->regbase + ETH_MAC_GMII_DATA_OFFSET); //把要写到网卡芯片寄存器的值写到ambarella SoC芯片的 lp->regbase+ETH_MAC_GMII_DATA_OFFSET 寄存器里 val = ETH_MAC_GMII_ADDR_PA(mii_id) | ETH_MAC_GMII_ADDR_GR(regnum); val |= ETH_MAC_GMII_ADDR_CR_250_300MHZ | ETH_MAC_GMII_ADDR_GW | ETH_MAC_GMII_ADDR_GB; writel_relaxed(val, lp->regbase + ETH_MAC_GMII_ADDR_OFFSET); //给lp->regbase+ETH_MAC_GMII_ADDR_OFFSET写入一些flag(包括要写入的网卡芯片寄存器的地址)触发 lp->regbase+ETH_MAC_GMII_DATA_OFFSET 里的值写入到网卡芯片相关寄存器里 for (cnt = AMBETH_MII_RETRY_CNT; cnt > 0; cnt--) { val = readl_relaxed(lp->regbase + ETH_MAC_GMII_ADDR_OFFSET); if (!(val & ETH_MAC_GMII_ADDR_GB)) break; udelay(10); } if ((cnt <= 0) && netif_msg_hw(lp)) { dev_err(&lp->ndev->dev, "MII Error: Postwrite tmo!\n"); ret_val = -EIO; goto ambhw_mdio_write_exit; } ambhw_mdio_write_exit: return ret_val; }
修改后发现打印信息如下:
MII Write: reg[0x00], val[0x8000]. MII Write: reg[0x0d], val[0x0002]. MII Write: reg[0x0e], val[0x0008]. MII Write: reg[0x0d], val[0x4002]. MII Write: reg[0x0d], val[0x0002]. MII Write: reg[0x0e], val[0x0008]. MII Write: reg[0x0d], val[0x4002]. MII Write: reg[0x0e], val[0x3df6]. MII Write: reg[0x0d], val[0x0002]. MII Write: reg[0x0e], val[0x0004]. MII Write: reg[0x0d], val[0x4002]. MII Write: reg[0x0d], val[0x0002]. MII Write: reg[0x0e], val[0x0004]. MII Write: reg[0x0d], val[0x4002]. MII Write: reg[0x0e], val[0x0027]. MII Write: reg[0x0d], val[0x0002]. MII Write: reg[0x0e], val[0x0005]. MII Write: reg[0x0d], val[0x4002]. MII Write: reg[0x0e], val[0x2222]. MII Write: reg[0x0d], val[0x0000]. MII Write: reg[0x0e], val[0x0004]. MII Write: reg[0x0d], val[0x4000]. MII Write: reg[0x0e], val[0x0006]. MII Write: reg[0x0d], val[0x0000]. MII Write: reg[0x0e], val[0x0003]. MII Write: reg[0x0d], val[0x4000]. MII Write: reg[0x0e], val[0x1a80]. MII Write: reg[0x00], val[0x1340]. MII Write: reg[0x00], val[0x1140]. MII Write: reg[0x09], val[0x0300]. MII Write: reg[0x09], val[0x1300].
对照KSZ9031 datasheet比较下来,导致问题的可疑之处定位在 MII Write: reg[0x09], val[0x1300] 上面。
本来09H的第12位网卡默认值为0的,但被设置为1了,从哪个位置设置的呢?
2. 为了找到在哪个地方把09H的第12位设置为1的,我找到了kernel source code里的 kernel/linux-4.4/drivers/net/phy/micrel.c ,这个驱动是Micrel(麦瑞半导体)系列网卡芯片相关寄存器配置的地方,找到如下函数:
static int ksz9031_config_aneg(struct phy_device *phydev) { u32 val; genphy_config_aneg(phydev); /* Set auto Master/Slave resolution process */ val = phy_read(phydev, MII_CTRL1000); val |= 0x1000; // MII_CTRL1000宏的值就是0x9,需要把此行注释掉 val &= ~(0x0800); phy_write(phydev, MII_CTRL1000, val); return 0; }
static struct phy_driver ksphy_driver[] = {
...
{
.phy_id = PHY_ID_KSZ9031,
.phy_id_mask = 0x00fffff0,
.name = "Micrel KSZ9031 Gigabit PHY",
.features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.driver_data = &ksz9021_type,
.config_init = ksz9031_config_init,
.config_aneg = ksz9031_config_aneg, //linux官方内核驱动这里是genphy_config_aneg,ambarella把这里修改成了ksz9031_config_aneg
.read_status = ksz9031_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
.driver = { .owner = THIS_MODULE, },
},
...
}
原来是在这里设置的 Enable master-slave manual configuration ,Microchip提供到kernel里的网卡驱动让ambarella修改了,注释掉 val |= 0x1000; 这一行,重新编译进内核或者编译成模块,问题解决。
拓展
这个问题的根本原因就是两个peanut板子都设置成了 使能手动主从控制,导致用网线直连两个网卡不能自动协商,其中有一个peanut板子 Disable master-slave manual configuration 都不会有这个问题, peanut板子与其他X86主板的连接也没有这个问题;
在问题发生后,给其中一个peanut板子的网口接到交换机上,正常识别后再拔掉网线会导致网卡 MDIO 配置的复位(9H寄存器的第12位恢复默认值0),这时候再用网线直连两个peanut板子,是可以正常协商成功的。
虽然这个问题解决了,但还是不知道ambarella原厂为啥做这样的修改,在其他ambarella SoC平台里用的Realtek网卡芯片ambarella没有做类似的修改,是为了修复什么bug? 在咨询过Micrel FAE后,得到的回复是千兆自动协商有时会遇到兼容性问题,例如某些千兆设备的连接,发现要在slave模式,或者必须工作在master模式,不然工作不稳定。
本文来自博客园,作者:闹闹爸爸,转载请注明原文链接:https://www.cnblogs.com/wanglouxiaozi/p/13903840.html