网络驱动简介==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寄存器地址02.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, },
} };