]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/phy/phy.c
uml: network formatting
[linux-2.6-omap-h63xx.git] / drivers / net / phy / phy.c
index f71dab347667cd3fc33b408aaa55c0fdf7e4e570..9bc11773705b2beaf68064e6ba3166953c20a5f6 100644 (file)
@@ -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
@@ -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>
@@ -204,7 +205,7 @@ static const struct phy_setting settings[] = {
        },
 };
 
-#define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
+#define MAX_NUM_SETTINGS ARRAY_SIZE(settings)
 
 /**
  * phy_find_setting - find a PHY settings array entry that matches speed & duplex
@@ -261,7 +262,7 @@ void phy_sanitize_settings(struct phy_device *phydev)
 
        /* Sanitize settings based on PHY capabilities */
        if ((features & SUPPORTED_Autoneg) == 0)
-               phydev->autoneg = 0;
+               phydev->autoneg = AUTONEG_DISABLE;
 
        idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
                        features);
@@ -374,7 +375,7 @@ int phy_mii_ioctl(struct phy_device *phydev,
                if (mii_data->phy_id == phydev->addr) {
                        switch(mii_data->reg_num) {
                        case MII_BMCR:
-                               if (val & (BMCR_RESET|BMCR_ANENABLE))
+                               if ((val & (BMCR_RESET|BMCR_ANENABLE)) == 0)
                                        phydev->autoneg = AUTONEG_DISABLE;
                                else
                                        phydev->autoneg = AUTONEG_ENABLE;
@@ -409,6 +410,7 @@ int phy_mii_ioctl(struct phy_device *phydev,
 
        return 0;
 }
+EXPORT_SYMBOL(phy_mii_ioctl);
 
 /**
  * phy_start_aneg - start auto-negotiation for this PHY device
@@ -423,7 +425,7 @@ int phy_start_aneg(struct phy_device *phydev)
 {
        int err;
 
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
 
        if (AUTONEG_DISABLE == phydev->autoneg)
                phy_sanitize_settings(phydev);
@@ -444,7 +446,7 @@ int phy_start_aneg(struct phy_device *phydev)
        }
 
 out_unlock:
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
        return err;
 }
 EXPORT_SYMBOL(phy_start_aneg);
@@ -489,10 +491,10 @@ void phy_stop_machine(struct phy_device *phydev)
 {
        del_timer_sync(&phydev->phy_timer);
 
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
        if (phydev->state > PHY_UP)
                phydev->state = PHY_UP;
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
 
        phydev->adjust_state = NULL;
 }
@@ -536,9 +538,9 @@ static void phy_force_reduction(struct phy_device *phydev)
  */
 void phy_error(struct phy_device *phydev)
 {
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
        phydev->state = PHY_HALTED;
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
 }
 
 /**
@@ -561,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);
 
@@ -631,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",
@@ -661,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;
 }
@@ -689,11 +702,12 @@ static void phy_change(struct work_struct *work)
        if (err)
                goto phy_err;
 
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
        if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
                phydev->state = PHY_CHANGELINK;
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
 
+       atomic_dec(&phydev->irq_disable);
        enable_irq(phydev->irq);
 
        /* Reenable interrupts */
@@ -707,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);
 }
@@ -717,13 +732,11 @@ phy_err:
  */
 void phy_stop(struct phy_device *phydev)
 {
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
 
        if (PHY_HALTED == phydev->state)
                goto out_unlock;
 
-       phydev->state = PHY_HALTED;
-
        if (phydev->irq != PHY_POLL) {
                /* Disable PHY Interrupts */
                phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
@@ -732,8 +745,10 @@ void phy_stop(struct phy_device *phydev)
                phy_clear_interrupt(phydev);
        }
 
+       phydev->state = PHY_HALTED;
+
 out_unlock:
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
 
        /*
         * Cannot call flush_scheduled_work() here as desired because
@@ -755,7 +770,7 @@ out_unlock:
  */
 void phy_start(struct phy_device *phydev)
 {
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
 
        switch (phydev->state) {
                case PHY_STARTING:
@@ -769,7 +784,7 @@ void phy_start(struct phy_device *phydev)
                default:
                        break;
        }
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
 }
 EXPORT_SYMBOL(phy_stop);
 EXPORT_SYMBOL(phy_start);
@@ -781,7 +796,7 @@ static void phy_timer(unsigned long data)
        int needs_aneg = 0;
        int err = 0;
 
-       spin_lock(&phydev->lock);
+       spin_lock_bh(&phydev->lock);
 
        if (phydev->adjust_state)
                phydev->adjust_state(phydev->attached_dev);
@@ -947,7 +962,7 @@ static void phy_timer(unsigned long data)
                        break;
        }
 
-       spin_unlock(&phydev->lock);
+       spin_unlock_bh(&phydev->lock);
 
        if (needs_aneg)
                err = phy_start_aneg(phydev);