网络驱动简介==PHY子系统(linux驱动开发篇)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、PHY芯片简介

  • PHY是IEEE 802.3规定的一个标准模块

  • SOC可以对PHY 进行配置或者读取PHY 相关状态这个就需要 PHY 内部寄存器去实现了。

  • PHY 芯片寄存器地址空间为 5位支持访问32个寄存器).IEEE 定义了0~15这 16个寄存器的功能16~31这16 个寄存器由厂商自行实现。

  • 也就是说不管你用的哪个厂家的 PHY 芯片其中 0~15 这 16 个寄存器是一模一样的。仅靠这16个寄存器是完全可以驱动起 PHY 芯片的至少能保证基本的网络数据通信因此 Linux 内核有通用 PHY 驱动

  • 前16个寄存器
    在这里插入图片描述
    PHY芯片LAN8720A

  • LAN8720A功能框图如图
    在这里插入图片描述

  • 具体实际连接结构图
    在这里插入图片描述

  • 内部寄存器
    1.BCR寄存器地址0

    2.BSR寄存器地址1PHY 的状态寄存器通过此寄存器可以获取到 PHY芯片的工作状态

    3.LAN8720A的PHY ID寄存器 1和 ID寄存器2地址为2和 3

二、PHY子系统简介

  • PHY子系统就是用于PHY 设备相关内容的分为 PHY 设备和PHY驱动和 platform总线一样** PHY 子系统也是一个设备、总线和驱动模型**

1、PHY设备

/*
@	phy_device 结构体 
@	定义在 include/linux/phy.h
*/
struct phy_device{
    /* Information about the PHY type */ 
    /* And management functions */ 
    struct phy_driver *drv;     /* PHY 设备驱动     */ 
    struct mii_bus *bus;         /* 对应的 MII 总线   */ 
    struct device dev;            /* 设备文件       */ 
    u32 phy_id;                   /* PHY ID       */ 
  
    struct phy_c45_device_ids c45_ids; 
    bool is_c45;                 
    bool is_internal; 
    bool has_fixups; 
    bool suspended; 
  
    enum phy_state state;        /* PHY 状态   */ 
    u32 dev_flags; 
    phy_interface_t interface;  /* PHY 接口   */ 
  
    /* Bus address of the PHY (0-31) */ 
    int addr;                     /* PHY 地址(0~31) */ 
  
    /* 
      * forced speed & duplex (no autoneg) 
      * partner speed & duplex & pause (autoneg) 
      */ 
    int speed;                    /* 速度     */ 
    int duplex;                    /* 双共模式     */ 
    int pause;                   
    int asym_pause; 
  
    /* The most recently read link state */ 
    int link; 
  
    /* Enabled Interrupts */ 
    u32 interrupts;               /* 中断使能标志 */ 
  
    /* Union of PHY and Attached devices' supported modes */ 
    /* See mii.h for more info */ 
    u32 supported; 
    u32 advertising; 
    u32 lp_advertising; 
    int autoneg; 
    int link_timeout;
     /* 
      * Interrupt number for this PHY 
      * -1 means no interrupt 
      */ 
    int irq;                      /* 中断号     */ 
  
    /* private data pointer */ 
    /* For use by PHYs to maintain extra state */ 
    void *priv;                 /* 私有数据 */ 
  
    /* Interrupt and Polling infrastructure */ 
    struct work_struct phy_queue; 
    struct delayed_work state_queue; 
    atomic_t irq_disable; 
    struct mutex lock; 
    struct net_device *attached_dev;    /* PHY 芯片对应的网络设备 */ 
    void (*adjust_link)(struct net_device *dev); 
 }; 

/*一个 PHY 设备对应一个 phy_device 实例然后需要向 Linux 内核注册这个实例*/

/*
@	向 Linux 内核注册这个phy_device 实例
@	phy需要注册的 PHY 设备
@	返回值0 成功负值 失败。
*/
int phy_device_register(struct phy_device *phy) 

/*
@	调用get_phy_device函数获取PHY设备
*/
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr,  bool is_c45) 
 { 
    struct phy_c45_device_ids c45_ids = {0}; 
    u32 phy_id = 0; 
    int r; 
  
    r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); /*获取PHY_ID==就是PHY的ID寄存器*/
    if (r) 
          return ERR_PTR(r); 
  
    /* If the phy_id is mostly Fs, there is no device there */ 
    if ((phy_id & 0x1fffffff) == 0x1fffffff) 
        return NULL; 
  
    return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);/*创建 phy_device*/ 
 } 

2、PHY驱动

  • 编写 PHY 驱动的主要工作就是实现这些函数
