phy子系统分析

来源:互联网 发布:无人机电调算法 编辑:程序博客网 时间:2024/05/22 06:29

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

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_deviceMAC驱动        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>;        };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设备
原创粉丝点击