1)开发板平台:创龙 AM5728 (tl5728-easy-evm)
2)PHY芯片:Marvell 88E1512
88E1512和88E1510都是一个系列的phy,采用marvell的通用phy驱动,驱动源码路径如下:drivers/net/phy/marvell.c
1)系统框图
AM5728连接两个PHY,PHY0的工作模式是 RGMII to Copper (RGMII到电口),PHY1的工作模式是 RGMII to SGMII。
两个PHY与处理器的连接方式都是RGMII,只是PHY出口的电气接口不一样,一个到电口,一个到SGMII与交换芯片相连。
PHY的工作模式:(P5)
几种电气接口的解释:(P33)
MDI:传输媒介接口
RGMII:系统接口
SGMII:系统接口/传输媒介接口
PHY的工作模式选择:Page 18 – Register 20
寄存器读写的API:
读写88E1512的寄存器前,先往Register 22写入要操作的 Page number。我们这里要操作的是 Page 18,Register 20,用于配置PHY的工作模式MODE[2:0]
1)设备树 (创龙)
AM57xx\Linux-RT\kernel\Linux-RT-4.9.65\arch\arm\boot\dts\am57xx-beagle-x15-common.dtsi
phy-mode属性是“rgmii-id”, “rgmii-id”和“rgmii”是有区别的,主要体现在RX和TX的延时是由MAC补偿还是由PHY补偿。如下:
(参考:http://www.360doc.com/content/19/0804/20/36367108_852988840.shtml
)
内核文档
AM57xx\Linux-RT\kernel\Linux-RT-4.9.65\Documentation\devicetree\bindings\net\ethernet.txt
对 phy-mode 属性进行了详细介绍,可以阅读查看。
上面的3个节点未做修改,创龙底板使用的PHY是KSZ9031(RGMII to Copper),沿用了这3个节点的属性。
2)驱动源码修改
AM57xx\Linux-RT\kernel\Linux-RT-4.9.65\drivers\net\phy\marvell.c
主要是修改了初始化函数:m88e1510_config_init(),根据设备树中的 operating-mode 属性来判断PHY的工作模式,以此来写寄存器的值。
/* add by heat */
static int m88e1510_config_init(struct phy_device *phydev)
{
int err;
int temp;
u32 mode_num;
int ret;
struct device_node *np = phydev->mdio.dev.of_node;
ret = of_property_read_u32(np, "operating-mode", &mode_num);
if (ret) {
printk("invalid 'operating-mode' property: %d\n", ret);
return ret;
} else {
if (0 == mode_num ) {
/* RGMII-to-Copper mode initialization */
printk("=== Enter RGMII-to Copper mode initialization. === \n");
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
/* Select page 18 */
err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
if (err < 0)
return err;
/* In reg 20, write MODE[2:0] = 0x0 (RGMII to Copper) */
temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_TO_COPPER;
err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
if (err < 0)
return err;
/* PHY reset is necessary after changing MODE[2:0] */
temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
if (err < 0)
return err;
/* heat test */
/* Select page 18 */
err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
if (err < 0)
return err;
/* read reg 22 */
temp = phy_read(phydev, MII_MARVELL_PHY_PAGE);
printk("=== ETH0: Reg22 : 0x%x . === \n", temp);
/* In reg 20, write MODE[2:0] = 0x0 (RGMII to Copper) */
temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
printk("=== ETH0: Reg20_Page18 : 0x%x . === \n", temp);
/* Reset page selection */
err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
if (err < 0)
return err;
}
} else if (2 == mode_num) {
/* RGMII-to-1000BASE-X mode initialization */
printk("=== Enter RGMII-to-1000BASE-X mode initialization. === \n");
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
/* Select page 18 */
err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
if (err < 0)
return err;
/* In reg 20, write MODE[2:0] = 0x4 (RGMII to SGMII) */
temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_TO_1000BASE_X; /* RGMII-to-1000BASE-X */
err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
if (err < 0)
return err;
/* PHY reset is necessary after changing MODE[2:0] */
temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
if (err < 0)
return err;
/* heat test */
/* Select page 18 */
err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
if (err < 0)
return err;
/* read reg 22 */
temp = phy_read(phydev, MII_MARVELL_PHY_PAGE);
printk("=== ETH1: Reg22 : 0x%x . === \n", temp);
temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
printk("=== ETH1: Reg20_Page18 : 0x%x . === \n", temp);
}
}
}
return m88e1121_config_init(phydev);
}
/* End add by heat */
其他地方暂未修改。
3)文件系统修改
上面的驱动在内核加载后,网卡eth0是电口,插上网线后直接phy0显示link up; 网卡eth1与交换芯片P9口连接,eth1可以看做是Fiber口,但是PHY并没有切至Fiber Page,MAC一直获取的是PHY的CopperPage的link状态,所以网卡eth1一直显示无法link。我们使用mdio 工具进行了切页,将PHY切至Fiber Page后,网卡link up;
文件路径: \etc\init.d\init-fatri
添加的命令如下
mdio eth1 22 1 用于切至Fiber Page,以便MAC正确判断PHY1的link状态;
mdio eth1 22 3;mdio eth1 16 0xa0 用于配置LED[1]的工作模式。
硬件设计时,PHY1的LED[1]引脚还用于接收PPS信号输入。在测试时发现该引脚会影响PPS信号质量,会拉低PPS信号电平。需操作PHY寄存器 LED[1]的工作模式为Hi-Z高阻状态。
寄存器配置:
此外,还有一项重要更改:
关闭AM5728 RGMII-1 内部时延补偿。因为我们使用的是 rgmii-id 模式,由 88E1512 PHY侧补偿 rgmii 的TX和RX时延,就不用MAC再进行补偿,所以应该关闭AM5728 MAC侧的时延补偿。
AM5728的手册:Page-4305 ,如下图所示。
Table 18-279. CTRL_CORE_SMA_SW_1
1) PHY 一般都有 Packet Generation 功能,可以通过此功能判断 PHY到Copper端的链路是否正常。
开发板与PC机直连,PC机打开Wireshark抓包,看是否可以抓到固定大小的包(5AA55AA5…)。
开发板可以打开 tcpdump 工具,由PC机ping 开发板,这样链路就是copper->PHY->MAC,通过 tcpdump抓包;
tcpdump命令:“tcpdump –i eth0 arp –XX -v”,抓ARP解析包,看能否收到。
2) PHY和MAC一般都具备CRC 统计功能,可以统计收发的数据包个数以及错包个数,以此来判断哪个方向的传输不正常。
头文件:
AM57xx\Linux-RT\kernel\Linux-RT-4.9.65\include\linux\marvell_phy.h
AM57xx\Linux-RT\kernel\Linux-RT-4.9.65\include\linux\phy.h