LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux下GMAC网络设备:硬件接口、GMAC/PHY、驱动、测试程序

1 嵌入式网络硬件接口

如下是常见的嵌入式网络硬件接口框图:

  • SOC集成MAC。
  • MAC通过MII系列接口和PHY之间传输数据,通过MDIO接口初始化配置PHY芯片。
  • PHY芯片和RJ45之间通过4组差分模拟信号传输数据,并驱动RJ45的LED信号灯。
  • RJ45通过网线和外部连接。

1.1 嵌入式网络几种常见架构

嵌入式网络常见的集中架构:

  • SOC和MAC+PHY芯片连接:

  •  SOC集成MAC IP,通过MII接口和外部PHY芯片连接:

1.2 MII/RMII/GMII/RGMII接口

MII(Media Independent Interface)/RMII(Reduced MII)接口:支持10Mbit/s100Mbit/s 数据传输模式。

GMII(Gigabit Media Independent Interface)/RGMII(Reduced GMII)接口:支持 10Mbit/s100Mbit/s 以及1000Mbit/s 数据传输模

1.3 MDIO接口

MDI(Management Data Input/Output)包含数据线MDIO和时钟线MDC。

MDIO 接口支持多达 32 PHY。同一时刻内只能对一个 PHY 进行操作。

PHY芯片可以通过地址引脚配置地址。

同一 MDIO接口下的所有PHY芯片,其器件地址不能冲突。

RJ45 座子上一般有两个灯,一个黄色(橙色),一个绿色, 一般绿色亮的话表示网络连接正常,黄色闪烁的话说明当前正在进行网络通信。

1.4 RJ45接口

网络设备是通过网线连接起来的,插入网线的叫做RJ45座。

RJ45 座要与 PHY 芯片连接在一起,但是中间需要一个网络变压器,网络变压器用于隔离以及滤波等,网络变压器也是一个芯片。

2 STM32 GMAC芯片

2.1 GMAC

GMAC集成到SOC,负责将收发网络数据:

  • AXI Master interface:DMA作为master通过AXI Master interface进行数据传输。
  • DMA:在系统内存和外设之间搬运数据。
  • MTL(MAC Transaction Layer):控制DMA和MAC之间数据传输。
  • MAC:实现Ethernet协议,对接不同MII接口。
  • MII/RMII/GMII/RGMII:MAC和PHY数据传输接口。
  • AHB  slave interface:提供访问CSR接口。
  • CSR:控制和状态寄存器,对DMA、MTL、MAC进行控制。

  • MDIO:MAC对PHY进行配置的控制通道。 

2.2 PHY芯片(RTL8211F-CG)

PHY IEEE 802.3 规定的一个标准模块。

PHY芯片寄存器地址空间为5位,地址 0~3132个寄存器,IEEE 定义了0~15 16个寄存器的功能,16~3116个寄存器由厂商自行实现。

 以RTL8211F-CG为例:

  • Phy从MAC收到数据,经过DAC转换从MDI接口传输出去。
  • Phy从MDI接口收到信号,经过ADC转换从GMII/RGMII传给MAC。
  • Phy通过MDIO接口接收MAC的控制。
  • Phy将状态信息通过LED信号对外输出。

2.3 原理图

  • 从SOC输出RGMII、MDIO、中断、复位、时钟等信号到PHY。
  • PHY通过4组差分信号线输出数据,并通过LED指示状态。

3 Linux网络设备配置

Linux下网络设备配置:

  • 配置网络协议。
  • 配置网络设备驱动,包括用到的总线、PHY等。
Networking support
  ->Networking options--繁多的网络协议配置选项。
    ->Packet socket
    ->Unix domain sockets
    ->TCP/IP networking
  ->CAN bus subsystem support--CAN总线子系统。
  ->Wireless--无线协议。
Device Drivers
  ->Network device support
    ->Network core driver support--网络设备核心层。
    ->Ethernet driver support--以太网设备驱动。       ->STMicroelectronics devices         ->STMicroelectronics Multi-Gigabit Ethernet driver--Synopsys以太网IP核心和驱动。           ->Support for STMMAC Selftests--STMMAC自测试功能。           ->STMMAC Platform bus support             ->Generic driver for DWMAC ->Synopsys devices
    ->MDIO bus device drivers--MDIO驱动。
    ->PHY Device support and infrastructure--PHY驱动
    ->USB Network Adapters--通过USB连接的网络设备。
    ->Wireless LAN--无线局域网设备。

4 Linux网络子系统初始化 

