linux PHY 驅動
firefly-3399
linux核心版本:4.4
MAC(Media Access Control)
1.網絡硬體部分
2.驅動部分
參考部落格
http://www.jianshu.com/p/77bb0ba1768c
http://www.360doc.com/content/13/0717/16/2768962_300623597.shtml
http://www.latelee.org/programming-under-linux/linux-phy-driver.html
https://www.cnblogs.com/jason-lu/articles/3195473.html
http://blog.csdn.net/heli200482128/article/details/54091677
1.MAC和PHY硬體及接口協定
1.1網絡硬體組成
網絡有PHY,MAC和CPU組成。針對不同的組合形式可以有下面幾種類型
方案一: CPU內建了MAC和PHY
方案二: CPU內建MAC,PHY采用獨立晶片(3399是這種)
方案三: CPU不內建MAC與PHY,MAC與PHY采用內建晶片
由于在 RK 系列的 SoC 中内置了以太網 MAC 控制器,是以隻需要搭配一顆以太網 PHY 晶片,
即可實作以太網卡功能。按照規範,即使是不同廠家的 PHY,仍然有一部分寄存器的定義是通用的,
隻要配置了這些通用的寄存器,基本上 PHY 就可以正常工作。
1.2如何進行資料傳輸
在軟體上網絡的操作分為下面幾步:
- 為資料收發配置設定記憶體
- 初始化MAC寄存器
- 初始化PHY寄存器
- 啟動收發
1.3 MAC與PHY
收發資料這種耗時的事情,使用DMA是最合适的。CPU隻需要告訴DMA起始位址和長度剩下的事情就可以自動完成,一般在MAC中有一組寄存器專門記錄資料位址,cpu按照MAC的要求把資料放好後,啟動MAC的資料發送就可以了
左邊的連接配接在處理器上,右邊的連接配接PHY晶片,MII DATA 與MIIM是與PHY進行資料傳輸和控制的
PHY晶片上的寄存器CPU不可以直接通路,隻能通過MAC上的MIIM寄存器實作間接通路;右邊就是我們的網口
1.4PHY與MAC之間的接口
RK3399使用的是RGMII,RGMII即Reduced GMII,是RGMII的簡化版本,将接口信号線數量從24根減少到14根(COL/CRS端口狀态訓示信号,這裡沒有畫出),時鐘頻率仍舊為125MHz,TX/RX資料寬度從8為變為4位,為了保持1000Mbps的傳輸速率不變,RGMII接口在時鐘的上升沿和下降沿都采樣資料。在參考時鐘的上升沿發送GMII接口中的TXD[3:0]/RXD[3:0],在參考時鐘的下降沿發送GMII接口中的TXD[7:4]/RXD[7:4]。RGMI同時也相容100Mbps和10Mbps兩種速率,此時參考時鐘速率分别為25MHz和2.5MHz。另外,MDC和MDIO是MIIM(MII Management)用來配置PHY寄存器的
RGMII接口
2.驅動
PHY驅動
phy_init //.\kernel\drivers\net\phy\phy_device.c
mdio_bus_init
class_register 在/sys/class/下建立mdio_bus
bus_register 建立總線mdio_bus
phy_drivers_register
phy_driver_register 注冊phy_driver
new_driver->driver.name = new_driver->name; //可以搜尋裝置樹中的這個名字
new_driver->driver.bus = &mdio_bus_type;
new_driver->driver.probe = phy_probe; //當比對成功回調用的函數
new_driver->driver.remove = phy_remove;
driver_register(&new_driver->driver); //device_driver注冊
phy_driver注冊成功了,等待phy_device的注冊。根據測試發現phy_device的注冊不依靠裝置樹(根據上面驅動的名字在裝置樹中搜不到),該裝置的注冊在後面的MDIO驅動中調用mdiobus_register中會注冊phy_device
MAC驅動
rk3399.dtsi
gmac: [email protected] {
compatible = "rockchip,rk3399-gmac";
reg = < >;
rockchip,grf = <&grf>;
interrupts = <GIC_SPI IRQ_TYPE_LEVEL_HIGH >;
interrupt-names = "macirq";
clocks = <&cru SCLK_MAC>, <&cru SCLK_MAC_RX>,
<&cru SCLK_MAC_TX>, <&cru SCLK_MACREF>,
<&cru SCLK_MACREF_OUT>, <&cru ACLK_GMAC>,
<&cru PCLK_GMAC>;
clock-names = "stmmaceth", "mac_clk_rx",
"mac_clk_tx", "clk_mac_ref",
"clk_mac_refout", "aclk_mac",
"pclk_mac";
resets = <&cru SRST_A_GMAC>;
reset-names = "stmmaceth";
power-domains = <&power RK3399_PD_GMAC>;
status = "disabled";
};
rk3399-firefly-linux.dts
&gmac {
phy-supply = <&vcc_phy>; //phy供電有&vcc_phy提供
phy-mode = "rgmii"; //rgmii 或 rmii
clock_in_out = "input"; //時鐘由PHY輸入給MAC
snps,reset-gpio = <&gpio3 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
snps,reset-delays-us = < >;
assigned-clocks = <&cru SCLK_RMII_SRC>; //MAC的時鐘源
assigned-clock-parents = <&clkin_gmac>; //MAC父時鐘,下面再繼續看
pinctrl-names = "default";
pinctrl- = <&rgmii_pins>; //必須和phy-mode一緻
tx_delay = <>;
rx_delay = <>;
status = "okay";
};
clkin_gmac: external-gmac-clock {
compatible = "fixed-clock";
clock-frequency = <>; //125Mhz
clock-output-names = "clkin_gmac";
#clock-cells = <>;
};
E:\linux_kernel\hfs-workstation\rk3399-linux-origin-\kernel\drivers\net\ethernet\stmicro\stmmac\dwmac-rk.c
通過上面的裝置樹成功比對了平台裝置
module_platform_driver(rk_gmac_dwmac_driver)
rk_gmac_probe
of_device_get_match_data //傳回的是比對的of_device_id的data
stmmac_get_platform_resources(pdev, &stmmac_res) //利用裝置樹中資源給stmmac_res指派
stmmac_res->irq = platform_get_irq_byname(pdev, "macirq") //名字和裝置樹對應上
platform_get_resource //得到IORESOURCE_MEM資源,然後映射後填充到stmmac_res
plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac) //填充plat_dat
of_get_mac_address //得到mac位址
plat->interface = of_get_phy_mode(np) // 得到接口類型,裝置樹中是RGMII
of_property_read_u32 //得到max speed
...
//一些指派
plat_dat->has_gmac = true;
plat_dat->init = rk_gmac_init; //選擇MAC與PHY通信接口(RGMII/GMII)
plat_dat->exit = rk_gmac_exit;
plat_dat->fix_mac_speed = rk_fix_speed;
plat_dat->get_eth_addr = rk_get_eth_addr_vendor;
rk_gmac_setup //配置設定填充bsp_priv然後放到plat_dat->bsp_priv
bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL) //配置設定結構體
bsp_priv->phy_iface = of_get_phy_mode(dev->of_node) //接口類型RGMII
of_property_read_string //和裝置樹對應上,給bsp_priv->clock_input = true;
of_property_read_u32 //讀取裝置樹“tx_delay”,bsp_priv->tx_delay = 0x28;
of_property_read_u32 //讀取裝置樹“rx_delay”,bsp_priv->rx_delay = 0x11;
gmac_clk_init //配置了clk到bsp_priv
rk_gmac_init //gmac初始化
bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay,bsp_priv->rx_delay) //rk_gmac_ops的set_to_rgmii方法
regmap_write // 寫了幾次寄存器
phy_power_on
gmac_clk_enable
pm_runtime_enable
pm_runtime_get_sync
stmmac_dvr_probe //E:\linux_kernel\hfs-workstation\rk3399-linux-origin-2017.4.5\kernel\drivers\net\ethernet\stmicro\stmmac\stmmac_main.c
alloc_etherdev //配置設定net_device
stmmac_set_ethtool_ops //設定了好多鈎子函數
dev_set_drvdata //把priv->dev == device->driver_data
stmmac_verify_args //合适一些驅動參數
stmmac_hw_init //初始化mac并且得到能力
...
stmmac_mdio_register //mdio bus 注冊
mdiobus_alloc //配置設定一個mii_bus
new_bus->read = &stmmac_mdio_read; //讀取phy内部寄存器的資料
new_bus->write = &stmmac_mdio_write; //寫phy内部寄存器的資料
new_bus->reset = &stmmac_mdio_reset; //重制MII總線
mdiobus_register //注冊mii_bus
device_register //注冊裝置檔案
bus->reset(bus); //總線複位
mdiobus_scan //循環掃描phy裝置
phydev = get_phy_device(bus, addr, false); //擷取建立phy裝置
err = phy_device_register(phydev); //注冊phy裝置
bus->state = MDIOBUS_REGISTERED; //狀态設定為已注冊
register_netdev //注冊net_device,裡面有個operations函數集合
當執行ifconfig eth0 up 後,執行了E:\linux_kernel\hfs-workstation\rk3399-linux-origin-\kernel\drivers\net\ethernet\stmicro\stmmac\stmmac_main.c的stmmac_open函數,在執行後phy_drivers的probe函數才會執行
stmmac_open
stmmac_check_ether_addr //檢查mac位址是否無效的
stmmac_init_phy //初始化phy
phy_connect //連接配接ethernet裝置和PHY裝置
bus_find_device_by_name //從mdio總線上根據名字查找device
to_phy_device //通過device得到phy_device
phy_connect_direct //連接配接ethernet裝置和特定的PHY裝置
phy_attach_direct
err = d->driver->probe(d); //phy_drivers的probe函數
device_bind_driver //綁定驅動到裝置
phy_init_hw //(這裡加了修改時鐘的函數,硬體改了phy)
phy_start_machine //啟動PHY狀态機
alloc_dma_desc_resources //配置設定TX/RX資源
init_dma_desc_rings //初始化RX/TX的回環描述
stmmac_hw_setup //設定mac為可用狀态,配置mac核心寄存器,然後DMA資料準備接收和發送
phy_start //從新啟動PHY裝置