diff options
Diffstat (limited to 'drivers/net/phy/phy.c')
-rw-r--r-- | drivers/net/phy/phy.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4da993dfcfd8..5a314edc2744 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Author: Andy Fleming | 7 | * Author: Andy Fleming |
8 | * | 8 | * |
9 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | 9 | * Copyright (c) 2004 Freescale Semiconductor, Inc. |
10 | * Copyright (c) 2006 Maciej W. Rozycki | 10 | * Copyright (c) 2006, 2007 Maciej W. Rozycki |
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or modify it | 12 | * This program is free software; you can redistribute it and/or modify it |
13 | * under the terms of the GNU General Public License as published by the | 13 | * under the terms of the GNU General Public License as published by the |
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/timer.h> | 35 | #include <linux/timer.h> |
36 | #include <linux/workqueue.h> | 36 | #include <linux/workqueue.h> |
37 | 37 | ||
38 | #include <asm/atomic.h> | ||
38 | #include <asm/io.h> | 39 | #include <asm/io.h> |
39 | #include <asm/irq.h> | 40 | #include <asm/irq.h> |
40 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
@@ -562,6 +563,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) | |||
562 | * queue will write the PHY to disable and clear the | 563 | * queue will write the PHY to disable and clear the |
563 | * interrupt, and then reenable the irq line. */ | 564 | * interrupt, and then reenable the irq line. */ |
564 | disable_irq_nosync(irq); | 565 | disable_irq_nosync(irq); |
566 | atomic_inc(&phydev->irq_disable); | ||
565 | 567 | ||
566 | schedule_work(&phydev->phy_queue); | 568 | schedule_work(&phydev->phy_queue); |
567 | 569 | ||
@@ -632,6 +634,7 @@ int phy_start_interrupts(struct phy_device *phydev) | |||
632 | 634 | ||
633 | INIT_WORK(&phydev->phy_queue, phy_change); | 635 | INIT_WORK(&phydev->phy_queue, phy_change); |
634 | 636 | ||
637 | atomic_set(&phydev->irq_disable, 0); | ||
635 | if (request_irq(phydev->irq, phy_interrupt, | 638 | if (request_irq(phydev->irq, phy_interrupt, |
636 | IRQF_SHARED, | 639 | IRQF_SHARED, |
637 | "phy_interrupt", | 640 | "phy_interrupt", |
@@ -662,13 +665,22 @@ int phy_stop_interrupts(struct phy_device *phydev) | |||
662 | if (err) | 665 | if (err) |
663 | phy_error(phydev); | 666 | phy_error(phydev); |
664 | 667 | ||
668 | free_irq(phydev->irq, phydev); | ||
669 | |||
665 | /* | 670 | /* |
666 | * Finish any pending work; we might have been scheduled to be called | 671 | * Cannot call flush_scheduled_work() here as desired because |
667 | * from keventd ourselves, but cancel_work_sync() handles that. | 672 | * of rtnl_lock(), but we do not really care about what would |
673 | * be done, except from enable_irq(), so cancel any work | ||
674 | * possibly pending and take care of the matter below. | ||
668 | */ | 675 | */ |
669 | cancel_work_sync(&phydev->phy_queue); | 676 | cancel_work_sync(&phydev->phy_queue); |
670 | 677 | /* | |
671 | free_irq(phydev->irq, phydev); | 678 | * If work indeed has been cancelled, disable_irq() will have |
679 | * been left unbalanced from phy_interrupt() and enable_irq() | ||
680 | * has to be called so that other devices on the line work. | ||
681 | */ | ||
682 | while (atomic_dec_return(&phydev->irq_disable) >= 0) | ||
683 | enable_irq(phydev->irq); | ||
672 | 684 | ||
673 | return err; | 685 | return err; |
674 | } | 686 | } |
@@ -695,6 +707,7 @@ static void phy_change(struct work_struct *work) | |||
695 | phydev->state = PHY_CHANGELINK; | 707 | phydev->state = PHY_CHANGELINK; |
696 | spin_unlock_bh(&phydev->lock); | 708 | spin_unlock_bh(&phydev->lock); |
697 | 709 | ||
710 | atomic_dec(&phydev->irq_disable); | ||
698 | enable_irq(phydev->irq); | 711 | enable_irq(phydev->irq); |
699 | 712 | ||
700 | /* Reenable interrupts */ | 713 | /* Reenable interrupts */ |
@@ -708,6 +721,7 @@ static void phy_change(struct work_struct *work) | |||
708 | 721 | ||
709 | irq_enable_err: | 722 | irq_enable_err: |
710 | disable_irq(phydev->irq); | 723 | disable_irq(phydev->irq); |
724 | atomic_inc(&phydev->irq_disable); | ||
711 | phy_err: | 725 | phy_err: |
712 | phy_error(phydev); | 726 | phy_error(phydev); |
713 | } | 727 | } |