diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h
index ac67c0403592..17b241264c2d 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h
@@ -36,6 +36,17 @@ struct ngbe_mac_addr {
 	u64 pools;
 };
 
+enum ngbe_adapter_state {
+	NGBE_TESTING,
+	NGBE_DOWN,
+	NGBE_REMOVING,
+	NGBE_RESETTING,
+	NGBE_NEEDS_RESTART,
+	NGBE_SERVICE_SCHED,
+	NGBE_SERVICE_DIS,
+	NGBE_STATE_NBITS		/* must be last */
+};
+
 /* board specific private data structure */
 struct ngbe_adapter {
 	u8 __iomem *io_addr;    /* Mainly for iounmap use */
@@ -48,7 +59,17 @@ struct ngbe_adapter {
 	struct ngbe_mac_addr *mac_table;
 	u16 msg_enable;
 
+	DECLARE_BITMAP(state, NGBE_STATE_NBITS);
 	DECLARE_BITMAP(flags, NGBE_FLAGS_NBITS);
+	unsigned long serv_tmr_period;
+	unsigned long serv_tmr_prev;
+	struct timer_list serv_tmr;
+	struct work_struct serv_task;
+
+	unsigned long link_check_timeout;
+
+	u32 link_speed;
+	bool link_up;
 
 	/* Tx fast path data */
 	int num_tx_queues;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
index 274d54832579..915b27b25c1c 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
@@ -8,6 +8,7 @@
 #include "../libwx/wx_type.h"
 #include "../libwx/wx_hw.h"
 #include "ngbe_type.h"
+#include "ngbe_phy.h"
 #include "ngbe_hw.h"
 #include "ngbe.h"
 
@@ -106,6 +107,10 @@ int ngbe_reset_hw(struct ngbe_hw *hw)
 	wr32(wxhw, WX_MIS_RST, reset | rd32(wxhw, WX_MIS_RST));
 	ngbe_reset_misc(hw);
 
+	status = ngbe_phy_init(hw);
+	if (status)
+		return status;
+
 	/* Store the permanent mac address */
 	wx_get_mac_addr(wxhw, wxhw->mac.perm_addr);
 
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index ebf3fcdc4719..83cc61eb3be2 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -9,14 +9,18 @@
 #include <linux/aer.h>
 #include <linux/etherdevice.h>
 #include <net/ip.h>
+#include <linux/ethtool.h>
 
 #include "../libwx/wx_type.h"
 #include "../libwx/wx_hw.h"
 #include "ngbe_type.h"
+#include "ngbe_phy.h"
 #include "ngbe_hw.h"
 #include "ngbe.h"
 char ngbe_driver_name[] = "ngbe";
 
+static struct workqueue_struct *ngbe_wq;
+
 /* ngbe_pci_tbl - PCI Device ID Table
  *
  * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
@@ -39,6 +43,81 @@ static const struct pci_device_id ngbe_pci_tbl[] = {
 	{ .device = 0 }
 };
 
+/**
+ * ngbe_service_event_schedule - schedule the service task to wake up
+ * @adapter: board private structure
+ *
+ * If not already scheduled, this puts the task into the work queue.
+ */
+static void ngbe_service_event_schedule(struct ngbe_adapter *adapter)
+{
+	if (!test_bit(NGBE_SERVICE_DIS, adapter->state) &&
+	    !test_and_set_bit(NGBE_SERVICE_SCHED, adapter->state) &&
+	    !test_bit(NGBE_NEEDS_RESTART, adapter->state))
+		queue_work(ngbe_wq, &adapter->serv_task);
+}
+
+static void ngbe_service_event_complete(struct ngbe_adapter *adapter)
+{
+	WARN_ON(!test_bit(NGBE_SERVICE_SCHED, adapter->state));
+
+	/* flush memory to make sure state is correct before next watchdog */
+	smp_mb__before_atomic();
+	clear_bit(NGBE_SERVICE_SCHED, adapter->state);
+}
+
+/**
+ * ngbe_service_event_stop - stop service task and cancel works
+ * @adapter: board private structure
+ */
+static int ngbe_service_event_stop(struct ngbe_adapter *adapter)
+{
+	int ret;
+
+	ret = test_and_set_bit(NGBE_SERVICE_DIS, adapter->state);
+	if (adapter->serv_tmr.function)
+		del_timer_sync(&adapter->serv_tmr);
+	if (adapter->serv_task.func)
+		cancel_work_sync(&adapter->serv_task);
+	clear_bit(NGBE_SERVICE_SCHED, adapter->state);
+
+	return ret;
+}
+
+/**
+ * ngbe_service_timer - timer callback to schedule service task
+ * @t: pointer to timer_list
+ */
+static void ngbe_service_timer(struct timer_list *t)
+{
+	struct ngbe_adapter *adapter =
+			from_timer(adapter, t, serv_tmr);
+
+	mod_timer(&adapter->serv_tmr, round_jiffies(adapter->serv_tmr_period + jiffies));
+	ngbe_service_event_schedule(adapter);
+}
+
+static void ngbe_sync_mac_table(struct ngbe_adapter *adapter)
+{
+	struct ngbe_hw *hw = &adapter->hw;
+	struct wx_hw *wxhw = &hw->wxhw;
+	int i;
+
+	for (i = 0; i < wxhw->mac.num_rar_entries; i++) {
+		if (adapter->mac_table[i].state & NGBE_MAC_STATE_MODIFIED) {
+			if (adapter->mac_table[i].state & NGBE_MAC_STATE_IN_USE) {
+				wx_set_rar(wxhw, i,
+					   adapter->mac_table[i].addr,
+					   adapter->mac_table[i].pools,
+					   WX_PSR_MAC_SWC_AD_H_AV);
+			} else {
+				wx_clear_rar(wxhw, i);
+			}
+			adapter->mac_table[i].state &= ~(NGBE_MAC_STATE_MODIFIED);
+		}
+	}
+}
+
 static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr)
 {
 	struct ngbe_hw *hw = &adapter->hw;
@@ -52,6 +131,161 @@ static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr)
 		   WX_PSR_MAC_SWC_AD_H_AV);
 }
 
