phy_polling_mode

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;

    /* We allocate the device, and initialize the default values */
    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->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);

    /* 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.
     */
    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;
}
EXPORT_SYMBOL(phy_device_create);

/**
 * phy_state_machine - Handle the state machine
 * @work: work_struct that describes the work to be done
 */
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);
    bool needs_aneg = false, do_suspend = false;
    enum phy_state old_state;
    int err = 0;

    mutex_lock(&phydev->lock);

    old_state = phydev->state;

    switch (phydev->state) {
    case PHY_DOWN:
    case PHY_READY:
        break;
    case PHY_UP:
        needs_aneg = true;

        break;
    case PHY_NOLINK:
    case PHY_RUNNING:
        err = phy_check_link_status(phydev);
        break;
    case PHY_HALTED:
        if (phydev->link) {
            phydev->link = 0;
            phy_link_down(phydev, true);
        }
        do_suspend = true;
        break;
    }

    mutex_unlock(&phydev->lock);

    if (needs_aneg)
        err = phy_start_aneg(phydev);
    else if (do_suspend)
        phy_suspend(phydev);

    if (err < 0)
        phy_error(phydev);

    if (old_state != phydev->state) {
        phydev_dbg(phydev, "PHY state change %s -> %s\n",
               phy_state_to_str(old_state),
               phy_state_to_str(phydev->state));
        if (phydev->drv && phydev->drv->link_change_notify)
            phydev->drv->link_change_notify(phydev);
    }

    /* Only re-schedule a PHY state machine change if we are polling the
     * PHY, if PHY_IGNORE_INTERRUPT is set, then we will be moving
     * between states from phy_mac_interrupt().
     *
     * In state PHY_HALTED the PHY gets suspended, so rescheduling the
     * state machine would be pointless and possibly error prone when
     * called from phy_disconnect() synchronously.
     */
    mutex_lock(&phydev->lock);
    if (phy_polling_mode(phydev) && phy_is_started(phydev))
        phy_queue_state_machine(phydev, PHY_STATE_TIME);
    mutex_unlock(&phydev->lock);
}

/**
 * phy_polling_mode - Convenience function for testing whether polling is
 * used to detect PHY status changes
 * @phydev: the phy_device struct
 */
static inline bool phy_polling_mode(struct phy_device *phydev)
{
    return phydev->irq == PHY_POLL;
}

void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies)
{
    mod_delayed_work(system_power_efficient_wq, &phydev->state_queue,
             jiffies);
}

/**
 * phy_start - start or restart a PHY device
 * @phydev: target phy_device struct
 *
 * Description: Indicates the attached device's readiness to
 *   handle PHY-related work.  Used during startup to start the
 *   PHY, and after a call to phy_stop() to resume operation.
 *   Also used to indicate the MDIO bus has cleared an error
 *   condition.
 */
void phy_start(struct phy_device *phydev)
{
    mutex_lock(&phydev->lock);

    if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) {
        WARN(1, "called from state %s\n",
             phy_state_to_str(phydev->state));
        goto out;
    }

    /* if phy was suspended, bring the physical link up again */
    __phy_resume(phydev);

    phydev->state = PHY_UP;

    phy_start_machine(phydev);
out:
    mutex_unlock(&phydev->lock);
}

void phy_start_machine(struct phy_device *phydev)
{
    phy_trigger_machine(phydev);
}

static void phy_trigger_machine(struct phy_device *phydev)
{
    phy_queue_state_machine(phydev, 0);
}

void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies)
{
    mod_delayed_work(system_power_efficient_wq, &phydev->state_queue,
             jiffies);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值