1. 程式人生 > >Linux網絡子系統之---- PHY 配置

Linux網絡子系統之---- PHY 配置

向量 driver otv wan probe clu all 分配 ++

MII即媒體獨立接口,也叫介質無關接口。

它包括一個數據接口,以及一個MAC和PHY之間的管理接口(圖1)。

數據接口包括分別用於發送器和接收器的兩條獨立信道。每條信道都有自己的數據、時鐘和控制信號。MII數據接口總共需16個信號。

管理接口是個雙信號接口:一個是時鐘信號,另一個是數據信號。通過管理接口,上層能監視和控制PHY。

RMII口是用兩根線來傳輸數據的,

MII口是用4根線來傳輸數據的,

GMII是用8根線來傳輸數據的。

GMII (Gigabit MII)

GMII是8bit並行同步收發接口,采用8位接口數據,工作時鐘125MHz,因此傳輸速率可達1000Mbps。同時兼容MII所規定的10/100 Mbps工作方式。

GMII接口數據結構符合IEEE以太網標準。該接口定義見IEEE802.3-2000。

發送器:

◇ GTXCLK——吉比特TX..信號的時鐘信號(125MHz)

◇ TXCLK——10/100M信號時鐘

◇ TXD[7..0]——被發送數據 ------- mii 為4,所以一個信道是8,兩個是16

◇ TXEN——發送器使能信號

◇ TXER——發送器錯誤(用於破壞一個數據包)

註:在千兆速率下,向PHY提供GTXCLK信號,TXD、TXEN、TXER信號與此時鐘信號同步。否則,在10/100M速率下,PHY提供 TXCLK時鐘信號,其它信號與此信號同步。其工作頻率為25MHz(100M網絡)或2.5MHz(10M網絡)。

接收器:

◇ RXCLK——接收時鐘信號(從收到的數據中提取,因此與GTXCLK無關聯)

◇ RXD[7..0]——接收數據

◇ RXDV——接收數據有效指示

◇ RXER——接收數據出錯指示

◇ COL——沖突檢測(僅用於半雙工狀態)

管理配置

◇ MDC——配置接口時鐘

◇ MDIO——配置接口I/O

管理配置接口控制PHY的特性。該接口有32個寄存器地址,每個地址16位。其中前16個已經在“IEEE 802.3,2000-22.2.4Management Functions”中規定了用途,其余的則由各器件自己指定。

MII/RMII只是一種接口,對於10M線速,MII的速率是2.5M,RMII則是5M;對於100M線速,MII的速率是25M,RMII則是50M。

SGMII--Serial Gigabit Media IndependentInterface

SGMII是PHY與MAC之間的接口,類似與GMII和RGMII,只不過GMII和RGMII都是並行的,而且需要隨路時鐘,PCB布線相對麻煩,而且不適應背板應用。

而SGMII是串行的,不需要提供另外的時鐘,MAC和PHY都需要CDR去恢復時鐘。另外SGMII是有8B/10b編碼的,速率是1.25G

在 linux 配置PHY

drivers/net/phy

配置的參數 自適應, 1000M, 全雙工。

phydev-> autonet, speed, duplex.

1. MDIO簡介

The MDIO interface is a simple, two-wire, serial interface to connect a management entity and a managed PHY for the purposes of controlling the PHY and gathering status from the PHY.
The two lines include the MDC line [Management Data Clock], and the MDIO line [Management Data Input/Output]. The clock is point-to-point, while the data line is a bi-directional multi-drop interface.
The data line is Tri-state able and can drive 32 devices.

MDIO接口,MAC與PHY間的管理接口(MII是數據接口),有2根線:時鐘線MDC,數據線MDIO(雙向)


技術分享圖片
MDIO工作流程:
* Preamle(PRE) 在沒有傳輸數據的空閑狀態時,數據線MDIO處於高阻態(一直為1)。
* Start of Frame(ST) MAC驅動MDIO線,出現一個2bit的開始標識碼(01)。
* Operation Code(OP) MAC驅動MDIO線,出現一個2bit數據來標識是讀操作(10)還是寫操作(01)。
* PHY Address(PHYAD) MAC驅動MDIO線,出現一個5bit數據標識PHY的地址。
* Reg Address(REGAD) MAC驅動MDIO線,出現一個5bitPHY寄存器地址。
* Turnaround(TA) 寫操作的話,MAC驅動MDIO線,出現10
讀操作的話,MDIO pin of MAC must be put in high-impedance state
在第二個周期,PHY驅動MDIO線,出現0

* Data MDIO串行讀出/寫入16bit的寄存器數據。

* MDIO恢復成空閑狀態,同時MDIO進入高阻狀態。


下面是PHY芯片 BCM5461 的一個例子:
技術分享圖片

2. PowerPC對MDIO的支持

PowerPC操作MDIO時,涉及以下寄存器:
MIIMCFG 配置寄存器
MIIMCOM 命令寄存器
MIIMADD 地址寄存器
MIIMCON 控制寄存器
MIIMSTAT 狀態寄存器
MIIMIND 指示寄存器

