PHY设备驱动是基于device、driver、bus的连接方式,驱动涉及如下几个重要部分: 总线 - sturct mii_bus (mii stand for media independent interface) 设备 - struct phy_device 驱动 - struct phy_driver。PHY驱动函数驱动功能:
函数名称 功能描述
soft_reset 执行 phy 的软件复位
config_init 在 phy 复位后将 phy 配置为一个既定的状态
probe 创建 phy->priv 并执行类似驱动绑定的过程
suspend/resume 电源管理挂起与恢复功能
config_aneq 修改速率双工模式自协商等配置
aneq_done 驱动自协商的结果
read_status 获取当前的速率双工与自协商配置状态
ack_interrupt 清楚挂起的中断
did_interrupt 检查是否 phy 触发了一个中断信号
config_intr 开启、关闭中断
remove 当驱动被移除时调用的接口
ts_info 请求查询 HW 时间戳状态
hwtstap 设置 phy 的 HW时间戳配置
rxtstamp 在 phy 这一层为 skb 请求一个接收时间戳
txtsamp 在 phy 这一层为 skd 请求一个发送时间戳
set_wol 使能 Wake-on-Lan
get_wol 获取 Wake-on-Lan 状态
read_mmd_indirect 读取 phy MMD 间接寄存器
write_mmd_indirect 写入 phy MMD 间接寄存器
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);
};
PHY驱动
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; //设备驱动文件
};
二,设备和驱动的注册函数
每个驱动都有独立的操作函数来对 phy 进行操作,也就是说网卡会直接调用类似 phy_ops 中的虚函数来使用 phy 的功能。而对于这种 phy 驱动而言,它与网卡驱动是独立的,将网卡驱动与 phy 驱动关联起来步骤:
PHY驱动的注册:在phy_init函数中不仅注册了mdio_bus总线,还注册了一个通用的PHY驱动作为缺省的内核PHY驱动,但是如果PHY芯片的内部寄存器和802.3定义的并不一样或者需要特殊的功能配置以实现更强的功能,这就需要专有的驱动。
注册PHY设备
int phy_device_register(struct phy_device *phydev)
{
int err;
if (phydev->bus->phy_map[phydev->addr]) //判断PHY是否已经给注册了
return -EINVAL;
phydev->bus->phy_map[phydev->addr] = phydev; //添加PHY到总线的phy_map里
phy_scan_fixups(phydev); //执行匹配的fixups
err = device_register(&phydev->dev); //注册设备
if (err) {
pr_err("phy %d failed to register\n", phydev->addr);
goto out;
}
return 0;
out:
phydev->bus->phy_map[phydev->addr] = NULL;
return err;
}
EXPORT_SYMBOL(phy_device_register);
PHY设备一般是动态注册的,在注册之前一般会调用get_phy_device函数
struct phy_device * get_phy_device(struct mii_bus *bus, int addr)
{
struct phy_device *dev = NULL;
u32 phy_id;
int r;
r = get_phy_id(bus, addr, &phy_id); //1 获取PHY ID
if (r)
return ERR_PTR(r);
if ((phy_id & 0x1fffffff) == 0x1fffffff)
return NULL;
if ((phy_id & 0x1fffffff) == 0x0000ffff)//add by cyj, support marvel phy
return NULL;
dev = phy_device_create(bus, addr, phy_id); //2 创建PHY设备
return dev;
}
EXPORT_SYMBOL(get_phy_device);
获取PHY ID
int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id)
{
int phy_reg;
//调用PHY的总线也就是mii总线的读方法获取PHY ID
phy_reg = bus->read(bus, addr, MII_PHYSID1); //获取PHYS ID1命令
if (phy_reg < 0)
return -EIO;
*phy_id = (phy_reg & 0xffff) << 16;
phy_reg = bus->read(bus, addr, MII_PHYSID2); //获取PHYS ID1命令
if (phy_reg < 0)
return -EIO;
*phy_id |= (phy_reg & 0xffff);
return 0;
}
EXPORT_SYMBOL(get_phy_id);
创建PHY设备
static struct phy_device* phy_device_create(struct mii_bus *bus,int addr, int phy_id)
{
struct phy_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配phy设备内存
if (NULL == dev)
return (struct phy_device*) PTR_ERR((void*)-ENOMEM);
dev->dev.release = phy_device_release;
dev->speed = 0; //速度
dev->duplex = -1; //双工模式
dev->pause = dev->asym_pause = 0;
dev->link = 1;
dev->interface = PHY_INTERFACE_MODE_GMII; //接口模式GMII
dev->autoneg = AUTONEG_ENABLE; //自动使能
dev->addr = addr; //地址
dev->phy_id = phy_id; //PHY ID
dev->bus = bus; //mii总线
dev->dev.parent = bus->parent; //父设备
dev->dev.bus = &mdio_bus_type; //总线类型
dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; //中断/轮询
dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); //PHY 设备文件名
dev->state = PHY_DOWN; //状态DOWN
mutex_init(&dev->lock);
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); //初始化PHY状态机
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
return dev;
}
注册PHY驱动
int phy_driver_register(struct phy_driver *new_driver)
{
int retval;
new_driver->driver.name = new_driver->name; //驱动名
new_driver->driver.bus = &mdio_bus_type; //总线类型
new_driver->driver.probe = phy_probe; //探测函数
new_driver->driver.remove = phy_remove; //移除函数
retval = driver_register(&new_driver->driver); //注册设备驱动
if (retval) {
printk(KERN_ERR "%s: Error %d in registering driver\n",new_driver->name, retval);
return retval;
}
pr_debug("%s: Registered new driver\n", new_driver->name);
return 0;
}
EXPORT_SYMBOL(phy_driver_register);
通过MDIO总线匹配
phy设备和驱动的总线类型都是mdio_bus_type
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match, //匹配方法
.pm = MDIO_BUS_PM_OPS,
};
EXPORT_SYMBOL(mdio_bus_type);
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct phy_device *phydev = to_phy_device(dev); //获取PHY设备
struct phy_driver *phydrv = to_phy_driver(drv); //获取PHY驱动
return ((phydrv->phy_id & phydrv->phy_id_mask) ==(phydev->phy_id & phydrv->phy_id_mask)); //比较phy_id
}
匹配成功就会调用phy驱动的probe方法,也即是phy_probe
static int phy_probe(struct device *dev)
{
struct phy_device *phydev;
struct phy_driver *phydrv;
struct device_driver *drv;
int err = 0;
phydev = to_phy_device(dev); //获取PHY设备
drv = get_driver(phydev->dev.driver);
phydrv = to_phy_driver(drv); //获取PHY驱动
phydev->drv = phydrv; //捆绑一下
if (!(phydrv->flags & PHY_HAS_INTERRUPT)) //设置中断方式
phydev->irq = PHY_POLL;
mutex_lock(&phydev->lock);
phydev->supported = phydrv->features; //设置PHY设备特性
phydev->advertising = phydrv->features; //设置PHY设备特性
phydev->state = PHY_READY; //状态设置为"准备好"
if (phydev->drv->probe) //如果驱动有probe方法
err = phydev->drv->probe(phydev); //则调用
mutex_unlock(&phydev->lock);
return err;
}
三,初始化流程
主函数主要初始化mdio总线、注册通用的phy设备驱动
static int __init phy_init(void)
{
int rc;
rc = mdio_bus_init(); //初始化mdio总线
if (rc)
return rc;
rc = phy_driver_register(&genphy_driver); //注册通用的PHY设备驱动
if (rc)
mdio_bus_exit();
return rc;
}
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.config_init = genphy_config_init, //初始化函数
.features = 0,
.config_aneg = genphy_config_aneg, //配置自协商(Auto-negotiation)
.read_status = genphy_read_status, //读状态
.suspend = genphy_suspend,
.resume = genphy_resume,
.driver = {.owner= THIS_MODULE, },
};
genphy_driver驱动中有初始化配置方法,配置速度、半双工/全双工、自协商模式等。
static int genphy_config_init(struct phy_device *phydev)
{
int val;
u32 features;
//默认支持特性
features = (SUPPORTED_TP | SUPPORTED_MII| SUPPORTED_AUI | SUPPORTED_FIBRE |SUPPORTED_BNC);
val = phy_read(phydev, MII_BMSR); //读基础状态
if (val < 0)
return val;
if (val & BMSR_ANEGCAPABLE) //支持(auto-negotiation)
features |= SUPPORTED_Autoneg;
if (val & BMSR_100FULL) //100兆全双工
features |= SUPPORTED_100baseT_Full;
if (val & BMSR_100HALF) //100兆半双工
features |= SUPPORTED_100baseT_Half;
if (val & BMSR_10FULL) //10兆全双工
features |= SUPPORTED_10baseT_Full;
if (val & BMSR_10HALF) //10兆半双工
features |= SUPPORTED_10baseT_Half;
if (val & BMSR_ESTATEN) {
val = phy_read(phydev, MII_ESTATUS); //读扩展状态
if (val < 0)
return val;
if (val & ESTATUS_1000_TFULL) //1000兆全双工
features |= SUPPORTED_1000baseT_Full;
if (val & ESTATUS_1000_THALF) //1000兆半双工
features |= SUPPORTED_1000baseT_Half;
}
phydev->supported = features; //PHY特性
phydev->advertising = features;
return 0;
}
*phy_start_aneg_priv-启动此phy设备的自动协商
*@phydev:phy_device结构
*@sync:表示是否等待工作队列取消
描述:对设置进行Sanitize(如果我们没有自动协商它们),然后调用驱动程序的config_aeg函数。如果PHYCONTROL层正在运行,我们会更改状态以反映自动协商或强制的开始
static int phy_start_aneg_priv(struct phy_device *phydev, bool sync)
{
bool trigger = 0;
int err;
if (!phydev->drv)
return -EIO;
mutex_lock(&phydev->lock);
printk("111111111111\n");
if (AUTONEG_DISABLE == phydev->autoneg)
phy_sanitize_settings(phydev);
printk("~~phydev->autoneg~~~~~~~~~~\n",phydev->autoneg);
/* Invalidate LP advertising flags */
phydev->lp_advertising = 0;
printk("222222222\n");
err = phydev->drv->config_aneg(phydev);
printk("err=%d\n",err );
if (err < 0)
goto out_unlock;
printk("333333333\n");
if (phydev->state != PHY_HALTED) {
if (AUTONEG_ENABLE == phydev->autoneg) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
} else {
phydev->state = PHY_FORCING;
phydev->link_timeout = PHY_FORCE_TIMEOUT;
}
}
/* Re-schedule a PHY state machine to check PHY status because
* negotiation may already be done and aneg interrupt may not be
* generated.
*/
if (phy_interrupt_is_valid(phydev) && (phydev->state == PHY_AN)) {
err = phy_aneg_done(phydev);
if (err > 0) {
trigger = true;
err = 0;
}
}
out_unlock:
mutex_unlock(&phydev->lock);
if (trigger)
phy_trigger_machine(phydev, sync);
return err;
}
四,状态类型
enum phy_state {
PHY_DOWN=0,
PHY_STARTING, //开始
PHY_READY, //准备好
PHY_PENDING, //挂起
PHY_UP, //开启
PHY_AN, //判断连接状态中 negotiating
PHY_RUNNING, //运行
PHY_NOLINK, //开启 未连接
PHY_FORCING, //设置中
PHY_CHANGELINK, //连接状态改变
PHY_HALTED, //停止
PHY_RESUMING //唤醒
};
状态机phy_state_machine
在phy_device_create函数中,开启了状态机
void phy_state_machine(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct phy_device *phydev = container_of(dwork, struct phy_device, state_queue);
int needs_aneg = 0;
int err = 0;
mutex_lock(&phydev->lock);
if (phydev->adjust_state)
phydev->adjust_state(phydev->attached_dev);
switch(phydev->state) {
case PHY_DOWN: //关闭((ifconfig eth0 down)
case PHY_STARTING: //开始
case PHY_READY: //准备好
case PHY_PENDING: //挂起
break;
case PHY_UP: //开启(ifconfig eth0 up)
needs_aneg = 1;
phydev->link_timeout = PHY_AN_TIMEOUT;
break;
case PHY_AN: //判断连接状态中 negotiating
err = phy_read_status(phydev);
if (err < 0)
break;
if (!phydev->link) {
phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
break;
}
err = phy_aneg_done(phydev);
if (err < 0)
break;
if (err > 0) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
}
else if (0 == phydev->link_timeout--) {
int idx;
needs_aneg = 1;
if (phydev->drv->flags & PHY_HAS_MAGICANEG)
break;
idx = phy_find_valid(0, phydev->supported);
phydev->speed = settings[idx].speed;
phydev->duplex = settings[idx].duplex;
phydev->autoneg = AUTONEG_DISABLE;
pr_info("Trying %d/%s\n", phydev->speed,DUPLEX_FULL ==phydev->duplex ?"FULL" : "HALF");
}
break;
case PHY_NOLINK: //开启 未连接
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
}
break;
case PHY_FORCING: //设置中
err = genphy_update_link(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
}
else {
if (0 == phydev->link_timeout--) {
phy_force_reduction(phydev);
needs_aneg = 1;
}
}
phydev->adjust_link(phydev->attached_dev);
break;
case PHY_RUNNING: //运行
if (PHY_POLL == phydev->irq)
phydev->state = PHY_CHANGELINK;
break;
case PHY_CHANGELINK: //连接状态改变
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
}
else {
phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev);
}
phydev->adjust_link(phydev->attached_dev);
if (PHY_POLL != phydev->irq)
err = phy_config_interrupt(phydev,PHY_INTERRUPT_ENABLED);
break;
case PHY_HALTED: //停止
if (phydev->link) {
phydev->link = 0;
netif_carrier_off(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
}
break;
case PHY_RESUMING: //唤醒
err = phy_clear_interrupt(phydev);
if (err)
break;
err = phy_config_interrupt(phydev,PHY_INTERRUPT_ENABLED);
if (err)
break;
if (AUTONEG_ENABLE == phydev->autoneg) {
err = phy_aneg_done(phydev);
if (err < 0)
break;
if (err > 0) {
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
}
else
phydev->state = PHY_NOLINK;
phydev->adjust_link(phydev->attached_dev);
}
else {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
}
}
else {
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
}
else
phydev->state = PHY_NOLINK;
phydev->adjust_link(phydev->attached_dev);
}
break;
}
mutex_unlock(&phydev->lock);
if (needs_aneg) //需要自动配置(例如ifconfig eth0 up就会调用)
err = phy_start_aneg(phydev); //开始自动配置
if (err < 0)
phy_error(phydev);
schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ);
}
调用phy_start_aneg自动配置函数
int phy_start_aneg(struct phy_device *phydev)
{
int err;
mutex_lock(&phydev->lock);
if (AUTONEG_DISABLE == phydev->autoneg)
phy_sanitize_settings(phydev);
err = phydev->drv->config_aneg(phydev); //调用驱动的config_aneg方法,默认是genphy_config_aneg
if (err < 0)
goto out_unlock;
if (phydev->state != PHY_HALTED) { //调整修改PHY设备状态
if (AUTONEG_ENABLE == phydev->autoneg) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
}
else {
phydev->state = PHY_FORCING;
phydev->link_timeout = PHY_FORCE_TIMEOUT;
}
}
out_unlock:
mutex_unlock(&phydev->lock);
return err;
}
EXPORT_SYMBOL(phy_start_aneg);
调用默认的自动协商方法genphy_config_aneg
int genphy_config_aneg(struct phy_device *phydev)
{
int result;
if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);
result = genphy_config_advert(phydev);
if (result < 0) /* error */
return result;
if (result == 0) {
int ctl = phy_read(phydev, MII_BMCR); //获取状态
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
result = 1; /* do restart aneg */
}
if (result > 0)
result = genphy_restart_aneg(phydev); //重新开启自动协商机制
return result;
}
EXPORT_SYMBOL(genphy_config_aneg);
接着调用genphy_restart_aneg,重新开启自动协商机制
int genphy_restart_aneg(struct phy_device *phydev)
{
int ctl;
ctl = phy_read(phydev, MII_BMCR); //获取基本状态
if (ctl < 0)
return ctl;
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); //使能自动协商机制使能 重启
/* Don't isolate the PHY if we're negotiating */
ctl &= ~(BMCR_ISOLATE);
ctl = phy_write(phydev, MII_BMCR, ctl); //写命令
return ctl;
}
EXPORT_SYMBOL(genphy_restart_aneg);
五,其它网口API函数调用
static inline int phy_read(struct phy_device *phydev, u32 regnum); //PHY读
static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val); //PHY写
void phy_start(struct phy_device *phydev); //PHY开始
void phy_stop(struct phy_device *phydev); //PHY停止
int phy_init_hw(struct phy_device *phydev); //PHY初始化硬件
struct phy_device * phy_attach(struct net_device *dev,const char *bus_id, u32 flags, phy_interface_t interface); //PHY接上
void phy_detach(struct phy_device *phydev); //PHY分离
struct phy_device *phy_find_first(struct mii_bus *bus); //查找mii_bus总线上第一个PHY
int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,void (*handler)(struct net_device *), u32 flags,phy_interface_t interface); //PHY直接连接网络设备
struct phy_device * phy_connect(struct net_device *dev, const char *bus_id,void (*handler)(struct net_device *), u32 flags,phy_interface_t interface); //PHY连接网络设备
void phy_disconnect(struct phy_device *phydev); //PHY断开与网络设备的连接
int phy_start_interrupts(struct phy_device *phydev);//PHY开始中断
int phy_stop_interrupts(struct phy_device *phydev); //PHY停止中断
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); //ethtool工具sset功能
int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); //ethtool工具gset功能
int phy_mii_ioctl(struct phy_device *phydev,struct ifreq *ifr, int cmd); //通用PHY/mii接口