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

📄 phy.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
 *   phy_detach. */void phy_stop_machine(struct phy_device *phydev){	del_timer_sync(&phydev->phy_timer);	spin_lock_bh(&phydev->lock);	if (phydev->state > PHY_UP)		phydev->state = PHY_UP;	spin_unlock_bh(&phydev->lock);	phydev->adjust_state = NULL;}/** * phy_force_reduction - reduce PHY speed/duplex settings by one step * @phydev: target phy_device struct * * Description: Reduces the speed/duplex settings by one notch, *   in this order-- *   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 - enter HALTED state for this PHY device * @phydev: target phy_device struct * * 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_bh(&phydev->lock);	phydev->state = PHY_HALTED;	spin_unlock_bh(&phydev->lock);}/** * phy_interrupt - PHY interrupt handler * @irq: interrupt line * @phy_dat: phy_device pointer * * 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 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);	schedule_work(&phydev->phy_queue);	return IRQ_HANDLED;}/** * phy_enable_interrupts - Enable the interrupts from the PHY side * @phydev: target phy_device struct */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);/** * phy_disable_interrupts - Disable the PHY interrupts from the PHY side * @phydev: target phy_device struct */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 - 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){	int err = 0;	INIT_WORK(&phydev->phy_queue, phy_change);	atomic_set(&phydev->irq_disable, 0);	if (request_irq(phydev->irq, phy_interrupt,				IRQF_SHARED,				"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);/** * phy_stop_interrupts - disable interrupts from a PHY device * @phydev: target phy_device struct */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);	/*	 * Cannot call flush_scheduled_work() here as desired because	 * of rtnl_lock(), but we do not really care about what would	 * be done, except from enable_irq(), so cancel any work	 * possibly pending and take care of the matter below.	 */	cancel_work_sync(&phydev->phy_queue);	/*	 * If work indeed has been cancelled, disable_irq() will have	 * been left unbalanced from phy_interrupt() and enable_irq()	 * has to be called so that other devices on the line work.	 */	while (atomic_dec_return(&phydev->irq_disable) >= 0)		enable_irq(phydev->irq);	return err;}EXPORT_SYMBOL(phy_stop_interrupts);/** * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes * @work: work_struct that describes the work to be done */static void phy_change(struct work_struct *work){	int err;	struct phy_device *phydev =		container_of(work, struct phy_device, phy_queue);	err = phy_disable_interrupts(phydev);	if (err)		goto phy_err;	spin_lock_bh(&phydev->lock);	if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))		phydev->state = PHY_CHANGELINK;	spin_unlock_bh(&phydev->lock);	atomic_dec(&phydev->irq_disable);	enable_irq(phydev->irq);	/* Reenable interrupts */	if (PHY_HALTED != phydev->state)		err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);	if (err)		goto irq_enable_err;	return;irq_enable_err:	disable_irq(phydev->irq);	atomic_inc(&phydev->irq_disable);phy_err:	phy_error(phydev);}/** * phy_stop - Bring down the PHY link, and stop checking the status * @phydev: target phy_device struct */void phy_stop(struct phy_device *phydev){	spin_lock_bh(&phydev->lock);	if (PHY_HALTED == phydev->state)		goto out_unlock;	if (phydev->irq != PHY_POLL) {		/* Disable PHY Interrupts */		phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);		/* Clear any pending interrupts */		phy_clear_interrupt(phydev);	}	phydev->state = PHY_HALTED;out_unlock:	spin_unlock_bh(&phydev->lock);	/*	 * Cannot call flush_scheduled_work() here as desired because	 * of rtnl_lock(), but PHY_HALTED shall guarantee phy_change()	 * will not reenable interrupts.	 */}/** * 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){	spin_lock_bh(&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_bh(&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_bh(&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:			err = phy_read_status(phydev);			if (err < 0)				break;			/* If the link is down, give up on			 * negotiation for now */			if (!phydev->link) {				phydev->state = PHY_NOLINK;				netif_carrier_off(phydev->attached_dev);				phydev->adjust_link(phydev->attached_dev);				break;			}			/* Check if negotiation is done.  Break			 * if there's an error */			err = phy_aneg_done(phydev);			if (err < 0)				break;			/* If AN is done, we're running */			if (err > 0) {				phydev->state = PHY_RUNNING;				netif_carrier_on(phydev->attached_dev);				phydev->adjust_link(phydev->attached_dev);			} else if (0 == phydev->link_timeout--) {				int idx;				needs_aneg = 1;				/* If we have the magic_aneg bit,				 * we try again */				if (phydev->drv->flags & PHY_HAS_MAGICANEG)					break;				/* The timer expired, and we still				 * don't have a setting, so we try				 * forcing it until we find one that				 * works, starting from the fastest speed,				 * and working our way down */				idx = phy_find_valid(0, phydev->supported);				phydev->speed = settings[idx].speed;				phydev->duplex = settings[idx].duplex;				phydev->autoneg = AUTONEG_DISABLE;				pr_info("Trying %d/%s\n", phydev->speed,						DUPLEX_FULL ==						phydev->duplex ?						"FULL" : "HALF");			}			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 = genphy_update_link(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_bh(&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);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -