一、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, }, } };

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

  1. 深入分析websocket协议,从3个方面设计网络应用层协议丨网络编程|网络IO|epoll|socket|网络协议丨c/c++linux服务器开发

    深入分析websocket协议,从3个方面设计网络应用层协议 视频讲解如下: 深入分析websocket协议,从3个方面设计网络应用层协议丨网络编程|网络IO|epoll|socket|网络协议丨c/ ...

  2. STM32MP157系列教程连载-Linux应用开发篇1:STM32MP1微处理器之Ubuntu安装与体验

    STM32MP157系列教程连载-Linux应用开发篇1:STM32MP1微处理器之Ubuntu安装与体验 截至目前上传的博文已经有6篇了(硬件4篇,安装环境2篇),最近手头在搞STM32MP157C ...

  3. linux 内核驱动的poll,嵌入式Linux驱动开发(五)——poll机制原理以及驱动实现...

    前情回顾: 再开始今天的内容之前,先简单review一下,我们都用了什么方案来获取按键值,他们的特点都是什么.只有不断地理清了思路,我们才能够更好的理解,为何会出现如此多的解决方案,当遇到问题的时候, ...

  4. WIFI驱动配置实战(Linux驱动开发篇)

    1.对象 正点原子的imx6ull-mini开发板的USB-HOST接口 芯片RTL8188EUS/CUS USB WIFI 2.目的 单片机经过wifi芯片可以连接热点. WIFI驱动不需要我们编写 ...

  5. platform平台驱动模型简述(linux驱动开发篇)

    此篇是驱动分离(总线.驱动和设备模型)的应用扩展,主要简述platform虚拟总线平台 一个现实的Linux设备和驱动通常挂接在一种总线上,对于本身依附于PCI.USB.I2C.SPI等的设备而言,这 ...

  6. linux内核驱动ldd3_手把手教Linux驱动7内核互斥锁

    互斥体概述 信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,mutex用于互斥操作.信号量的count初始化为1,down()/up()也可以实现类似mutex的作用. mutex ...

  7. linux编译CH340驱动报错,CH340 Linux驱动使用教程

    CH340 Linux驱动使用教程 在官方Linux内核版本中自Kernel2.6以后就默认包含了对CH340/CH341芯片的驱动支持了,但比较遗憾的是该自带驱动版本较老(由开源社区开发者提交)已不 ...

  8. 从零开始之驱动发开、linux驱动(六十七、内核调试篇--printk使用)

    printk的使用我们在内核,驱动调试的时候使用的非常多 比如前面在调试usb驱动的时候 #include <linux/init.h> #include <linux/usb/in ...

  9. linux sd卡驱动视频,详解linux 驱动编写(sd卡驱动)

    随着sd卡的流行,sd卡在嵌入式设备上使用的场景也越来越多.那下面我们可以看一下,linux驱动框架上是怎么处理sd卡驱动的? 1.代码目录地址 drivers/mmc 2.基本结构 从mmc的代码结 ...

最新文章

  1. “算法战:DARPA下一代人工智能计划初见成效” 背景分析与初步研判
  2. IDEA spring boot maven架包
  3. 进程初识和multiprocessing模块之Process
  4. 计算机课四年级说课稿,小学信息技术说课稿
  5. 《数据中台实战》:用户留存分析
  6. Python3 函数(方法)
  7. 将Chrome设置为Jupyter_notebook的默认浏览器
  8. Java Set集合
  9. 用C语言创建多个用户,实现支持多用户在线的FTP程序(C/S)
  10. ie7浏览器传输中文的问题
  11. jquerymobile在手机上很小_手机如何建立自己的网站
  12. 年过20载,超1000万人在用,还说要被淘汰?
  13. python主函数怎么写_类中的Python主函数
  14. 嵌入式软件设计中的哲学思想
  15. PICKIT3 programmer下载地址
  16. 风寒感冒和风热感冒的药膳方
  17. uni 获取本地文件_uni-app 图片(文件) 本地存储解决方案
  18. 罗永浩“卖艺”还债:所有命运馈赠的礼物,都早已在暗中标好了价格
  19. ibm大型机服务器虚拟化,PowerVM:IBM小型机虚拟化的利器
  20. 《财务共享服务》读书笔记

热门文章

  1. js鼠标拖动元素移动
  2. 快递100物流查询接口的实现
  3. 行测-判断推理-图形推理-样式规律-数量规律-素数量-部分数特征
  4. 大年初七,小灰在西双版纳看人妖
  5. 纯shader实现雷达扫描效果(three.js实战13)
  6. 如何彻底卸载Office办公软件?
  7. 上品折扣连续三年初一闭店 人性化管理赢得称赞
  8. android 11.0添加开机铃声
  9. 斯坦福图机器学习CS224W笔记自用:Heterogeeneous Graphs and Knowledge Graph Embeddings
  10. 利用yum安装卸载软件常用命令