4.1 通用网络设备相关组件初始化:net_dev_init

net_dev_init进行网络设备相关组件初始化:

  • 创建每进程网络相关proc节点。
  • 收发软终端注册。
  • 注册会换设备lo。
  • 初始化数据包接收队列等。
net_dev_init
  dev_proc_init
    register_pernet_subsys
      dev_proc_ops--为每个进程创建/proc/xxx/net/dev、softnet_stat、ptype节点。
      dev_mc_net_ops--创建/proc/xxx/net/dev_mcast节点。
  netdev_kobject_init
  register_pernet_subsys
  register_pernet_device
  open_softirq--注册NET_RX/NET_TX软中断。
  cpuhp_setup_state_nocalls

5 STM32MP1 GMAC驱动

5.1 设备树

如下是stm32 dwmac的dts,具体可以参考stm32-dwmac.txt。关于Synopsis部分可以参考snps,dwmac.yaml
网络通用的controller和phy的配置可以参考:ethernet-controller.yamlethernet-phy.yaml

ethernet@5800a000 {
    compatible = "st,stm32mp1-dwmac\0snps,dwmac-4.20a";
    reg = <0x5800a000 0x2000>;
    reg-names = "stmmaceth";
    interrupts-extended = <0x06 0x00 0x3d 0x04 0x18 0x46 0x04>;
    interrupt-names = "macirq\0eth_wake_irq";--macirq是MAC中断;eth_wake_irq是eth唤醒系统的中断。
    clock-names = "stmmaceth\0mac-clk-tx\0mac-clk-rx\0ethstp";--stmmaceth是主时钟;mac-clk-tx是MAC TX时钟;mac-clk-rx是MAC RX时钟;ethstp。
    clocks = <0x0e 0x69 0x0e 0x67 0x0e 0x68 0x0e 0x70>;
    st,syscon = <0x0d 0x04>;
    snps,mixed-burst;--DMA使用mixed burst模式。
    snps,pbl = <0x02>;--配置tx/rx burst长度。
    snps,en-tx-lpi-clockgating;--允许在TX低功耗模式下将MAX TX时钟关闭。
    snps,axi-config = <0x67>;--选择AXI总线模式。
    snps,tso;--使能TSO,即TCP Segment Offload。利用网卡的处理能力,降低CPU发送数据包负载的技术。
    power-domains = <0x12>;
    status = "okay";--驱动使能本设备。
    pinctrl-0 = <0x68>;
    pinctrl-1 = <0x69>;--定义GMAC引脚复用功能。
    pinctrl-names = "default\0sleep";
    phy-mode = "rgmii-id";--和PHY接口类型,表示使用的是RGMII接口,有PHY提供RX和TX延迟,MAC不需要添加RX和TX延时内容。
    max-speed = <0x3e8>;--以Mbit/s为单位的最高速率,即1000Mbit/s。
    phy-handle = <0x6a>;--指向PHY设备句柄。

    mdio0 {--描述MDIO总线。
        #address-cells = <0x01>;
        #size-cells = <0x00>;
        compatible = "snps,dwmac-mdio";

        ethernet-phy@0 {
            reg = <0x00>;--PHY芯片地址。
            phandle = <0x6a>;
        };
    };
};

5.2 网络驱动源码解析

如下是stm32 gmac驱动涉及到的文件,相关的驱动模块有:

stm32_dwmac:

phy_module:注册struct phy_driver,在GMAC驱动里面根据读取到的ID匹配。

