Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 66852
b: refs/heads/master
c: 0ac4952
h: refs/heads/master
v: v3
  • Loading branch information
Maciej W. Rozycki authored and David S. Miller committed Oct 10, 2007
1 parent ea98da4 commit e1995a0
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 6 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f7ab697d328b0a417d9e3cb891d45693ea89e83d
refs/heads/master: 0ac49527318bc388a881152d60f49d7951606024
24 changes: 19 additions & 5 deletions trunk/drivers/net/phy/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
* Copyright (c) 2006 Maciej W. Rozycki
* Copyright (c) 2006, 2007 Maciej W. Rozycki
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Expand Down Expand Up @@ -35,6 +35,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>

#include <asm/atomic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
Expand Down Expand Up @@ -562,6 +563,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
* 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);

Expand Down Expand Up @@ -632,6 +634,7 @@ int phy_start_interrupts(struct phy_device *phydev)

INIT_WORK(&phydev->phy_queue, phy_change);

atomic_set(&phydev->irq_disable, 0);
if (request_irq(phydev->irq, phy_interrupt,
IRQF_SHARED,
"phy_interrupt",
Expand Down Expand Up @@ -662,13 +665,22 @@ int phy_stop_interrupts(struct phy_device *phydev)
if (err)
phy_error(phydev);

free_irq(phydev->irq, phydev);

/*
* Finish any pending work; we might have been scheduled to be called
* from keventd ourselves, but cancel_work_sync() handles that.
* 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);

free_irq(phydev->irq, phydev);
/*
* 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;
}
Expand All @@ -695,6 +707,7 @@ static void phy_change(struct work_struct *work)
phydev->state = PHY_CHANGELINK;
spin_unlock_bh(&phydev->lock);

atomic_dec(&phydev->irq_disable);
enable_irq(phydev->irq);

/* Reenable interrupts */
Expand All @@ -708,6 +721,7 @@ static void phy_change(struct work_struct *work)

irq_enable_err:
disable_irq(phydev->irq);
atomic_inc(&phydev->irq_disable);
phy_err:
phy_error(phydev);
}
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include <linux/timer.h>
#include <linux/workqueue.h>

#include <asm/atomic.h>

#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
SUPPORTED_10baseT_Full | \
SUPPORTED_100baseT_Half | \
Expand Down Expand Up @@ -281,6 +283,7 @@ struct phy_device {
/* Interrupt and Polling infrastructure */
struct work_struct phy_queue;
struct timer_list phy_timer;
atomic_t irq_disable;

spinlock_t lock;

Expand Down

0 comments on commit e1995a0

Please sign in to comment.