本文轉自公眾號歡迎關注
一.前言
以太網驅動的編寫與調試往往從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.
字段 | 描述 |
---|---|
IDLE | gmii_mdc_o無時鐘,MDIO處于高阻態。通過外部MDIO上拉到高電平 |
PREAMBLE | 32 個連續的1,前導抑制模式則只有一個bit。 |
START | 包開始2’b00 |
OPCODE | 操作碼■ 2’b00 讀寫地址寄存器■ 2’b01 讀寫數據, 讀寫完地址不遞增■ 2’b10 讀寫數據,讀寫完地址自動遞增■ 2’b11 讀寫數據,僅寫完地址自動遞增 |
PHY ADDR | 5-bit 的PHY地址,可以編碼最多32 個PHY。 |
DEV ADDR | 5-bit 設備地址可以編碼32個設備。 |
TA | Turnaround寄存器地址和幀的數據字段之間的2位時間間隔,以避免在讀取事務期間發生爭用。■ 2’bZ0: 讀時 第一個bit MAC和PHY都處于高組態,第二個bit是PHY拉低?!?2’b10: 寫時 10都是MAC驅動。Z 表示三態 |
DATA/ADDRESS | 16-bit 值: 對于地址周期(OPCODE = 2'b00), 表示下一個周期需要訪問的寄存器地址. 對于數據寫周期,表示需要寫入寄存器的數據內容. 對于read,post-read遞增幀,表示從PHY讀出的寄存器內容. |
完成一次C45寄存器讀寫需要兩步,第一步發送地址包,第二步再讀寫
**3.2 ****Clause **22
MAC_MDIO_Address 的C45E 配置為0使用C22.
幀結構如下
字段 | 描述 |
---|---|
IDLE | gmii_mdc_o無時鐘,MDIO處于高阻態。通過外部MDIO上拉到高電平。 |
PREAMBLE | 32 個連續的1,前導抑制模式則只有一個bit。 |
START | 包開始2’b01 |
OPCODE | ■ 2’b01 寫■ 2’b10 讀 |
PHY ADDR | 5-bit 的PHY地址,可以編碼最多32 個PHY。 |
REG ADDR | 5-bit 寄存器地址可以編碼32個寄存器。尋址對應指定MMD的某一個寄存器。 |
TA | Turnaround寄存器地址和幀的數據字段之間的2位時間間隔,以避免在讀取事務期間發生爭用?!?2’bZ0: 讀時 第一個bit MAC和PHY都處于高組態,第二個bit是PHY拉低?!?2’b10: 寫時 10都是MAC驅動。Z 表示三態 |
DATA | 16-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的功能都非常清晰。
審核編輯 黃宇
-
測試
+關注
關注
8文章
5157瀏覽量
126466 -
嵌入式
+關注
關注
5068文章
19014瀏覽量
303229 -
以太網
+關注
關注
40文章
5374瀏覽量
171093 -
寄存器
+關注
關注
31文章
5317瀏覽量
120001 -
驅動
+關注
關注
12文章
1825瀏覽量
85177
發布評論請先 登錄
相關推薦
評論