BCM89XXX系列PHY使用的MDIO配置默认通信协议是C45,Linux内核驱动默认使用C22协议对PHY进行相关配置,所以Linux内核驱动需要使用C22协议模仿C45协议对PHY进行相关配置。
- 识别BCM89881
修改/linux_zvs/drivers/net/phy/phy_device.c中读取Phy id函数内容,具体修改内容如下:
static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id,
bool is_c45, struct phy_c45_device_ids *c45_ids)
{
int phy_reg;
if (is_c45)
return get_phy_c45_ids(bus, addr, phy_id, c45_ids);
/* Grab the bits from PHYIR1, and put them in the upper half */
mdiobus_write(bus, addr, 0x0d, 0x01);
mdiobus_write(bus, addr, 0x0e, MII_PHYSID1);
mdiobus_write(bus, addr, 0x0d, 0x4001);
phy_reg = mdiobus_read(bus, addr, 0x0e);
if (phy_reg < 0) {
/* if there is no device, return without an error so scanning
* the bus works properly
*/
if (phy_reg == -EIO || phy_reg == -ENODEV) {
*phy_id = 0xffffffff;
return 0;
}
return -EIO;
}
*phy_id = (phy_reg & 0xffff) << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */
mdiobus_write(bus, addr, 0x0d, 0x01);
mdiobus_write(bus, addr, 0x0e, MII_PHYSID2);
mdiobus_write(bus, addr, 0x0d, 0x4001);
phy_reg = mdiobus_read(bus, addr, 0x0e);
if (phy_reg < 0)
return -EIO;
*phy_id |= (phy_reg & 0xffff);
return 0;
}
- 千兆模式配置
板卡启动后通过如下脚本配置BCM89881工作在1000Mbps slave模式:
#!/bin/sh
./mdio eth0 0x0 0xA000 #power down the phy
usleep 2000
./mdio eth0 0x0 0xA000 #power down the phy
usleep 5000
./mdio eth0 0 0x2000 #power on the phy
./mdio eth0 0 0x2000 #power on the phy
slave 1000M
./mdio eth0 0x0d 0x01
./mdio eth0 0x0e 0x0834
./mdio eth0 0x0d 0x4001
./mdio eth0 0x0e 0x8001
./mdio eth0 0x0d 0x01
./mdio eth0 0x0e 0xa010
./mdio eth0 0x0d 0x4001
./mdio eth0 0x0e 0x101
- 百兆模式配置
3.1. Mac驱动程序修改
若使BCM89881工作在100Mbps速率,需要对Linux内核的mac配置进行修改,对/linux_zvs/drivers/net/ethernet/cadence/macb_main.c
进行如下修改:
static void macb_handle_link_change(struct net_device *dev)
{
struct macb *bp = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
unsigned long flags;
int status_change = 0;
spin_lock_irqsave(&bp->lock, flags);
if (phydev->link) {
if ((bp->speed != phydev->speed) ||
(bp->duplex != phydev->duplex)) {
u32 reg;
reg = macb_readl(bp, NCFGR);
reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
if (macb_is_gem(bp))
reg &= ~GEM_BIT(GBE);
if (phydev->duplex)
reg |= MACB_BIT(FD);
if (phydev->speed == SPEED_100)
reg |= MACB_BIT(SPD);
if (phydev->speed == SPEED_1000 &&
bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE)
reg |= GEM_BIT(GBE);
/* 2023 02 08 */
phydev->speed = SPEED_100;
reg |= MACB_BIT(SPD);
reg &= ~GEM_BIT(GBE);
phydev->duplex = DUPLEX_FULL;
macb_or_gem_writel(bp, NCFGR, reg);
bp->speed = phydev->speed;
bp->duplex = phydev->duplex;
status_change = 1;
}
}
if (phydev->link != bp->link) {
if (!phydev->link) {
bp->speed = 0;
bp->duplex = -1;
}
bp->link = phydev->link;
status_change = 1;
}
spin_unlock_irqrestore(&bp->lock, flags);
if (status_change) {
if (phydev->link) {
/* Update the TX clock rate if and only if the link is
* up and there has been a link change.
*/
macb_set_tx_clk(bp->tx_clk, phydev->speed, dev);
netif_carrier_on(dev);
netdev_info(dev, "link up (%d/%s)\n",
phydev->speed,
phydev->duplex == DUPLEX_FULL ?
"Full" : "Half");
} else {
netif_carrier_off(dev);
netdev_info(dev, "link down\n");
}
}
}
3.2. Slave模式配置脚本
#!/bin/sh
slave 100M
#C22
./mdio eth0 0x0 0xA000 #power down the phy
usleep 2000
./mdio eth0 0x0 0xA000 #power down the phy
usleep 5000
./mdio eth0 0 0x2000 #power on the phy
./mdio eth0 0 0x2000 #power on the phy
./mdio eth0 0x0d 0x01
./mdio eth0 0x0e 0x0834
./mdio eth0 0x0d 0x4001
./mdio eth0 0x0e 0x8000
./mdio eth0 0x0d 0x01
./mdio eth0 0x0e 0xa010
./mdio eth0 0x0d 0x4001
./mdio eth0 0x0e 0x101