ILD

PHY layer and MMD register
作者:Yuan Jianpeng 邮箱:yuanjianpeng@xiaomi.com
发布时间:2022-2-7 站点:Inside Linux Development

1 MAC和PHY体系结构

在嵌入式网络设备中,MAC和PHY是两个层级的底层的网络设备。


MAC对应了MAC controller或者叫做Ethernet controller,软件驱动为以太驱动。它创建netdevice,如eth0,负责收发包。MAC层对外的接口为各种类型的GMII,可以连接phy,也可以连接交换机的MAC。


而PHY对应的是Transceiver。


通常ethernet controller集成在SOC中。通过MII接口外接PHY或者交换机。通过MDIO总线控制PHY和交换机。


以MT7981为例,它有两个2 ethernet controller,各导出一个HSGMII接口,每个HSGMII接口的最大速率可以达到2.5Gbps。然后可以有很多种外挂方案:


1 外挂一个MT7531交换机,两个HSGMII都接到交换机,分别接到交换机的port 5和port 6。

这样可以LAN走一个HSGMII,WAN走一个HSGMII。LAN/WAN使用交换机的port 0-4。


软件层面

以太驱动位于:drivers/net/ethernet/mediatek/

负责GMAC初始化,创建网络接口,注册中断,通过DMA收发包。

负责MDIO总线的初始化和注册。


PHY驱动位于:drivers/net/phy/mtk/mt753x/

由于是外挂的交换机,因此mt7531实际上一个phy层的设备。通过MDIO来控制和初始化。


DTS如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
ethernet@15100000 {
    compatible = "mediatek,mt7981-eth";
    reg = <0x00 0x15100000 0x00 0x80000>;
    interrupts = <0x00 0xc4 0x04 0x00 0xc5 0x04 0x00 0xc6 0x04 0x00 0xc7 0x04>;
    clocks = <0x0e 0x00 0x0e 0x01 0x0e 0x02 0x0e 0x03 0x0f 0x00 0x0f 0x01 0x0f 0x02 0x0f 0x03 0x10 0x00 0x10 0x01 0x10 0x02 0x10 0x03>;
    clock-names = "fe\0gp2\0gp1\0wocpu0\0sgmii_tx250m\0sgmii_rx250m\0sgmii_cdr_ref\0sgmii_cdr_fb\0sgmii2_tx250m\0sgmii2_rx250m\0sgmii2_cdr_ref\0sgmii2_cdr_fb";
    assigned-clocks = <0x08 0x60 0x08 0x61>;
    assigned-clock-parents = <0x08 0x1b 0x08 0x22>;
    mediatek,ethsys = <0x0e>;
    mediatek,sgmiisys = <0x0f 0x10>;
    mediatek,infracfg = <0x11>;
    #reset-cells = <0x01>;
    #address-cells = <0x01>;
    #size-cells = <0x00>;
    status = "okay";
 
    mac@0 {
        compatible = "mediatek,eth-mac";
        reg = <0x00>;
        phy-mode = "2500base-x";
 
        fixed-link {
            speed = <0x9c4>;
            full-duplex;
            pause;
        };
    };
 
    mac@1 {
        compatible = "mediatek,eth-mac";
        reg = <0x01>;
        phy-mode = "2500base-x";
 
        fixed-link {
            speed = <0x9c4>;
            full-duplex;
            pause;
        };
    };
 
    mdio-bus {
        #address-cells = <0x01>;
        #size-cells = <0x00>;
        phandle = <0x1a>;
 
        ethernet-phy@0 {
            compatible = "ethernet-phy-id03a2.9461";
            reg = <0x00>;
            phy-mode = "gmii";
            nvmem-cells = <0x12>;
            nvmem-cell-names = "phy-cal-data";
        };
    };
};
 
gsw@0 {
    compatible = "mediatek,mt753x";
    mediatek,ethsys = <0x0e>;
    #address-cells = <0x01>;
    #size-cells = <0x00>;
    mediatek,mdio = <0x1a>;
    mediatek,portmap = "llllw";
    mediatek,mdio_master_pinmux = <0x00>;
    reset-gpios = <0x0d 0x27 0x00>;
    interrupt-parent = <0x0d>;
    interrupts = <0x26 0x04>;
    status = "okay";
 
    port@5 {
        compatible = "mediatek,mt753x-port";
        reg = <0x05>;
        phy-mode = "sgmii";
 
        fixed-link {
            speed = <0x9c4>;
            full-duplex;
        };
    };
 
    port@6 {
        compatible = "mediatek,mt753x-port";
        mediatek,ssc-on;
        reg = <0x06>;
        phy-mode = "sgmii";
 
        fixed-link {
            speed = <0x9c4>;
            full-duplex;
        };
    };
};



2 一个HSGMII接MT7531交换机作为LAN,一个HSGMII接一个2.5G phy作为WAN。


MDIO总线

mdio总线是共享总线,外挂的PHY,MAC,都接到一个MDIO总线上。通过PHY Address来通信。每个设备有一个phy address。通信数据广播到总线上,每个设备根据phy address来接收自己的消息。这个phy address是设备自己决定的。通常可以通过引脚的高低电平,来设置设备的phy address,在多个预置的地址里面设置一个,不写死,而是可调,是避免发生地址冲突。