+static void ngbe_flush_sw_mac_table(struct ngbe_adapter *adapter)
+{
+	struct ngbe_hw *hw = &adapter->hw;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 i;
+
+	for (i = 0; i < wxhw->mac.num_rar_entries; i++) {
+		adapter->mac_table[i].state |= NGBE_MAC_STATE_MODIFIED;
+		adapter->mac_table[i].state &= ~NGBE_MAC_STATE_IN_USE;
+		memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
+		adapter->mac_table[i].pools = 0;
+	}
+	ngbe_sync_mac_table(adapter);
+}
+
+/**
+ * ngbe_watchdog_update_link_status - update the link status
+ * @adapter: pointer to the device adapter structure
+ **/
+static void ngbe_watchdog_update_link_status(struct ngbe_adapter *adapter)
+{
+	u32 link_speed = adapter->link_speed;
+	struct ngbe_hw *hw = &adapter->hw;
+	bool link_up = adapter->link_up;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 lan_speed = 0;
+	u32 reg;
+
+	if (!test_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags))
+		return;
+
+	link_speed = SPEED_1000;
+	link_up = true;
+	ngbe_phy_check_link(hw, &link_speed, &link_up, false);
+
+	if (link_up || time_after(jiffies, (adapter->link_check_timeout +
+					    4 * HZ)))
+		set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+
+	adapter->link_speed = link_speed;
+	switch (link_speed) {
+	case SPEED_1000:
+		lan_speed = 2;
+		break;
+	case SPEED_100:
+		lan_speed = 1;
+		break;
+	case SPEED_10:
+		lan_speed = 0;
+		break;
+	default:
+		break;
+	}
+	wr32m(wxhw, NGBE_CFG_LAN_SPEED, 0x3, lan_speed);
+
+	adapter->link_up = link_up;
+	adapter->link_speed = link_speed;
+	if (link_up) {
+		if (link_speed & (SPEED_1000 | SPEED_100 | SPEED_10)) {
+			reg = rd32(wxhw, WX_MAC_TX_CFG);
+			reg &= ~WX_MAC_TX_CFG_SPEED_MASK;
+			reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE;
+			wr32(wxhw, WX_MAC_TX_CFG, reg);
+		}
+		/* Re configure MAC RX */
+		reg = rd32(wxhw, WX_MAC_RX_CFG);
+		wr32(wxhw, WX_MAC_RX_CFG, reg);
+		wr32(wxhw, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
+		reg = rd32(wxhw, WX_MAC_WDG_TIMEOUT);
+		wr32(wxhw, WX_MAC_WDG_TIMEOUT, reg);
+	}
+}
+
+static void ngbe_watchdog_link_is_up(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	const char *speed_str;
+
+	/* only continue if link was previously down */
+	if (netif_carrier_ok(netdev))
+		return;
+
+	switch (adapter->link_speed) {
+	case SPEED_1000:
+		speed_str = "1 Gbps";
+		break;
+	case SPEED_100:
+		speed_str = "100 Mbps";
+		break;
+	case SPEED_10:
+		speed_str = "10 Mbps";
+		break;
+	default:
+		speed_str = "unknown speed";
+		break;
+	}
+
+	netif_info(adapter, drv, netdev,
+		   "NIC Link is Up %s\n", speed_str);
+
+	netif_carrier_on(netdev);
+}
+
+static void ngbe_watchdog_link_is_down(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+
+	adapter->link_up = false;
+	adapter->link_speed = 0;
+
+	/* only continue if link was up previously */
+	if (!netif_carrier_ok(netdev))
+		return;
+
+	netif_info(adapter, drv, netdev, "NIC Link is Down\n");
+	netif_carrier_off(netdev);
+}
+
+/**
+ * ngbe_watchdog_subtask - check and bring link up
+ * @adapter: pointer to the device adapter structure
+ **/
+static void ngbe_watchdog_subtask(struct ngbe_adapter *adapter)
+{
+	/* if interface is down do nothing */
+	if (test_bit(NGBE_DOWN, adapter->state) ||
+	    test_bit(NGBE_REMOVING, adapter->state) ||
+	    test_bit(NGBE_RESETTING, adapter->state))
+		return;
+
+	ngbe_watchdog_update_link_status(adapter);
+
+	if (adapter->link_up)
+		ngbe_watchdog_link_is_up(adapter);
+	else
+		ngbe_watchdog_link_is_down(adapter);
+}
+
+/**
+ * ngbe_service_task - manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+static void ngbe_service_task(struct work_struct *work)
+{
+	struct ngbe_adapter *adapter = container_of(work,
+						    struct ngbe_adapter,
+						    serv_task);
+
+	if (test_bit(NGBE_DOWN, adapter->state))
+		return;
+
+	ngbe_watchdog_subtask(adapter);
+	ngbe_service_event_complete(adapter);
+}
+
 /**
  *  ngbe_init_type_code - Initialize the shared code
  *  @hw: pointer to hardware structure
@@ -221,12 +455,96 @@ static void ngbe_oem_conf_in_rom(struct ngbe_hw *hw)
 			    &hw->phy.gphy_efuse[1]);
 }
 
+static void ngbe_reset(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	struct ngbe_hw *hw = &adapter->hw;
+	u8 old_addr[ETH_ALEN];
+	int err;
+
+	err = ngbe_reset_hw(hw);
+	if (err)
+		dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err);
+	/* do not flush user set addresses */
+	memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
+	ngbe_flush_sw_mac_table(adapter);
+	ngbe_mac_set_default_filter(adapter, old_addr);
+}
+
+static void ngbe_disable_device(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	struct ngbe_hw *hw = &adapter->hw;
+
+	/* signal that we are down to the interrupt handler */
+	if (test_and_set_bit(NGBE_DOWN, adapter->state))
+		return; /* do nothing if already down */
+
+	wx_disable_pcie_master(&hw->wxhw);
+	/* disable receives */
+	wx_disable_rx(&hw->wxhw);
+
+	/* call carrier off first to avoid false dev_watchdog timeouts */
+	netif_carrier_off(netdev);
+	netif_tx_disable(netdev);
+
+	clear_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+
+	del_timer_sync(&adapter->serv_tmr);
+}
+
 static void ngbe_down(struct ngbe_adapter *adapter)
 {
-	netif_carrier_off(adapter->netdev);
-	netif_tx_disable(adapter->netdev);
+	ngbe_disable_device(adapter);
+
+	ngbe_reset(adapter);
 };
 
