irpas技术客

网络驱动简介==PHY子系统(linux驱动开发篇)_栋哥修炼日记_linux phy驱动

网络 4318

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


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Linux #phy驱动 #进行配置或者读取PHY #相关状态这个就需要 #phy #内部寄存器去实现了