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

Posted lh03061238

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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\net\ethernet\ti\cpsw.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\n");//test

 

       mutex_lock(&phydev->lock);

 

       if (AUTONEG_DISABLE == phydev->autoneg)

              phy_sanitize_settings(phydev);

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

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

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

printk("phydev->supported: 0x%08X\n ",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\n");//test

                     phydev->state = PHY_AN;

                     phydev->link_timeout = PHY_AN_TIMEOUT;

              } else {

                      printk("phydev->state = PHY_FORCING\n");//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\n");

              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\n");

              return 0;

}

I

 

int vir_swphy_config_init(struct phy_device *phydev)

{

       int val;

       u32 features;

       printk("vir_swphy_config_init now\n");

 

       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\n ", 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\n ");// 

              }

       else

              {

                  features |= SUPPORTED_10baseT_Full;

                  printk(" features is SUPPORTED_10baseT_Full\n ");// 

              }

             

       printk(" features: 0x%08X\n ", 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\n");

       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\n");

 

       /* 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\n ", psr);//

              if (psr < 0)

                     return psr;

 

              if (psr & mv88e6321_FULL_DPX)

                     {

                         phydev->duplex = DUPLEX_FULL;

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

                     }

              else

                     phydev->duplex = DUPLEX_HALF;

 

               if (psr & mv88e6321_SPEED_100M)

                      {

                         phydev->speed = SPEED_100;

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

                      }

              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\n");///////test

                     needs_aneg = true;

      

                     phydev->link_timeout = PHY_AN_TIMEOUT;

      

                     break;

              case PHY_AN:

                     printk("PHY_AN now\n");///////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\n");///////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\n");///////test

                     err = genphy_update_link(phydev);

                     if (err)

                            break;

      

                     if (phydev->link) {

                            phydev->state = PHY_RUNNING;

                            printk("GO TO PHY_RUNNING\n");///////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\n");///////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\n");///////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\n");///////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\n");///////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\n",

                     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);

       }

 

以上是关于基于335X平台Linux交换芯片驱动开发的主要内容,如果未能解决你的问题,请参考以下文章

基于335X的Linux网口驱动分析

Linux——Linux驱动之iMX6ULL平台下多点触摸屏驱动开发实战(MT协议多点触摸API基于框架的触摸驱动编写触摸芯片驱动)

Linux——Linux驱动之iMX6ULL平台下多点触摸屏驱动开发实战(MT协议多点触摸API基于框架的触摸驱动编写触摸芯片驱动)

Linux——Linux驱动之iMX6ULL平台下多点触摸屏驱动开发实战(MT协议多点触摸API基于框架的触摸驱动编写触摸芯片驱动)

RK3399驱动开发 | 21 -Marvell交换机芯片88E6390X调试(基于linux主线5.4.32内核)

RK3399驱动开发 | 21 -Marvell交换机芯片88E6390X调试(基于linux主线5.4.32内核)