mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: phy: Threaded interrupts allow some simplification
The PHY interrupts are now handled in a threaded interrupt handler, which can sleep. The work queue is no longer needed, phy_change() can be called directly. phy_mac_interrupt() still needs to be safe to call in interrupt context, so keep the work queue, and use a helper to call phy_change(). Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									c974bdbc3e
								
							
						
					
					
						commit
						664fcf123a
					
				
					 3 changed files with 30 additions and 22 deletions
				
			
		| 
						 | 
					@ -664,7 +664,7 @@ static void phy_error(struct phy_device *phydev)
 | 
				
			||||||
 * @phy_dat: phy_device pointer
 | 
					 * @phy_dat: phy_device pointer
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Description: When a PHY interrupt occurs, the handler disables
 | 
					 * Description: When a PHY interrupt occurs, the handler disables
 | 
				
			||||||
 * interrupts, and schedules a work task to clear the interrupt.
 | 
					 * interrupts, and uses phy_change to handle the interrupt.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 | 
					static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -673,15 +673,10 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 | 
				
			||||||
	if (PHY_HALTED == phydev->state)
 | 
						if (PHY_HALTED == phydev->state)
 | 
				
			||||||
		return IRQ_NONE;		/* It can't be ours.  */
 | 
							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);
 | 
						disable_irq_nosync(irq);
 | 
				
			||||||
	atomic_inc(&phydev->irq_disable);
 | 
						atomic_inc(&phydev->irq_disable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	queue_work(system_power_efficient_wq, &phydev->phy_queue);
 | 
						phy_change(phydev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return IRQ_HANDLED;
 | 
						return IRQ_HANDLED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -766,12 +761,6 @@ int phy_stop_interrupts(struct phy_device *phydev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_irq(phydev->irq, 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
 | 
						/* If work indeed has been cancelled, disable_irq() will have
 | 
				
			||||||
	 * been left unbalanced from phy_interrupt() and enable_irq()
 | 
						 * been left unbalanced from phy_interrupt() and enable_irq()
 | 
				
			||||||
	 * has to be called so that other devices on the line work.
 | 
						 * has to be called so that other devices on the line work.
 | 
				
			||||||
| 
						 | 
					@ -784,14 +773,11 @@ int phy_stop_interrupts(struct phy_device *phydev)
 | 
				
			||||||
EXPORT_SYMBOL(phy_stop_interrupts);
 | 
					EXPORT_SYMBOL(phy_stop_interrupts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes
 | 
					 * phy_change - Called by the phy_interrupt to handle PHY changes
 | 
				
			||||||
 * @work: work_struct that describes the work to be done
 | 
					 * @phydev: phy_device struct that interrupted
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void phy_change(struct work_struct *work)
 | 
					void phy_change(struct phy_device *phydev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct phy_device *phydev =
 | 
					 | 
				
			||||||
		container_of(work, struct phy_device, phy_queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (phy_interrupt_is_valid(phydev)) {
 | 
						if (phy_interrupt_is_valid(phydev)) {
 | 
				
			||||||
		if (phydev->drv->did_interrupt &&
 | 
							if (phydev->drv->did_interrupt &&
 | 
				
			||||||
		    !phydev->drv->did_interrupt(phydev))
 | 
							    !phydev->drv->did_interrupt(phydev))
 | 
				
			||||||
| 
						 | 
					@ -832,6 +818,18 @@ void phy_change(struct work_struct *work)
 | 
				
			||||||
	phy_error(phydev);
 | 
						phy_error(phydev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * phy_change_work - Scheduled by the phy_mac_interrupt to handle PHY changes
 | 
				
			||||||
 | 
					 * @work: work_struct that describes the work to be done
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void phy_change_work(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct phy_device *phydev =
 | 
				
			||||||
 | 
							container_of(work, struct phy_device, phy_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						phy_change(phydev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * phy_stop - Bring down the PHY link, and stop checking the status
 | 
					 * phy_stop - Bring down the PHY link, and stop checking the status
 | 
				
			||||||
 * @phydev: target phy_device struct
 | 
					 * @phydev: target phy_device struct
 | 
				
			||||||
| 
						 | 
					@ -1116,6 +1114,15 @@ void phy_state_machine(struct work_struct *work)
 | 
				
			||||||
				   PHY_STATE_TIME * HZ);
 | 
									   PHY_STATE_TIME * HZ);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * phy_mac_interrupt - MAC says the link has changed
 | 
				
			||||||
 | 
					 * @phydev: phy_device struct with changed link
 | 
				
			||||||
 | 
					 * @new_link: Link is Up/Down.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Description: The MAC layer is able indicate there has been a change
 | 
				
			||||||
 | 
					 *   in the PHY link status. Set the new link status, and trigger the
 | 
				
			||||||
 | 
					 *   state machine, work a work queue.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
void phy_mac_interrupt(struct phy_device *phydev, int new_link)
 | 
					void phy_mac_interrupt(struct phy_device *phydev, int new_link)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	phydev->link = new_link;
 | 
						phydev->link = new_link;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -347,7 +347,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_init(&dev->lock);
 | 
						mutex_init(&dev->lock);
 | 
				
			||||||
	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
 | 
						INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
 | 
				
			||||||
	INIT_WORK(&dev->phy_queue, phy_change);
 | 
						INIT_WORK(&dev->phy_queue, phy_change_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Request the appropriate module unconditionally; don't
 | 
						/* Request the appropriate module unconditionally; don't
 | 
				
			||||||
	 * bother trying to do so only if it isn't already loaded,
 | 
						 * bother trying to do so only if it isn't already loaded,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -343,7 +343,7 @@ struct phy_c45_device_ids {
 | 
				
			||||||
 * giving up on the current attempt at acquiring a link
 | 
					 * giving up on the current attempt at acquiring a link
 | 
				
			||||||
 * irq: IRQ number of the PHY's interrupt (-1 if none)
 | 
					 * irq: IRQ number of the PHY's interrupt (-1 if none)
 | 
				
			||||||
 * phy_timer: The timer for handling the state machine
 | 
					 * phy_timer: The timer for handling the state machine
 | 
				
			||||||
 * phy_queue: A work_queue for the interrupt
 | 
					 * phy_queue: A work_queue for the phy_mac_interrupt
 | 
				
			||||||
 * attached_dev: The attached enet driver's device instance ptr
 | 
					 * attached_dev: The attached enet driver's device instance ptr
 | 
				
			||||||
 * adjust_link: Callback for the enet controller to respond to
 | 
					 * adjust_link: Callback for the enet controller to respond to
 | 
				
			||||||
 * changes in the link state.
 | 
					 * changes in the link state.
 | 
				
			||||||
| 
						 | 
					@ -802,7 +802,8 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
 | 
				
			||||||
int phy_drivers_register(struct phy_driver *new_driver, int n,
 | 
					int phy_drivers_register(struct phy_driver *new_driver, int n,
 | 
				
			||||||
			 struct module *owner);
 | 
								 struct module *owner);
 | 
				
			||||||
void phy_state_machine(struct work_struct *work);
 | 
					void phy_state_machine(struct work_struct *work);
 | 
				
			||||||
void phy_change(struct work_struct *work);
 | 
					void phy_change(struct phy_device *phydev);
 | 
				
			||||||
 | 
					void phy_change_work(struct work_struct *work);
 | 
				
			||||||
void phy_mac_interrupt(struct phy_device *phydev, int new_link);
 | 
					void phy_mac_interrupt(struct phy_device *phydev, int new_link);
 | 
				
			||||||
void phy_start_machine(struct phy_device *phydev);
 | 
					void phy_start_machine(struct phy_device *phydev);
 | 
				
			||||||
void phy_stop_machine(struct phy_device *phydev);
 | 
					void phy_stop_machine(struct phy_device *phydev);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue