基于DWC_ether_qos的以太网驱动开发-MDIO驱动编写与测试

描述

本文转自公众号欢迎关注

一.前言

以太网驱动的编写与调试往往从MDIO接口开始,MDIO是MAC访问PHY的接口。实现通过MDIO对PHY进行操作才能配置PHY,所以实现MDIO读写是第一步。DWC_ether_qos提供了SMA模块,操作两个寄存器即可实现PHY寄存器的读写,比较简单方便,且支持C45和C22两种模式,另外有比较灵活的配置参数后面会详讲。

二.SMA模块介绍

DWC_ether_qos中SMA(Station Management Agent)是一种双线站管理接口(MIM:Station Management interface),即MDIO管理接口,通过SMA模块即MDIO接口可以访问PHY的寄存器。

其具备以下特征:

l时钟

IEEE 802.3中规定,MDC的最大操作频率(gmi_MDC_o)是2.5MHz。在DWC_ether_qos中,gmii_mdc_o时钟是从csr_i分频得到的,通过MAC_MDIO_Address寄存器中的CR字段配置,

要注意该分频值一般要使得MDC满足不大于2.5MHz的要求。如果系统支持更高的频率该分频也可以分频到大于2.5MHz的频率。

l模式选择:MAC_MDIO_Address 的C45E 配置使用C45还是C22格式。

l前导抑制:MAC_MDIO_Address的PSE控制是否发送只有一个bit的前导的帧。

l时钟保持与连续传输:MAC_MDIO_Address 的NTC可以配置MDIO帧发送完后时钟保持07个CLK。在此基础上还可以配置MAC_MDIO_Address 的BTB使能连续传输,此时不等07个时钟保持结束,Busy信号就会发送完帧后立即清除,软件可以开始下一次传输,否则需要等到配置的0~7个保持时钟之后Busy才会清除。BTB必须在NTC大于0时才能使能。

三. MDIO帧结构

MDIO帧的一些特征:

MDC一般是1MHz~2.5MHz。

MDIO引脚需要一个1.5k欧姆的上拉电阻,以在空闲和周转期间保持MDIO高电平。

MDC下降沿修改数据,上升沿锁存数据,这样使得数据的建立和保持时间都是半个周期。

高位先发送,高字节先发送。

参考

IEEE 802.3,Section 22.2.4.5

**3.1 **Clause 45

SMA支持在该模式下操作时的读后递增地址。

MAC_MDIO_Address 的C45E 配置为1使用C45.

