Chinaunix首页 | 论坛 | 博客
  • 博客访问: 420456
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2017-09-12 14:48:32

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);
}












阅读(3803) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~