+/**
+ * ngbe_phy_setlink_config - set up non-SFP+ link
+ * @hw: pointer to private hardware struct
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int ngbe_phy_setlink_config(struct ngbe_hw *hw)
+{
+	u32 speed = 0;
+	int ret = 0;
+
+	hw->phy.autoneg = 1;
+	speed = SPEED_1000 | SPEED_100 | SPEED_10;
+
+	if (hw->wol_enabled || hw->ncsi_enabled)
+		return 0;
+
+	ret = ngbe_phy_setup_link(hw, speed, false);
+
+	return ret;
+}
+
+static void ngbe_up_complete(struct ngbe_adapter *adapter)
+{
+	struct ngbe_hw *hw = &adapter->hw;
+	int err;
+
+	wx_control_hw(&hw->wxhw, true);
+
+	/* make sure to complete pre-operations */
+	smp_mb__before_atomic();
+	clear_bit(NGBE_DOWN, adapter->state);
+
+	err = ngbe_phy_setlink_config(hw);
+	if (err)
+		dev_err(&adapter->pdev->dev, "Phy hardware Error: %d\n", err);
+
+	/* bring the link up in the watchdog, this could race with our first
+	 * link up interrupt but shouldn't be a problem
+	 */
+	set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+	adapter->link_check_timeout = jiffies;
+	mod_timer(&adapter->serv_tmr, round_jiffies(jiffies + adapter->serv_tmr_period));
+}
+
 /**
  * ngbe_open - Called when a network interface is made active
  * @netdev: network interface device structure
@@ -239,10 +557,8 @@ static void ngbe_down(struct ngbe_adapter *adapter)
 static int ngbe_open(struct net_device *netdev)
 {
 	struct ngbe_adapter *adapter = netdev_priv(netdev);
-	struct ngbe_hw *hw = &adapter->hw;
-	struct wx_hw *wxhw = &hw->wxhw;
 
-	wx_control_hw(wxhw, true);
+	ngbe_up_complete(adapter);
 
 	return 0;
 }
@@ -491,6 +807,15 @@ static int ngbe_probe(struct pci_dev *pdev,
 	eth_hw_addr_set(netdev, wxhw->mac.perm_addr);
 	ngbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr);
 
+	/* setup service timer and periodic service task */
+	timer_setup(&adapter->serv_tmr, ngbe_service_timer, 0);
+	adapter->serv_tmr_period = HZ;
+	INIT_WORK(&adapter->serv_task, ngbe_service_task);
+	clear_bit(NGBE_SERVICE_SCHED, adapter->state);
+	clear_bit(NGBE_SERVICE_DIS, adapter->state);
+	/* since everything is good, start the service timer */
+	mod_timer(&adapter->serv_tmr, round_jiffies(jiffies + adapter->serv_tmr_period));
+
 	err = register_netdev(netdev);
 	if (err)
 		goto err_register;