字段描述
IDLEgmii_mdc_o无时钟,MDIO处于高阻态。通过外部MDIO上拉到高电平
PREAMBLE32 个连续的1,前导抑制模式则只有一个bit。
START包开始2’b00
OPCODE操作码■ 2’b00 读写地址寄存器■ 2’b01 读写数据, 读写完地址不递增■ 2’b10 读写数据,读写完地址自动递增■ 2’b11 读写数据,仅写完地址自动递增
PHY ADDR5-bit 的PHY地址,可以编码最多32 个PHY。
DEV ADDR5-bit 设备地址可以编码32个设备。
TATurnaround寄存器地址和帧的数据字段之间的2位时间间隔,以避免在读取事务期间发生争用。■ 2’bZ0: 读时 第一个bit MAC和PHY都处于高组态,第二个bit是PHY拉低。■ 2’b10: 写时 10都是MAC驱动。Z 表示三态
DATA/ADDRESS16-bit 值: 对于地址周期(OPCODE = 2'b00), 表示下一个周期需要访问的寄存器地址. 对于数据写周期,表示需要写入寄存器的数据内容. 对于read,post-read递增帧,表示从PHY读出的寄存器内容.

完成一次C45寄存器读写需要两步,第一步发送地址包,第二步再读写

寄存器

**3.2 ****Clause **22

MAC_MDIO_Address 的C45E 配置为0使用C22.

帧结构如下

字段描述
IDLEgmii_mdc_o无时钟,MDIO处于高阻态。通过外部MDIO上拉到高电平。
PREAMBLE32 个连续的1,前导抑制模式则只有一个bit。
START包开始2’b01
OPCODE■ 2’b01 写■ 2’b10 读
PHY ADDR5-bit 的PHY地址,可以编码最多32 个PHY。
REG ADDR5-bit 寄存器地址可以编码32个寄存器。寻址对应指定MMD的某一个寄存器。
TATurnaround寄存器地址和帧的数据字段之间的2位时间间隔,以避免在读取事务期间发生争用。■ 2’bZ0: 读时 第一个bit MAC和PHY都处于高组态,第二个bit是PHY拉低。■ 2’b10: 写时 10都是MAC驱动。Z 表示三态
DATA16-bit 值:■ 写时DWC_ether_qos 驱动MDIO■ 读时PHY 驱动MDIO

完成C22读写只需要一步

寄存器

读写波形如下

寄存器

3.3使用C22模式访问C45寄存器

使用C22访问C45寄存器

参考https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf

用13号寄存器作为命令寄存器,14号寄存器作为寄存器/数据寄存器

寄存器

寄存器

步骤如下,分为4步完成:

1.C22模式写寄存器13,高两位FN为00,低5位DEV设备地址。

2.C22模式将16位寄存器地址写入14号寄存器。

3.C22模式写寄存器13,高两位FN为01或者10或者11,低5位DEV设备地址。

4.读:转至步骤5。写:转至步骤6

5.C22模式读14号寄存器的16位内容。

6.C22模式将16位寄存器内容写入14号寄存器。

四.相关寄存器

MDIO操作仅需要两个寄存器

MAC_MDIO_Address和MAC_MDIO_Data

4.1 MAC_MDIO_Address

Offset: 0x200

PSE[27]: bit27,置位则只发送1bit的前导,否则发送32位的前导

BTB **[26]: **bit26,NTC 不为0时生效,NTC为0时不能设置为1.

设置为1时,NTC不为0时,MDIO发送完帧不等NTC个时钟就立即清除GB位,可以立即重新下一次读写。

设置为0,则等待NTC保持时钟之后GB才清除。

**PA[25:21] **5位的PHY地址

RDA[22:16] 对于C22模式的寄存器地址,C45的设备地址MMD。

NTC[14:12] MDIO帧的发送完后,MDC再保持该配置个时钟。

CR[11:8]:MDC时钟选择 一般配置为1~2.5MHz

0000: CSR clock = 60-100 MHz; MDC clock = CSR clock/42
0001: CSR clock = 100-150 MHz; MDC clock = CSR clock/62
0010: CSR clock = 20-35 MHz; MDC clock = CSR clock/16
0011: CSR clock = 35-60 MHz; MDC clock = CSR clock/26
0100: CSR clock = 150-250 MHz; MDC clock = CSR clock/102
0101: CSR clock = 250-300 MHz; MDC clock = CSR clock/124
0110, 0111: Reserved

1000: CSR clock/4
1001: CSR clock/6
1010: CSR clock/8
1011: CSR clock/10
1100: CSR clock/12
1101: CSR clock/14
1110: CSR clock/16
1111: CSR clock/18

SKAP[4]:只有C45E使能时才有,置位时不发送地址包

GOC_1_0[3:2]:

00: Reserved
01: Write
10: Post Read Increment Address for Clause 45 PHY
11: Read

C45E[1]: 使能C45模式

GB[0]: 软件写1触发读写操作,读写完硬件清零。

4.2 MAC_MDIO_Data

Offset: 0x204

寄存器

RA[31:16]:C45E模式时指定16位寄存器地址。

GD[15:0]:读出或者写入寄存器的16位值。

五.MDIO读写驱动

5.1 寄存器操作接口

实现基本的寄存器级别的操作接口,

后面再对关键参数MDC时钟,NTC,PSE等测试,抓取波形进行验证。

/*
 * GB_MASK bit0 置位,触发MDIO操作, 操作完GB_MASK位硬件清零
 */
GMAC_INLINE int gmac_cfg_mdio_address(uint8_t id, uint8_t pse, uint8_t btb, uint8_t phyadd, 
                                    uint8_t reg_dev, uint8_t ntc, uint32_t csrclk,
                                    uint8_t skap, uint8_t cmd, uint8_t c45)
{
    (void)id;
    uint32_t tmp = 0;
    uint8_t csr = GMAC_CLK_60_100MHZ;
    if(csrclk >= 250000000ul)
    {
        csr = GMAC_CLK_250_300MHZ;
    }
    else if(csrclk >= 150000000ul)
    {
        csr = GMAC_CLK_150_250MHZ;
    }
    else if(csrclk >= 100000000ul)
    {
        csr = GMAC_CLK_100_150MHZ;
    }
    else if(csrclk >= 60000000ul)
    {
        csr = GMAC_CLK_60_100MHZ;
    }
    else if(csrclk >= 35000000ul)
    {
        csr = GMAC_CLK_35_60MHZ;
    }
    else
    {
        csr = GMAC_CLK_20_35MHZ;
    }
    tmp = ((uint32_t)csr < < CR_OFFSET) 
            | ((uint32_t)cmd < < GOC_0_OFFSET) 
            | (uint32_t)GB_MASK 
            | (((uint32_t)reg_dev < < RDA_OFFSET) & RDA_MASK) 
            | (((uint32_t)phyadd < < PA_OFFSET) & PA_MASK)
            | (((uint32_t)pse < < PSE_OFFSET) & PSE_MASK)
            | (((uint32_t)btb < < BTB_OFFSET) & BTB_MASK)
            | (((uint32_t)ntc < < NTC_OFFSET) & NTC_MASK)
            | (((uint32_t)skap < < SKAP_OFFSET) & SKAP_MASK)
            | (((uint32_t)c45 < < C45E_OFFSET) & C45E_MASK);
    GMAC_WRITE_REG(CFG_MAC_MDIO_ADDRESS_ADDR, tmp);
    return 0;
}


GMAC_INLINE int gmac_get_mdio_data(uint8_t id, uint16_t* data, uint16_t* regaddr)
{
    (void)id;
    uint32_t tmp = 0;
    tmp = GMAC_READ_REG(CFG_MAC_MDIO_DATA_ADDR);
    if(data != (void*)0)
    {
        *data = (tmp & GD_MASK) > > GD_OFFSET;
    }
    if(regaddr != (void*)0)
    {
        *regaddr = (tmp & REG_ADDR_MASK) > > REG_ADDR_OFFSET;
    }
    return 0;
}


GMAC_INLINE int gmac_set_mdio_data(uint8_t id, uint16_t data, uint16_t regaddr)
{
    (void)id;
    uint32_t tmp = 0;
    tmp = (((uint32_t)regaddr<

1 MDC时钟

我这里时钟是60MHz,设置CR为0,即分频42,理论上60/42=1.429MHz

实测MDC频率为1.449MHz

寄存器

寄存器

设置CR为3,即分频26,理论上60/26=2.31MHz

实测MDC频率为2.326MHz

寄存器

寄存器

2 NTC

NTC=0,帧发送完后MDC立即结束

寄存器

寄存器

NTC=7,帧发送完后MDC保持7个CLK

寄存器

寄存器

3 PSE

PSE=0,发送32个前导1

寄存器

寄存器

PSE=1,发送1个前导1,可以看到这个PHY是不支持前导抑制的,即发送1个前导时不能读写。

寄存器

5.2 C22读写

驱动代码如下

int iot_gmac_mdio_read(uint8_t id, uint8_t phyadd, uint8_t reg, uint16_t *data, uint32_t clk)
{
    int timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    gmac_cfg_mdio_address(id, 0, 0, phyadd, reg, 0, clk, 0, GMAC_CMD_PHY_RD, 0);


    timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    gmac_get_mdio_data(id, data, (void*)0);
    return 0;
}


int iot_gmac_mdio_write(uint8_t id, uint8_t phyadd, uint8_t reg, uint16_t data, uint32_t clk)
{
    int timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    gmac_set_mdio_data(id, data, 0);
    gmac_cfg_mdio_address(id, 0, 0, phyadd, reg, 0, clk, 0, GMAC_CMD_PHY_WR, 0);
    timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    return 0;
}

如下是往0号寄存器写0x9040即Reset软复位PHY

寄存器

如下是读0号寄存器为0x1040即Reset软复位PHY完成,硬件自清零了Reset位。

寄存器

5.3 C45读写

驱动代码如下

int iot_gmac_mdio_readc45(uint8_t id, uint8_t phyadd, uint8_t dev, uint16_t reg, uint16_t *data, uint32_t clk)
{
    int timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }
    gmac_set_mdio_data(id, 0, reg);
    gmac_cfg_mdio_address(id, 0, 0, phyadd, dev, 0, clk, 0, GMAC_CMD_PHY_RD, 1);


    timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    gmac_get_mdio_data(id, data, (void*)0);
    return 0;
}


int iot_gmac_mdio_writec45(uint8_t id, uint8_t phyadd, uint8_t dev, uint16_t reg, uint16_t data, uint32_t clk)
{
    int timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    gmac_set_mdio_data(id, data, reg);
    gmac_cfg_mdio_address(id, 0, 0, phyadd, dev, 0, clk, 0, GMAC_CMD_PHY_WR, 1);
    timeout = IOT_GMAC_MDIO_TIMEOUT;
    while(gmac_is_mdio_busy(id) && (timeout-- > 0));
    if(timeout <= 0)
    {
        return -1;
    }


    return 0;
}

波形如下:两个包,一个是地址包,一个是数据包

寄存器

寄存器

5.4 C22模式读写C45寄存器

以下是RTL8211F的MMD寄存器,不支持直接C45模式,但是支持C22模式读C45寄存器,

通过13和14号寄存器实现。

寄存器

驱动代码如下

int dwc_mdio_c22readc45(uint8_t id, uint8_t phyadd, uint8_t dev, uint16_t reg, uint16_t *data, uint32_t clk)
{
    iot_gmac_mdio_write(id, phyadd, 13, (0< < 14) | dev, clk);
    iot_gmac_mdio_write(id, phyadd, 14, reg, clk);
    iot_gmac_mdio_write(id, phyadd, 13, (1u< < 14) | dev, clk);
    iot_gmac_mdio_read(id, phyadd, 14, data, clk);
    return 0;
}


int dwc_mdio_c22writec45(uint8_t id, uint8_t phyadd, uint8_t dev, uint16_t reg, uint16_t data, uint32_t clk)
{
    iot_gmac_mdio_write(id, phyadd, 13, (0< < 14) | dev, clk);
    iot_gmac_mdio_write(id, phyadd, 14, reg, clk);
    iot_gmac_mdio_write(id, phyadd, 13, (1u< < 14) | dev, clk);
    iot_gmac_mdio_write(id, phyadd, 14, data, clk);
    return 0;
}

测试代码如下

uint16_t c45reg = 0;
    dwc_mdio_c22writec45(p_ctrl.unit,p_ctrl.phy_addr,3,0,1u< < 10,p_ctrl.clk);
    dwc_mdio_c22readc45(p_ctrl.unit,p_ctrl.phy_addr,3,0,&c45reg,p_ctrl.clk);
    printf("PC1R=%xrn",c45reg);
    c45reg = 0;
    dwc_mdio_c22readc45(p_ctrl.unit,p_ctrl.phy_addr,3,1,&c45reg,p_ctrl.clk);
    printf("PS1R=%xrn",c45reg);
    c45reg = 0;
    dwc_mdio_c22readc45(p_ctrl.unit,p_ctrl.phy_addr,3,20,&c45reg,p_ctrl.clk);
    printf("EEECR=%xrn",c45reg);
    c45reg = 0;
    dwc_mdio_c22readc45(p_ctrl.unit,p_ctrl.phy_addr,3,22,&c45reg,p_ctrl.clk);
    printf("EEEWER=%xrn",c45reg);
    c45reg = 0;
    dwc_mdio_c22readc45(p_ctrl.unit,p_ctrl.phy_addr,7,60,&c45reg,p_ctrl.clk);
    printf("EEEAR=%xrn",c45reg);
    c45reg = 0;
    dwc_mdio_c22readc45(p_ctrl.unit,p_ctrl.phy_addr,7,61,&c45reg,p_ctrl.clk);
    printf("EEELPAR=%xrn",c45reg);

打印结果如下

可以看到PC1R的bit10设置为了1,其他寄存器对照手册可以看到都是默认值

寄存器

波形如下

写4步完成

寄存器

读4步完成

寄存器

六. 总结

以太网驱动编写调试的第一步都是调通MDIO接口,重点是要去抓取信号分析实际的波形,比如寄存器的每一个参数对应波形的什么变化,这在驱动编写调试中很重要,一定要对寄存器每一个bit的功能都非常清晰。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分