以MPC8560舉例,這些寄存器在CCSR中的位置如下:

技術分享圖片




2.1 MIIMCFG:配置寄存器
技術分享圖片

ResetMgmt: 用於重置MDIO模塊
MgmtClockSet:時鐘設置,是CCB的 2的n次方之一



2.2 MIIMCOM 命令寄存器
技術分享圖片

ReadCycle: 0->1 觸發MDIO讀時序


2.3 MIIMADD 地址寄存器
技術分享圖片

PHYaddr:PHY地址,共5bit,系統最多聯31個PHY(地址0為保留)
REGaddr:寄存器地址,共5bit,一個PHY上最多32個寄存器地址(可以使用shadow value技術,訪問更多的寄存器)


2.4 MIIMCON 控制寄存器

技術分享圖片

PHYcontrol:在寫流程時,這裏存放要寫入寄存器的值


2.5 MIIMSTAT 狀態寄存器

技術分享圖片

PHYstatus:讀流程時,PHY reg的內容會放到此

2.6 MIIMIND 指示寄存器
技術分享圖片

NotVal:若置1,表示讀流程結束,可以去讀MIIMSTAT
Scan: 若置1,表示掃描流程進行中
Busy: 只有置0時,才能進行新的讀寫流程



3. linux中MDIO的實現

讀寫PHY寄存器時通過2個函數

phy_read()和phy_write(),

最終調用
int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, int regnum, u16 value)

參數regs就是MDIO相關寄存器:
  1. struct gfar_mii {
  2. u32 miimcfg; /* 0x.520 - MII Management Config Register */
  3. u32 miimcom; /* 0x.524 - MII Management Command Register */
  4. u32 miimadd; /* 0x.528 - MII Management Address Register */
  5. u32 miimcon; /* 0x.52c - MII Management Control Register */
  6. u32 miimstat; /* 0x.530 - MII Management Status Register */
  7. u32 miimind; /* 0x.534 - MII Management Indicator Register */
  8. };
參數mii_id,就是PHY的id
參數regnum,就是寄存器地址


上代碼,簡單不解釋
  1. int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
  2. {
  3. u16 value;
  4. /* Set the PHY address and the register address we want to read */
  5. gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
  6. /* Clear miimcom, and then initiate a read */
  7. gfar_write(&regs->miimcom, 0);
  8. gfar_write(&regs->miimcom, MII_READ_COMMAND);
  9. /* Wait for the transaction to finish */
  10. while (gfar_read(&regs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
  11. cpu_relax();
  12. /* Grab the value of the register from miimstat */
  13. value = gfar_read(&regs->miimstat);
  14. return value;
  15. }


  1. int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id,
  2. int regnum, u16 value)
  3. {
  4. /* Set the PHY address and the register address we want to write */
  5. gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
  6. /* Write out the value we want */
  7. gfar_write(&regs->miimcon, value);
  8. /* Wait for the transaction to finish */
  9. while (gfar_read(&regs->miimind) & MIIMIND_BUSY)
  10. cpu_relax();
  11. return 0;
  12. }

技術分享圖片

內核啟動時的準備工作

4.1 初始化網絡相關的全局數據結構,並掛載處理網絡相關軟中斷的鉤子函數
start_kernel()
--> rest_init()
--> do_basic_setup()
--> do_initcall
-->net_dev_init

__init net_dev_init()
{
//每個CPU都有一個CPU私有變量 _get_cpu_var(softnet_data)
//_get_cpu_var(softnet_data).poll_list很重要,軟中斷中需要遍歷它的
for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog;
queue->backlog.weight = weight_p;
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); //在軟中斷上掛網絡發送handler
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); //在軟中斷上掛網絡接收handler
}
4.2 加載網絡設備的驅動
NOTE:這裏的網絡設備是指MAC層的網絡設備,即TSEC和PCI網卡(bcm5461是phy)
在網絡設備驅動中創建net_device數據結構,並初始化其鉤子函數 open(),close() 等
掛載TSEC的驅動的入口函數是 gfar_probe

// 平臺設備 TSEC 的數據結構
static struct platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
.driver = {
.name = "fsl-gianfar",
},
};

int gfar_probe(struct platform_device *pdev)
{
dev = alloc_etherdev(sizeof (*priv)); // 創建net_device數據結構

dev->open = gfar_enet_open;
dev->hard_start_xmit = gfar_start_xmit;
dev->tx_timeout = gfar_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
#ifdef CONFIG_GFAR_NAPI
netif_napi_add(dev, &priv->napi,gfar_poll,GFAR_DEV_WEIGHT); //軟中斷裏會調用poll鉤子函數
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = gfar_netpoll;
#endif
dev->stop = gfar_close;
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
dev->set_mac_address = gfar_set_mac_address;
dev->ethtool_ops = &gfar_ethtool_ops;
}


五、啟用網絡設備
5.1 用戶調用ifconfig等程序,然後通過ioctl系統調用進入內核
socket的ioctl()系統調用
--> sock_ioctl()
--> dev_ioctl() //判斷SIOCSIFFLAGS
--> __dev_get_by_name(net, ifr->ifr_name) //根據名字選net_device
--> dev_change_flags() //判斷IFF_UP
--> dev_open(net_device) //調用open鉤子函數

