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

一、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寄存器(地址1):PHY 的状态寄存器,通过此寄存器可以获取到 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, }, 
 } }; 
  • 2
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值