drivers/net/
├── ethernet
│   ├── stmicro
│   │   └── stmmac
│   │       ├── chain_mode.c--Chain Mode。
│   │       ├── ring_mode.c--Ring Mode。 │   │   ├── dwmac1000_core.c--MAC1000 core函数集。 │   │   ├── dwmac1000_dma.c--MAC1000 DMA函数集。 │   │   ├── dwmac100_core.c │   │   ├── dwmac100_dma.c │   │   ├── dwmac4_core.c--处理DWC Ether MAC 4.xx core函数集。 │   │   ├── dwmac4_descs.c--处理DWC Ether MAC 4.xx descriptor函数集。 │   │   ├── dwmac4_dma.c--处理DWC Ether MAC 4.xx DMA函数集。 │   │   ├── dwmac4_lib.c--处理DWC Ether MAC 4.xx DMA函数集。 │   │   ├── dwmac5.c--处理DWC Ether 5.xx QoS Core函数集。 │   │   ├── dwmac
-dwc-qos-eth.c │   │   ├── dwmac-generic.c │   │   ├── dwmac_lib.c │   │   ├── dwmac-stm32.c--适配STM32的DWMAC Glue层。 │   │   ├── dwxgmac2_core.c │   │   ├── dwxgmac2_descs.c │   │   ├── dwxgmac2_dma.c │   │   ├── enh_desc.c--处理enhanced descriptors。
│   │       ├── norm_desc.c--处理normal descriptors。 │   │   ├── hwif.c--STMMAC的硬件接口初始化,遍历stmmac_hw。 │   │   ├── mmc_core.c--Management Counters。 │   │   ├── stmmac_ethtool.c--实现struct ethtool_ops结构体stmmac_ethtool_ops。 │   │   ├── stmmac_hwtstamp.c--管理HW timerstamp和PTP接口。 │   │   ├── stmmac_main.c--提供STMMAC通用API。 │   │   ├── stmmac_mdio.c--GMAC驱动MDIO注册和去注册接口,以及read/write实现。 │   │   ├── stmmac_platform.c--GMAC驱动platform device相关API。 │   │   ├── stmmac_ptp.c--Precision Time Protocol的注册去注册接口,以及stmmac_ptp_clock_ops函数集。 │   │   └── stmmac_tc.c--Transparent Clock。 ├── loopback.c ├── macvlan.c ├── macvtap.c ├── mii.c ├── net_failover.c ├── phy
│   ├── fixed_phy.c │   ├── mdio-bitbang.c │   ├── mdio-boardinfo.c │   ├── mdio_bus.c │   ├── mdio_device.c │   ├── motorcomm.c--Motorcomm的PHY驱动。 │   ├── phy-c45.c │   ├── phy-core.c │   ├── phy_device.c │   ├── phylink.c │   ├── phy.c--查找和配置PHY框架,以及通用PHY驱动。 │   └── swphy.c ├── Space.c ├── tap.c ├── tun.c ├── veth.c └── virtio_net.c

STM32MP1网络驱动主要包括:

  • 从DTS中获取时钟、中断、寄存器、pinctrl、电源域等配置,以及GMAC特有的属性。
  • 初始化GMAC作为网络设备结构体net_device,以及系统资源初始化。
  • 配置GMAC硬件。
  • 配置MDIO、PHY。
  • 注册GMAC到网络设备子系统中。
  • 创建debufs等调试节点。
stm32_dwmac_probe
  ->stmmac_get_platform_resources--从设备树获取资源,包括macirq、eth_wake_irq、寄存器地址范围。
  ->stmmac_probe_config_dt--获取mac字符串,以及从设备树获取参数填充plat_stmmacenet_data结构体。
    ->stmmac_dt_phy--
  ->of_device_get_match_data--根据设备树compatible和驱动的stm32_dwmac_match进行匹配。成功则返回data,否则退出。
  ->stm32_dwmac_parse_data--获取mac-clk-tx、mac-clk-rx、st,syscon属性值。
  ->stm32_dwmac_wake_init--配置GMAC中断唤醒系统。
    ->device_set_wakeup_capable--配置设备允许唤醒系统。
    ->device_pm_set_wake_irq--将设备中断附着为唤醒中断。
  ->stm32_dwmac_init--进行PHY接口模式设置,使能rx/tx时钟。
  ->stmmac_dvr_probe--进行网络注册,完成PHY接口初始化。
    ->devm_alloc_etherdev_mqs--分配struct net_device结构体,并对其进行初始化。
    ->stmmac_set_ethtool_ops--初始化struct ethtool_ops为stmmac_ethtool_ops
    ->stmmac_verify_args--验证驱动module参数,如果有错则使用默认参数。
    ->create_singlethread_workqueue--创建stmmac_wq工作队列。
    ->stmmac_hw_init--GMAC硬件初始化。
    ->stmmac_check_ether_addr--检查MAC地址合法性,失败则分配一个随机MAC地址。
    ->stmmac_tc_init
    ->netif_msg_init
    ->stmmac_napi_poll_rx/stmmac_napi_poll_tx--NAPI驱动要提供poll函数来轮询处理发送和接收数据。
    ->stmmac_mdio_register--向内核注册MDIO总线。
      ->mdiobus_alloc--分配struct mii_bus结构体。
      ->of_mdiobus_register--解析mdio属性,及其子节点。
        ->stmmac_mdio_read/stmmac_mdio_write/stmmac_mdio_reset--通过MDIO对PHY的读/写/复位接口。
      ->mdiobus_get_phy--从0开始遍历,最多32个。获取phy对应的phy_device结构。
      ->phy_attached_info
    ->stmmac_phy_setup
      ->phylink_create--phylink是net_device和phy_device中介,当两者之间连接变化时phylink做出对应配置改变。关于phylink参考《phylink》。
    ->register_netdev--在对struct net_device进行初始化后,进行网络设备注册,设备操作函数集为stmmac_netdev_ops
    ->stmmac_init_fs--创建/sys/kernel/debug/stmmaceth/eth0节点,提供DMA rx/tx rings和DMA HW特性信息。 stm32_dwmac_remvoe