對於TSEC來說,掛的鉤子函數是 gfar_enet_open(net_device)

5.2 在網絡設備的open鉤子函數裏,分配接收bd,掛中斷ISR(包括rx、tx、err),對於TSEC來說
gfar_enet_open
--> 給Rx Tx Bd 分配一致性DMA內存
--> 把Rx Bd的“EA地址”賦給數據結構,物理地址賦給TSEC寄存器
--> 把Tx Bd的“EA地址”賦給數據結構,物理地址賦給TSEC寄存器
--> 給 tx_skbuff 指針數組 分配內存,並初始化為NULL
--> 給 rx_skbuff 指針數組 分配內存,並初始化為NULL

--> 初始化Tx Bd
--> 初始化Rx Bd,提前分配存儲以太網包的skb,這裏使用的是一次性dma映射
(註意:#define DEFAULT_RX_BUFFER_SIZE 1536保證了skb能存一個以太網包)
rxbdp = priv->rx_bd_base;
for (i = 0; i < priv->rx_ring_size; i++) {
struct sk_buff *skb = NULL;
rxbdp->status = 0;
//這裏真正分配skb,並且初始化rxbpd->bufPtr, rxbdpd->length
skb = gfar_new_skb(dev, rxbdp);
priv->rx_skbuff[i] = skb;
rxbdp++;
}
rxbdp--;
rxbdp->status |= RXBD_WRAP; // 給最後一個bd設置標記WRAP標記

--> 註冊TSEC相關的中斷handler: 錯誤,接收,發送
request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev)
request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev)//包發送完
request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev) //包接收完

-->gfar_start(net_device)
// 使能Rx、Tx
// 開啟TSEC的 DMA 寄存器
// Mask 掉我們不關心的中斷event

最終,TSEC相關的Bd等數據結構應該是下面這個樣子的
技術分享圖片
六、中斷裏接收以太網包

TSEC的RX已經使能了,網絡數據包進入內存的流程為:
網線 --> Rj45網口 --> MDI 差分線
--> bcm5461(PHY芯片進行數模轉換) --> MII總線
--> TSEC的DMA Engine 會自動檢查下一個可用的Rx bd
--> 把網絡數據包 DMA 到 Rx bd 所指向的內存,即skb->data

接收到一個完整的以太網數據包後,TSEC會根據event mask觸發一個 Rx 外部中斷。
cpu保存現場,根據中斷向量,開始執行外部中斷處理函數do_IRQ()

do_IRQ 偽代碼
{
上半部處理硬中斷
查看中斷源寄存器,得知是網絡外設產生了外部中斷
執行網絡設備的rx中斷handler(設備不同,函數不同,但流程類似,TSEC是gfar_receive)
1. mask 掉 rx event,再來數據包就不會產生rx中斷
2. 給napi_struct.state加上 NAPI_STATE_SCHED 狀態
3. 掛網絡設備自己的napi_struct結構到cpu私有變量_get_cpu_var(softnet_data).poll_list
4. 觸發網絡接收軟中斷
下半部處理軟中斷
依次執行所有軟中斷handler,包括timer,tasklet等等
執行網絡接收的軟中斷handler net_rx_action
1. 遍歷cpu私有變量_get_cpu_var(softnet_data).poll_list
2. 取出poll_list上面掛的napi_struct 結構,執行鉤子函數napi_struct.poll()
(設備不同,鉤子函數不同,流程類似,TSEC是gfar_poll)
3. 若poll鉤子函數處理完所有包,則打開rx event mask,再來數據包的話會產生rx中斷
4. 調用napi_complete(napi_struct *n)
把napi_struct 結構從_get_cpu_var(softnet_data).poll_list 上移走
同時去掉 napi_struct.state 的 NAPI_STATE_SCHED 狀態
}

6.1 TSEC的接收中斷處理函數
gfar_receive
{
#ifdef CONFIG_GFAR_NAPI
// test_and_set當前net_device的napi_struct.state 為 NAPI_STATE_SCHED
// 在軟中斷裏調用 net_rx_action 會檢查狀態 napi_struct.state
if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
tempval &= IMASK_RX_DISABLED; //mask掉rx,不再產生rx中斷
gfar_write(&priv->regs->imask, tempval);
// 將當前net_device的 napi_struct.poll_list 掛到
// CPU私有變量__get_cpu_var(softnet_data).poll_list 上,並觸發軟中斷
// 所以,在軟中斷中調用 net_rx_action 的時候,就會執行當前net_device的
// napi_struct.poll()鉤子函數,即 gfar_poll()
__netif_rx_schedule(dev, &priv->napi);
}
#else
gfar_clean_rx_ring(dev, priv->rx_ring_size);
#endif
}

http://tech.watchstor.com/storage-network-115062.htm

http://blog.csdn.net/jw212/article/details/6738457

https://blog.csdn.net/jk198310/article/details/12909341

Linux網絡子系統之---- PHY 配置