⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 phy.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 2 页
字号:
{	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 + -