本文轉(zhuǎn)自公眾號歡迎關(guān)注
一.前言
以太網(wǎng)驅(qū)動的編寫與調(diào)試往往從MDIO接口開始,MDIO是MAC訪問PHY的接口。實現(xiàn)通過MDIO對PHY進行操作才能配置PHY,所以實現(xiàn)MDIO讀寫是第一步。DWC_ether_qos提供了SMA模塊,操作兩個寄存器即可實現(xiàn)PHY寄存器的讀寫,比較簡單方便,且支持C45和C22兩種模式,另外有比較靈活的配置參數(shù)后面會詳講。
二.SMA模塊介紹
DWC_ether_qos中SMA(Station Management Agent)是一種雙線站管理接口(MIM:Station Management interface),即MDIO管理接口,通過SMA模塊即MDIO接口可以訪問PHY的寄存器。
其具備以下特征:
l時鐘
IEEE 802.3中規(guī)定,MDC的最大操作頻率(gmi_MDC_o)是2.5MHz。在DWC_ether_qos中,gmii_mdc_o時鐘是從csr_i分頻得到的,通過MAC_MDIO_Address寄存器中的CR字段配置,
要注意該分頻值一般要使得MDC滿足不大于2.5MHz的要求。如果系統(tǒng)支持更高的頻率該分頻也可以分頻到大于2.5MHz的頻率。
l模式選擇:MAC_MDIO_Address 的C45E 配置使用C45還是C22格式。
l前導(dǎo)抑制:MAC_MDIO_Address的PSE控制是否發(fā)送只有一個bit的前導(dǎo)的幀。
l時鐘保持與連續(xù)傳輸:MAC_MDIO_Address 的NTC可以配置MDIO幀發(fā)送完后時鐘保持07個CLK。在此基礎(chǔ)上還可以配置MAC_MDIO_Address 的BTB使能連續(xù)傳輸,此時不等07個時鐘保持結(jié)束,Busy信號就會發(fā)送完幀后立即清除,軟件可以開始下一次傳輸,否則需要等到配置的0~7個保持時鐘之后Busy才會清除。BTB必須在NTC大于0時才能使能。
三. MDIO幀結(jié)構(gòu)
MDIO幀的一些特征:
MDC一般是1MHz~2.5MHz。
MDIO引腳需要一個1.5k歐姆的上拉電阻,以在空閑和周轉(zhuǎn)期間保持MDIO高電平。
MDC下降沿修改數(shù)據(jù),上升沿鎖存數(shù)據(jù),這樣使得數(shù)據(jù)的建立和保持時間都是半個周期。
高位先發(fā)送,高字節(jié)先發(fā)送。
參考
IEEE 802.3,Section 22.2.4.5
**3.1 **Clause 45
SMA支持在該模式下操作時的讀后遞增地址。
MAC_MDIO_Address 的C45E 配置為1使用C45.
字段 | 描述 |
---|---|
IDLE | gmii_mdc_o無時鐘,MDIO處于高阻態(tài)。通過外部MDIO上拉到高電平 |
PREAMBLE | 32 個連續(xù)的1,前導(dǎo)抑制模式則只有一個bit。 |
START | 包開始2’b00 |
OPCODE | 操作碼■ 2’b00 讀寫地址寄存器■ 2’b01 讀寫數(shù)據(jù), 讀寫完地址不遞增■ 2’b10 讀寫數(shù)據(jù),讀寫完地址自動遞增■ 2’b11 讀寫數(shù)據(jù),僅寫完地址自動遞增 |
PHY ADDR | 5-bit 的PHY地址,可以編碼最多32 個PHY。 |
DEV ADDR | 5-bit 設(shè)備地址可以編碼32個設(shè)備。 |
TA | Turnaround寄存器地址和幀的數(shù)據(jù)字段之間的2位時間間隔,以避免在讀取事務(wù)期間發(fā)生爭用?!?2’bZ0: 讀時 第一個bit MAC和PHY都處于高組態(tài),第二個bit是PHY拉低。■ 2’b10: 寫時 10都是MAC驅(qū)動。Z 表示三態(tài) |
DATA/ADDRESS | 16-bit 值: 對于地址周期(OPCODE = 2'b00), 表示下一個周期需要訪問的寄存器地址. 對于數(shù)據(jù)寫周期,表示需要寫入寄存器的數(shù)據(jù)內(nèi)容. 對于read,post-read遞增幀,表示從PHY讀出的寄存器內(nèi)容. |
完成一次C45寄存器讀寫需要兩步,第一步發(fā)送地址包,第二步再讀寫
**3.2 ****Clause **22
MAC_MDIO_Address 的C45E 配置為0使用C22.
幀結(jié)構(gòu)如下
字段 | 描述 |
---|---|
IDLE | gmii_mdc_o無時鐘,MDIO處于高阻態(tài)。通過外部MDIO上拉到高電平。 |
PREAMBLE | 32 個連續(xù)的1,前導(dǎo)抑制模式則只有一個bit。 |
START | 包開始2’b01 |
OPCODE | ■ 2’b01 寫■ 2’b10 讀 |
PHY ADDR | 5-bit 的PHY地址,可以編碼最多32 個PHY。 |
REG ADDR | 5-bit 寄存器地址可以編碼32個寄存器。尋址對應(yīng)指定MMD的某一個寄存器。 |
TA | Turnaround寄存器地址和幀的數(shù)據(jù)字段之間的2位時間間隔,以避免在讀取事務(wù)期間發(fā)生爭用?!?2’bZ0: 讀時 第一個bit MAC和PHY都處于高組態(tài),第二個bit是PHY拉低?!?2’b10: 寫時 10都是MAC驅(qū)動。Z 表示三態(tài) |
DATA | 16-bit 值:■ 寫時DWC_ether_qos 驅(qū)動MDIO■ 讀時PHY 驅(qū)動MDIO |
完成C22讀寫只需要一步
讀寫波形如下
3.3使用C22模式訪問C45寄存器
使用C22訪問C45寄存器
參考https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf
用13號寄存器作為命令寄存器,14號寄存器作為寄存器/數(shù)據(jù)寄存器
步驟如下,分為4步完成:
1.C22模式寫寄存器13,高兩位FN為00,低5位DEV設(shè)備地址。
2.C22模式將16位寄存器地址寫入14號寄存器。
3.C22模式寫寄存器13,高兩位FN為01或者10或者11,低5位DEV設(shè)備地址。
4.讀:轉(zhuǎn)至步驟5。寫:轉(zhuǎn)至步驟6
5.C22模式讀14號寄存器的16位內(nèi)容。
6.C22模式將16位寄存器內(nèi)容寫入14號寄存器。
四.相關(guān)寄存器
MDIO操作僅需要兩個寄存器
MAC_MDIO_Address和MAC_MDIO_Data
4.1 MAC_MDIO_Address
Offset: 0x200
PSE[27]: bit27,置位則只發(fā)送1bit的前導(dǎo),否則發(fā)送32位的前導(dǎo)
BTB **[26]: **bit26,NTC 不為0時生效,NTC為0時不能設(shè)置為1.
設(shè)置為1時,NTC不為0時,MDIO發(fā)送完幀不等NTC個時鐘就立即清除GB位,可以立即重新下一次讀寫。
設(shè)置為0,則等待NTC保持時鐘之后GB才清除。
**PA[25:21] **5位的PHY地址
RDA[22:16] 對于C22模式的寄存器地址,C45的設(shè)備地址MMD。
NTC[14:12] MDIO幀的發(fā)送完后,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使能時才有,置位時不發(fā)送地址包
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觸發(fā)讀寫操作,讀寫完硬件清零。
4.2 MAC_MDIO_Data
Offset: 0x204
RA[31:16]:C45E模式時指定16位寄存器地址。
GD[15:0]:讀出或者寫入寄存器的16位值。
五.MDIO讀寫驅(qū)動
5.1 寄存器操作接口
實現(xiàn)基本的寄存器級別的操作接口,
后面再對關(guān)鍵參數(shù)MDC時鐘,NTC,PSE等測試,抓取波形進行驗證。
/*
* GB_MASK bit0 置位,觸發(fā)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,設(shè)置CR為0,即分頻42,理論上60/42=1.429MHz
實測MDC頻率為1.449MHz
設(shè)置CR為3,即分頻26,理論上60/26=2.31MHz
實測MDC頻率為2.326MHz
2 NTC
NTC=0,幀發(fā)送完后MDC立即結(jié)束
NTC=7,幀發(fā)送完后MDC保持7個CLK
3 PSE
PSE=0,發(fā)送32個前導(dǎo)1
PSE=1,發(fā)送1個前導(dǎo)1,可以看到這個PHY是不支持前導(dǎo)抑制的,即發(fā)送1個前導(dǎo)時不能讀寫。
5.2 C22讀寫
驅(qū)動代碼如下
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軟復(fù)位PHY
讀
如下是讀0號寄存器為0x1040即Reset軟復(fù)位PHY完成,硬件自清零了Reset位。
5.3 C45讀寫
驅(qū)動代碼如下
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;
}
波形如下:兩個包,一個是地址包,一個是數(shù)據(jù)包
讀
寫
5.4 C22模式讀寫C45寄存器
以下是RTL8211F的MMD寄存器,不支持直接C45模式,但是支持C22模式讀C45寄存器,
通過13和14號寄存器實現(xiàn)。
驅(qū)動代碼如下
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);
打印結(jié)果如下
可以看到PC1R的bit10設(shè)置為了1,其他寄存器對照手冊可以看到都是默認值
波形如下
寫4步完成
讀4步完成
六. 總結(jié)
以太網(wǎng)驅(qū)動編寫調(diào)試的第一步都是調(diào)通MDIO接口,重點是要去抓取信號分析實際的波形,比如寄存器的每一個參數(shù)對應(yīng)波形的什么變化,這在驅(qū)動編寫調(diào)試中很重要,一定要對寄存器每一個bit的功能都非常清晰。
審核編輯 黃宇
-
測試
+關(guān)注
關(guān)注
8文章
5100瀏覽量
126338 -
嵌入式
+關(guān)注
關(guān)注
5059文章
18974瀏覽量
302062 -
以太網(wǎng)
+關(guān)注
關(guān)注
40文章
5343瀏覽量
170804 -
寄存器
+關(guān)注
關(guān)注
31文章
5294瀏覽量
119816 -
驅(qū)動
+關(guān)注
關(guān)注
12文章
1818瀏覽量
85110
發(fā)布評論請先 登錄
相關(guān)推薦
評論