MDIO接口包括两根信号线:MDC和MDIO,通过它,MAC层芯片(或其它控制芯片)可以访问物理层芯片的寄存器。作为MA和PHY之间的控制总线,数据总线是RMII/MII。、
其实mdio和I2C接口非常类似,是为了规避专利,特意重新命名了一种总线。
下面是使用gpio模拟mdc/mdio通信,经过验证,此代码可行。
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include "soc.h"
#include "irq.h"
#include "bios.h"
#include "gpio.h"
/* bb:bit-bang,通过gpio引脚,用软件模拟通信*/
#define MDIO_PORT GPIO7
#define MDIO_PIN GPIO_Pin_9
#define MDC_PORT GPIO7
#define MDC_PIN GPIO_Pin_10
#define MDIO_DELAY 10 // us
#define MDIO_READ_DELAY 10 // us
/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit
* IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips.
*/
#define MII_ADDR_C45 (1<<30)
#define MDIO_READ 2
#define MDIO_WRITE 1
#define MDIO_C45 (1<<15)
#define MDIO_C45_ADDR (MDIO_C45 | 0)
#define MDIO_C45_READ (MDIO_C45 | 3)
#define MDIO_C45_WRITE (MDIO_C45 | 1)
//#define READ_REG 0x37
//#define WRITE_REG 0x38
#define MDIO_C45_TEST 0
typedef struct gpio_ctrl_blk{
int pin;
int value;
}gpio_cblk_t;
typedef struct phy_reg_blk{
unsigned int phy_address;
unsigned int reg_address;
unsigned int reg_value;
}phy_reg_cblk_t;
#define MDIO_DEV_ID 't'
#define READ_REG _IOWR (MDIO_DEV_ID,0x37,phy_reg_cblk_t)
#define WRITE_REG _IOWR (MDIO_DEV_ID,0x38,phy_reg_cblk_t)
static void MDC_OUT(void);
static void MDIO_OUT(void);
static void MDIO_IN(void);
static void MDC_H(void);
static void MDC_L(void);
static int GET_MDIO(void);
static void SET_MDIO(int val);
/* 设置MDC为输出引脚,在MDC输出时钟之前设置 */
static void MDC_OUT(void)
{
struct gpio_cfg cfg;
cfg.pins = MDC_PIN;
cfg.dir = GPIO_OUT;
cfg.irq_mode = GPIO_IRQ_DISABLE;
gpio_init(MDC_PORT, &cfg);
}
/* 设置MDIO的gpio引脚为输出引脚 */
static void MDIO_OUT(void)
{
struct gpio_cfg cfg;
cfg.pins = MDIO_PIN;
cfg.dir = GPIO_OUT;
cfg.irq_mode = GPIO_IRQ_DISABLE;
gpio_init(MDIO_PORT, &cfg);
}
/* 设置MDIO的gpio引脚为输入引脚 */
static void MDIO_IN(void)
{
struct gpio_cfg cfg;
cfg.pins = MDIO_PIN;
cfg.dir = GPIO_IN;
cfg.irq_mode = GPIO_IRQ_DISABLE;
gpio_init(MDIO_PORT, &cfg);
}
/* MDC输出高电平,在MDC设置为输出后调用 */
static void MDC_H(void)
{
gpio_write_pin(MDC_PORT, MDC_PIN, GPIO_OUTPUT_HIGH);
}
/* MDC输出低电平,在MDC设置为输出后调用 */
static void MDC_L(void)
{
gpio_write_pin(MDC_PORT, MDC_PIN, GPIO_OUTPUT_LOW);
}
/* 获得MDIO的数据,只获得一个bit */
static int GET_MDIO(void)
{
return gpio_read_input_pin(MDIO_PORT, MDIO_PIN);
}
/* 设置MDIO的数据,一个bit */
static void SET_MDIO(int val)
{
gpio_write_pin(MDIO_PORT, MDIO_PIN, val);
}
/* MDIO发送一个bit的数据,MDIO必须已经被配置为输出 */
static void mdio_bb_send_bit(int val)
{
MDC_OUT();
SET_MDIO(val);
bios_udelay(MDIO_DELAY);
MDC_H();
bios_udelay(MDIO_DELAY);
MDC_L();
//bios_udelay(MDIO_DELAY);
}
/* MDIO 获取一个bit的数据,MDIO必须已经被配置为输入. */
static int mdio_bb_get_bit(void)
{
int value;
MDC_OUT();
bios_udelay(MDIO_DELAY);
MDC_H();
bios_udelay(MDIO_READ_DELAY);
value = GET_MDIO();
// bios_udelay(MDIO_DELAY);
MDC_L();
return value;
}
/*
* MDIO发送一个数据,MDIO 必须被配置为输出模式.
* value:要发送的数据
* bits:数据的位数
*
* */
static void mdio_bb_send_num(unsigned int value ,int bits)
{
int i;
MDIO_OUT();
for(i = bits - 1; i >= 0; i--)
mdio_bb_send_bit((value >> i) & 1);
}
/*
* MDIO获取一个数据,MDIO 必须被配置为输入模式.
* bits:获取数据的位数
*
* */
static int mdio_bb_get_num(int bits)
{
int i;
int ret = 0;
for(i = bits - 1; i >= 0; i--)
{
ret <<= 1;
ret |= mdio_bb_get_bit();
}
return ret;
}
/* Utility to send the preamble, address, and
* register (common to read and write).
*/
static void mdio_bb_cmd(int op,int phy,int reg)
{
int i = 0 ;
MDIO_OUT(); //设置MDIO引脚为输出引脚
/*发送32bit的1,这个帧前缀域不是必须的,某些物理层芯片的MDIO操作就没有这个域*/
for(i = 0; i < 32; i++)
mdio_bb_send_bit(1);
/* 发送开始位(01),和读操作码(10),写操作码(01)
* Clause 45 操作,开始位是(00),(11)为读,(10)为写
*/
#if MDIO_C45_TEST
mdio_bb_send_bit(0);
if(op & MDIO_C45)
mdio_bb_send_bit(0);
else
mdio_bb_send_bit(1);
#else
mdio_bb_send_bit(0);
mdio_bb_send_bit(1);
#endif
mdio_bb_send_bit((op >> 1) & 1);
mdio_bb_send_bit((op >> 0) & 1);
mdio_bb_send_num(phy,5);
mdio_bb_send_num(reg,5);
}
static int mdio_bb_cmd_addr(int phy,int addr)
{
unsigned int dev_addr = (addr >> 16) & 0x1F;
unsigned int reg = addr & 0xFFFF;
mdio_bb_cmd(MDIO_C45_ADDR,phy,dev_addr);
/* send the turnaround (10) */
mdio_bb_send_bit(1);
mdio_bb_send_bit(0);
mdio_bb_send_num(reg,16);
MDIO_IN();
mdio_bb_get_bit();
return dev_addr;
}
void mdio_set_turnaround(void)
{
int i = 0;
MDIO_IN();
MDC_OUT();
for(i=0;i<1;i++)
{
bios_udelay(MDIO_DELAY);
MDC_H();
bios_udelay(MDIO_DELAY);
MDC_L();
}
}
unsigned int mdio_bb_read(int phy,int reg)
{
unsigned int ret,i;
#if MDIO_C45_TEST
/* 寄存器是否满足有C45标志 */
if(reg & MII_ADDR_C45)
{
reg = mdio_bb_cmd_addr(phy,reg);
mdio_bb_cmd(MDIO_C45_READ,phy,reg);
}
else
mdio_bb_cmd(MDIO_READ,phy,reg);
#else
mdio_bb_cmd(MDIO_READ,phy,reg);
#endif
MDIO_IN();
//mdio_set_turnaround();
#if 1
/* check the turnaround bit: the PHY should be driving it to zero */
if(mdio_bb_get_bit() != 0)
{
/* PHY didn't driver TA low -- flush any bits it may be trying to send*/
for(i = 0; i < 32; i++)
mdio_bb_get_bit();
//bios_log("PHY didn't driver TA low! \r\n");
return 0xFFFF;
}
#endif
ret = mdio_bb_get_num(16);
mdio_bb_get_bit();
return ret;
}
int mdio_bb_write(unsigned int phy,unsigned int reg,unsigned int val)
{
#if MDIO_C45_TEST
if(reg & MII_ADDR_C45)
{
reg = mdio_bb_cmd_addr(phy,reg);
mdio_bb_cmd(MDIO_C45_WRITE,phy,reg);
}
else
mdio_bb_cmd(MDIO_WRITE,phy,reg);
#else
mdio_bb_cmd(MDIO_WRITE,phy,reg);
#endif
#if 1
/* send the turnaround (10) */
mdio_bb_send_bit(1);
mdio_bb_send_bit(0);
#else
mdio_set_turnaround();
#endif
mdio_bb_send_num(val,16);
MDIO_IN();
//mdio_bb_get_bit();
return 0;
}
int mdio_init(int id)
{
// set PAD49/PAD50 as gpio
*(volatile uint32_t *)(SYSCTL_BASE + 0x78) &= ~(0xF << 8);
*(volatile uint32_t *)(SYSCTL_BASE + 0x78) &= ~(0xF << 4);
}

浙公网安备 33010602011771号