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

  • 相关阅读:
    Java 8 Lambda 表达式
    OSGi 系列(十二)之 Http Service
    OSGi 系列(十三)之 Configuration Admin Service
    OSGi 系列(十四)之 Event Admin Service
    OSGi 系列(十六)之 JDBC Service
    OSGi 系列(十)之 Blueprint
    OSGi 系列(七)之服务的监听、跟踪、声明等
    OSGi 系列(六)之服务的使用
    OSGi 系列(三)之 bundle 事件监听
    OSGi 系列(三)之 bundle 详解
  • 原文地址:https://www.cnblogs.com/Ph-one/p/8990550.html
Copyright © 2011-2022 走看看