MII总线里面包括MDIO/MDC,来实现MDIO总线的连接。交换机通常有SMI接口,来将交换机接到CPU的MDIO总线,如下:


PHY MMD寄存器

PHY除了正常的寄存器外,还有MMD寄存器,比如(Energy Efficient Ethernet)相关的寄存器,它们也是通过MDIO访问的,但是读写方式有些特殊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int mt753x_mii_read(struct gsw_mt753x *gsw, int phy, int reg)
{
        int val;
 
        if (phy < MT753X_NUM_PHYS)
                phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK;
 
        mutex_lock(&gsw->mii_lock);
        val = mt753x_mii_rw(gsw, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22);
        mutex_unlock(&gsw->mii_lock);
 
        return val;
}
 
int mt753x_mmd_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg)
{
        int val;
 
        if (addr < MT753X_NUM_PHYS)
                addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK;
 
        mutex_lock(&gsw->mii_lock);
        mt753x_mii_rw(gsw, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45);
        val = mt753x_mii_rw(gsw, addr, devad, 0, MDIO_CMD_READ_C45,
                            MDIO_ST_C45);
        mutex_unlock(&gsw->mii_lock);
 
        return val;
}


交换机寄存器和交换机下phy寄存器读写架构

SOC的MDIO总线挂一个交换机,交换机再接PHY。那如何读取交换机寄存器和交换机下挂PHY的寄存器呢?


交换机代理,读取交换机下挂PHY的寄存器。


1 交换机寄存器读取

通过MDIO总线指定交换机的phy address来读取交换机的寄存器。也就是说交换机的寄存器,可以通过特殊的PHY寄存器方式通过MDIO读写,具体怎么通过PHY寄存器实现交换机寄存器的读写,这个是交换机设计厂商决定的。例如MT7531:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
u32 mt753x_reg_read(struct gsw_mt753x *gsw, u32 reg)
{
        u32 high, low;
        mutex_lock(&gsw->host_bus->mdio_lock);
        gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f,
                (reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S);
        low = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr,
                (reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S);
        high = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x10);
        mutex_unlock(&gsw->host_bus->mdio_lock);
        return (high << 16) | (low & 0xffff);
}
 
void mt753x_reg_write(struct gsw_mt753x *gsw, u32 reg, u32 val)
{
        mutex_lock(&gsw->host_bus->mdio_lock);
        gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f,
                (reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S);
        gsw->host_bus->write(gsw->host_bus, gsw->smi_addr,
                (reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S, val & 0xffff);
        gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, val >> 16);
        mutex_unlock(&gsw->host_bus->mdio_lock);
}


如上gsw->host_bus是mdio bus,通过mdio bus的read和write标准phy读写寄存器,指定交换机的phy address gsw->smi_addr,来和交换机通信。具体的方案就不研究了。drivers/net/phy/mtk/mt753x/mt753x.h 里面有详细的注释:Procedure of MT753x Internal Register Access。


2 交换机下LAN/WAN PHY寄存器读取

通过交换机代理,通过第一步的接口,读取交换机某个寄存器来实现phy的读写:

具体可以看:drivers/net/phy/mtk/mt753x/mt753x_mdio.c


/* Indirect MDIO clause 22/45 access */

static int mt753x_mii_rw(struct gsw_mt753x *gsw, int phy, int reg, u16 data,

                         u32 cmd, u32 st)

{

}


int mt753x_mii_read(struct gsw_mt753x *gsw, int phy, int reg)

{

        int val;


        if (phy < MT753X_NUM_PHYS)

                phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK;


        mutex_lock(&gsw->mii_lock);

        val = mt753x_mii_rw(gsw, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22);

        mutex_unlock(&gsw->mii_lock);


        return val;

}


void mt753x_mii_write(struct gsw_mt753x *gsw, int phy, int reg, u16 val)

{

        if (phy < MT753X_NUM_PHYS)

                phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK;


        mutex_lock(&gsw->mii_lock);

        mt753x_mii_rw(gsw, phy, reg, val, MDIO_CMD_WRITE, MDIO_ST_C22);

        mutex_unlock(&gsw->mii_lock);

}


交换机驱动通常会为自己下挂的PHY注册一个mdio总线,来方便PHY寄存器的读写:

        gsw->gphy_bus->name = "mt753x_mdio";

        gsw->gphy_bus->read = mt753x_mdio_read;

        gsw->gphy_bus->write = mt753x_mdio_write;

        gsw->gphy_bus->priv = gsw;

        gsw->gphy_bus->parent = gsw->dev;

        gsw->gphy_bus->phy_mask = BIT(MT753X_NUM_PHYS) - 1;



参考:


MANAGEMENT REGISTERS AND MIB OBJECTS

https://www.ieee802.org/3/bn/public/mar13/frazier_3bn_01_0313.pdf


https://www.kernel.org/doc/html/latest/networking/phy.html


https://www.totalphase.com/support/articles/200349206-MDIO-Background


Copyright © insidelinuxdev.net 2017-2021. Some Rights Reserved.