/* start the watchdog. */
        hw->mac.get_link_status = 1;
+       schedule_work(&adapter->watchdog_task);
 }
 
 /**
                kfree_rcu(q_vector, rcu);
 }
 
+/* Need to wait a few seconds after link up to get diagnostic information from
+ * the phy
+ */
+static void igc_update_phy_info(struct timer_list *t)
+{
+       struct igc_adapter *adapter = from_timer(adapter, t, phy_info_timer);
+
+       igc_get_phy_info(&adapter->hw);
+}
+
+/**
+ * igc_has_link - check shared code for link and determine up/down
+ * @adapter: pointer to driver private info
+ */
+static bool igc_has_link(struct igc_adapter *adapter)
+{
+       struct igc_hw *hw = &adapter->hw;
+       bool link_active = false;
+
+       /* get_link_status is set on LSC (link status) interrupt or
+        * rx sequence error interrupt.  get_link_status will stay
+        * false until the igc_check_for_link establishes link
+        * for copper adapters ONLY
+        */
+       switch (hw->phy.media_type) {
+       case igc_media_type_copper:
+               if (!hw->mac.get_link_status)
+                       return true;
+               hw->mac.ops.check_for_link(hw);
+               link_active = !hw->mac.get_link_status;
+               break;
+       default:
+       case igc_media_type_unknown:
+               break;
+       }
+
+       if (hw->mac.type == igc_i225 &&
+           hw->phy.id == I225_I_PHY_ID) {
+               if (!netif_carrier_ok(adapter->netdev)) {
+                       adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
+               } else if (!(adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)) {
+                       adapter->flags |= IGC_FLAG_NEED_LINK_UPDATE;
+                       adapter->link_check_timeout = jiffies;
+               }
+       }
+
+       return link_active;
+}
+
 /**
  * igc_watchdog - Timer Call-back
  * @data: pointer to adapter cast into an unsigned long
 static void igc_watchdog(struct timer_list *t)
 {
        struct igc_adapter *adapter = from_timer(adapter, t, watchdog_timer);
+       /* Do the rest outside of interrupt context */
+       schedule_work(&adapter->watchdog_task);
+}
+
+static void igc_watchdog_task(struct work_struct *work)
+{
+       struct igc_adapter *adapter = container_of(work,
+                                                  struct igc_adapter,
+                                                  watchdog_task);
+       struct net_device *netdev = adapter->netdev;
+       struct igc_hw *hw = &adapter->hw;
+       struct igc_phy_info *phy = &hw->phy;
+       u16 phy_data, retry_count = 20;
+       u32 connsw;
+       u32 link;
+       int i;
+
+       link = igc_has_link(adapter);
+
+       if (adapter->flags & IGC_FLAG_NEED_LINK_UPDATE) {
+               if (time_after(jiffies, (adapter->link_check_timeout + HZ)))
+                       adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
+               else
+                       link = false;
+       }
+
+       /* Force link down if we have fiber to swap to */
+       if (adapter->flags & IGC_FLAG_MAS_ENABLE) {
+               if (hw->phy.media_type == igc_media_type_copper) {
+                       connsw = rd32(IGC_CONNSW);
+                       if (!(connsw & IGC_CONNSW_AUTOSENSE_EN))
+                               link = 0;
+               }
+       }
+       if (link) {
+               if (!netif_carrier_ok(netdev)) {
+                       u32 ctrl;
+
+                       hw->mac.ops.get_speed_and_duplex(hw,
+                                                        &adapter->link_speed,
+                                                        &adapter->link_duplex);
+
+                       ctrl = rd32(IGC_CTRL);
+                       /* Link status message must follow this format */
+                       netdev_info(netdev,
+                                   "igc: %s NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n",
+                                   netdev->name,
+                                   adapter->link_speed,
+                                   adapter->link_duplex == FULL_DUPLEX ?
+                                   "Full" : "Half",
+                                   (ctrl & IGC_CTRL_TFCE) &&
+                                   (ctrl & IGC_CTRL_RFCE) ? "RX/TX" :
+                                   (ctrl & IGC_CTRL_RFCE) ?  "RX" :
+                                   (ctrl & IGC_CTRL_TFCE) ?  "TX" : "None");
+
+                       /* check if SmartSpeed worked */
+                       igc_check_downshift(hw);
+                       if (phy->speed_downgraded)
+                               netdev_warn(netdev, "Link Speed was downgraded by SmartSpeed\n");
+
+                       /* adjust timeout factor according to speed/duplex */
+                       adapter->tx_timeout_factor = 1;
+                       switch (adapter->link_speed) {
+                       case SPEED_10:
+                               adapter->tx_timeout_factor = 14;
+                               break;
+                       case SPEED_100:
+                               /* maybe add some timeout factor ? */
+                               break;
+                       }
+
+                       if (adapter->link_speed != SPEED_1000)
+                               goto no_wait;
+
+                       /* wait for Remote receiver status OK */
+retry_read_status:
+                       if (!igc_read_phy_reg(hw, PHY_1000T_STATUS,
+                                             &phy_data)) {
+                               if (!(phy_data & SR_1000T_REMOTE_RX_STATUS) &&
+                                   retry_count) {
+                                       msleep(100);
+                                       retry_count--;
+                                       goto retry_read_status;
+                               } else if (!retry_count) {
+                                       dev_err(&adapter->pdev->dev, "exceed max 2 second\n");
+                               }
+                       } else {
+                               dev_err(&adapter->pdev->dev, "read 1000Base-T Status Reg\n");
+                       }
+no_wait:
+                       netif_carrier_on(netdev);
+
+                       /* link state has changed, schedule phy info update */
+                       if (!test_bit(__IGC_DOWN, &adapter->state))
+                               mod_timer(&adapter->phy_info_timer,
+                                         round_jiffies(jiffies + 2 * HZ));
+               }
+       } else {
+               if (netif_carrier_ok(netdev)) {
+                       adapter->link_speed = 0;
+                       adapter->link_duplex = 0;
+
+                       /* Links status message must follow this format */
+                       netdev_info(netdev, "igc: %s NIC Link is Down\n",
+                                   netdev->name);
+                       netif_carrier_off(netdev);
+
+                       /* link state has changed, schedule phy info update */
+                       if (!test_bit(__IGC_DOWN, &adapter->state))
+                               mod_timer(&adapter->phy_info_timer,
+                                         round_jiffies(jiffies + 2 * HZ));
+
+                       /* link is down, time to check for alternate media */
+                       if (adapter->flags & IGC_FLAG_MAS_ENABLE) {
+                               if (adapter->flags & IGC_FLAG_MEDIA_RESET) {
+                                       schedule_work(&adapter->reset_task);
+                                       /* return immediately */
+                                       return;
+                               }
+                       }
+
+               /* also check for alternate media here */
+               } else if (!netif_carrier_ok(netdev) &&
+                          (adapter->flags & IGC_FLAG_MAS_ENABLE)) {
+                       if (adapter->flags & IGC_FLAG_MEDIA_RESET) {
+                               schedule_work(&adapter->reset_task);
+                               /* return immediately */
+                               return;
+                       }
+               }
+       }
+
+       spin_lock(&adapter->stats64_lock);
+       igc_update_stats(adapter);
+       spin_unlock(&adapter->stats64_lock);
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               struct igc_ring *tx_ring = adapter->tx_ring[i];
+
+               if (!netif_carrier_ok(netdev)) {
+                       /* We've lost link, so the controller stops DMA,
+                        * but we've got queued Tx work that's never going
+                        * to get done, so reset controller to flush Tx.
+                        * (Do the reset outside of interrupt context).
+                        */
+                       if (igc_desc_unused(tx_ring) + 1 < tx_ring->count) {
+                               adapter->tx_timeout_count++;
+                               schedule_work(&adapter->reset_task);
+                               /* return immediately since reset is imminent */
+                               return;
+                       }
+               }
+
+               /* Force detection of hung controller every watchdog period */
+               set_bit(IGC_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags);
+       }
+
+       /* Cause software interrupt to ensure Rx ring is cleaned */
+       if (adapter->flags & IGC_FLAG_HAS_MSIX) {
+               u32 eics = 0;
+
+               for (i = 0; i < adapter->num_q_vectors; i++)
+                       eics |= adapter->q_vector[i]->eims_value;
+               wr32(IGC_EICS, eics);
+       } else {
+               wr32(IGC_ICS, IGC_ICS_RXDMT0);
+       }
+
+       /* Reset the timer */
+       if (!test_bit(__IGC_DOWN, &adapter->state)) {
+               if (adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)
+                       mod_timer(&adapter->watchdog_timer,
+                                 round_jiffies(jiffies +  HZ));
+               else
+                       mod_timer(&adapter->watchdog_timer,
+                                 round_jiffies(jiffies + 2 * HZ));
+       }
 }
 
 /**
 
        /* start the watchdog. */
        hw->mac.get_link_status = 1;
+       schedule_work(&adapter->watchdog_task);
 
        return IGC_SUCCESS;
 
        wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
 
        timer_setup(&adapter->watchdog_timer, igc_watchdog, 0);
+       timer_setup(&adapter->phy_info_timer, igc_update_phy_info, 0);
 
        INIT_WORK(&adapter->reset_task, igc_reset_task);
+       INIT_WORK(&adapter->watchdog_task, igc_watchdog_task);
 
        /* Initialize link properties that are user-changeable */
        adapter->fc_autoneg = true;
        set_bit(__IGC_DOWN, &adapter->state);
 
        del_timer_sync(&adapter->watchdog_timer);
+       del_timer_sync(&adapter->phy_info_timer);
 
        cancel_work_sync(&adapter->reset_task);
+       cancel_work_sync(&adapter->watchdog_task);
 
        /* Release control of h/w to f/w.  If f/w is AMT enabled, this
         * would have already happened in close and is redundant.