1. phy设备何处获得irq编号
在phy_device_create函数里面, dev->irq从bus->irq[addr]获得, 所以如果希望phy
设备跟中断联系起来,需要在mdio bus里面为irq[addr]赋值。
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
{
struct phy_device *dev;
/* We allocate the device, and initialize the default values */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
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 = 0;
dev->asym_pause = 0;
dev->link = 1;
dev->interface = PHY_INTERFACE_MODE_GMII;
dev->autoneg = AUTONEG_ENABLE;
dev->is_c45 = is_c45;
dev->addr = addr;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
dev->bus = bus;
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);
dev->state = PHY_DOWN;
mutex_init(&dev->lock);
/* 注意这里初始化了两个工作队列 */
/* dev->state_queue这个工作队列在这里只是初始化了一下,当并没有调度,在什么地方调度呢? */
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
INIT_WORK(&dev->phy_queue, phy_change);
/* Request the appropriate module unconditionally; don't
* bother trying to do so only if it isn't already loaded,
* because that gets complicated. A hotplug event would have
* done an unconditional modprobe anyway.
* We don't do normal hotplug because it won't work for MDIO
* -- because it relies on the device staying around for long
* enough for the driver to get loaded. With MDIO, the NIC
* driver will get bored and give up as soon as it finds that
* there's no driver _already_ loaded.
*/
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
device_initialize(&dev->dev);
return dev;
}
2. net_device的和phy_device何时关联到一起
net_device的open函数通常会调用phy_connect函数将net_device的和phy_device关联到一起。
以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);
static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
{
.......
slave->phy = phy_connect(priv->ndev, slave->data->phy_id,
&cpsw_adjust_link, slave->data->phy_if);
if (IS_ERR(slave->phy)) {
dev_err(priv->dev, "phy %s not found on slave %d\n",
slave->data->phy_id, slave->slave_num);
slave->phy = NULL;
} else {
dev_info(priv->dev, "phy found : id is : 0x%x\n",
slave->phy->phy_id);
phy_start(slave->phy);
/* Configure GMII_SEL register */
cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface,
slave->slave_num);
}
}
------
看看phy_connect实际干了什么
mdio总线在scan时,已经调用phy_device_create函数创建了phy_device, 并且把这个device
注册到mdio总线上了,此处从总线上找到名称为bus_id的phy_device. 将net_device与
phy_device建立起联系。
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;
/* Search the list of PHY devices on the mdio bus for the
* PHY with the requested name
*/
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);
if (rc)
return ERR_PTR(rc);
return phydev;
}
在phy_device_create中会有对state_queue的初始化
{
...
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
...
}
void phy_start_machine(struct phy_device *phydev)
{
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
}
真正将net_device与phy_device建立起联系的函数是phy_connect_direct
int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
void (*handler)(struct net_device *),
phy_interface_t interface)
{
int rc;
rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
if (rc)
return rc;
phy_prepare_link(phydev, handler);
/* phy_start_machine实际上是
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
将phydev->state_queue这个工作队列调度到system_power_efficient_wq上等待执行 */
phy_start_machine(phydev);
/* 如果这个phy设备支持中断,就会进行中断的注册 */
if (phydev->irq > 0)
phy_start_interrupts(phydev);
return 0;
}
3. phy中断的注册
函数phy_connect_direct中,如果phy设备支持中断,就会调用
phy_start_interrupts进行中断的注册
cpsw_slave_open->
phy_connect(priv->ndev, slave->data->phy_id,
&cpsw_adjust_link, slave->data->phy_if);
->phy_start_interrupts
/**
* phy_start_interrupts - request and enable interrupts for a PHY device
* @phydev: target phy_device struct
*
* Description: Request the interrupt for the given PHY.
* If this fails, then we set irq to PHY_POLL.
* Otherwise, we enable the interrupts in the PHY.
* This should only be called with a valid IRQ number.
* Returns 0 on success or < 0 on error.
*/
int phy_start_interrupts(struct phy_device *phydev)
{
atomic_set(&phydev->irq_disable, 0);
if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt",
phydev) < 0) {
pr_warn("%s: Can't get IRQ %d (PHY)\n",
phydev->bus->name, phydev->irq);
phydev->irq = PHY_POLL;
return 0;
}
return phy_enable_interrupts(phydev);
/* clean 中断,config中断,
实际上是调用phydev->drv->ack_interrupt
phydev->drv->config_intr */
}
/**
* phy_enable_interrupts - Enable the interrupts from the PHY side
* @phydev: target phy_device struct
*/
static int phy_enable_interrupts(struct phy_device *phydev)
{
int err = phy_clear_interrupt(phydev); /* phydev->drv->ack_interrupt */
if (err < 0)
return err;
return phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); /* phydev->drv->config_intr */
}
看看中断服务程序干了些什么
phy_interrupt这个isr会把phydev->phy_queue这个工作队列调度到system_power_efficient_wq上
static irqreturn_t phy_interrupt(int irq, void *phy_dat)
{
struct phy_device *phydev = phy_dat;
if (PHY_HALTED == phydev->state)
return IRQ_NONE; /* It can't be ours. */
/* The MDIO bus is not allowed to be written in interrupt
* context, so we need to disable the irq here. A work
* queue will write the PHY to disable and clear the
* interrupt, and then reenable the irq line.
*/
disable_irq_nosync(irq);
atomic_inc(&phydev->irq_disable);
/* 调度phydev->phy_queue。 phy_queue在phy_device_create已经初始化了 */
queue_work(system_power_efficient_wq, &phydev->phy_queue);
return IRQ_HANDLED;
}
在phy_device_create函数里,已经初始化了phy_queue,相应的处理函数是phy_change.
phy_device_create
{...
INIT_WORK(&dev->phy_queue, phy_change);
....
}
看看phy_change干了什么?
/**
* phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes
* @work: work_struct that describes the work to be done
*/
void phy_change(struct work_struct *work)
{
struct phy_device *phydev =
container_of(work, struct phy_device, phy_queue);
if (phydev->drv->did_interrupt &&
!phydev->drv->did_interrupt(phydev))
goto ignore;
if (phy_disable_interrupts(phydev))
goto phy_err;
mutex_lock(&phydev->lock);
if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
phydev->state = PHY_CHANGELINK;
mutex_unlock(&phydev->lock);
atomic_dec(&phydev->irq_disable);
enable_irq(phydev->irq);
/* Reenable interrupts */
if (PHY_HALTED != phydev->state &&
phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED))
goto irq_enable_err;
/* reschedule state queue work to run as soon as possible */
cancel_delayed_work_sync(&phydev->state_queue);/* phydev->state_queue本来是一个delayed work_queue, 中断来时,将这个delayed的wq取消掉,改成立刻调度 */
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0); /* 立刻调度state_queue */
return;
ignore:
atomic_dec(&phydev->irq_disable);
enable_irq(phydev->irq);
return;
irq_enable_err:
disable_irq(phydev->irq);
atomic_inc(&phydev->irq_disable);
phy_err:
phy_error(phydev);
}
阅读(3981) | 评论(0) | 转发(0) |