@@ -505,6 +830,7 @@ static int ngbe_probe(struct pci_dev *pdev,
 	return 0;
 
 err_register:
+	ngbe_service_event_stop(adapter);
 	wx_control_hw(wxhw, false);
 err_free_mac_table:
 	kfree(adapter->mac_table);
@@ -531,6 +857,10 @@ static void ngbe_remove(struct pci_dev *pdev)
 	struct ngbe_adapter *adapter = pci_get_drvdata(pdev);
 	struct net_device *netdev;
 
+	set_bit(NGBE_REMOVING, adapter->state);
+	cancel_work_sync(&adapter->serv_task);
+	ngbe_service_event_stop(adapter);
+
 	netdev = adapter->netdev;
 	unregister_netdev(netdev);
 	pci_release_selected_regions(pdev,
@@ -550,7 +880,45 @@ static struct pci_driver ngbe_driver = {
 	.shutdown = ngbe_shutdown,
 };
 
-module_pci_driver(ngbe_driver);
+/**
+ * ngbe_module_init - Driver registration routine
+ *
+ * ngbe_module_init is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init ngbe_module_init(void)
+{
+	int status;
+
+	ngbe_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+	if (!ngbe_wq) {
+		pr_err("Failed to create workqueue\n");
+		return -ENOMEM;
+	}
+
+	status = pci_register_driver(&ngbe_driver);
+	if (status) {
+		pr_err("failed to register PCI driver, err %d\n", status);
+		destroy_workqueue(ngbe_wq);
+	}
+
+	return status;
+}
+module_init(ngbe_module_init);
+
+/**
+ * ngbe_module_exit - Driver exit cleanup routine
+ *
+ * ngbe_module_exit is called just before the driver is removed
+ * from memory.
+ */
+static void __exit ngbe_module_exit(void)
+{
+	pci_unregister_driver(&ngbe_driver);
+	destroy_workqueue(ngbe_wq);
+	pr_info("module unloaded\n");
+}
+module_exit(ngbe_module_exit);
 
 MODULE_DEVICE_TABLE(pci, ngbe_pci_tbl);
 MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@net-swift.com>");
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index f6b257e84319..672716e47042 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -83,6 +83,7 @@ enum NGBE_MSCA_CMD_value {
 #define NGBE_MSCC_BUSY				BIT(22)
 #define NGBE_MDIO_CLK(v)			((0x7 & (v)) << 19)
 
+#define NGBE_CFG_LAN_SPEED			0x14440
 /* GPIO Registers */
 #define NGBE_GPIO_DR				0x14800
 #define NGBE_GPIO_DDR				0x14804
