天天看點

phy子系統分析linux PHY 驅動

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子系統分析linux PHY 驅動

網絡有PHY,MAC和CPU組成。針對不同的組合形式可以有下面幾種類型

方案一: CPU內建了MAC和PHY

方案二: CPU內建MAC,PHY采用獨立晶片(3399是這種)

方案三: CPU不內建MAC與PHY,MAC與PHY采用內建晶片

由于在 RK 系列的 SoC 中内置了以太網 MAC 控制器,是以隻需要搭配一顆以太網 PHY 晶片,

即可實作以太網卡功能。按照規範,即使是不同廠家的 PHY,仍然有一部分寄存器的定義是通用的,

隻要配置了這些通用的寄存器,基本上 PHY 就可以正常工作。

1.2如何進行資料傳輸

phy子系統分析linux PHY 驅動

在軟體上網絡的操作分為下面幾步:

  1. 為資料收發配置設定記憶體
  2. 初始化MAC寄存器
  3. 初始化PHY寄存器
  4. 啟動收發

1.3 MAC與PHY

收發資料這種耗時的事情,使用DMA是最合适的。CPU隻需要告訴DMA起始位址和長度剩下的事情就可以自動完成,一般在MAC中有一組寄存器專門記錄資料位址,cpu按照MAC的要求把資料放好後,啟動MAC的資料發送就可以了
phy子系統分析linux PHY 驅動
左邊的連接配接在處理器上,右邊的連接配接PHY晶片,MII DATA 與MIIM是與PHY進行資料傳輸和控制的
phy子系統分析linux PHY 驅動
PHY晶片上的寄存器CPU不可以直接通路,隻能通過MAC上的MIIM寄存器實作間接通路;右邊就是我們的網口

1.4PHY與MAC之間的接口

phy子系統分析linux PHY 驅動
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接口

phy子系統分析linux PHY 驅動

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裝置
           

繼續閱讀