aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/phy.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-12-05 09:37:56 -0500
committerDavid Howells <dhowells@warthog.cambridge.redhat.com>2006-12-05 09:37:56 -0500
commit4c1ac1b49122b805adfa4efc620592f68dccf5db (patch)
tree87557f4bc2fd4fe65b7570489c2f610c45c0adcd /drivers/net/phy/phy.c
parentc4028958b6ecad064b1a6303a6a5906d4fe48d73 (diff)
parentd916faace3efc0bf19fe9a615a1ab8fa1a24cd93 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: drivers/infiniband/core/iwcm.c drivers/net/chelsio/cxgb2.c drivers/net/wireless/bcm43xx/bcm43xx_main.c drivers/net/wireless/prism54/islpci_eth.c drivers/usb/core/hub.h drivers/usb/input/hid-core.c net/core/netpoll.c Fix up merge failures with Linus's head and fix new compilation failures. Signed-Off-By: David Howells <dhowells@redhat.com>
Diffstat (limited to 'drivers/net/phy/phy.c')
-rw-r--r--drivers/net/phy/phy.c113
1 files changed, 65 insertions, 48 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index a443976d5dcf..4044bb1ada86 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -7,6 +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 * 11 *
11 * 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
12 * 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
@@ -32,6 +33,8 @@
32#include <linux/mii.h> 33#include <linux/mii.h>
33#include <linux/ethtool.h> 34#include <linux/ethtool.h>
34#include <linux/phy.h> 35#include <linux/phy.h>
36#include <linux/timer.h>
37#include <linux/workqueue.h>
35 38
36#include <asm/io.h> 39#include <asm/io.h>
37#include <asm/irq.h> 40#include <asm/irq.h>
@@ -484,6 +487,9 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
484{ 487{
485 struct phy_device *phydev = phy_dat; 488 struct phy_device *phydev = phy_dat;
486 489
490 if (PHY_HALTED == phydev->state)
491 return IRQ_NONE; /* It can't be ours. */
492
487 /* The MDIO bus is not allowed to be written in interrupt 493 /* The MDIO bus is not allowed to be written in interrupt
488 * context, so we need to disable the irq here. A work 494 * context, so we need to disable the irq here. A work
489 * queue will write the PHY to disable and clear the 495 * queue will write the PHY to disable and clear the
@@ -577,6 +583,13 @@ int phy_stop_interrupts(struct phy_device *phydev)
577 if (err) 583 if (err)
578 phy_error(phydev); 584 phy_error(phydev);
579 585
586 /*
587 * Finish any pending work; we might have been scheduled
588 * to be called from keventd ourselves, though.
589 */
590 if (!current_is_keventd())
591 flush_scheduled_work();
592
580 free_irq(phydev->irq, phydev); 593 free_irq(phydev->irq, phydev);
581 594
582 return err; 595 return err;
@@ -604,7 +617,8 @@ static void phy_change(struct work_struct *work)
604 enable_irq(phydev->irq); 617 enable_irq(phydev->irq);
605 618
606 /* Reenable interrupts */ 619 /* Reenable interrupts */
607 err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); 620 if (PHY_HALTED != phydev->state)
621 err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
608 622
609 if (err) 623 if (err)
610 goto irq_enable_err; 624 goto irq_enable_err;
@@ -625,18 +639,24 @@ void phy_stop(struct phy_device *phydev)
625 if (PHY_HALTED == phydev->state) 639 if (PHY_HALTED == phydev->state)
626 goto out_unlock; 640 goto out_unlock;
627 641
628 if (phydev->irq != PHY_POLL) { 642 phydev->state = PHY_HALTED;
629 /* Clear any pending interrupts */
630 phy_clear_interrupt(phydev);
631 643
644 if (phydev->irq != PHY_POLL) {
632 /* Disable PHY Interrupts */ 645 /* Disable PHY Interrupts */
633 phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); 646 phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
634 }
635 647
636 phydev->state = PHY_HALTED; 648 /* Clear any pending interrupts */
649 phy_clear_interrupt(phydev);
650 }
637 651
638out_unlock: 652out_unlock:
639 spin_unlock(&phydev->lock); 653 spin_unlock(&phydev->lock);
654
655 /*
656 * Cannot call flush_scheduled_work() here as desired because
657 * of rtnl_lock(), but PHY_HALTED shall guarantee phy_change()
658 * will not reenable interrupts.
659 */
640} 660}
641 661
642 662
@@ -694,60 +714,57 @@ static void phy_timer(unsigned long data)
694 714
695 break; 715 break;
696 case PHY_AN: 716 case PHY_AN:
717 err = phy_read_status(phydev);
718
719 if (err < 0)
720 break;
721
722 /* If the link is down, give up on
723 * negotiation for now */
724 if (!phydev->link) {
725 phydev->state = PHY_NOLINK;
726 netif_carrier_off(phydev->attached_dev);
727 phydev->adjust_link(phydev->attached_dev);
728 break;
729 }
730
697 /* Check if negotiation is done. Break 731 /* Check if negotiation is done. Break
698 * if there's an error */ 732 * if there's an error */
699 err = phy_aneg_done(phydev); 733 err = phy_aneg_done(phydev);
700 if (err < 0) 734 if (err < 0)
701 break; 735 break;
702 736
703 /* If auto-negotiation is done, we change to 737 /* If AN is done, we're running */
704 * either RUNNING, or NOLINK */
705 if (err > 0) { 738 if (err > 0) {
706 err = phy_read_status(phydev); 739 phydev->state = PHY_RUNNING;
740 netif_carrier_on(phydev->attached_dev);
741 phydev->adjust_link(phydev->attached_dev);
742
743 } else if (0 == phydev->link_timeout--) {
744 int idx;
707 745
708 if (err) 746 needs_aneg = 1;
747 /* If we have the magic_aneg bit,
748 * we try again */
749 if (phydev->drv->flags & PHY_HAS_MAGICANEG)
709 break; 750 break;
710 751
711 if (phydev->link) { 752 /* The timer expired, and we still
712 phydev->state = PHY_RUNNING; 753 * don't have a setting, so we try
713 netif_carrier_on(phydev->attached_dev); 754 * forcing it until we find one that
714 } else { 755 * works, starting from the fastest speed,
715 phydev->state = PHY_NOLINK; 756 * and working our way down */
716 netif_carrier_off(phydev->attached_dev); 757 idx = phy_find_valid(0, phydev->supported);
717 }
718 758
719 phydev->adjust_link(phydev->attached_dev); 759 phydev->speed = settings[idx].speed;
760 phydev->duplex = settings[idx].duplex;
720 761
721 } else if (0 == phydev->link_timeout--) { 762 phydev->autoneg = AUTONEG_DISABLE;
722 /* The counter expired, so either we
723 * switch to forced mode, or the
724 * magic_aneg bit exists, and we try aneg
725 * again */
726 if (!(phydev->drv->flags & PHY_HAS_MAGICANEG)) {
727 int idx;
728
729 /* We'll start from the
730 * fastest speed, and work
731 * our way down */
732 idx = phy_find_valid(0,
733 phydev->supported);
734
735 phydev->speed = settings[idx].speed;
736 phydev->duplex = settings[idx].duplex;
737
738 phydev->autoneg = AUTONEG_DISABLE;
739 phydev->state = PHY_FORCING;
740 phydev->link_timeout =
741 PHY_FORCE_TIMEOUT;
742
743 pr_info("Trying %d/%s\n",
744 phydev->speed,
745 DUPLEX_FULL ==
746 phydev->duplex ?
747 "FULL" : "HALF");
748 }
749 763
750 needs_aneg = 1; 764 pr_info("Trying %d/%s\n", phydev->speed,
765 DUPLEX_FULL ==
766 phydev->duplex ?
767 "FULL" : "HALF");
751 } 768 }
752 break; 769 break;
753 case PHY_NOLINK: 770 case PHY_NOLINK:
@@ -763,7 +780,7 @@ static void phy_timer(unsigned long data)
763 } 780 }
764 break; 781 break;
765 case PHY_FORCING: 782 case PHY_FORCING:
766 err = phy_read_status(phydev); 783 err = genphy_update_link(phydev);
767 784
768 if (err) 785 if (err)
769 break; 786 break;