1、背景介绍
模块上有9个PHY,其中两个PHY通过ZYNQ PS端的MDIO总线连接,其余7个PHY单独通过GPIO进行控制,需要实现GPIO模拟MDC/MDIO协议。
2、vivado工程设计
vivado工程内为每个PHY建立两个GPIO IP核,分别用来代表MDC和MDIO(虽然有点浪费)
MDC配置为默认输出
MDIO配置为双向
MDIO管脚默认上拉
3、MDIO时序介绍
MDIO接口包括两条线,MDIO和MDC,其中MDIO是双向数据线,而MDC是由STA驱动的时钟线。MDC时钟的最高速率一般为2.5MHz,MDC也可以是非固定频率,甚至可以是非周期的。MDIO接口只是会在MDC时钟的上升沿进行采样,而并不在意MDC时钟的频率(类似于I2C接口)。MDIO是一个PHY的管理接口,用来读/写PHY的寄存器,以控制PHY的行为或获取PHY的状态,MDC为MDIO提供时钟。
Preamble+Start:32bits的前导码以及2bit的开始位。
OP Code:2bits的操作码,10表示读,01表示写。
PHYAD:5bits的PHY地址,一般PHY地址,从0开始顺序编号,例如6口switch中PHY地址为0-5。
REGAD:5bits的寄存器地址,即要读或写的寄存器。
Turn Around:2bits的TA,在读命令中,MDIO在此时由MAC驱动改为PHY驱动,并等待一个时钟周期准备发送数据。在写命令中,不需要MDIO方向发生变化,则只是等待两个时钟周期准备写入数据。
Data:16bits数据,在读命令中,PHY芯片将读到的对应PHYAD的REGAD寄存器的数据写到Data中,在写命令中,MAC将要写入对应PHYAD的REGAD寄存器的值写入Data中。
Idle:空闲状态,此时MDIO无源驱动,处高阻状态,但一般用上拉电阻使其处在高电平,上拉电阻一般为1.5K。(空闲电平是低电平)
逻辑分析上抓到的示意图如下:
4、应用程序设计
参考上面的时序,采用GPIO翻转实现,注意AXI GPIO IP核0x4写0为输出,写1为输入,主要是用来操作MDIO方向
代码如下:
/*
* Copyright (c) 2012 Xilinx, Inc. All rights reserved.
*
* Xilinx, Inc.
* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR
* STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
* IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE
* FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO
* ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
* FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/mii.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <linux/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include "xil_io.h"
#define PHY2_YT8521_ADDR 0x01
#define PHY3_YT8521_ADDR 0x02
#define PHY5_YT8521_ADDR 0x04
#define PHY6_YT8521_ADDR 0x05
#define PHY7_YT8521_ADDR 0x06
#define PHY8_YT8521_ADDR 0x07
#define PHY9_YT8521_ADDR 0x00
#define PHY_YT8521_ID_REG_ADDR 0x03
#define PHY_YT8521_REG_DEBUG_ADDR_OFFSET 0x1E
#define PHY_YT8521_REG_DEBUG_DATA 0x1F
#define PHY_YT8521_EXT_REG_ADDR 0xa00d
#define MDC2_GPIO_ADDR 0x412B0000
#define MDC3_GPIO_ADDR 0x412D0000
#define MDC5_GPIO_ADDR 0x412F0000
#define MDC6_GPIO_ADDR 0x41310000
#define MDC7_GPIO_ADDR 0x41330000
#define MDC8_GPIO_ADDR 0x41350000
#define MDC9_GPIO_ADDR 0x41370000
#define MDIO2_GPIO_ADDR 0x412C0000
#define MDIO3_GPIO_ADDR 0x412E0000
#define MDIO5_GPIO_ADDR 0x41300000
#define MDIO6_GPIO_ADDR 0x41320000
#define MDIO7_GPIO_ADDR 0x41340000
#define MDIO8_GPIO_ADDR 0x41360000
#define MDIO9_GPIO_ADDR 0x41380000
#define PHY2_SOFT_SMI_MDIO_READ Xil_In32(MDIO2_GPIO_ADDR)
#define PHY3_SOFT_SMI_MDIO_READ Xil_In32(MDIO3_GPIO_ADDR)
#define PHY5_SOFT_SMI_MDIO_READ Xil_In32(MDIO5_GPIO_ADDR)
#define PHY6_SOFT_SMI_MDIO_READ Xil_In32(MDIO6_GPIO_ADDR)
#define PHY7_SOFT_SMI_MDIO_READ Xil_In32(MDIO7_GPIO_ADDR)
#define PHY8_SOFT_SMI_MDIO_READ Xil_In32(MDIO8_GPIO_ADDR)
#define PHY9_SOFT_SMI_MDIO_READ Xil_In32(MDIO9_GPIO_ADDR)
typedef unsigned char u8;
typedef unsigned short u16;
void mdio_out(int index)
{
switch(index)
{
case 2:
Xil_Out32(MDIO2_GPIO_ADDR+0x4,0x0);
break;
case 3:
Xil_Out32(MDIO3_GPIO_ADDR+0x4,0x0);
break;
case 5:
Xil_Out32(MDIO5_GPIO_ADDR+0x4,0x0);
break;
case 6:
Xil_Out32(MDIO6_GPIO_ADDR+0x4,0x0);
break;
case 7:
Xil_Out32(MDIO7_GPIO_ADDR+0x4,0x0);
break;
case 8:
Xil_Out32(MDIO8_GPIO_ADDR+0x4,0x0);
break;
case 9:
Xil_Out32(MDIO9_GPIO_ADDR+0x4,0x0);
break;
default:
break;
}
}
void mdio_in(int index)
{
switch(index)
{
case 2:
Xil_Out32(MDIO2_GPIO_ADDR+0x4,0x1);
break;
case 3:
Xil_Out32(MDIO3_GPIO_ADDR+0x4,0x1);
break;
case 5:
Xil_Out32(MDIO5_GPIO_ADDR+0x4,0x1);
break;
case 6:
Xil_Out32(MDIO6_GPIO_ADDR+0x4,0x1);
break;
case 7:
Xil_Out32(MDIO7_GPIO_ADDR+0x4,0x1);
break;
case 8:
Xil_Out32(MDIO8_GPIO_ADDR+0x4,0x1);
break;
case 9:
Xil_Out32(MDIO9_GPIO_ADDR+0x4,0x1);
break;
default:
break;
}
}
void mdc_low(int index)
{
switch(index)
{
case 2:
Xil_Out32(MDC2_GPIO_ADDR,0x0);
break;
case 3:
Xil_Out32(MDC3_GPIO_ADDR,0x0);
break;
case 5:
Xil_Out32(MDC5_GPIO_ADDR,0x0);
break;
case 6:
Xil_Out32(MDC6_GPIO_ADDR,0x0);
break;
case 7:
Xil_Out32(MDC7_GPIO_ADDR,0x0);
break;
case 8:
Xil_Out32(MDC8_GPIO_ADDR,0x0);
break;
case 9:
Xil_Out32(MDC9_GPIO_ADDR,0x0);
break;
default:
break;
}
}
void mdc_high(int index)
{
switch(index)
{
case 2:
Xil_Out32(MDC2_GPIO_ADDR,0xffffffff);
break;
case 3:
Xil_Out32(MDC3_GPIO_ADDR,0xffffffff);
break;
case 5:
Xil_Out32(MDC5_GPIO_ADDR,0xffffffff);
break;
case 6:
Xil_Out32(MDC6_GPIO_ADDR,0xffffffff);
break;
case 7:
Xil_Out32(MDC7_GPIO_ADDR,0xffffffff);
break;
case 8:
Xil_Out32(MDC8_GPIO_ADDR,0xffffffff);
break;
case 9:
Xil_Out32(MDC9_GPIO_ADDR,0xffffffff);
break;
default:
break;
}
}
void mdio_low(int index)
{
switch(index)
{
case 2:
Xil_Out32(MDIO2_GPIO_ADDR,0x0);
break;
case 3:
Xil_Out32(MDIO3_GPIO_ADDR,0x0);
break;
case 5:
Xil_Out32(MDIO5_GPIO_ADDR,0x0);
break;
case 6:
Xil_Out32(MDIO6_GPIO_ADDR,0x0);
break;
case 7:
Xil_Out32(MDIO7_GPIO_ADDR,0x0);
break;
case 8:
Xil_Out32(MDIO8_GPIO_ADDR,0x0);
break;
case 9:
Xil_Out32(MDIO9_GPIO_ADDR,0x0);
break;
default:
break;
}
}
void mdio_high(int index)
{
switch(index)
{
case 2:
Xil_Out32(MDIO2_GPIO_ADDR,0xffffffff);
break;
case 3:
Xil_Out32(MDIO3_GPIO_ADDR,0xffffffff);
break;
case 5:
Xil_Out32(MDIO5_GPIO_ADDR,0xffffffff);
break;
case 6:
Xil_Out32(MDIO6_GPIO_ADDR,0xffffffff);
break;
case 7:
Xil_Out32(MDIO7_GPIO_ADDR,0xffffffff);
break;
case 8:
Xil_Out32(MDIO8_GPIO_ADDR,0xffffffff);
break;
case 9:
Xil_Out32(MDIO9_GPIO_ADDR,0xffffffff);
break;
default:
break;
}
}
void Mcu_Yt8521_Soft_Smi_Bit_Set(int index,u8 bit)
{
// set mdio dir out
// set mdio
mdio_out(index);
if(bit) mdio_high(index);
else mdio_low(index);
// set clk.
mdc_low(index);
mdc_high(index);
}
void Mcu_Yt8521_Soft_Smi_Bit_Get(int index,u8 *bit)
{
// set mdio dir in
// get mdio
mdio_in(index); //MDIO线设置为输入
switch(index)
{
case 2:
*bit = !!PHY2_SOFT_SMI_MDIO_READ;
break;
case 3:
*bit = !!PHY3_SOFT_SMI_MDIO_READ;
break;
case 5:
*bit = !!PHY5_SOFT_SMI_MDIO_READ;
break;
case 6:
*bit = !!PHY6_SOFT_SMI_MDIO_READ;
break;
case 7:
*bit = !!PHY7_SOFT_SMI_MDIO_READ;
break;
case 8:
*bit = !!PHY8_SOFT_SMI_MDIO_READ;
break;
case 9:
*bit = !!PHY9_SOFT_SMI_MDIO_READ;
break;
default:
break;
}
// set clk.
mdc_low(index);
mdc_high(index);
}
u16 Srv_Yt8521_Soft_I2c_Device_Write(int index,u8 phy_addr, u8 reg_addr, u16 data)
{
u16 i = 0;
//idel
for(i = 0; i < 32; i++)
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
//start
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
//op code
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
//phy address
for(i = 0; i < 5; i++)
{
if( phy_addr & (0x10 >> i) )
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
else
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
}
}
//register address
for(i = 0; i < 5; i++)
{
if( reg_addr & (0x10 >> i) )
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
else
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
}
}
//TA翻转状态(ack)
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
//register data
for(i = 0; i < 16; i++)
{
if( data & (0x8000 >> i) )
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
else
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
}
}
return 0;
}
u16 Srv_Yt8521_Soft_I2c_Device_Read(int index,u8 phy_addr, u8 reg_addr)
{
u16 i = 0;
u16 data = 0; //待获取的数据
u8 bit = 0;
//idel
for(i = 0; i < 32; i++)
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
//start
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
//op code
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
//phy address
for(i = 0; i < 5; i++)
{
if( phy_addr & (0x10 >> i) )
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
else
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
}
}
//register address
for(i = 0; i < 5; i++)
{
if( reg_addr & (0x10 >> i) )
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,1);
}
else
{
Mcu_Yt8521_Soft_Smi_Bit_Set(index,0);
}
}
//TA翻转状态(ack)
Mcu_Yt8521_Soft_Smi_Bit_Get(index,&bit);
Mcu_Yt8521_Soft_Smi_Bit_Get(index,&bit);
//register data
for(i = 0; i < 16; i++)
{
data<<=1;
Mcu_Yt8521_Soft_Smi_Bit_Get(index,&bit);
if(bit)
{
data |= 0x1;
}
}
return data;
}
int main(int argc, char *argv[])
{
int phy_index=0;
int reg_addr=0;
int value=0;
u16 Reg_Value;
if(argc<4)
{
printf("Usage mdio_test w/r phy_index[2,3,5,6,7,8,9] reg_addr [value]\n ");
return 0;
}
if(argc==4)
{
phy_index=(uint16_t) strtoul(argv[2], NULL, 0);
reg_addr=(uint16_t) strtoul(argv[3], NULL, 0);
switch(phy_index)
{
case 2:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(2,PHY2_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
case 3:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(3,PHY3_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
case 5:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(5,PHY5_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
case 6:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(6,PHY6_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
case 7:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(7,PHY7_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
case 8:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(8,PHY8_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
case 9:
Reg_Value=Srv_Yt8521_Soft_I2c_Device_Read(9,PHY9_YT8521_ADDR, reg_addr);
printf("[READ]phy_index %d reg_addr 0x%x value is 0x%x\n",phy_index,reg_addr,Reg_Value);
break;
default:
break;
}
}
else if(argc==5)
{
phy_index=(uint16_t) strtoul(argv[2], NULL, 0);
reg_addr=(uint16_t) strtoul(argv[3], NULL, 0);
value=(uint16_t) strtoul(argv[4], NULL, 0);
printf("[WRITE] phy_index is %d reg_addr is 0x%x value is 0x%x\n",phy_index,reg_addr,value);
switch(phy_index)
{
case 2:
Srv_Yt8521_Soft_I2c_Device_Write(2,PHY2_YT8521_ADDR, reg_addr,value);
break;
case 3:
Srv_Yt8521_Soft_I2c_Device_Write(3,PHY3_YT8521_ADDR, reg_addr,value);
break;
case 5:
Srv_Yt8521_Soft_I2c_Device_Write(5,PHY5_YT8521_ADDR, reg_addr,value);
break;
case 6:
Srv_Yt8521_Soft_I2c_Device_Write(6,PHY6_YT8521_ADDR, reg_addr,value);
break;
case 7:
Srv_Yt8521_Soft_I2c_Device_Write(7,PHY7_YT8521_ADDR, reg_addr,value);
break;
case 8:
Srv_Yt8521_Soft_I2c_Device_Write(8,PHY8_YT8521_ADDR, reg_addr,value);
break;
case 9:
Srv_Yt8521_Soft_I2c_Device_Write(9,PHY9_YT8521_ADDR, reg_addr,value);
break;
default:
break;
}
}
#if 0
u16 id = 0;
printf("//***************** Read SMI Reg of YT8521 ******************//\r\n");
printf("------ PHY Identification Registers ------\r\n");
id = Srv_Yt8521_Soft_I2c_Device_Read(2,PHY2_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY2_YT8521 id = 0x%x\n", id);
id = Srv_Yt8521_Soft_I2c_Device_Read(3,PHY3_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY3_YT8521 id = 0x%x\n", id);
id = Srv_Yt8521_Soft_I2c_Device_Read(5,PHY5_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY5_YT8521 id = 0x%x\n", id);
id = Srv_Yt8521_Soft_I2c_Device_Read(6,PHY6_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY6_YT8521 id = 0x%x\n", id);
id = Srv_Yt8521_Soft_I2c_Device_Read(7,PHY7_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY7_YT8521 id = 0x%x\n", id);
id = Srv_Yt8521_Soft_I2c_Device_Read(8,PHY8_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY8_YT8521 id = 0x%x\n", id);
id = Srv_Yt8521_Soft_I2c_Device_Read(9,PHY9_YT8521_ADDR, PHY_YT8521_ID_REG_ADDR); //reg_addr:0x03 读取的值应为0x11a
printf("PHY9_YT8521 id = 0x%x\n", id);
printf("Setting PHY2\n");
Srv_Yt8521_Soft_I2c_Device_Write(2,PHY2_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(2,PHY2_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
printf("Setting PHY3\n");
Srv_Yt8521_Soft_I2c_Device_Write(3,PHY3_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(3,PHY3_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
printf("Setting PHY5\n");
Srv_Yt8521_Soft_I2c_Device_Write(5,PHY5_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(5,PHY5_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
printf("Setting PHY6\n");
Srv_Yt8521_Soft_I2c_Device_Write(6,PHY6_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(6,PHY6_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
printf("Setting PHY7\n");
Srv_Yt8521_Soft_I2c_Device_Write(7,PHY7_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(7,PHY7_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
printf("Setting PHY8\n");
Srv_Yt8521_Soft_I2c_Device_Write(8,PHY8_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(8,PHY8_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
printf("Setting PHY9\n");
Srv_Yt8521_Soft_I2c_Device_Write(9,PHY9_YT8521_ADDR, PHY_YT8521_REG_DEBUG_ADDR_OFFSET, PHY_YT8521_EXT_REG_ADDR);
Srv_Yt8521_Soft_I2c_Device_Write(9,PHY9_YT8521_ADDR, PHY_YT8521_REG_DEBUG_DATA,0x604);
#endif
return 0;
}
5、测试验证
读取裕太8521的PHY ID