gpio模擬mdio代碼


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

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM