PHY设备驱动分析
声明:
使用其他作者原创资料较多就不一一列举,冒犯之处望海涵。我原创也不少咯。
MAC和PHY可能是集成在CPU中可能独立,下图介绍典型的MAC集成,PHY独立。两者搭配实现网卡功能。
struct phy_device {
struct phy_driver *drv; //PHY设备驱动
struct mii_bus *bus; //对应的MII总线
struct device dev; //设备文件
u32 phy_id; //PHY ID
enum phy_state state; //PHY状态
u32 dev_flags;
phy_interface_t interface; //PHY接口
int addr; //PHY 总线地址(0~31)
int speed; //速度
int duplex; //双工模式
int pause; //停止
int asym_pause; //
int link;
u32 interrupts; //中断使能标志
u32 supported;
u32 advertising;
int autoneg;
int link_timeout; //026
int irq; //中断号
void *priv; //私有数据
struct work_struct phy_queue; //PHY工作队列
struct delayed_work state_queue; //PHY延时工作队列
atomic_t irq_disable;
struct mutex lock;
struct net_device *attached_dev; //网络设备
void (*adjust_link)(struct net_device *dev);
void (*adjust_state)(struct net_device *dev);
};
struct phy_driver {
u32 phy_id; //PHY ID
char *name; //PHY名
unsigned int phy_id_mask;
u32 features; //特性
u32 flags; //标记
int (*config_init)(struct phy_device *phydev); //配置初始化
int (*probe)(struct phy_device *phydev); //探测到 probe方法
int (*suspend)(struct phy_device *phydev); //唤醒
int (*resume)(struct phy_device *phydev); //挂起
int (*config_aneg)(struct phy_device *phydev); //支援(Auto-negotiation)配置
int (*read_status)(struct phy_device *phydev); //读支援(Auto-negotiation)状态
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 (*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); //发送时间戳
struct device_driver driver; //设备驱动文件
};
enum phy_state {
PHY_DOWN = 0, // PHY芯片和驱动没准备好,一般情况下少发生
PHY_STARTING, // PHY芯片OK了,但驱动还没有准备好
PHY_READY, // 准备好了,在probe中赋值,接下来会切到PHY_UP
PHY_PENDING,
PHY_UP, // phy启动了,可以工作了,接下来会到PHY_AN
PHY_AN, // 自动协商
PHY_RUNNING, // 正在运行中,在网络连接(插上网线)时会到这个状态
PHY_NOLINK, // 断网了
PHY_FORCING, // 强制,当自动协商不使能时,就会进行此状态(实际上会读PHY寄存器进行设置速率、双工,等)
PHY_CHANGELINK, // 变化,这个状态很重要,当连接时,会换到PHY_RUNNING,当断网时,会切到PHY_NOLINK
PHY_HALTED,
PHY_RESUMING
};
Mdio设备注册到总线
文件路径:drivers\net\ethernet\ti\davinci_mdio.c
device_initcall(davinci_mdio_init);
–> davinci_mdio_init
–>platform_driver_register(&davinci_mdio_driver);
davinci_mdio_probe
–> mdiobus_regist
Phy设备初始化:
Linux内核通过mdio总线访问、控制PHY , phy_device的注册不依靠设备树(根据上面驱动的名字在设备树中搜不到),该设备的注册在MDIO驱动中调用mdiobus_register中会注册phy_device
文件路径:drivers\net\phy\mdio_bus.c
–> mdiobus_register /* register the mii bus */
–> device_register
–> mdiobus_scan /这个过程就是根据phy id通过mdio的mdiobus_read去读phy芯片的寄存器来检测改id下的phy设备是否存在,如果存在就把该设备使用phy_device_register注册/
–> get_phy_device
–> get_phy_id // 读寄存器
–> phy_device_create // 创建phy设备,创建phy设备时会设置phy的接口,例如PHY_INTERFACE_MODE_GMII,以及从总线上扫描到的设备的信息赋值给phy设备
–> INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); // !!!初始化状态机函数
–> phy_device_register
在phy_device_create 时会使用mdio_bus_type这个结构体,里面有个匹配函数具体如下
/**
* mdio_bus_match - determine if given PHY driver supports the given PHY device
* @dev: target PHY device
* @drv: given PHY driver
*
* Description: Given a PHY device, and a PHY driver, return 1 if
* the driver supports the device. Otherwise, return 0.
*/
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);
return ((phydrv->phy_id & phydrv->phy_id_mask) ==
(phydev->phy_id & phydrv->phy_id_mask));
}
Phy驱动注册:
nt phy_driver_register(struct phy_driver *new_driver)
{
int retval;
new_driver->driver.name = new_driver->name;
new_driver->driver.bus = &a