4. CPSW以太网控制器驱动开发基础

CPSW(Common Platform Ethernet Switch)是TI Sitara系列处理器(如AM335x、AM437x、AM57xx)内部集成的双端口千兆以太网交换子系统。它并非简单的两个独立MAC拼接,而是包含两个外部MAC端口(Port 1/Port 2)、一个内部主机端口(Port 0,连接CPU)、CPDMA控制器、ALE地址查找引擎以及中断合并模块的完整交换架构。理解这一硬件结构,是编写和调试Linux网络设备驱动的前提。

双千兆MAC与端口结构

CPSW的两个外部MAC(通常称为slave 0和slave 1)各自拥有独立的PHY接口(RGMII或RMII),可分别连接两个外部PHY芯片。每个MAC端口具备独立的发送/接收FIFO、统计计数器和流控逻辑。Port 0是内部主机端口,CPU通过它读写数据包,不占用外部引脚。ALE(Address Lookup Engine)负责在三个端口之间转发帧:当从Port 1收到帧时,ALE根据目的MAC地址决定是转发到Port 2(交换模式)还是上送Port 0(本机接收)。在典型的Linux驱动使用场景中,两个外部端口通常被配置为两个独立的网络接口(eth0、eth1),此时ALE工作在“旁路”或“双MAC”模式,每个端口的数据直接与主机端口交互,不进行端口间交换。

每个MAC端口的关键寄存器包括:MACCONTROL(控制RGMII模式、全双工、速率)、MACSTATUS(链路状态)、RXMBP(接收最大背压)、以及各统计寄存器。驱动初始化时需要根据PHY协商结果配置这些寄存器。例如,当PHY协商为1000Mbps全双工时,MACCONTROL的FULLDUPLEX位应置1,GIGABIT位应置1,同时RGMII接口需要配置为千兆模式(RGMII_ID或RGMII_TXID/RXID根据PCB走线延迟决定)。

CPDMA与DMA描述符

CPDMA(Common Platform DMA)是CPSW的数据搬运引擎,负责在外部MAC FIFO与系统内存之间搬移以太网帧。它使用描述符链表(Descriptor List)来管理缓冲区。每个描述符是一个32字节的结构体,包含以下关键字段:

  • Next Descriptor Pointer:指向下一个描述符的物理地址,形成链表。
  • Buffer Pointer:指向数据缓冲区的物理地址。
  • Buffer Length:缓冲区长度(接收时由DMA写入实际帧长)。
  • Packet Length:有效数据包长度。
  • Flags:包括SOP(Start of Packet)、EOP(End of Packet)、OWNER(当前归属CPU还是DMA)、EOQ(End of Queue)等。

发送和接收使用独立的描述符队列。发送时,驱动填充缓冲区内容,设置SOP/EOP标志,清除OWNER位(表示归DMA所有),然后写CPDMA的TX_HDP寄存器触发发送。接收时,驱动预先分配好空缓冲区,设置OWNER位为DMA,DMA收到帧后写入缓冲区并翻转OWNER位,同时更新Packet Length,然后产生接收中断。驱动在中断处理中遍历接收描述符,将数据包交给网络协议栈,然后重新初始化描述符并归还给DMA。

描述符的内存必须满足对齐要求(通常32字节对齐),且其物理地址必须连续可被CPDMA访问。在Linux中,通常使用dma_alloc_coherent()分配一致性内存来存放描述符环,避免cache一致性问题。

CPSW 双千兆MAC + CPDMA 描述符数据流 PHY 0 RGMII PHY 1 RGMII CPSW 交换子系统 MAC Port 1 FIFO/统计 MAC Port 2 FIFO/统计 ALE 地址查找/转发 CPDMA 控制器 TX_HDP / RX_HDP 寄存器 DMA 描述符环(内存) 描述符 0 Next | BufPtr | Len | Flags(OWNER/SOP/EOP) 描述符 1 Next | BufPtr | Len | Flags(OWNER/SOP/EOP) 描述符 N Next | BufPtr | Len | Flags(OWNER/SOP/EOP) 数据缓冲区(sk_buff / DMA内存) 以太网帧数据 Linux 网络设备驱动 net_device / NAPI / 中断处理 IRQ MAC端口 ALE CPDMA 描述符 数据缓冲区

中断处理与NAPI

CPSW的中断源包括:接收完成中断(RX)、发送完成中断(TX)、以及各种错误中断(如接收溢出、描述符错误)。在Linux驱动中,通常采用NAPI(New API)机制来平衡中断开销与吞吐量。具体做法是:在中断处理函数(hardirq)中,读取CPDMA的RX中断状态寄存器,确认有接收完成事件后,调用napi_schedule()触发软中断,然后屏蔽该中断源。在NAPI的poll函数中,驱动循环处理接收描述符,直到处理完所有待处理帧或达到预算(budget),然后重新使能中断。

发送完成中断的处理相对简单:驱动在发送描述符时记录哪些缓冲区正在使用,发送完成中断到来后,遍历已完成的发送描述符,释放对应的sk_buff内存。为了减少中断频率,CPSW还支持中断合并(Interrupt Pacing),可以通过配置CPDMA的INT_CONTROL寄存器设置接收中断的合并阈值和延迟。

Linux网络设备驱动框架

CPSW驱动在Linux中注册为平台设备驱动(platform_driver),其核心是struct net_device结构体。驱动需要实现以下关键回调函数:

  • ndo_open():打开网络接口时调用,负责初始化CPDMA描述符环、申请中断、启动PHY状态机、使能MAC和DMA。
  • ndo_stop():关闭接口时调用,停止DMA、释放中断、关闭PHY。
  • ndo_start_xmit():发送数据包时调用,将sk_buff数据映射到DMA缓冲区,填充发送描述符,触发发送。
  • ndo_get_stats():读取MAC统计寄存器,返回网络统计信息。
  • ndo_set_rx_mode():处理多播和混杂模式,配置ALE或MAC的地址过滤。

此外,驱动还需要通过register_netdev()注册网络设备,并在probe函数中完成硬件资源映射(寄存器基地址、中断号)、时钟使能、PHY连接等初始化工作。

设备树配置

在设备树中,CPSW节点需要描述MAC地址、PHY连接方式、中断和寄存器范围。以下是一个典型的AM335x CPSW设备树片段示例:

mac: ethernet@4a100000 {
    compatible = "ti,am335x-cpsw";
    reg = <0x4a100000 0x1000>;
    interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
    interrupt-names = "rx", "tx", "misc", "host";
    cpsw-phy-sel = <&phy_sel>;
    status = "okay";

    slave0: slave@0 {
        phy-mode = "rgmii-id";
        phy-handle = <&phy0>;
    };

    slave1: slave@1 {
        phy-mode = "rgmii-id";
        phy-handle = <&phy1>;
    };
};

其中phy-mode指定RGMII接口的延迟模式,phy-handle指向对应的PHY节点。驱动在probe时会解析这些属性,并根据phy-mode配置MAC的RGMII控制寄存器。如果PHY的复位GPIO或时钟需要控制,也应在设备树中通过reset-gpios和clocks属性描述。

调试提示:当网络接口无法收发数据时,首先检查ifconfig eth0或ip link show eth0确认接口是否UP,然后通过ethtool eth0查看链路状态和速率。若链路未UP,检查PHY的复位和时钟;若链路UP但无法ping通,检查CPDMA描述符的OWNER位是否正确翻转,以及ALE是否将帧正确路由到主机端口。