]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/phy/phy.c
phylib: two dynamic mii_bus allocation fallout fixes
[mv-sheeva.git] / drivers / net / phy / phy.c
index 7c9e6e349503eaf2a7b91448a05a73d19e499bb8..df4e6257d4a77e3e9bba739db57dec8543315d79 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/spinlock.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/mii.h>
@@ -58,51 +57,6 @@ void phy_print_status(struct phy_device *phydev)
 EXPORT_SYMBOL(phy_print_status);
 
 
-/**
- * phy_read - Convenience function for reading a given PHY register
- * @phydev: the phy_device struct
- * @regnum: register number to read
- *
- * NOTE: MUST NOT be called from interrupt context,
- * because the bus read/write functions may wait for an interrupt
- * to conclude the operation.
- */
-int phy_read(struct phy_device *phydev, u16 regnum)
-{
-       int retval;
-       struct mii_bus *bus = phydev->bus;
-
-       spin_lock_bh(&bus->mdio_lock);
-       retval = bus->read(bus, phydev->addr, regnum);
-       spin_unlock_bh(&bus->mdio_lock);
-
-       return retval;
-}
-EXPORT_SYMBOL(phy_read);
-
-/**
- * phy_write - Convenience function for writing a given PHY register
- * @phydev: the phy_device struct
- * @regnum: register number to write
- * @val: value to write to @regnum
- *
- * NOTE: MUST NOT be called from interrupt context,
- * because the bus read/write functions may wait for an interrupt
- * to conclude the operation.
- */
-int phy_write(struct phy_device *phydev, u16 regnum, u16 val)
-{
-       int err;
-       struct mii_bus *bus = phydev->bus;
-
-       spin_lock_bh(&bus->mdio_lock);
-       err = bus->write(bus, phydev->addr, regnum, val);
-       spin_unlock_bh(&bus->mdio_lock);
-
-       return err;
-}
-EXPORT_SYMBOL(phy_write);
-
 /**
  * phy_clear_interrupt - Ack the phy device's interrupt
  * @phydev: the phy_device struct
@@ -363,7 +317,8 @@ int phy_mii_ioctl(struct phy_device *phydev,
        switch (cmd) {
        case SIOCGMIIPHY:
                mii_data->phy_id = phydev->addr;
-               break;
+               /* fall through */
+
        case SIOCGMIIREG:
                mii_data->val_out = phy_read(phydev, mii_data->reg_num);
                break;
@@ -403,12 +358,14 @@ int phy_mii_ioctl(struct phy_device *phydev,
                
                if (mii_data->reg_num == MII_BMCR 
                                && val & BMCR_RESET
-                               && phydev->drv->config_init)
+                               && phydev->drv->config_init) {
+                       phy_scan_fixups(phydev);
                        phydev->drv->config_init(phydev);
+               }
                break;
 
        default:
-               return -ENOTTY;
+               return -EOPNOTSUPP;
        }
 
        return 0;
@@ -428,7 +385,7 @@ int phy_start_aneg(struct phy_device *phydev)
 {
        int err;
 
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
 
        if (AUTONEG_DISABLE == phydev->autoneg)
                phy_sanitize_settings(phydev);
@@ -449,13 +406,14 @@ int phy_start_aneg(struct phy_device *phydev)
        }
 
 out_unlock:
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
        return err;
 }
 EXPORT_SYMBOL(phy_start_aneg);
 
 
 static void phy_change(struct work_struct *work);
