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,MAC和CPU组成。针对不同的组合形式可以有下面几种类型
方案一: CPU集成了MAC和PHY
方案二: CPU集成MAC,PHY采用独立芯片(3399是这种)
方案三: CPU不集成MAC与PHY,MAC与PHY采用集成芯片

由于在 RK 系列的 SoC 中内置了以太网 MAC 控制器,所以只需要搭配一颗以太网 PHY 芯片,
即可实现以太网卡功能。按照规范,即使是不同厂家的 PHY,仍然有一部分寄存器的定义是通用的,
只要配置了这些通用的寄存器,基本上 PHY 就可以正常工作。

1.2如何进行数据传输

在软件上网络的操作分为下面几步:

  1. 为数据收发分配内存
  2. 初始化MAC寄存器
  3. 初始化PHY寄存器
  4. 启动收发

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.驱动

  • 2.1: 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
  • 2.2: MAC驱动DTS
        rk3399.dtsi
        gmac: ethernet@fe300000 {
                compatible = "rockchip,rk3399-gmac";
                reg = <0x0 0xfe300000 0x0 0x10000>;
                rockchip,grf = <&grf>;
                interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH 0>;
                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 15 GPIO_ACTIVE_LOW>;
                snps,reset-active-low;
                snps,reset-delays-us = <0 10000 50000>;
                assigned-clocks = <&cru SCLK_RMII_SRC>;     //MAC的时钟源
                assigned-clock-parents = <&clkin_gmac>;     //MAC父时钟,下面再继续看
                pinctrl-names = "default";
                pinctrl-0 = <&rgmii_pins>;          //必须和phy-mode一致
                tx_delay = <0x28>;
                rx_delay = <0x11>;
                status = "okay";
        };
        clkin_gmac: external-gmac-clock {
                compatible = "fixed-clock";
                clock-frequency = <125000000>;      //125Mhz
                clock-output-names = "clkin_gmac";
                #clock-cells = <0>;
        };
  • 2.3: MAC驱动Driver
 E:\linux_kernel\hfs-workstation\rk3399-linux-origin-2017.4.5\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-2017.4.5\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设备

猜你喜欢

转载自blog.csdn.net/zhuyong006/article/details/80893603
今日推荐