zoukankan      html  css  js  c++  java
  • Linux网络子系统之---- PHY 配置

     MII即媒体独立接口,也叫介质无关接口。

    它包括一个数据接口,以及一个MAC和PHY之间的管理接口(图1)。

    数据接口包括分别用于发送器和接收器的两条独立信道。每条信道都有自己的数据、时钟和控制信号。MII数据接口总共需16个信号。

    管理接口是个双信号接口:一个是时钟信号,另一个是数据信号。通过管理接口,上层能监视和控制PHY。

    RMII口是用两根线来传输数据的,

    MII口是用4根线来传输数据的,

    GMII是用8根线来传输数据的。

    GMII (Gigabit MII)

    GMII是8bit并行同步收发接口,采用8位接口数据,工作时钟125MHz,因此传输速率可达1000Mbps。同时兼容MII所规定的10/100 Mbps工作方式。

    GMII接口数据结构符合IEEE以太网标准。该接口定义见IEEE802.3-2000。

    发送器:

    ◇ GTXCLK——吉比特TX..信号的时钟信号(125MHz)

    ◇ TXCLK——10/100M信号时钟

    ◇ TXD[7..0]——被发送数据  -------  mii  为4,所以一个信道是8,两个是16

    ◇ TXEN——发送器使能信号

    ◇ TXER——发送器错误(用于破坏一个数据包)

    注:在千兆速率下,向PHY提供GTXCLK信号,TXD、TXEN、TXER信号与此时钟信号同步。否则,在10/100M速率下,PHY提供 TXCLK时钟信号,其它信号与此信号同步。其工作频率为25MHz(100M网络)或2.5MHz(10M网络)。

    接收器:

    ◇ RXCLK——接收时钟信号(从收到的数据中提取,因此与GTXCLK无关联)

    ◇ RXD[7..0]——接收数据

    ◇ RXDV——接收数据有效指示

    ◇ RXER——接收数据出错指示

    ◇ COL——冲突检测(仅用于半双工状态)

    管理配置

    ◇ MDC——配置接口时钟

    ◇ MDIO——配置接口I/O

    管理配置接口控制PHY的特性。该接口有32个寄存器地址,每个地址16位。其中前16个已经在“IEEE 802.3,2000-22.2.4Management Functions”中规定了用途,其余的则由各器件自己指定。

    MII/RMII只是一种接口,对于10M线速,MII的速率是2.5M,RMII则是5M;对于100M线速,MII的速率是25M,RMII则是50M。

    SGMII--Serial Gigabit Media IndependentInterface

    SGMII是PHY与MAC之间的接口,类似与GMII和RGMII,只不过GMII和RGMII都是并行的,而且需要随路时钟,PCB布线相对麻烦,而且不适应背板应用。

    而SGMII是串行的,不需要提供另外的时钟,MAC和PHY都需要CDR去恢复时钟。另外SGMII是有8B/10b编码的,速率是1.25G

    在 linux 配置PHY

    drivers/net/phy

    配置的参数  自适应, 1000M, 全双工。

    phydev-> autonet, speed, duplex.

    1. MDIO简介

      The MDIO interface is a simple, two-wire, serial interface to connect a management entity and a managed PHY for the purposes of controlling the PHY and gathering status from the PHY.
       The two lines include the MDC line [Management Data Clock], and the MDIO line [Management Data Input/Output]. The clock is point-to-point, while the data line is a bi-directional multi-drop interface.
       The data line is Tri-state able and can drive 32 devices.

       MDIO接口,MAC与PHY间的管理接口(MII是数据接口),有2根线:时钟线MDC,数据线MDIO(双向)



       MDIO工作流程:
        * Preamle(PRE)       在没有传输数据的空闲状态时,数据线MDIO处于高阻态(一直为1)。
        * Start of Frame(ST) MAC驱动MDIO线,出现一个2bit的开始标识码(01)。
        * Operation Code(OP) MAC驱动MDIO线,出现一个2bit数据来标识是读操作(10)还是写操作(01)。
        * PHY Address(PHYAD) MAC驱动MDIO线,出现一个5bit数据标识PHY的地址。
        * Reg Address(REGAD) MAC驱动MDIO线,出现一个5bitPHY寄存器地址。
        * Turnaround(TA)     写操作的话,MAC驱动MDIO线,出现10
                             读操作的话,MDIO pin of MAC must be put in high-impedance state
                                         在第二个周期,PHY驱动MDIO线,出现0

        * Data               MDIO串行读出/写入16bit的寄存器数据。

        * MDIO恢复成空闲状态,同时MDIO进入高阻状态。


        下面是PHY芯片 BCM5461 的一个例子:


    2. PowerPC对MDIO的支持

    PowerPC操作MDIO时,涉及以下寄存器:
    MIIMCFG  配置寄存器
    MIIMCOM  命令寄存器
    MIIMADD  地址寄存器
    MIIMCON  控制寄存器
    MIIMSTAT 状态寄存器
    MIIMIND  指示寄存器

    以MPC8560举例,这些寄存器在CCSR中的位置如下:






    2.1 MIIMCFG:配置寄存器


    ResetMgmt:   用于重置MDIO模块
    MgmtClockSet:时钟设置,是CCB的 2的n次方之一



    2.2 MIIMCOM  命令寄存器


    ReadCycle: 0->1 触发MDIO读时序


    2.3 MIIMADD  地址寄存器


    PHYaddr:PHY地址,共5bit,系统最多联31个PHY(地址0为保留)
    REGaddr:寄存器地址,共5bit,一个PHY上最多32个寄存器地址(可以使用shadow value技术,访问更多的寄存器)


    2.4 MIIMCON  控制寄存器



    PHYcontrol:在写流程时,这里存放要写入寄存器的值


    2.5 MIIMSTAT 状态寄存器



    PHYstatus:读流程时,PHY reg的内容会放到此

    2.6 MIIMIND  指示寄存器


    NotVal:若置1,表示读流程结束,可以去读MIIMSTAT
    Scan:  若置1,表示扫描流程进行中
    Busy:  只有置0时,才能进行新的读写流程



    3. linux中MDIO的实现

    读写PHY寄存器时通过2个函数 

    phy_read()和phy_write(),

    最终调用
    int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
    int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, int regnum, u16 value)

    参数regs就是MDIO相关寄存器:
    1. struct gfar_mii {
    2.     u32 miimcfg; /* 0x.520 - MII Management Config Register */
    3.     u32 miimcom; /* 0x.524 - MII Management Command Register */
    4.     u32 miimadd; /* 0x.528 - MII Management Address Register */
    5.     u32 miimcon; /* 0x.52c - MII Management Control Register */
    6.     u32 miimstat; /* 0x.530 - MII Management Status Register */
    7.     u32 miimind; /* 0x.534 - MII Management Indicator Register */
    8. };
    参数mii_id,就是PHY的id
    参数regnum,就是寄存器地址


    上代码,简单不解释
    1. int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
    2. {
    3.     u16 value;
    4.     /* Set the PHY address and the register address we want to read */
    5.     gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
    6.     /* Clear miimcom, and then initiate a read */
    7.     gfar_write(&regs->miimcom, 0);
    8.     gfar_write(&regs->miimcom, MII_READ_COMMAND);
    9.     /* Wait for the transaction to finish */
    10.     while (gfar_read(&regs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
    11.         cpu_relax();
    12.     /* Grab the value of the register from miimstat */
    13.     value = gfar_read(&regs->miimstat);
    14.     return value;
    15. }


    1. int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id,
    2.               int regnum, u16 value)
    3. {
    4.     /* Set the PHY address and the register address we want to write */
    5.     gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
    6.     /* Write out the value we want */
    7.     gfar_write(&regs->miimcon, value);
    8.     /* Wait for the transaction to finish */
    9.     while (gfar_read(&regs->miimind) & MIIMIND_BUSY)
    10.         cpu_relax();
    11.     return 0;
    12. }
     

    内核启动时的准备工作

    4.1 初始化网络相关的全局数据结构,并挂载处理网络相关软中断的钩子函数
    start_kernel()
        --> rest_init()
            --> do_basic_setup()
                --> do_initcall
                   -->net_dev_init

    __init net_dev_init()
    {
        //每个CPU都有一个CPU私有变量 _get_cpu_var(softnet_data)
        //_get_cpu_var(softnet_data).poll_list很重要,软中断中需要遍历它的
        for_each_possible_cpu(i) {
            struct softnet_data *queue;
            queue = &per_cpu(softnet_data, i);
            skb_queue_head_init(&queue->input_pkt_queue);
            queue->completion_queue = NULL;
          INIT_LIST_HEAD(&queue->poll_list);
            queue->backlog.poll = process_backlog;
            queue->backlog.weight = weight_p;
        }
        open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); //在软中断上挂网络发送handler
        open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); //在软中断上挂网络接收handler
    }
       4.2 加载网络设备的驱动
    NOTE:这里的网络设备是指MAC层的网络设备,即TSEC和PCI网卡(bcm5461是phy)
    在网络设备驱动中创建net_device数据结构,并初始化其钩子函数 open(),close() 等
    挂载TSEC的驱动的入口函数是 gfar_probe

    // 平台设备 TSEC 的数据结构
    static struct platform_driver gfar_driver = {
        .probe = gfar_probe,
        .remove = gfar_remove,
        .driver = {
            .name = "fsl-gianfar",
        },
    };

    int gfar_probe(struct platform_device *pdev)
    {
        dev = alloc_etherdev(sizeof (*priv)); // 创建net_device数据结构

        dev->open = gfar_enet_open;
        dev->hard_start_xmit = gfar_start_xmit;
        dev->tx_timeout = gfar_timeout;
        dev->watchdog_timeo = TX_TIMEOUT;
    #ifdef CONFIG_GFAR_NAPI
        netif_napi_add(dev, &priv->napi,gfar_poll,GFAR_DEV_WEIGHT); //软中断里会调用poll钩子函数
    #endif
    #ifdef CONFIG_NET_POLL_CONTROLLER
        dev->poll_controller = gfar_netpoll;
    #endif
        dev->stop = gfar_close;
        dev->change_mtu = gfar_change_mtu;
        dev->mtu = 1500;
        dev->set_multicast_list = gfar_set_multi;
        dev->set_mac_address = gfar_set_mac_address;
        dev->ethtool_ops = &gfar_ethtool_ops;
    }


    五、启用网络设备
    5.1 用户调用ifconfig等程序,然后通过ioctl系统调用进入内核
    socket的ioctl()系统调用
        --> sock_ioctl()
            --> dev_ioctl()                              //判断SIOCSIFFLAGS
              --> __dev_get_by_name(net, ifr->ifr_name)  //根据名字选net_device
                 --> dev_change_flags()                  //判断IFF_UP
                    --> dev_open(net_device)             //调用open钩子函数 

    对于TSEC来说,挂的钩子函数是 gfar_enet_open(net_device)

    5.2 在网络设备的open钩子函数里,分配接收bd,挂中断ISR(包括rx、tx、err),对于TSEC来说
    gfar_enet_open
        --> 给Rx Tx Bd 分配一致性DMA内存 
        --> 把Rx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
        --> 把Tx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
        --> 给 tx_skbuff 指针数组 分配内存,并初始化为NULL
        --> 给 rx_skbuff 指针数组 分配内存,并初始化为NULL

        --> 初始化Tx Bd
        --> 初始化Rx Bd,提前分配存储以太网包的skb,这里使用的是一次性dma映射
           (注意:#define DEFAULT_RX_BUFFER_SIZE  1536保证了skb能存一个以太网包)
            rxbdp = priv->rx_bd_base;
            for (i = 0; i < priv->rx_ring_size; i++) {
                struct sk_buff *skb = NULL;
                rxbdp->status = 0;
                //这里真正分配skb,并且初始化rxbpd->bufPtr, rxbdpd->length
               skb = gfar_new_skb(dev, rxbdp);    
                priv->rx_skbuff[i] = skb;
                rxbdp++;
            }
            rxbdp--;
            rxbdp->status |= RXBD_WRAP; // 给最后一个bd设置标记WRAP标记
            
        --> 注册TSEC相关的中断handler: 错误,接收,发送
            request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev)
            request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev)//包发送完
            request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev)  //包接收完

        -->gfar_start(net_device)
            // 使能Rx、Tx
            // 开启TSEC的 DMA 寄存器
            // Mask 掉我们不关心的中断event

    最终,TSEC相关的Bd等数据结构应该是下面这个样子的

    六、中断里接收以太网包

     TSEC的RX已经使能了,网络数据包进入内存的流程为:
        网线 --> Rj45网口 --> MDI 差分线
             --> bcm5461(PHY芯片进行数模转换) --> MII总线 
             --> TSEC的DMA Engine 会自动检查下一个可用的Rx bd 
             --> 把网络数据包 DMA 到 Rx bd 所指向的内存,即skb->data

    接收到一个完整的以太网数据包后,TSEC会根据event mask触发一个 Rx 外部中断。
    cpu保存现场,根据中断向量,开始执行外部中断处理函数do_IRQ()

    do_IRQ 伪代码
    {
       上半部处理硬中断
           查看中断源寄存器,得知是网络外设产生了外部中断
           执行网络设备的rx中断handler(设备不同,函数不同,但流程类似,TSEC是gfar_receive)
              1. mask 掉 rx event,再来数据包就不会产生rx中断
              2. 给napi_struct.state加上 NAPI_STATE_SCHED 状态
              3. 挂网络设备自己的napi_struct结构到cpu私有变量_get_cpu_var(softnet_data).poll_list
              4. 触发网络接收软中断
        下半部处理软中断
            依次执行所有软中断handler,包括timer,tasklet等等
            执行网络接收的软中断handler net_rx_action
              1. 遍历cpu私有变量_get_cpu_var(softnet_data).poll_list 
              2. 取出poll_list上面挂的napi_struct 结构,执行钩子函数napi_struct.poll()
                 (设备不同,钩子函数不同,流程类似,TSEC是gfar_poll)
              3. 若poll钩子函数处理完所有包,则打开rx event mask,再来数据包的话会产生rx中断
              4. 调用napi_complete(napi_struct *n)
                 把napi_struct 结构从_get_cpu_var(softnet_data).poll_list 上移走
                 同时去掉 napi_struct.state 的 NAPI_STATE_SCHED 状态
    }

    6.1 TSEC的接收中断处理函数
    gfar_receive
    {
    #ifdef CONFIG_GFAR_NAPI
        // test_and_set当前net_device的napi_struct.state 为 NAPI_STATE_SCHED
        // 在软中断里调用 net_rx_action 会检查状态 napi_struct.state
        if (netif_rx_schedule_prep(dev, &priv->napi)) {  
            tempval = gfar_read(&priv->regs->imask);            
            tempval &= IMASK_RX_DISABLED; //mask掉rx,不再产生rx中断
            gfar_write(&priv->regs->imask, tempval);    
            // 将当前net_device的 napi_struct.poll_list 挂到
            // CPU私有变量__get_cpu_var(softnet_data).poll_list 上,并触发软中断
            // 所以,在软中断中调用 net_rx_action 的时候,就会执行当前net_device的
            // napi_struct.poll()钩子函数,即 gfar_poll()
            __netif_rx_schedule(dev, &priv->napi);   
        } 
    #else
        gfar_clean_rx_ring(dev, priv->rx_ring_size);
    #endif
    }

    http://tech.watchstor.com/storage-network-115062.htm

    http://blog.csdn.net/jw212/article/details/6738457

     https://blog.csdn.net/jk198310/article/details/12909341

  • 相关阅读:
    Kafka 文档用例
    Kafka 文档引言
    关于工作流核心选择
    IEEE浮点标准
    新项目的CQRS设计
    持续集成:CruiseControl.NET + VisualSVN.Server
    商城(一)
    Maven基础学习(一)—Maven入门
    Mybatis基础学习(五)—缓存
    Mybatis基础学习(四)—关系映射
  • 原文地址:https://www.cnblogs.com/Ph-one/p/8990550.html
Copyright © 2011-2022 走看看