+static void phy_state_machine(struct work_struct *work);
 static void phy_timer(unsigned long data);
 
 /**
@@ -476,6 +434,7 @@ void phy_start_machine(struct phy_device *phydev,
 {
        phydev->adjust_state = handler;
 
+       INIT_WORK(&phydev->state_queue, phy_state_machine);
        init_timer(&phydev->phy_timer);
        phydev->phy_timer.function = &phy_timer;
        phydev->phy_timer.data = (unsigned long) phydev;
@@ -493,11 +452,12 @@ void phy_start_machine(struct phy_device *phydev,
 void phy_stop_machine(struct phy_device *phydev)
 {
        del_timer_sync(&phydev->phy_timer);
+       cancel_work_sync(&phydev->state_queue);
 
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
        if (phydev->state > PHY_UP)
                phydev->state = PHY_UP;
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
 
        phydev->adjust_state = NULL;
 }
@@ -539,11 +499,11 @@ static void phy_force_reduction(struct phy_device *phydev)
  * Must not be called from interrupt context, or while the
  * phydev->lock is held.
  */
-void phy_error(struct phy_device *phydev)
+static void phy_error(struct phy_device *phydev)
 {
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
        phydev->state = PHY_HALTED;
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
 }
 
 /**
@@ -705,10 +665,10 @@ static void phy_change(struct work_struct *work)
        if (err)
                goto phy_err;
 
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
        if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
                phydev->state = PHY_CHANGELINK;
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
 
        atomic_dec(&phydev->irq_disable);
        enable_irq(phydev->irq);
@@ -720,6 +680,12 @@ static void phy_change(struct work_struct *work)
        if (err)
                goto irq_enable_err;
 
+       /* Stop timer and run the state queue now.  The work function for
+        * state_queue will start the timer up again.
+        */
+       del_timer(&phydev->phy_timer);
+       schedule_work(&phydev->state_queue);
+
        return;
 
 irq_enable_err:
@@ -735,7 +701,7 @@ phy_err:
  */
 void phy_stop(struct phy_device *phydev)
 {
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
 
        if (PHY_HALTED == phydev->state)
                goto out_unlock;
@@ -751,7 +717,7 @@ void phy_stop(struct phy_device *phydev)
        phydev->state = PHY_HALTED;
 
 out_unlock:
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
 
        /*
         * Cannot call flush_scheduled_work() here as desired because
@@ -773,7 +739,7 @@ out_unlock:
  */
 void phy_start(struct phy_device *phydev)
 {
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
 
        switch (phydev->state) {
                case PHY_STARTING:
@@ -787,19 +753,26 @@ void phy_start(struct phy_device *phydev)
                default:
                        break;
        }
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
 }
 EXPORT_SYMBOL(phy_stop);
 EXPORT_SYMBOL(phy_start);
 
-/* PHY timer which handles the state machine */
-static void phy_timer(unsigned long data)
+/**
+ * phy_state_machine - Handle the state machine
+ * @work: work_struct that describes the work to be done
+ *
+ * Description: Scheduled by the state_queue workqueue each time
+ *   phy_timer is triggered.
+ */
+static void phy_state_machine(struct work_struct *work)
 {
-       struct phy_device *phydev = (struct phy_device *)data;
+       struct phy_device *phydev =
+                       container_of(work, struct phy_device, state_queue);
        int needs_aneg = 0;
        int err = 0;
 
-       spin_lock_bh(&phydev->lock);
+       mutex_lock(&phydev->lock);
 
        if (phydev->adjust_state)
                phydev->adjust_state(phydev->attached_dev);
@@ -965,7 +938,7 @@ static void phy_timer(unsigned long data)
                        break;
        }
 
-       spin_unlock_bh(&phydev->lock);
+       mutex_unlock(&phydev->lock);
 
        if (needs_aneg)
                err = phy_start_aneg(phydev);
@@ -976,3 +949,14 @@ static void phy_timer(unsigned long data)
        mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
 }
 
+/* PHY timer which schedules the state machine work */
+static void phy_timer(unsigned long data)
+{
+       struct phy_device *phydev = (struct phy_device *)data;
+
+       /*
+        * PHY I/O operations can potentially sleep so we ensure that
+        * it's done from a process context
+        */
+       schedule_work(&phydev->state_queue);
+}