1. phy如何获取phy中断中的irq编号
在phy_device_create 函数里面, dev->irq 从bus->irq[addr] 获得, 所以如果希望phy 设备跟中断联系起来,需要在mdio bus 里面为irq[addr] 赋值
- 源码路径:
drivers\net\phy\phy_device.c
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
{
struct phy_device *dev;
struct mdio_device *mdiodev;
int ret = 0;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
mdiodev = &dev->mdio;
mdiodev->dev.parent = &bus->dev;
mdiodev->dev.bus = &mdio_bus_type;
mdiodev->dev.type = &mdio_bus_phy_type;
mdiodev->bus = bus;
mdiodev->bus_match = phy_bus_match;
mdiodev->addr = addr;
mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
mdiodev->device_free = phy_mdio_device_free;
mdiodev->device_remove = phy_mdio_device_remove;
dev->speed = SPEED_UNKNOWN;
dev->duplex = DUPLEX_UNKNOWN;
dev->pause = 0;
dev->asym_pause = 0;
dev->link = 0;
dev->port = PORT_TP;
dev->interface = PHY_INTERFACE_MODE_GMII;
dev->autoneg = AUTONEG_ENABLE;
dev->is_c45 = is_c45;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
dev->irq = bus->irq[addr];
dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
device_initialize(&mdiodev->dev);
dev->state = PHY_DOWN;
mutex_init(&dev->lock);
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
if (is_c45 && c45_ids) {
const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
int i;
for (i = 1; i < num_ids; i++) {
if (c45_ids->device_ids[i] == 0xffffffff)
continue;
ret = phy_request_driver_module(dev,
c45_ids->device_ids[i]);
if (ret)
break;
}
} else {
ret = phy_request_driver_module(dev, phy_id);
}
if (ret) {
put_device(&mdiodev->dev);
dev = ERR_PTR(ret);
}
return dev;
}
2. net_device的和phy_device何时关联到一起
net_device 的open 函数通常会调用phy_connect 函数将net_device 的和phy_device 关联到一起。
以cpsw.c 为例,函数调用的顺序为:
- 源码路径:
drivers\net\ethernet\ti\cpsw.c
cpsw_ndo_open -> cpsw_slave_open -> phy_connect(priv->ndev, slave->data->phy_id,&cpsw_adjust_link, slave->data->phy_if); -> phy_connect_direct -> phy_start_machine(phydev); -> queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
在cpsw_slave_open 函数中,调用了phy_connect ,如下:
phy = phy_connect(priv->ndev, slave->data->phy_id,
&cpsw_adjust_link, slave->data->phy_if);
struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
void (*handler)(struct net_device *),
phy_interface_t interface)
{
struct phy_device *phydev;
struct device *d;
int rc;
d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);
if (!d) {
pr_err("PHY %s not found\n", bus_id);
return ERR_PTR(-ENODEV);
}
phydev = to_phy_device(d);
rc = phy_connect_direct(dev, phydev, handler, interface);
put_device(d);
if (rc)
return ERR_PTR(rc);
return phydev;
}
这里phy_connect 主要是将mdio 总线在scan 时,已经调用phy_device_create 函数创建了phy_device , 并且把这个device 注册到mdio 总线上了,此处从总线上找到名称为bus_id 的phy_device . 将net_device 与phy_device 建立起联系。
3. phy_device_create 时对队列的初始化
在phy_device_create 中会有对state_queue 的初始化
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
4. phy_device和phy_driver建立的关键
在上述中,提到了phy_connect ,它主要是对phy_device 和phy_driver 进行连接,其中,调用了phy_connect_direct 函数,这个函数才是dev 和drv 建立的关键。如下源代码中:phy_attach_direct 会找到phy_device ,然后将其连接到phy_driver 上。
int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
void (*handler)(struct net_device *),
phy_interface_t interface)
{
int rc;
if (!dev)
return -EINVAL;
rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
if (rc)
return rc;
phy_prepare_link(phydev, handler);
if (phy_interrupt_is_valid(phydev))
phy_request_interrupt(phydev);
return 0;
}
5. phy中断的注册
在上述第4 小节函数phy_connect_direct 中,phy 的driver 和device 进行了匹配连接,在连接完成后,判断了phy 是否支持中断,如果支持中断,就会进行phy 中断的注册
if (phy_interrupt_is_valid(phydev))
phy_request_interrupt(phydev);
void phy_request_interrupt(struct phy_device *phydev)
{
int err;
err = request_threaded_irq(phydev->irq, NULL, phy_interrupt,
IRQF_ONESHOT | IRQF_SHARED,
phydev_name(phydev), phydev);
if (err) {
phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n",
err, phydev->irq);
phydev->irq = PHY_POLL;
} else {
if (phy_enable_interrupts(phydev)) {
phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n");
phy_free_interrupt(phydev);
phydev->irq = PHY_POLL;
}
}
}
EXPORT_SYMBOL(phy_request_interrupt);
6. phy中断的回调函数
在注册phy中断的过程中,注册了phy中断回调函数,进入回调函数后,会调用phy的工作队列,这个工作队列前面也提到了,在phy_device_create时已经对其进行了初始化。
static irqreturn_t phy_interrupt(int irq, void *phy_dat)
{
struct phy_device *phydev = phy_dat;
struct phy_driver *drv = phydev->drv;
if (drv->handle_interrupt)
return drv->handle_interrupt(phydev);
if (drv->did_interrupt && !drv->did_interrupt(phydev))
return IRQ_NONE;
phy_trigger_machine(phydev);
if (!drv->did_interrupt && phy_clear_interrupt(phydev)) {
phy_error(phydev);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
返回总目录
|