5.3 MDIO总线

5.3.1 MDIO总线初始化

MDIO总线初始化:

mdio_bus_init
  ->class_register--注册mdio_bus类mdio_bus_class。
  ->bus_register--注册mdio_bus总线mdio_bus_type。

mdio_bus_type定义了MDIO总线上设备和驱动match函数,以及uevent函数:

struct bus_type mdio_bus_type = {
    .name        = "mdio_bus",
    .match        = mdio_bus_match,
    .uevent        = mdio_uevent,
};

当设备或者驱动注册到MDIO总线上后,mdio_bus_match进行MDIO总线上设备和驱动匹配:

mdio_bus_match
  ->of_driver_match_device
  ->mdio->bus_match
    ->mdio_device_bus_match--通过mdiobus_create_device()函数创建的设备。判断名称是否一致。
    ->phy_bus_match--通过phy_device_create()创建的设备。通过phy_id和phy_id_mask的与运算之后比较设备和driver是否一致。

5.3.2 MDIO总线相关API

mdiobus_alloc()分配一个struct mii_bus结构体,然后of_mdiobus_register根据从DTS中获取的MDIO总线、PHY设备等信息进行初始化:

  • 注册MDIO总线。
  • 遍历MDIO总线下的PHY设备,注册PHY设备。
  • 创建PHY设备和GMAC之间的连接。
->of_mdiobus_register--注册MDIO总线并且根据设备树创建PHY设备。
  ->mdiobus_register--向MDIO总线注册设备。
    ->__mdiobus_register
      ->device_register--注册MDIO设备,类型为mdio_bus_class。
  ->of_mdiobus_register_phy--从设备树获取PHY进行注册。
    ->get_phy_device--
      ->get_phy_id--通过MDIO总线读取MII_PHYSID1和MII_PHYSID2,即PHY设备ID。
      ->phy_device_create--创建PHY设备,设备总线为mdio_bus_type,设备类型为mdio_bus_phy_type。
        ->phy_state_machine     
->phy_device_register       ->mdiobus_register_device--将PHY设备注册到MDIO总线。       ->phy_device_reset--对PHY进行复位。       ->phy_scan_fixups--判断设备是否需要Fixup,如需要则调用fixup->run()。       ->device_add

mdio_bus_phy_type为Phy设备提供了统一的属性定义、释放函数、电源管理等:

static struct attribute *phy_dev_attrs[] = {
    &dev_attr_phy_id.attr,
    &dev_attr_phy_interface.attr,
    &dev_attr_phy_has_fixups.attr,
    NULL,
};
ATTRIBUTE_GROUPS(phy_dev);

static const struct device_type mdio_bus_phy_type = {
    .name = "PHY",
    .groups = phy_dev_groups,
    .release = phy_device_release,
    .pm = MDIO_BUS_PHY_PM_OPS,
};

如下为创建的节点:

/sys/class/mdio_bus/stmmac-0--MDIO总线。
|-- device -> ../../../5800a000.ethernet
|-- of_node -> ../../../../../../firmware/devicetree/base/soc/ethernet@5800a000/mdio0
|-- stmmac-0:00--MDIO总线上的PHY设备。
|   |-- attached_dev -> ../../../net/eth0--表示此PHY设备时附着到哪一个设备上的。
|   |-- driver -> ../../../../../../../bus/mdio_bus/drivers/YT8511\ Gigabit\ Ethernet
|   |-- of_node -> ../../../../../../../firmware/devicetree/base/soc/ethernet@5800a000/mdio0/ethernet-phy@0
|   |-- phy_has_fixups
|   |-- phy_id
|   |-- phy_interface--PHY接口类型。
|   |-- subsystem -> ../../../../../../../bus/mdio_bus
|   `-- uevent
|-- subsystem -> ../../../../../../class/mdio_bus
`-- uevent

5.4 phylink

GMAC在内核中被注册成struct net_device设备,phy芯片被注册成struct phy_device设备。phy_device和net_device之间的关联通过phy_link表达,维护两者之间的连接状态变化。