/*
@	phy_driver 结构体 
@	定义在include/linux/phy.h文件中
*/
 struct phy_driver { 
    u32 phy_id;            /* PHY ID     */ 
    char *name; 
    unsigned int phy_id_mask;    /* PHY ID 掩码 */ 
    u32 features; 
    u32 flags; 
    const void *driver_data; 
  
    int (*soft_reset)(struct phy_device *phydev); 
    int (*config_init)(struct phy_device *phydev); 
    int (*probe)(struct phy_device *phydev); 
    int (*suspend)(struct phy_device *phydev); 
    int (*resume)(struct phy_device *phydev); 
    int (*config_aneg)(struct phy_device *phydev); 
    int (*aneg_done)(struct phy_device *phydev); 
    int (*read_status)(struct phy_device *phydev); 
    int (*ack_interrupt)(struct phy_device *phydev); 
    int (*config_intr)(struct phy_device *phydev); 
    int (*did_interrupt)(struct phy_device *phydev); 
    void (*remove)(struct phy_device *phydev); 
    int (*match_phy_device)(struct phy_device *phydev); 
    int (*ts_info)(struct phy_device *phydev,  struct ethtool_ts_info *ti); 
    int  (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr); 
    bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb,  int type); 
    void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb,  int type); 
    int (*set_wol)(struct phy_device *dev,  
struct ethtool_wolinfo *wol); 
    void (*get_wol)(struct phy_device *dev,  
struct ethtool_wolinfo *wol); 
    void (*link_change_notify)(struct phy_device *dev); 
    int (*read_mmd_indirect)(struct phy_device *dev, int ptrad, 
    int devnum, int regnum); 
    void (*write_mmd_indirect)(struct phy_device *dev, int ptrad, int devnum, int regnum, u32 val); 
    int (*module_info)(struct phy_device *dev, 
struct ethtool_modinfo *modinfo); 
    int (*module_eeprom)(struct phy_device *dev, 
struct ethtool_eeprom *ee, u8 *data); 
  
 	 struct device_driver driver; 
 };  

/*
@	phy_driver 结构体初始化完成以后就需要向 Linux 内核注册
@	new_driver需要注册的PHY驱动
@	返回值0 成功负值 失败
*/  
int phy_driver_register(struct phy_driver *new_driver)  
/*
@	连续注册多个 PHY驱动
@	new_driver需要注册的多个 PHY驱动数组
@	n要注册的驱动数量。
@	返回值0 成功负值 失败
*/
int phy_drivers_register(struct phy_driver *new_driver, int n) 

/*
@	卸载PHY驱动 
@	new_driver需要卸载的PHY驱动
@	返回值无
*/
void phy_driver_unregister(struct phy_driver *drv)

3、MDIO总线

  • PHY 子系统也是遵循设备、总线、驱动模型的设备和驱动就是 phy_device和phy_driver。总线就是 MDIO 总线
  • 因为PHY 芯片是通过 MIDO 接口来管理的MDIO总线最
    主要的工作就是匹配 PHY 设备和 PHY 驱动
/*
@	文件 drivers/net/phy/mdio_bus.c 中
@	mdio 总线 
*/
 struct bus_type mdio_bus_type = { 
     .name       = "mdio_bus", 
     .match      = mdio_bus_match, /*总线匹配函数*/
     .pm     = MDIO_BUS_PM_OPS, 
     .dev_groups = mdio_dev_groups, 
 }; 
/*
@	 mdio_bus_match 匹配函数 
*/
static int mdio_bus_match(struct device *dev, struct device_driver *drv) 
 { 
    struct phy_device *phydev = to_phy_device(dev); 
    struct phy_driver *phydrv = to_phy_driver(drv); 
  
  	/*检查 compatible 属性值与匹配表 of_match_table 里面的内容是否一致*/
    if (of_driver_match_device(dev, drv)) /*设备树方式匹配*/
          return 1; 
          
  	/*有没有提供匹配函数 match_phy_device如果有的话就直接调用 PHY 驱动提供的匹配函数完成与设备的匹配*/
    if (phydrv->match_phy_device) 
        return phydrv->match_phy_device(phydev); 
  
  	/*对比 PHY 驱动和 PHY 设备中的 phy_id 是否一致*/
    return (phydrv->phy_id & phydrv->phy_id_mask) == 
        (phydev->phy_id & phydrv->phy_id_mask); 
 } 
  • 如果 PHY 设备和 PHY 驱动匹配那么就使用指定的 PHY 驱动如果不匹配的话就使用Linux内核自带的通用 PHY 驱动

**三、通用PHY驱动 **

  • 前面多次提到Linux内核已经集成了通用PHY驱动通用PHY驱动名字为“Generic PHY”,驱动文件位drivers/net/phy/phy_device.c
/*
@	phy_init 函数====phy_init 是整个 PHY 子系统的入口函数
*/
static int __init phy_init(void) 
 { 
    int rc; 
  
    rc = mdio_bus_init(); 
    if (rc) 
          return rc; 
  	
  	/*genphy_driver也就是通用 PHY 驱动也就是说 Linux 系统启动以后默认就已经存在了通用 PHY 驱动*/
    rc = phy_drivers_register(genphy_driver, /*向内核直接注册一个通用 PHY 驱动*/
                ARRAY_SIZE(genphy_driver)); /*genphy_driver 是一个数组有两个数组元素表示有两个通用的 PHY 驱动*/
    if (rc) 
        mdio_bus_exit(); 
    return rc; 
 } 
 /*
 @	通用 PHY 驱动 ===== genphy_driver定义在drivers/net/phy/phy_device.c
 */
  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, }, 
 } }; 
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux