📄 phy.c
字号:
{ del_timer_sync(&phydev->phy_timer); spin_lock(&phydev->lock); if (phydev->state > PHY_UP) phydev->state = PHY_UP; spin_unlock(&phydev->lock); if (phydev->irq != PHY_POLL) phy_stop_interrupts(phydev); phydev->adjust_state = NULL;}/* phy_force_reduction * * description: Reduces the speed/duplex settings by * one notch. The order is so: * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, * 10/FULL, 10/HALF. The function bottoms out at 10/HALF. */static void phy_force_reduction(struct phy_device *phydev){ int idx; idx = phy_find_setting(phydev->speed, phydev->duplex); idx++; idx = phy_find_valid(idx, phydev->supported); phydev->speed = settings[idx].speed; phydev->duplex = settings[idx].duplex; pr_info("Trying %d/%s\n", phydev->speed, DUPLEX_FULL == phydev->duplex ? "FULL" : "HALF");}/* phy_error: * * Moves the PHY to the HALTED state in response to a read * or write error, and tells the controller the link is down. * Must not be called from interrupt context, or while the * phydev->lock is held. */void phy_error(struct phy_device *phydev){ spin_lock(&phydev->lock); phydev->state = PHY_HALTED; spin_unlock(&phydev->lock);}/* phy_interrupt * * description: When a PHY interrupt occurs, the handler disables * interrupts, and schedules a work task to clear the interrupt. */static irqreturn_t phy_interrupt(int irq, void *phy_dat, struct pt_regs *regs){ struct phy_device *phydev = phy_dat; /* 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); schedule_work(&phydev->phy_queue); return IRQ_HANDLED;}/* Enable the interrupts from the PHY side */int phy_enable_interrupts(struct phy_device *phydev){ int err; err = phy_clear_interrupt(phydev); if (err < 0) return err; err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); return err;}EXPORT_SYMBOL(phy_enable_interrupts);/* Disable the PHY interrupts from the PHY side */int phy_disable_interrupts(struct phy_device *phydev){ int err; /* Disable PHY interrupts */ err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); if (err) goto phy_err; /* Clear the interrupt */ err = phy_clear_interrupt(phydev); if (err) goto phy_err; return 0;phy_err: phy_error(phydev); return err;}EXPORT_SYMBOL(phy_disable_interrupts);/* phy_start_interrupts * * 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. * Returns 0 on success. * This should only be called with a valid IRQ number. */int phy_start_interrupts(struct phy_device *phydev){ int err = 0; INIT_WORK(&phydev->phy_queue, phy_change, phydev); if (request_irq(phydev->irq, phy_interrupt, SA_SHIRQ, "phy_interrupt", phydev) < 0) { printk(KERN_WARNING "%s: Can't get IRQ %d (PHY)\n", phydev->bus->name, phydev->irq); phydev->irq = PHY_POLL; return 0; } err = phy_enable_interrupts(phydev); return err;}EXPORT_SYMBOL(phy_start_interrupts);int phy_stop_interrupts(struct phy_device *phydev){ int err; err = phy_disable_interrupts(phydev); if (err) phy_error(phydev); free_irq(phydev->irq, phydev); return err;}EXPORT_SYMBOL(phy_stop_interrupts);/* Scheduled by the phy_interrupt/timer to handle PHY changes */static void phy_change(void *data){ int err; struct phy_device *phydev = data; err = phy_disable_interrupts(phydev); if (err) goto phy_err; spin_lock(&phydev->lock); if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state)) phydev->state = PHY_CHANGELINK; spin_unlock(&phydev->lock); enable_irq(phydev->irq); /* Reenable interrupts */ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); if (err) goto irq_enable_err; return;irq_enable_err: disable_irq(phydev->irq);phy_err: phy_error(phydev);}/* Bring down the PHY link, and stop checking the status. */void phy_stop(struct phy_device *phydev){ spin_lock(&phydev->lock); if (PHY_HALTED == phydev->state) goto out_unlock; if (phydev->irq != PHY_POLL) { /* Clear any pending interrupts */ phy_clear_interrupt(phydev); /* Disable PHY Interrupts */ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); } phydev->state = PHY_HALTED;out_unlock: spin_unlock(&phydev->lock);}/* phy_start * * 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){ spin_lock(&phydev->lock); switch (phydev->state) { case PHY_STARTING: phydev->state = PHY_PENDING; break; case PHY_READY: phydev->state = PHY_UP; break; case PHY_HALTED: phydev->state = PHY_RESUMING; default: break; } spin_unlock(&phydev->lock);}EXPORT_SYMBOL(phy_stop);EXPORT_SYMBOL(phy_start);/* PHY timer which handles the state machine */static void phy_timer(unsigned long data){ struct phy_device *phydev = (struct phy_device *)data; int needs_aneg = 0; int err = 0; spin_lock(&phydev->lock); if (phydev->adjust_state) phydev->adjust_state(phydev->attached_dev); switch(phydev->state) { case PHY_DOWN: case PHY_STARTING: case PHY_READY: case PHY_PENDING: break; case PHY_UP: needs_aneg = 1; phydev->link_timeout = PHY_AN_TIMEOUT; break; case PHY_AN: /* Check if negotiation is done. Break * if there's an error */ err = phy_aneg_done(phydev); if (err < 0) break; /* If auto-negotiation is done, we change to * either RUNNING, or NOLINK */ 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; netif_carrier_off(phydev->attached_dev); } phydev->adjust_link(phydev->attached_dev); } else if (0 == phydev->link_timeout--) { /* The counter expired, so either we * switch to forced mode, or the * magic_aneg bit exists, and we try aneg * again */ if (!(phydev->drv->flags & PHY_HAS_MAGICANEG)) { int idx; /* We'll start from the * fastest speed, and work * our way down */ idx = phy_find_valid(0, phydev->supported); phydev->speed = settings[idx].speed; phydev->duplex = settings[idx].duplex; phydev->autoneg = AUTONEG_DISABLE; phydev->state = PHY_FORCING; phydev->link_timeout = PHY_FORCE_TIMEOUT; pr_info("Trying %d/%s\n", phydev->speed, DUPLEX_FULL == phydev->duplex ? "FULL" : "HALF"); } needs_aneg = 1; } 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 = phy_read_status(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: /* Only register a CHANGE if we are * polling */ 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; /* err > 0 if AN is done. * Otherwise, it's 0, and we're * still waiting for AN */ if (err > 0) { phydev->state = PHY_RUNNING; } else { phydev->state = PHY_AN; phydev->link_timeout = PHY_AN_TIMEOUT; } } else phydev->state = PHY_RUNNING; break; } spin_unlock(&phydev->lock); if (needs_aneg) err = phy_start_aneg(phydev); if (err < 0) phy_error(phydev); mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);}#endif /* CONFIG_PHYCONTROL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -