mii-tool是net-tools package里面的一个小工具。
在Denverton SOC 上,针对光口使用mii-tool工具时打印出
SIOCGMIIREG on eth0 failed: Invalid argument
经过定位发现,drivers/net/mdio.c中
mdio_mii_ioctl函数对于寄存器的地址进行了限制, 导致读取0x4-0xa寄存器时,返回-EINVAL
mii-tool连续读取0---0xa这些寄存器, 读0x4->0xa 这些寄存器时都返回 -EINVAL, 导致mii-tool打印Invalid argument
/**
* mdio_mii_ioctl - MII ioctl interface for MDIO (clause 22 or 45) PHYs
* @mdio: MDIO interface
* @mii_data: MII ioctl data structure
* @cmd: MII ioctl command
*
* Returns 0 on success, negative on error.
*/
int mdio_mii_ioctl(const struct mdio_if_info *mdio,
struct mii_ioctl_data *mii_data, int cmd)
{
int prtad, devad;
u16 addr = mii_data->reg_num;
/* Validate/convert cmd to one of SIOC{G,S}MIIREG */
switch (cmd) {
case SIOCGMIIPHY:
/* prtad:0x1f */
if (mdio->prtad == MDIO_PRTAD_NONE)
return -EOPNOTSUPP;
mii_data->phy_id = mdio->prtad;
cmd = SIOCGMIIREG;
break;
case SIOCGMIIREG:
case SIOCSMIIREG:
break;
default:
return -EOPNOTSUPP;
}
/* 在ixgbe_main.c中,ixgbe_probe函数中
hw->phy.mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
*/
/* Validate/convert phy_id */
if ((mdio->mode_support & MDIO_SUPPORTS_C45) &&
mdio_phy_id_is_c45(mii_data->phy_id)) {
prtad = mdio_phy_id_prtad(mii_data->phy_id);
devad = mdio_phy_id_devad(mii_data->phy_id);
} else if ((mdio->mode_support & MDIO_SUPPORTS_C22) &&
mii_data->phy_id < 0x20) {
prtad = mii_data->phy_id;
devad = MDIO_DEVAD_NONE;
addr &= 0x1f;
} else if ((mdio->mode_support & MDIO_EMULATE_C22) &&
mdio->prtad != MDIO_PRTAD_NONE &&
mii_data->phy_id == mdio->prtad) {
/* Denverton 走这个分支,并mmds=0, 所以只有寄存器0,1,2,3可以读出来 */
/* mii-tool连续读取0---0xa这些寄存器, 读0x4->0xa 这些寄存器时都返回 -EINVAL, 导致mii-tool打印Invalid argument */
/* Remap commonly-used MII registers. */
prtad = mdio->prtad;
switch (addr) {
case MII_BMCR:
case MII_BMSR:
case MII_PHYSID1:
case MII_PHYSID2:
/* mmds=0, devad=0 */
devad = __ffs(mdio->mmds);
break;
case MII_ADVERTISE:
case MII_LPA:
if (!(mdio->mmds & MDIO_DEVS_AN))
return -EINVAL;
devad = MDIO_MMD_AN;
if (addr == MII_ADVERTISE)
addr = MDIO_AN_ADVERTISE;
else
addr = MDIO_AN_LPA;
break;
default:
return -EINVAL;
}
} else {
return -EINVAL;
}
if (cmd == SIOCGMIIREG) {
int rc = mdio->mdio_read(mdio->dev, prtad, devad, addr);
if (rc < 0)
return rc;
mii_data->val_out = rc;
return 0;
} else {
return mdio->mdio_write(mdio->dev, prtad, devad, addr,
mii_data->val_in);
}
}
在ixgbe_main.c中,ixgbe_probe函数中对phy初始设置:
/* PHY */
hw->phy.ops = *ii->phy_ops;
hw->phy.sfp_type = ixgbe_sfp_type_unknown;
/* ixgbe_identify_phy_generic will set prtad and mmds properly */
hw->phy.mdio.prtad = MDIO_PRTAD_NONE;
hw->phy.mdio.mmds = 0;
hw->phy.mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
hw->phy.mdio.dev = netdev;
hw->phy.mdio.mdio_read = ixgbe_mdio_read;
hw->phy.mdio.mdio_write = ixgbe_mdio_write;
在ixgbe_read_mng_if_sel_x550em函数里面,
hw->phy.mdio.prtad 会被重新设置
/**
* ixgbe_read_mng_if_sel_x550em - Read NW_MNG_IF_SEL register
* @hw: pointer to hardware structure
*
* Read NW_MNG_IF_SEL register and save field values.
*/
static void ixgbe_read_mng_if_sel_x550em(struct ixgbe_hw *hw)
{
/* Save NW management interface connected on board. This is used
* to determine internal PHY mode.
*/
/* hw->phy.nw_mng_if_sel = 0x186820fb */
hw->phy.nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
/* If X552 (X550EM_a) and MDIO is connected to external PHY, then set
* PHY address. This register field was has only been used for X552.
*/
if (!hw->phy.nw_mng_if_sel) {
if (hw->mac.type == ixgbe_mac_x550em_a) {
struct ixgbe_adapter *adapter = hw->back;
e_warn(drv, "nw_mng_if_sel not set\n");
}
return;
}
hw->phy.mdio.prtad = (hw->phy.nw_mng_if_sel &
IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD) >>
IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT;
}
阅读(1484) | 评论(0) | 转发(0) |