• 基于335X平台Linux交换芯片驱动开发


    基于335X平台Linux交换芯片驱动开发

     

    一、软硬件平台资料

    1、开发板:创龙AM3359核心板,网口采用RMII形式。

    2、Kernel版本:4.4.12,采用FDT

    3、交换芯片MARVELL的88E6321.

     

    二、移植准备工作

     

    1、熟悉88E6321的datasheet及Functional_Specification_Rev.0.05

    2、熟悉设备树相关理论和用法

    3、熟悉Linux网络驱动MDIO、PHY部分的软件流程

     

    三、DTS文件修改

    本工程的DTS文件以am335x-icev2.dts为基础进行修改

    1、修改网络接口MODE

    我们板子上使用的是335X的RMII接口与SW相连,使用SW的RMII接口提供的50M时钟信号。根据硬件连接关系,335X的RMII接口与SW的PORT5相连,根据SW使用单芯片模式,根据SW的访问机制,这里设置phy_id为0x15.所以对设备树相关接口模式部分做如下修改

    -  &cpsw_emac0 {

    -   phy_id = <&davinci_mdio>, <0>;

    -   phy-mode = "rgmii";

    -  };

    + &cpsw_emac0 {

    +  phy_id = <&davinci_mdio>, <0x15>;

    +  phy-mode = "rmii";

    + };

     

    2、修改网络接口pinmux

    将原有cpsw_default、cpsw_sleep部分的pinmux改为RMII模式相对应的pinmux配置,如下:

    cpsw_default: cpsw_default {

           pinctrl-single,pins = <

               /* Slave 1, RMII mode */

                   AM33XX_IOPAD(0x914, PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* mii1_txen.rmii1_txen */

                   AM33XX_IOPAD(0x924, PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* mii1_txd1.rmii1_td1 */

                   AM33XX_IOPAD(0x928, PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* mii1_txd0.rmii1_td0 */   

                   AM33XX_IOPAD(0x93c, PIN_INPUT_PULLDOWN | MUX_MODE1)  /* mii1_rxd1.rmii1_rd1 */

                   AM33XX_IOPAD(0x940, PIN_INPUT_PULLDOWN | MUX_MODE1)  /* mii1_rxd0.rmii1_rd0 */

                   AM33XX_IOPAD(0x944, PIN_INPUT_PULLDOWN | MUX_MODE0)  /* RMII1_REF_CLK.rmii1_refclk*/

                   AM33XX_IOPAD(0x910, PIN_INPUT_PULLDOWN | MUX_MODE1)  /* MII1_RX_ER. rmii1_rxerr*/

                   AM33XX_IOPAD(0x90c, PIN_INPUT_PULLDOWN | MUX_MODE1)  /* MII1_CRS. rmii1_crs_dv*/

           >;

        };

     

        cpsw_sleep: cpsw_sleep {

           pinctrl-single,pins = <

               /* Slave 1 reset value */

                AM33XX_IOPAD(0x914, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x924, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x928, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x93c, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x940, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x944, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x910, PIN_INPUT_PULLDOWN | MUX_MODE7)

                AM33XX_IOPAD(0x90c, PIN_INPUT_PULLDOWN | MUX_MODE7)

           >;

        };

     

    四、网口驱动架构及流程分析

    网口驱动架构见文档:基于335X的Linux网口驱动分析

    4.1.网口底层驱动启动及加载过程大致如下:

     

    1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23

    --> kernel相关启动及初始化
      --> phy_init
      --> mdio_bus_init
      --> phy_driver_register
      --> davinci_mdio_probe
      --> cpsw_probe
      --> cpsw_ndo_open
      --> cpsw_slave_open
      --> phy_probe
      --> PHY_READY->PHY_UP
      --> phy_state_machine状态机函数
      --> phy_start_aneg
          --> genphy_config_aneg
              --> genphy_config_advert
              --> genphy_restart_aneg
          --> PHY_AN
       
      --> PHY_AN->HY_RUNNING
       
      --> _cpsw_adjust_link
          --> phy_print_status
              --> "Link is Up -   xxxx"
      --> PHY_RUNNING->PHY_CHANGELINK->PHY_RUNNING(循环)

     

    4.2.网口底层驱动关键过程分析

    4.2.1.phy_driver初始化

         在phy_device.c中,函数phy_init进行相关phy设备驱动的加载注册,具体过程为:

         phy_init()

         ->mdio_bus_init

         ->phy_drivers_register

    phy_init函数主要完成了mdio_bus的初始化工作,注册了mdio_bus_class,注册了mdio_bus_type,其中mdio_bus_type中的mdio_bus_match成员函数规定了phy_device和phy_driver的匹配方式。

    4.2.2. davinci_mdio设备及驱动

    在davinci_mdio.c文件中会进行davinci_mdio_driver的注册,davinci_mdio_driver中的davinci_mdio_of_mtable有compatible字符。

    在系统启动时,会进行设备树文件的解析,将设备树中的设备节点转换为设备结构体,会转换出davinci_mdio相关的设备。davinci_mdio设备及驱动都属于platform_bus。通过比较设备树文件中davinci_mdio节点的compatible属性以及davinci_mdio_driver中的davinci_mdio_of_mtable包含的compatible内容。如果匹配,就会执行davinci_mdio_probe函数。

    4.2.3 davinci_mdio_probe函数分析

    最主要的工作是进行mdio_bus的相关对象内容的填充及mdio_bus_class总线类型的设置。主要是完成MDIO读、写、复位函数的关联。之后通过__mdiobus_register函数中的bus->reset(bus)对CPSW的alive寄存器进行读取,其值标识这MDIO总线检测到“活着”的phy设备。再通过mdiobus_scan对检测到的phy设备进行通过phydev->bus->phy_map[phydev->addr]对其进行管理。并对其分别注册。

    在注册PHY设备时,就会进行与4.2.1小节中进行注册的phy_driver进行匹配,若匹配成功,则执行phy_probe函数。注意,这里可能会检测到多个PHY,也可能会有多个PHY和多个phy_driver的匹配,最终具体使用哪个PHY及phy_driver。在cpsw_probe中连接PHY设备时才会选择,这里只是对“活着”的PHY设备进行驱动的匹配。

    4.2.4 cpsw设备及驱动的匹配

    与davinci_mdio设备类似,cpsw设备及驱动也是属于platform_bus,系统启动时将设备树中的设备节点转换为设备结构体,会转换出cpsw相关的设备。也是使用compatible属性和cpsw_driver进行匹配,匹配成功后就会执行cpsw_probe函数。

    4.2.5 cpsw_probe函数分析

          与网口底层设置及关键的调用如下:


     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    代码路径:drivers etethernet icpsw.c
      cpsw_probe
      --> CPSW平台数据、资源、DMA初始化、赋值netdev_ops、ethtool_ops
      --> 注册网络设备:register_netdev
      --> cpsw_ndo_open (此处根据实际经验得到,目前还没有从源代码级别解答出调用过程)
      --> cpsw_slave_open
          --> phy_connect   (传递cpsw_adjust_link)
          --> bus_find_device_by_name (从mdio总线上查找device)
          --> to_phy_device (从device结构体中找到phy_device)
          --> phy_connect_direct (传递handler,初始化 ***)
              --> phy_attach_direct   ()
                  -->   phydev->attached_dev = dev; (有函数使用到attached_dev指针)
                  --> phydev->state =   PHY_READY; (将PHY状态标记为PHY_READY)
                  --> phy_init_hw (硬件级的初始化,最后会调用genphy_config_init)
              --> phy_prepare_link   (赋值adjust_link为cpsw_adjust_link)
              --> phy_start_machine   (启动PHY状态机 ***)
              --> phy_start_interrupts (经测了,但好像没调用到这里)
      --> phy_start(PHY_READY变成PHY_UP)
      --> 其它的CPSW的初始化

     

     

     

     

     

     

     

     

     

     

     

    phy_connect函数传入的slave->data->phy_id对应设备树文件中的phy_id,slave->data->phy_if对应于设备树文件中的phy-mode = "rmii"。

    这里就会在MDIO总线上“活着”的PHY中找到对应的phydev。phy_attach_direct函数中进行phydev相关成员的初始化,phy_init_hw会调用genphy_config_init进行PHY的初始化配置。phy_prepare_link函数赋值adjust_link为cpsw_adjust_link。最终_cpsw_adjust_link函数会根据link状态及phy的速率及模式等对mac_control寄存器进行赋值。这也是CPSW层重要的配置步骤。    

    之后phy_start_machine开启phy_state_machine。phy_start函数将PHY_READY变成PHY_UP,之后进行phy_state_machine的状态转换。有关phy_state_machine的相关状态变化可以参考相关代码及文档。主要就是根据现有状态及phy_driver的相关config_init、config_aneg、aneg_done、read_status函数进行状态的读取及变更。

    phy_state_machine常规的初始状态为PHY_UP

    在PHY_UP中会将需要自动协商的标志是能,进一步通过phy_start_aneg函数调用phydev->drv->config_aneg(phydev);最终根据自动协商使能与否,转换到状态PHY_AN或PHY_FORCING。

    PHY_AN状态通过read_status函数及aneg_done后的情况转换到状态PHY_RUNNING并且调用adjust_link或PHY_NOLINK

    PHY_FORCING则通过genphy_update_link后的状态决定是否转换状态到PHY_RUNNING。最后,还是会执行adjust_link,进行mac_control的配置。

    在PHY_AN及PHY_FORCING中如果要转换状态到PHY_RUNNING,都会有一步netif_carrier_on(phydev->attached_dev)操作。这里应该理解为打开网络传输的上层操作吧。这个尤为重要。是最终网络传输开始的开关。

    PHY_RUNNING状态下,会根据read_status后的结果是否改变决定是否改变状态为PHY_CHANGELINK,或是维持在PHY_RUNNING状态。

    PHY_CHANGELINK就是会根据据read_status后的结果来判断转换到PHY_RUNNING并且netif_carrier_on或PHY_NOLINK并且netif_carrier_off,

    所以,最重要的是是状态机转换到PHY_RUNNING并且netif_carrier_on,病保持在这一状态。

     

    五、switch芯片的驱动构建

    这里使用类 PHY设备的形式进行SW驱动构建,由于并不是PHY设备,所以在phy_device中的genphy_driver中添加一项VIRPHY_DRV_SW。利用88E6321的PORT5相关寄存器来进行相关状态的读取,并且在这里禁止自动协商。最终的PHY状态及状态变化为PHY_UP->PHY_FORCING->PHY_RUNNING.

    这里需要改变、加打印、及添加的函数为

    Phy.c  //加打印参数

    int phy_start_aneg(struct phy_device *phydev)

    {

           int err;

           printk("phy_start_aneg now ");//test

           mutex_lock(&phydev->lock);

           if (AUTONEG_DISABLE == phydev->autoneg)

                  phy_sanitize_settings(phydev);

    //*******************************************//

    printk(" phydev->speed: 0x%08X ", phydev->speed);         //for test

    printk(" phydev->duplex: 0x%08X ", phydev->duplex);      //

    printk("phydev->supported: 0x%08X ",phydev->supported);    //

    //*******************************************//

           /* Invalidate LP advertising flags */

           phydev->lp_advertising = 0;

           err = phydev->drv->config_aneg(phydev);

           if (err < 0)

                  goto out_unlock;

           if (phydev->state != PHY_HALTED) {

                  if (AUTONEG_ENABLE == phydev->autoneg) {

                         printk("phydev->state = PHY_AN ");//test

                         phydev->state = PHY_AN;

                         phydev->link_timeout = PHY_AN_TIMEOUT;

                  } else {

                          printk("phydev->state = PHY_FORCING ");//test

                         phydev->state = PHY_FORCING;

                         phydev->link_timeout = PHY_FORCE_TIMEOUT;

                  }

           }

    out_unlock:

           mutex_unlock(&phydev->lock);

           return err;

    }

    Phy_device.c //添加

    //////////////////////////////////////////////////////////

    #define MV88E6321_Port_Status_REG             0x0

    #define MV88E6321_Physical_Control_REG            0x1

    #define MV88E6321_Jamming_Control_REG           0x2

    #define MV88E6321_Product_Identifier_REG          0x3

    #define MV88E6321_Port_Control_REG           0x4

    #define MV88E6321_Port_Control_1_REG        0x5

    #define MV88E6321_Port_Based_VLAN_REG          0x6

    #define MV88E6321_Port_VLAN_ID_REG         0x7

    #define MV88E6321_LED_Control_REG  0x16

    #define MV88E6321_internal_phy3_addr         0x03

    #define MV88E6321_internal_phy4_addr        0x04

    #define MV88E6321_internal_serdes0_addr     0x0c

    #define MV88E6321_internal_serdes1_addr    0x0d

    #define MV88E6321_internal_phyid1_REG      0x02

    #define MV88E6321_internal_phyid2_REG      0x03

    /* Basic mode PortStatus REG. */

    #define mv88e6321_LINK_STA      0x0800

    #define mv88e6321_FULL_DPX  0x0400

    #define mv88e6321_SPEED_100M    0x0100

    #define mv88e6321_C_Mode_RMII    0x0004

    ////////////////////////////////////////////////////////////

    添加一个驱动项

    ////////////////////////////////////////////

    enum genphy_driver {

           GENPHY_DRV_1G,

           GENPHY_DRV_10G,

           VIRPHY_DRV_SW,

           GENPHY_DRV_MAX

    };

    //************** add SW driver  **************//

    static int vir_swphy_soft_reset(struct phy_device *phydev)

    {

                  /* Do nothing for now */

                  printk("vir_swphy_soft_reset now ");

                  return 0;

    }

    //************** add SW driver **************//

    static int vir_swphy_soft_reset(struct phy_device *phydev)

    {

                  /* Do nothing for now */

                  printk("vir_swphy_soft_reset now ");

                  return 0;

    }

    I

    int vir_swphy_config_init(struct phy_device *phydev)

    {

           int val;

           u32 features;

           printk("vir_swphy_config_init now ");

           features = (SUPPORTED_TP | SUPPORTED_MII

                         | SUPPORTED_AUI | SUPPORTED_FIBRE |

                         SUPPORTED_BNC);

           /* Do we support autonegotiation? */

           val = phy_read(phydev, MV88E6321_Port_Status_REG);

           printk(" MV88E6321_Port_Status_REG: 0x%04X ", val);//

            if (val < 0)

                  return val;

           if( (val & mv88e6321_FULL_DPX)&&(val & mv88e6321_SPEED_100M))

                  {

                     features |= SUPPORTED_100baseT_Full;

                     printk(" features is SUPPORTED_100baseT_Full ");// 

                  }

           else

                  {

                      features |= SUPPORTED_10baseT_Full;

                      printk(" features is SUPPORTED_10baseT_Full ");// 

                  }

                 

           printk(" features: 0x%08X ", features);//

           phydev->supported &= features;

           phydev->advertising &= features;

          

           phydev->autoneg == AUTONEG_DISABLE;    //不需要自动协商

           return 0;

    }

    int vir_swphy_config_aneg(struct phy_device *phydev)

    {

           printk("vir_swphy_config_aneg ");

           if (AUTONEG_ENABLE != phydev->autoneg)

                  {

                       phydev->pause = 0;

                       phydev->asym_pause = 0;

                        phydev->speed=SPEED_100;

                        phydev->duplex=DUPLEX_FULL;

                   

                  }

           else

                  {

                     phydev->autoneg=AUTONEG_DISABLE;

                   }

                   return 0;

    }

    int vir_swphy_read_status(struct phy_device *phydev)

    {

           int adv;

           int err;

           int lpa;

           int lpagb = 0;

           int common_adv;

           int common_adv_gb = 0;

           printk("vir_swphy_read_status ");

           /* Update the link, but return if there was an error */

           err = genphy_update_link(phydev);

           if (err)

                  return err;

           phydev->lp_advertising = 0;

        if (AUTONEG_ENABLE == phydev->autoneg)

                  {

                       phydev->autoneg=AUTONEG_DISABLE;

                  }

         else

                 {

                  int psr = phy_read(phydev, MV88E6321_Port_Status_REG);

                  printk(" MV88E6321_Port_Status_REG: 0x%08X ", psr);//

                  if (psr < 0)

                         return psr;

                  if (psr & mv88e6321_FULL_DPX)

                         {

                             phydev->duplex = DUPLEX_FULL;

                             printk("phydev->duplex = DUPLEX_FULL ");

                         }

                  else

                         phydev->duplex = DUPLEX_HALF;

                   if (psr & mv88e6321_SPEED_100M)

                          {

                             phydev->speed = SPEED_100;

                             printk(" phydev->speed = SPEED_100 ");

                          }

                  else

                         phydev->speed = SPEED_10;

                  phydev->pause = 0;

                  phydev->asym_pause = 0;

                 }

           return 0;

    }

    ///////////////////////////////////////////////////////////

    修改genphy_update_link函数

    /**

     * genphy_update_link - update link status in @phydev

     * @phydev: target phy_device struct

     *

     * Description: Update the value in phydev->link to reflect the

     *   current link value.  In order to do this, we need to read

     *   the status register twice, keeping the second value.

     */

    /*

    int genphy_update_link(struct phy_device *phydev)

    {

           int status;

           status = phy_read(phydev, MII_BMSR);// Do a fake read

           if (status < 0)

                  return status;

           status = phy_read(phydev, MII_BMSR);// Read link and autonegotiation status

           if (status < 0)

                  return status;

           if ((status & BMSR_LSTATUS) == 0)

                  phydev->link = 0;

           else

                  phydev->link = 1;

           return 0;

    }

    */

    int genphy_update_link(struct phy_device *phydev)

    {

           int status;

           /* Do a fake read */

           status = phy_read(phydev, MV88E6321_Port_Status_REG);

           if (status < 0)

                  return status;

           /* Read link and autonegotiation status */

           status = phy_read(phydev, MV88E6321_Port_Status_REG);

           if (status < 0)

                  return status;

           if ((status &mv88e6321_LINK_STA)&&(status &mv88e6321_C_Mode_RMII)) // linked&&RMII 

                  phydev->link = 1;

           else

                  phydev->link = 0;

                        

           return 0;

    }

    最终在genphy_driver增加一个SW PHY驱动

    static struct phy_driver genphy_driver[] = {

    {

               .phy_id             = 0xffffffff,

               .phy_id_mask    = 0xffffffff,

               .name        = "Generic PHY",

               .soft_reset  = genphy_soft_reset,

               .config_init = genphy_config_init,

               .features    = PHY_GBIT_FEATURES | SUPPORTED_MII |

                           SUPPORTED_AUI | SUPPORTED_FIBRE |

                           SUPPORTED_BNC,

               .config_aneg     = genphy_config_aneg,

               .aneg_done      = genphy_aneg_done,

               .read_status      = genphy_read_status,

               .suspend    = genphy_suspend,

               .resume            = genphy_resume,

               .driver        = { .owner = THIS_MODULE, },

    }, {

               .phy_id         = 0xffffffff,

               .phy_id_mask    = 0xffffffff,

               .name           = "Generic 10G PHY",

               .soft_reset  = gen10g_soft_reset,

               .config_init    = gen10g_config_init,

               .features       = 0,

               .config_aneg    = gen10g_config_aneg,

               .read_status    = gen10g_read_status,

               .suspend        = gen10g_suspend,

               .resume         = gen10g_resume,

               .driver         = {.owner = THIS_MODULE, },

    }, {

               .phy_id             = 0x00003102,

               .phy_id_mask    = 0x0000ffff,

               .name        = "virtual SW PHY",

               .soft_reset  = vir_swphy_soft_reset,

               .config_init = vir_swphy_config_init,

               .features    = PHY_GBIT_FEATURES | SUPPORTED_MII |

                           SUPPORTED_AUI | SUPPORTED_FIBRE |

                           SUPPORTED_BNC,

               .config_aneg     = vir_swphy_config_aneg,

               .read_status      = vir_swphy_read_status,

               .suspend    = vir_swphy_suspend,

               .resume            = vir_swphy_resume,

               .driver        = { .owner = THIS_MODULE, },

    }};

     

    Phy.c    //加打印,为了查看状态变化情况

    void phy_state_machine(struct work_struct *work)

    {

                  struct delayed_work *dwork = to_delayed_work(work);

                  struct phy_device *phydev =

                                container_of(dwork, struct phy_device, state_queue);

                  bool needs_aneg = false, do_suspend = false;

                  enum phy_state old_state;

                  int err = 0;

                  int old_link;

          

                  mutex_lock(&phydev->lock);

          

                  old_state = phydev->state;

          

                  if (phydev->drv->link_change_notify)

                         phydev->drv->link_change_notify(phydev);

          

                  switch (phydev->state) {

                  case PHY_DOWN:

                  case PHY_STARTING:

                  case PHY_READY:

                  case PHY_PENDING:

                         break;

                  case PHY_UP:

                         printk("PHY_UP now ");///////test

                         needs_aneg = true;

          

                         phydev->link_timeout = PHY_AN_TIMEOUT;

          

                         break;

                  case PHY_AN:

                         printk("PHY_AN now ");///////test

                         err = phy_read_status(phydev);

                         if (err < 0)

                                break;

          

                         /* If the link is down, give up on negotiation for now */

                         if (!phydev->link) {

                                phydev->state = PHY_NOLINK;

                                netif_carrier_off(phydev->attached_dev);

                                phydev->adjust_link(phydev->attached_dev);

                                break;

                         }

          

                         /* Check if negotiation is done.  Break if there's an error */

                         err = phy_aneg_done(phydev);

                         if (err < 0)

                                break;

          

                         /* If AN is done, we're running */

                         if (err > 0) {

                                phydev->state = PHY_RUNNING;

                                netif_carrier_on(phydev->attached_dev);

                                phydev->adjust_link(phydev->attached_dev);

          

                         } else if (0 == phydev->link_timeout--)

                                needs_aneg = true;

                         break;

                  case PHY_NOLINK:

                         printk("PHY_NOLINK now ");///////test

                         if (phy_interrupt_is_valid(phydev))

                                break;

          

                         err = phy_read_status(phydev);

                         if (err)

                                break;

          

                         if (phydev->link) {

                                if (AUTONEG_ENABLE == phydev->autoneg) {

                                       err = phy_aneg_done(phydev);

                                       if (err < 0)

                                              break;

          

                                       if (!err) {

                                              phydev->state = PHY_AN;

                                              phydev->link_timeout = PHY_AN_TIMEOUT;

                                              break;

                                       }

                                }

                                phydev->state = PHY_RUNNING;

                                netif_carrier_on(phydev->attached_dev);

                                phydev->adjust_link(phydev->attached_dev);

                         }

                         break;

                  case PHY_FORCING:

                         printk("PHY_FORCING now ");///////test

                         err = genphy_update_link(phydev);

                         if (err)

                                break;

          

                         if (phydev->link) {

                                phydev->state = PHY_RUNNING;

                                printk("GO TO PHY_RUNNING ");///////test

                                netif_carrier_on(phydev->attached_dev);

                         } else {

                                if (0 == phydev->link_timeout--)

                                       needs_aneg = true;

                         }

          

                         phydev->adjust_link(phydev->attached_dev);

                         break;

                  case PHY_RUNNING:

           //           printk("PHY_RUNNING now ");///////test

                         /* Only register a CHANGE if we are polling or ignoring

                          * interrupts and link changed since latest checking.

                          */

                         if (!phy_interrupt_is_valid(phydev)) {

                                old_link = phydev->link;

                                err = phy_read_status(phydev);

                                if (err)

                                       break;

          

                                if (old_link != phydev->link)

                                       phydev->state = PHY_CHANGELINK;

                         }

                         break;

                  case PHY_CHANGELINK:

                         printk("PHY_CHANGELINK now ");///////test

                         err = phy_read_status(phydev);

                         if (err)

                                break;

          

                         if (phydev->link) {

                                phydev->state = PHY_RUNNING;

                                netif_carrier_on(phydev->attached_dev);

                         } else {

                                phydev->state = PHY_NOLINK;

                                netif_carrier_off(phydev->attached_dev);

                         }

          

                         phydev->adjust_link(phydev->attached_dev);

          

                         if (phy_interrupt_is_valid(phydev))

                                err = phy_config_interrupt(phydev,

                                                        PHY_INTERRUPT_ENABLED);

                         break;

                  case PHY_HALTED:

                         printk("PHY_HALTED now ");///////test

                         if (phydev->link) {

                                phydev->link = 0;

                                netif_carrier_off(phydev->attached_dev);

                                phydev->adjust_link(phydev->attached_dev);

                                do_suspend = true;

                         }

                         break;

                  case PHY_RESUMING:

                         printk("PHY_RESUMING now ");///////test

                         if (AUTONEG_ENABLE == phydev->autoneg) {

                                err = phy_aneg_done(phydev);

                                if (err < 0)

                                       break;

          

                                /* err > 0 if AN is done.

                                 * Otherwise, it's 0, and we're  still waiting for AN

                                 */

                                if (err > 0) {

                                       err = phy_read_status(phydev);

                                       if (err)

                                              break;

          

                                       if (phydev->link) {

                                              phydev->state = PHY_RUNNING;

                                              netif_carrier_on(phydev->attached_dev);

                                       } else      {

                                              phydev->state = PHY_NOLINK;

                                       }

                                       phydev->adjust_link(phydev->attached_dev);

                                } else {

                                       phydev->state = PHY_AN;

                                       phydev->link_timeout = PHY_AN_TIMEOUT;

                                }

                         } else {

                                err = phy_read_status(phydev);

                                if (err)

                                       break;

          

                                if (phydev->link) {

                                       phydev->state = PHY_RUNNING;

                                       netif_carrier_on(phydev->attached_dev);

                                } else      {

                                       phydev->state = PHY_NOLINK;

                                }

                                phydev->adjust_link(phydev->attached_dev);

                         }

                         break;

                  }

          

                  mutex_unlock(&phydev->lock);

          

                  if (needs_aneg)

                         err = phy_start_aneg(phydev);

                  else if (do_suspend)

                         phy_suspend(phydev);

          

                  if (err < 0)

                         phy_error(phydev);

          

                  dev_dbg(&phydev->dev, "PHY state change %s -> %s ",

                         phy_state_to_str(old_state), phy_state_to_str(phydev->state));

          

                  queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,

                                   PHY_STATE_TIME * HZ);

           }

  • 相关阅读:
    使用python将文字写入word文档中
    将图片显示到excel中
    新的写入xlsxwriter和追加写入openpyxl
    oracle 12.2 alter table move online
    主从复制管理和故障处理方法
    MySQL中的权限管理
    windows的CMD如何全屏最大化
    Troubleshooting query v$asm_disk v$asm_diskgroup hang
    library cache锁争用解决
    一则由ORA-1652引起的fixed object相关问题
  • 原文地址:https://www.cnblogs.com/lh03061238/p/10829768.html
Copyright © 2020-2023  润新知