更多关于phylink说明参考《phylink》,API参考《Linux Networking and Network Devices APIs》。

5.5 PHY子系统和PHY驱动

5.5.1 PHY子系统初始化

关于PHY、phylink、MDIO更多参考《PHY Abstraction Layer》。

PHY子系统注册两个默认的PHY驱动:

phy_init  
  ->mdio_bus_init
  ->features_init
  ->phy_driver_register
    ->genphy_c45_driver
    ->genphy_driver

genphy_driver是一个通用PHY驱动:

static struct phy_driver genphy_driver = {
    .phy_id        = 0xffffffff,
    .phy_id_mask    = 0xffffffff,
    .name        = "Generic PHY",
    .soft_reset    = genphy_no_soft_reset,
    .get_features    = genphy_read_abilities,
    .aneg_done    = genphy_aneg_done,
    .suspend    = genphy_suspend,
    .resume        = genphy_resume,
    .set_loopback   = genphy_loopback,
};

5.5.2 YT8511驱动

YT8511驱动:

module_phy_driver(ytphy_drvs)
  ->phy_module_driver
    ->phy_module_init
      ->phy_drivers_register
        ->phy_driver_register--将ytphy_drvs注册到MDIO总线上。

当读取到的ID匹配后,phy_device和phy_driver就关联上了。对phy_device的操作,就可以调用具体phy_driver的驱动函数:

static struct phy_driver ytphy_drvs[] = {
    {
        .phy_id        = PHY_ID_YT8511,
        .name        = "YT8511 Gigabit Ethernet",
        .phy_id_mask    = MOTORCOMM_PHY_ID_MASK,
        .features    = PHY_GBIT_FEATURES,
        //.flags        = PHY_HAS_INTERRUPT,
        .config_aneg    = genphy_config_aneg,
#if GMAC_CLOCK_INPUT_NEEDED
        .config_init    = yt8511_config_init,
#else
        .config_init    = genphy_config_init,
#endif
        .read_status    = genphy_read_status,
        .suspend    = genphy_suspend,
        .resume        = genphy_resume,
    },
};

6 网络驱动测试

6.1 iperf

启动iperf服务器端:iperf -s。

------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[  4] local 192.168.137.1 port 5001 connected with 192.168.137.111 port 48680
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0-30.0 sec  3.29 GBytes    942 Mbits/sec

启动iperf客户端:iperf -c 192.168.137.1 -i 10 -t 30。

------------------------------------------------------------
Client connecting to 192.168.137.1, TCP port 5001
TCP window size:  306 KByte (default)
------------------------------------------------------------
[  3] local 192.168.137.111 port 48680 connected with 192.168.137.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.10 GBytes   942 Mbits/sec
[  3] 10.0-20.0 sec  1.10 GBytes   946 Mbits/sec
[  3] 20.0-30.0 sec  1.10 GBytes   942 Mbits/sec
[  3]  0.0-30.0 sec  3.29 GBytes   943 Mbits/sec

6.2 trace event

Kernel在/sys/kernel/debug/tracing/events提供了网络相关的trace event:

bridge/:
br_fdb_add                 enable
br_fdb_external_learn_add  fdb_delete
br_fdb_update              filter

mdio:
enable       filter       mdio_access

napi:
enable     filter     napi_poll

net:
enable                        netif_receive_skb
filter                        netif_receive_skb_entry
napi_gro_frags_entry          netif_receive_skb_exit
napi_gro_frags_exit           netif_receive_skb_list_entry
napi_gro_receive_entry        netif_receive_skb_list_exit
napi_gro_receive_exit         netif_rx
net_dev_queue                 netif_rx_entry
net_dev_start_xmit            netif_rx_exit
net_dev_xmit                  netif_rx_ni_entry
net_dev_xmit_timeout          netif_rx_ni_exit

skb:
consume_skb              filter                   skb_copy_datagram_iovec
enable                   kfree_skb

sock/:
enable                 inet_sock_set_state    sock_rcvqueue_full
filter                 sock_exceed_buf_limit

tcp:
enable                 tcp_probe              tcp_retransmit_skb
filter                 tcp_rcv_space_adjust   tcp_retransmit_synack
tcp_destroy_sock       tcp_receive_reset      tcp_send_reset

udp:
enable                  filter                  udp_fail_queue_rcv_skb

 

posted on 2024-06-02 23:59  ArnoldLu  阅读(2070)  评论(0编辑  收藏  举报

导航