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); }