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如下:
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访问的,但是读写方式有些特殊。
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:
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;







