Merge backports 20160122

Change-Id: I5bcde969e55b9f1c62879d7ac27ac8cf2efe0043
diff --git a/.local-symbols b/.local-symbols
index ee8c920..fe0ef34 100644
--- a/.local-symbols
+++ b/.local-symbols
@@ -175,6 +175,10 @@
 ATH10K_DEBUGFS=
 ATH10K_TRACING=
 ATH10K_DFS_CERTIFIED=
+ATH10K_USE_NCNB_DESCR=
+ATH10K_USE_NCNB_SKB=
+ATH10K_USE_COMCERTO_WIFI_FASTPATH=
+ATH10K_USE_DMA_COHERENT_SKB=
 WCN36XX=
 WCN36XX_DEBUGFS=
 WLAN_VENDOR_ATMEL=
@@ -280,6 +284,7 @@
 P54_SPI=
 P54_SPI_DEFAULT_EEPROM=
 P54_LEDS=
+QUANTENNA_PCIE_HOST_MPS_FIX=
 WLAN_VENDOR_MARVELL=
 MWL8K=
 LIBERTAS=
diff --git a/backport-include/linux/nl80211.h b/backport-include/linux/nl80211.h
index fcb0b8b..2521809 100644
--- a/backport-include/linux/nl80211.h
+++ b/backport-include/linux/nl80211.h
@@ -3,8 +3,6 @@
 #include_next <linux/nl80211.h>
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-#define NL80211_FEATURE_SK_TX_STATUS 0
-#endif
+#define NL80211_FEATURE_SK_TX_STATUS 1
 
 #endif /* __BACKPORT_LINUX_NL80211_H */
diff --git a/compat/backport-3.15.c b/compat/backport-3.15.c
index 381fe65..a02556f 100644
--- a/compat/backport-3.15.c
+++ b/compat/backport-3.15.c
@@ -18,6 +18,8 @@
 #include <linux/vmalloc.h>
 #include <net/net_namespace.h>
 
+extern void vfree(const void *addr);
+
 #if IS_ENABLED(CPTCFG_IEEE802154_6LOWPAN)
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
 /* the above kernel dependency is set to match the dependencies file */
diff --git a/compat/compat-3.6.c b/compat/compat-3.6.c
index c82bfb8..e63fdf9 100644
--- a/compat/compat-3.6.c
+++ b/compat/compat-3.6.c
@@ -149,6 +149,7 @@
 EXPORT_SYMBOL_GPL(sg_alloc_table_from_pages);
 
 /* whoopsie ! */
+#if 0
 #ifndef CONFIG_COMMON_CLK
 int clk_enable(struct clk *clk)
 {
@@ -161,3 +162,4 @@
 }
 EXPORT_SYMBOL_GPL(clk_disable);
 #endif
+#endif
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 0b69794..905359c 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -23,6 +23,8 @@
 
 #include <linux/module.h>
 #include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -36,6 +38,8 @@
 #define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
 #define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
 
+static char *bdaddr_base;
+
 int btbcm_check_bdaddr(struct hci_dev *hdev)
 {
 	struct hci_rp_read_bd_addr *bda;
@@ -381,6 +385,62 @@
 	{ }
 };
 
+static int bachk(const char *str)
+{
+	if (!str)
+		return -1;
+
+	if (strlen(str) != 17)
+		return -1;
+
+	while (*str) {
+		if (!isxdigit(*str++))
+			return -1;
+
+		if (!isxdigit(*str++))
+			return -1;
+
+		if (*str == 0)
+			break;
+
+		if (*str++ != ':')
+			return -1;
+	}
+
+	return 0;
+}
+
+static int str2ba(const char *str, bdaddr_t *ba)
+{
+	int i;
+
+	if (bachk(str) < 0) {
+		memset(ba, 0, sizeof(*ba));
+		return -1;
+	}
+
+	for (i = 5; i >= 0; i--, str += 3)
+		ba->b[i] = simple_strtol(str, NULL, 16);
+
+	return 0;
+}
+
+static void btbcm_set_bdaddr_base(struct hci_dev *hdev)
+{
+	bdaddr_t addr;
+
+	if (!bdaddr_base)
+		return;
+
+	if (str2ba(bdaddr_base, &addr) < 0) {
+		BT_ERR("bdaddr_base is invalid");
+		return;
+	}
+
+	btbcm_set_bdaddr(hdev, &addr);
+	btbcm_reset(hdev);
+}
+
 int btbcm_setup_patchram(struct hci_dev *hdev)
 {
 	char fw_name[64];
@@ -466,6 +526,8 @@
 
 	err = request_firmware(&fw, fw_name, &hdev->dev);
 	if (err < 0) {
+		if (err == -ENOENT)
+			goto set_bdaddr;
 		BT_INFO("%s: BCM: Patch %s not found", hdev->name, fw_name);
 		return 0;
 	}
@@ -501,6 +563,9 @@
 	BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
 	kfree_skb(skb);
 
+set_bdaddr:
+	btbcm_set_bdaddr_base(hdev);
+
 	btbcm_check_bdaddr(hdev);
 
 	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
@@ -549,6 +614,9 @@
 }
 EXPORT_SYMBOL_GPL(btbcm_setup_apple);
 
+module_param(bdaddr_base, charp, 0644);
+MODULE_PARM_DESC(bdaddr_base, "Bluetooth adapter base address");
+
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION);
 MODULE_VERSION(VERSION);
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index f25a825..f249a4d 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -20,6 +20,8 @@
 
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <linux/mmc/sdio_func.h>
@@ -29,6 +31,8 @@
 
 #define VERSION "1.0"
 
+static char *bdaddr_base;
+
 /*
  * This function is called by interface specific interrupt handler.
  * It updates Power Save & Host Sleep states, and wakes up the main
@@ -329,6 +333,81 @@
 }
 EXPORT_SYMBOL_GPL(btmrvl_enable_hs);
 
+static int bachk(const char *str)
+{
+	if (!str)
+		return -1;
+
+	if (strlen(str) != 17)
+		return -1;
+
+	while (*str) {
+		if (!isxdigit(*str++))
+			return -1;
+
+		if (!isxdigit(*str++))
+			return -1;
+
+		if (*str == 0)
+			break;
+
+		if (*str++ != ':')
+			return -1;
+	}
+
+	return 0;
+}
+
+static int str2ba(const char *str, bdaddr_t *ba)
+{
+	int i;
+
+	if (bachk(str) < 0) {
+		memset(ba, 0, sizeof(*ba));
+		return -1;
+	}
+
+	for (i = 5; i >= 0; i--, str += 3)
+		ba->b[i] = simple_strtol(str, NULL, 16);
+
+	return 0;
+}
+
+typedef struct {
+	__u8 parameter_id;
+	__u8 bdaddr_len;
+	bdaddr_t bdaddr;
+} __packed btmrvl_write_bdaddr_t;
+
+static int btmrvl_set_bdaddr_base(struct btmrvl_private *priv)
+{
+	btmrvl_write_bdaddr_t param;
+	int ret;
+
+	if (!bdaddr_base)
+		return 0;
+
+	param.parameter_id = 0xfe;
+	param.bdaddr_len = sizeof(param.bdaddr);
+
+	if (str2ba(bdaddr_base, &param.bdaddr) < 0) {
+		BT_ERR("bdaddr_base is invalid");
+		return -EINVAL;
+	}
+
+	/* Set BD Address */
+	ret = btmrvl_send_sync_cmd(priv, 0xfc22, &param, sizeof(param));
+	if (ret)
+		BT_ERR("Set BD Address command failed");
+
+	/* Reset */
+	ret = btmrvl_send_sync_cmd(priv, HCI_OP_RESET, NULL, 0);
+	if (ret)
+		BT_ERR("HCI_OP_RESET command failed");
+
+	return ret;
+}
+
 int btmrvl_prepare_command(struct btmrvl_private *priv)
 {
 	int ret = 0;
@@ -562,6 +641,8 @@
 
 	btmrvl_send_hscfg_cmd(priv);
 
+	btmrvl_set_bdaddr_base(priv);
+
 	return 0;
 }
 
@@ -790,6 +871,9 @@
 }
 EXPORT_SYMBOL_GPL(btmrvl_remove_card);
 
+module_param(bdaddr_base, charp, 0644);
+MODULE_PARM_DESC(bdaddr_base, "Bluetooth adapter base address");
+
 MODULE_AUTHOR("Marvell International Ltd.");
 MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);
 MODULE_VERSION(VERSION);
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index cc3a779..a89ee21 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -26,6 +26,7 @@
 source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
+source "drivers/net/wireless/quantenna/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 2138b85..03b04b0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -11,6 +11,7 @@
 obj-$(CPTCFG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CPTCFG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CPTCFG_WLAN_VENDOR_MEDIATEK) += mediatek/
+obj-$(CPTCFG_WLAN_VENDOR_QUANTENNA) += quantenna/
 obj-$(CPTCFG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CPTCFG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CPTCFG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index c15948a..0e92d38 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -49,3 +49,42 @@
 	---help---
 	This option enables DFS support for initiating radiation on
 	ath10k.
+
+config ATH10K_USE_NCNB_DESCR
+	bool "Atheros support for GFP_DMA_NCNB for descriptors"
+	depends on ATH10K
+	depends on COMCERTO_ZONE_DMA_NCNB
+	default n
+	---help---
+	  This option enables use of GFP_DMA_NCNB for descriptors
+	  shared with the firmware, on platforms like the Mindspeed
+	  Comcerto C2000.
+
+config ATH10K_USE_NCNB_SKB
+	bool "Atheros support for GFP_DMA_NCNB for sk_buffs"
+	depends on ATH10K
+	depends on COMCERTO_ZONE_DMA_NCNB
+	default n
+	---help---
+	  This option enables use of GFP_DMA_NCNB for sk_buffs on
+	  platforms like the Mindspeed Comcerto C2000.
+
+config ATH10K_USE_DMA_COHERENT_SKB
+	bool "Atheros support for DMA coherent skb heads"
+	depends on ATH10K
+	depends on COMCERTO_DMA_COHERENT_SKB
+	depends on ATH10K_USE_COMCERTO_WIFI_FASTPATH
+	default n
+	---help---
+	  This option enables use of DMA coherent skb heads (data buffers) on
+	  platforms like the NXP (formerly Freescale) QorIQ LS1024A
+	  (f.k.a. Mindspeed Comcerto 2000). This requires a custom modification
+	  to the kernel that adds the dma_coherent field to struct sk_buff.
+
+config ATH10K_USE_COMCERTO_WIFI_FASTPATH
+	bool "Atheros support for comcerto_wifi_rx_fastpath"
+	depends on ATH10K
+	default n
+	---help---
+	  This option enables use of comcerto_wifi_rx_fastpath() on
+	  Mindspeed Comcerto 2000 platforms.
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index edf3629..e3696cc 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -313,6 +313,8 @@
 	/* Update Source Ring Write Index */
 	write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
 
+	mb();
+
 	/* WORKAROUND */
 	if (!(flags & CE_SEND_FLAG_GATHER))
 		ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
@@ -885,19 +887,26 @@
 	struct ath10k_ce_ring *src_ring;
 	u32 nentries = attr->src_nentries;
 	dma_addr_t base_addr;
+	gfp_t gfpflag;
 
 	nentries = roundup_pow_of_two(nentries);
 
 	src_ring = kzalloc(sizeof(*src_ring) +
 			   (nentries *
 			    sizeof(*src_ring->per_transfer_context)),
-			   GFP_KERNEL);
+			   GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (src_ring == NULL)
 		return ERR_PTR(-ENOMEM);
 
 	src_ring->nentries = nentries;
 	src_ring->nentries_mask = nentries - 1;
 
+#ifdef CPTCFG_ATH10K_USE_NCNB_DESCR
+	gfpflag = GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH|GFP_DMA_NCNB;
+#else
+	gfpflag = GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH;
+#endif
+
 	/*
 	 * Legacy platforms that do not support cache
 	 * coherent DMA are unsupported
@@ -906,7 +915,8 @@
 		dma_alloc_coherent(ar->dev,
 				   (nentries * sizeof(struct ce_desc) +
 				    CE_DESC_RING_ALIGN),
-				   &base_addr, GFP_KERNEL);
+				   &base_addr, gfpflag);
+
 	if (!src_ring->base_addr_owner_space_unaligned) {
 		kfree(src_ring);
 		return ERR_PTR(-ENOMEM);
@@ -937,7 +947,7 @@
 	dest_ring = kzalloc(sizeof(*dest_ring) +
 			    (nentries *
 			     sizeof(*dest_ring->per_transfer_context)),
-			    GFP_KERNEL);
+			    GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (dest_ring == NULL)
 		return ERR_PTR(-ENOMEM);
 
@@ -952,7 +962,7 @@
 		dma_alloc_coherent(ar->dev,
 				   (nentries * sizeof(struct ce_desc) +
 				    CE_DESC_RING_ALIGN),
-				   &base_addr, GFP_KERNEL);
+				   &base_addr, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (!dest_ring->base_addr_owner_space_unaligned) {
 		kfree(dest_ring);
 		return ERR_PTR(-ENOMEM);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index b41eb3f..c0b9797 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1336,6 +1336,16 @@
 {
 	struct ath10k *ar = container_of(work, struct ath10k, restart_work);
 
+	if (ar->restart_do_reset) {
+		char *argv[] = { "/bin/ath10k-reset", NULL };
+		ar->restart_do_reset = 0;
+		ath10k_err(ar, "trying to call userspace: ath10k-reset\n");
+		if (!call_usermodehelper(argv[0], argv, NULL, UMH_WAIT_EXEC)) {
+			return;
+		}
+		ath10k_err(ar, "failed to execute ath10k-reset.  trying driver-level reset.\n");
+	}
+
 	set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
 
 	/* Place a barrier to make sure the compiler doesn't reorder
@@ -2023,6 +2033,7 @@
 
 	INIT_WORK(&ar->register_work, ath10k_core_register_work);
 	INIT_WORK(&ar->restart_work, ath10k_core_restart);
+	ar->restart_do_reset = 0;
 
 	ret = ath10k_debug_create(ar);
 	if (ret)
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 80a4ca4..4d3f002 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -797,6 +797,7 @@
 
 	struct work_struct register_work;
 	struct work_struct restart_work;
+	int restart_do_reset;
 
 	/* cycle count is reported twice for each visited channel during scan.
 	 * access protected by data_lock */
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index dde720b..8b01e3e 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -232,7 +232,7 @@
 	bool enabled;
 	int i;
 
-	buf = kzalloc(buf_len, GFP_KERNEL);
+	buf = kzalloc(buf_len, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (!buf)
 		return -ENOMEM;
 
@@ -595,6 +595,7 @@
 	struct ath10k *ar = file->private_data;
 	char buf[32];
 	int ret;
+	size_t ocount = count;
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -643,7 +644,7 @@
 		goto exit;
 	}
 
-	ret = count;
+	ret = ocount;
 
 exit:
 	mutex_unlock(&ar->conf_mutex);
@@ -1946,7 +1947,7 @@
 	struct ath10k *ar = file->private_data;
 	char *buf;
 
-	buf = kzalloc(size, GFP_KERNEL);
+	buf = kzalloc(size, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (buf == NULL)
 		return -ENOMEM;
 
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 3900934..2329260 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -38,6 +38,9 @@
 	ATH10K_DBG_WMI_PRINT	= 0x00002000,
 	ATH10K_DBG_PCI_PS	= 0x00004000,
 	ATH10K_DBG_ANY		= 0xffffffff,
+
+	// Use high bit so conflicts are less likely with upstream updates.
+	ATH10K_DBG_BEACON	= 0x10000000,
 };
 
 enum ath10k_pktlog_filter {
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 5b3c6bc..ca927c0 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -34,7 +34,12 @@
 	struct sk_buff *skb;
 	struct ath10k_skb_cb *skb_cb;
 
+#ifdef CPTCFG_ATH10K_USE_NCNB_SKB
+	skb = __dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE,
+	                      GFP_DMA_NCNB | GFP_ATOMIC);
+#else
 	skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
+#endif
 	if (!skb)
 		return NULL;
 
@@ -762,7 +767,12 @@
 {
 	struct sk_buff *skb;
 
+#ifdef CPTCFG_ATH10K_USE_NCNB_SKB
+	skb = __dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr),
+	                      GFP_DMA_NCNB | GFP_ATOMIC);
+#else
 	skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
+#endif
 	if (!skb)
 		return NULL;
 
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 91afa3a..2b1d2e2 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -100,7 +100,15 @@
 
 	idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
 	while (num > 0) {
+#ifdef CPTCFG_ATH10K_USE_NCNB_SKB
+		skb = __dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN,
+		                      GFP_DMA_NCNB | GFP_ATOMIC);
+#elif defined(CPTCFG_ATH10K_USE_DMA_COHERENT_SKB)
+		skb = alloc_dma_coherent_skb(HTT_RX_BUF_SIZE +
+				HTT_RX_DESC_ALIGN);
+#else
 		skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+#endif
 		if (!skb) {
 			ret = -ENOMEM;
 			goto fail;
@@ -530,7 +538,7 @@
 
 	htt->rx_ring.netbufs_ring =
 		kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
-			GFP_KERNEL);
+			GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (!htt->rx_ring.netbufs_ring)
 		goto err_netbuf;
 
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index c5ec0b6..6deffaa 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -2850,7 +2850,7 @@
 	}
 
 	len = sizeof(struct wmi_channel_arg) * arg.n_channels;
-	arg.channels = kzalloc(len, GFP_KERNEL);
+	arg.channels = kzalloc(len, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (!arg.channels)
 		return -ENOMEM;
 
@@ -7165,7 +7165,7 @@
 	if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
 		channels = kmemdup(ath10k_2ghz_channels,
 				   sizeof(ath10k_2ghz_channels),
-				   GFP_KERNEL);
+				   GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 		if (!channels) {
 			ret = -ENOMEM;
 			goto err_free;
@@ -7183,7 +7183,7 @@
 	if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
 		channels = kmemdup(ath10k_5ghz_channels,
 				   sizeof(ath10k_5ghz_channels),
-				   GFP_KERNEL);
+				   GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 		if (!channels) {
 			ret = -ENOMEM;
 			goto err_free;
@@ -7204,6 +7204,15 @@
 		BIT(NL80211_IFTYPE_AP) |
 		BIT(NL80211_IFTYPE_MESH_POINT);
 
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+		/* TODO:  Have to deal with 2x2 chips if/when the come out. */
+		ar->cfg_tx_chainmask = TARGET_10X_TX_CHAIN_MASK;
+		ar->cfg_rx_chainmask = TARGET_10X_RX_CHAIN_MASK;
+	} else {
+		ar->cfg_tx_chainmask = TARGET_TX_CHAIN_MASK;
+		ar->cfg_rx_chainmask = TARGET_RX_CHAIN_MASK;
+	}
+
 	ar->hw->wiphy->available_antennas_rx = ar->cfg_rx_chainmask;
 	ar->hw->wiphy->available_antennas_tx = ar->cfg_tx_chainmask;
 
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index d43ced0..a926b61 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -750,7 +750,14 @@
 	dma_addr_t paddr;
 	int ret;
 
+#ifdef CPTCFG_ATH10K_USE_NCNB_SKB
+	skb = __dev_alloc_skb(pipe->buf_sz,
+	                      GFP_DMA_NCNB | GFP_ATOMIC);
+#elif defined(CPTCFG_ATH10K_USE_DMA_COHERENT_SKB)
+	skb = alloc_dma_coherent_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+#else
 	skb = dev_alloc_skb(pipe->buf_sz);
+#endif
 	if (!skb)
 		return -ENOMEM;
 
@@ -1403,6 +1410,9 @@
 
 	spin_unlock_bh(&ar->data_lock);
 
+reset:
+	ath10k_err(ar, "scheduling ath10k-reset\n");
+	ar->restart_do_reset = 1;
 	queue_work(ar->workqueue, &ar->restart_work);
 }
 
@@ -1734,7 +1744,7 @@
 	if (resp && resp_len && *resp_len == 0)
 		return -EINVAL;
 
-	treq = kmemdup(req, req_len, GFP_KERNEL);
+	treq = kmemdup(req, req_len, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 	if (!treq)
 		return -ENOMEM;
 
@@ -1746,7 +1756,7 @@
 	}
 
 	if (resp && resp_len) {
-		tresp = kzalloc(*resp_len, GFP_KERNEL);
+		tresp = kzalloc(*resp_len, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
 		if (!tresp) {
 			ret = -ENOMEM;
 			goto err_req;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 6009b78..ab8eaa3 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -5948,6 +5948,8 @@
 	cmd->disable_hw_ack  = __cpu_to_le32(arg->disable_hw_ack);
 	cmd->beacon_interval = __cpu_to_le32(arg->bcn_intval);
 	cmd->dtim_period     = __cpu_to_le32(arg->dtim_period);
+	printk(KERN_WARNING "ath10k_wmi_vdev_start_restart: dtim_period=%d\n",
+	       arg->dtim_period);
 	cmd->flags           = __cpu_to_le32(flags);
 	cmd->bcn_tx_rate     = __cpu_to_le32(arg->bcn_tx_rate);
 	cmd->bcn_tx_power    = __cpu_to_le32(arg->bcn_tx_power);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index de3369d..9c2c77a 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -21,6 +21,7 @@
 #include <linux/ath9k_platform.h>
 #include <linux/module.h>
 #include <linux/relay.h>
+#include <linux/spinlock.h>
 #include <net/ieee80211_radiotap.h>
 
 #include "ath9k.h"
@@ -37,8 +38,23 @@
 MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 
+static DEFINE_SPINLOCK(debug_lock);
 static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
-module_param_named(debug, ath9k_debug, uint, 0);
+static int *ath_common_debug = NULL;
+static int ath9k_update_debug(const char *val, const struct kernel_param *kp) {
+	int ret = param_set_uint(val, kp);
+	if (ret < 0) return ret;
+
+	spin_lock(&debug_lock);
+	if (ath_common_debug) *ath_common_debug = ath9k_debug;
+	spin_unlock(&debug_lock);
+	return 0;
+}
+static const struct kernel_param_ops ath9k_debug_param_ops = {
+	.set = ath9k_update_debug,
+	.get = param_get_uint,
+};
+module_param_cb(debug, &ath9k_debug_param_ops, &ath9k_debug, 0644);
 MODULE_PARM_DESC(debug, "Debugging mask");
 
 int ath9k_modparam_nohwcrypt;
@@ -587,10 +603,14 @@
 	common->ah = ah;
 	common->hw = sc->hw;
 	common->priv = sc;
-	common->debug_mask = ath9k_debug;
 	common->btcoex_enabled = ath9k_btcoex_enable == 1;
 	common->disable_ani = false;
 
+	spin_lock(&debug_lock);
+	ath_common_debug = &common->debug_mask;
+	spin_unlock(&debug_lock);
+	common->debug_mask = ath9k_debug;
+
 	/*
 	 * Platform quirks.
 	 */
@@ -1015,6 +1035,9 @@
 void ath9k_deinit_device(struct ath_softc *sc)
 {
 	struct ieee80211_hw *hw = sc->hw;
+	spin_lock(&debug_lock);
+	ath_common_debug = NULL;
+	spin_unlock(&debug_lock);
 
 	ath9k_ps_wakeup(sc);
 
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e0aa4a4..e5a48f9 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -287,7 +287,6 @@
 
 	__ath_cancel_work(sc);
 
-	disable_irq(sc->irq);
 	tasklet_disable(&sc->intr_tq);
 	tasklet_disable(&sc->bcon_tasklet);
 	spin_lock_bh(&sc->sc_pcu_lock);
@@ -334,7 +333,6 @@
 		r = -EIO;
 
 out:
-	enable_irq(sc->irq);
 	spin_unlock_bh(&sc->sc_pcu_lock);
 	tasklet_enable(&sc->bcon_tasklet);
 	tasklet_enable(&sc->intr_tq);
@@ -520,6 +518,11 @@
 	if (!ath9k_hw_intrpend(ah))
 		return IRQ_NONE;
 
+	if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
+		ath9k_hw_kill_interrupts(ah);
+		return IRQ_HANDLED;
+	}
+
 	/*
 	 * Figure out the reason(s) for the interrupt.  Note
 	 * that the hal returns a pseudo-ISR that may include
@@ -530,9 +533,6 @@
 	ath9k_debug_sync_cause(sc, sync_cause);
 	status &= ah->imask;	/* discard unasked-for bits */
 
-	if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
-		return IRQ_HANDLED;
-
 	/*
 	 * If there are no status bits set, then this interrupt was not
 	 * for me (should have been caught above).
@@ -605,7 +605,6 @@
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	int r;
 
-	ath9k_hw_kill_interrupts(sc->sc_ah);
 	set_bit(ATH_OP_HW_RESET, &common->op_flags);
 
 	ath9k_ps_wakeup(sc);
@@ -626,7 +625,6 @@
 #ifdef CPTCFG_ATH9K_DEBUGFS
 	RESET_STAT_INC(sc, type);
 #endif
-	ath9k_hw_kill_interrupts(sc->sc_ah);
 	set_bit(ATH_OP_HW_RESET, &common->op_flags);
 	ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
 }
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index e7adef7..85b5df0 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -1361,6 +1361,16 @@
 			     const u8 *mac, struct station_info *sinfo)
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+	struct mwifiex_sta_node *node;
+
+	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+		list_for_each_entry(node, &priv->sta_list, list) {
+			if (memcmp(mac, node->mac_addr, ETH_ALEN) == 0)
+				return mwifiex_dump_station_info(priv, node,
+								 sinfo);
+		}
+		return -ENOENT;
+	}
 
 	if (!priv->media_connected)
 		return -ENOENT;
diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
index cb25aa7..5f626bc 100644
--- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
@@ -25,6 +25,7 @@
 #include "wmm.h"
 #include "11n.h"
 #include "11ac.h"
+#include <linux/ratelimit.h>
 
 /*
  * This function initializes a command node.
@@ -59,11 +60,13 @@
 {
 	struct cmd_ctrl_node *cmd_node;
 	unsigned long flags;
+	static DEFINE_RATELIMIT_STATE(ratelimit, 120 * HZ, 10);
 
 	spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
 	if (list_empty(&adapter->cmd_free_q)) {
-		mwifiex_dbg(adapter, ERROR,
-			    "GET_CMD_NODE: cmd node not available\n");
+		if ((__ratelimit(&ratelimit)))
+			mwifiex_dbg(adapter, ERROR,	
+				    "GET_CMD_NODE: cmd node not available\n");
 		spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
 		return NULL;
 	}
@@ -503,6 +506,7 @@
 	struct mwifiex_adapter *adapter = priv->adapter;
 	struct cmd_ctrl_node *cmd_node;
 	struct host_cmd_ds_command *cmd_ptr;
+	static DEFINE_RATELIMIT_STATE(ratelimit, 120 * HZ, 10);
 
 	if (!adapter) {
 		pr_err("PREP_CMD: adapter is NULL\n");
@@ -545,8 +549,9 @@
 	cmd_node = mwifiex_get_cmd_node(adapter);
 
 	if (!cmd_node) {
-		mwifiex_dbg(adapter, ERROR,
-			    "PREP_CMD: no free cmd node\n");
+		if (__ratelimit(&ratelimit))
+			mwifiex_dbg(adapter, ERROR,
+			            "PREP_CMD: no free cmd node\n");
 		return -1;
 	}
 
diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
index 0b9c580..7d0465a 100644
--- a/drivers/net/wireless/marvell/mwifiex/debugfs.c
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -824,6 +824,43 @@
 	return ret;
 }
 
+/*
+ * Proc crash write handler.
+ *
+ * This function is called when the 'crash' file is opened for writing.
+ *
+ * Write a '1' to this file to simulate a device disconnect/reconnect.  In
+ * the future, other numbers may be used to simulate different kinds of
+ * crashes.
+ */
+static ssize_t
+mwifiex_crash_write(struct file *file,
+			const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct mwifiex_private *priv =
+		(struct mwifiex_private *) file->private_data;
+	char buf[16];
+	int mode;
+
+	memset(buf, 0, sizeof(buf));
+	if (simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count) < 0) {
+		return -EFAULT;
+	}
+
+	mode = simple_strtol(buf, NULL, 0);
+	*ppos += count;
+	switch (mode) {
+	case 1:
+		pr_warn("causing crash: disconnect/reconnect");
+		priv->adapter->if_ops.card_reset(priv->adapter);
+		break;
+	default:
+		return -EIO;
+	}
+
+	return count;
+}
+
 static ssize_t
 mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf,
 			    size_t count, loff_t *ppos)
@@ -934,6 +971,7 @@
 MWIFIEX_DFS_FILE_READ_OPS(device_dump);
 MWIFIEX_DFS_FILE_OPS(regrdwr);
 MWIFIEX_DFS_FILE_OPS(rdeeprom);
+MWIFIEX_DFS_FILE_WRITE_OPS(crash);
 MWIFIEX_DFS_FILE_OPS(memrw);
 MWIFIEX_DFS_FILE_OPS(hscfg);
 MWIFIEX_DFS_FILE_OPS(histogram);
@@ -961,6 +999,7 @@
 	MWIFIEX_DFS_ADD_FILE(getlog);
 	MWIFIEX_DFS_ADD_FILE(regrdwr);
 	MWIFIEX_DFS_ADD_FILE(rdeeprom);
+	MWIFIEX_DFS_ADD_FILE(crash);
 	MWIFIEX_DFS_ADD_FILE(device_dump);
 	MWIFIEX_DFS_ADD_FILE(memrw);
 	MWIFIEX_DFS_ADD_FILE(hscfg);
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index ac9591d..31ab9b8 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -31,6 +31,12 @@
 const char driver_version[] = "mwifiex " VERSION " (%s) ";
 static char *cal_data_cfg;
 module_param(cal_data_cfg, charp, 0);
+static char *wifi_addr;
+module_param(wifi_addr, charp, 0644);
+MODULE_PARM_DESC(wifi_addr, "Wifi adapter base mac address");
+
+extern bool mac_pton(const char *s, u8 *mac);
+static int mwifiex_set_mac_address(struct net_device *dev, void *addr);
 
 static unsigned short driver_mode;
 module_param(driver_mode, ushort, 0);
@@ -574,12 +580,12 @@
 	}
 
 	rtnl_lock();
-	/* Create station interface by default */
-	wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
-					NL80211_IFTYPE_STATION, NULL, NULL);
+	/* Create AP interface by default for Google Fiber TV Box */
+	wdev = mwifiex_add_virtual_intf(adapter->wiphy, "wlan%d", NET_NAME_ENUM,
+					NL80211_IFTYPE_AP, NULL, NULL);
 	if (IS_ERR(wdev)) {
 		mwifiex_dbg(adapter, ERROR,
-			    "cannot create default STA interface\n");
+			    "cannot create default AP interface\n");
 		rtnl_unlock();
 		goto err_add_intf;
 	}
@@ -608,6 +614,23 @@
 	}
 	rtnl_unlock();
 
+	/* set mac address if provided */
+	if (!wifi_addr) {
+		pr_info("%s: optionally set macaddr with wifi_addr=XX:XX:XX:XX:XX:XX\n", __func__);
+	} else {
+		struct sockaddr hwaddr = { 0 };
+		if (!mac_pton(wifi_addr, hwaddr.sa_data)) {
+			pr_err("%s: failed to parse macaddr: %s\n", __func__, wifi_addr);
+		} else {
+			struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_ROLE_UAP];
+			if (priv && priv->netdev && mwifiex_set_mac_address(priv->netdev, &hwaddr)) {
+				pr_err("%s: failed to set wlan0 macaddr: %s\n", __func__, wifi_addr);
+			} else {
+				pr_info("%s: set wlan0 macaddr to %s\n", __func__, wifi_addr);
+			}
+		}
+	}
+
 	mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
 	mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt);
 	goto done;
diff --git a/drivers/net/wireless/quantenna/Kconfig b/drivers/net/wireless/quantenna/Kconfig
new file mode 100644
index 0000000..878e558
--- /dev/null
+++ b/drivers/net/wireless/quantenna/Kconfig
@@ -0,0 +1,11 @@
+menu "Quantenna drivers"
+	config QUANTENNA_PCIE_HOST
+		tristate "PCIe host"
+		default n
+		depends on m
+
+	config QUANTENNA_PCIE_HOST_MPS_FIX
+		bool "PCIe MPS fix"
+		default n
+		depends on QUANTENNA_PCIE_HOST
+endmenu
diff --git a/drivers/net/wireless/quantenna/pcie2/host/arm/Makefile b/drivers/net/wireless/quantenna/pcie2/host/arm/Makefile
new file mode 100644
index 0000000..c2375cd
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/arm/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for arm platform
+#
+
+EXTRA_CFLAGS	+= -Wall -Wno-deprecated-declarations	\
+		   -I$(src)		\
+		   -I$(src)/../../include \
+		   -I$(src)/../common
+
+EXTRA_CFLAGS    += -DQTN_TX_SKBQ_SUPPORT -DQTN_WAKEQ_SUPPORT
+
+PWD	:= $(shell pwd)
+
+default: all
+
+COMMON_DIR	:= ../common
+qdpc-host-objs   := $(COMMON_DIR)/qdpc_init.o $(COMMON_DIR)/qdpc_pcie.o $(COMMON_DIR)/topaz_vnet.o qdpc_platform.o
+obj-m           :=  qdpc-host.o
+
+qdpc_host.o: $(qdpc-host-objs)
+	ld -r $^ -o $@
+
+all:
+	make -C $(KERNELDIR) $(CROSS) M=$(PWD) modules
+
+
+clean:
+	rm -rf $(COMMON_DIR)/.*.cmd $(COMMON_DIR)/.tmp_versions
+	rm -rf Module.markers  Module.symvers modules.order *~ $(qdpc-host-objs) *.o *.ko *.mod.o *.mod.c
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/arm/qdpc_platform.c b/drivers/net/wireless/quantenna/pcie2/host/arm/qdpc_platform.c
new file mode 100644
index 0000000..d26114a
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/arm/qdpc_platform.c
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+
+#include <linux/interrupt.h>
+
+#include <qdpc_platform.h>
+#include <topaz_vnet.h>
+#include <qdpc_regs.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+/*
+ * Enable MSI interrupt of PCIe.
+ */
+void enable_vmac_ints(struct vmac_priv *vmp)
+{
+	volatile uint32_t *dma_wrd_imwr = QDPC_BAR_VADDR(vmp->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET);
+
+	writel(vmp->dma_msi_imwr, dma_wrd_imwr);
+}
+
+/*
+ * Disable MSI interrupt of PCIe.
+ */
+void disable_vmac_ints(struct vmac_priv *vmp)
+{
+	volatile uint32_t *dma_wrd_imwr = QDPC_BAR_VADDR(vmp->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET);
+
+	writel(vmp->dma_msi_dummy, dma_wrd_imwr);
+}
+
+
+/*
+ * Enable interrupt for detecting EP reset.
+ */
+void enable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Disable interrupt for detecting EP reset.
+ */
+void disable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Interrupt context for detecting EP reset.
+ * This function should do:
+ *   1. check interrupt status to see if EP reset.
+ *   2. if EP reset, handle it.
+ */
+void handle_ep_rst_int(struct net_device *ndev)
+{
+}
diff --git a/drivers/net/wireless/quantenna/pcie2/host/arm/qdpc_platform.h b/drivers/net/wireless/quantenna/pcie2/host/arm/qdpc_platform.h
new file mode 100644
index 0000000..b3f678b
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/arm/qdpc_platform.h
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+#ifndef __QDPC_PFDEP_H__
+#define __QDPC_PFDEP_H__
+
+#include <linux/version.h>
+
+#include <topaz_vnet.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define IOREMAP      ioremap_wc
+#else    /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) */
+#define IOREMAP      ioremap
+#endif
+
+/* IO functions */
+#ifndef readb
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#endif
+
+#ifndef readw
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#endif
+
+#ifndef readl
+#define readl(addr) (*(volatile unsigned int *) (addr))
+#endif
+
+#ifndef writeb
+#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b))
+#endif
+
+#ifndef writew
+#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b))
+#endif
+
+#ifndef writel
+#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b))
+#endif
+
+#ifndef virt_to_bus
+#define virt_to_bus virt_to_phys
+#endif
+
+/* Bit number and mask of MSI in the interrupt mask and status register */
+#define	QDPC_INTR_MSI_BIT		0
+#define QDPC_INTR_MSI_MASK		(1 << QDPC_INTR_MSI_BIT)
+
+/* Enable MSI interrupt of PCIe */
+extern void enable_vmac_ints(struct vmac_priv *vmp);
+/* Disable MSI interrupt of PCIe */
+extern void disable_vmac_ints(struct vmac_priv *vmp);
+
+/* Enable interrupt for detecting EP reset */
+extern void enable_ep_rst_detection(struct net_device *ndev);
+/* Disable interrupt for detecting EP reset */
+extern void disable_ep_rst_detection(struct net_device *ndev);
+/* Interrupt context for detecting EP reset */
+extern void handle_ep_rst_int(struct net_device *ndev);
+
+/* Allocated buffer size for a packet */
+#define SKB_BUF_SIZE		2048
+
+/* Transmit Queue Length */
+#define QDPC_TX_QUEUE_SIZE	180
+
+/* Receive Queue Length */
+#define QDPC_RX_QUEUE_SIZE	384
+
+/* Customer defined function	*/
+#define qdpc_platform_init()                  0
+#define qdpc_platform_exit()                  do { } while(0)
+
+/* PCIe driver update resource in PCI configure space after EP reset */
+#define qdpc_update_hw_bar(pdev, index)       do { } while(0)
+
+/* TODO: If MSI IRQ-loss issue can be fixed, remove macro below */
+/*#define QDPC_PLATFORM_IRQ_FIXUP*/
+
+#endif /* __QDPC_PFDEP_H__ */
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
new file mode 100644
index 0000000..09c8112
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
@@ -0,0 +1,938 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/netlink.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+
+#include "qdpc_config.h"
+#include "qdpc_debug.h"
+#include "qdpc_init.h"
+#include "qdpc_regs.h"
+#include "qdpc_platform.h"
+#include "topaz_vnet.h"
+#define QDPC_TOPAZ_IMG		"topaz-linux.lzma.img"
+#define QDPC_TOPAZ_UBOOT	"u-boot.bin"
+#define MAX_IMG_NUM		2
+
+#define EP_BOOT_FROM_FLASH 0
+
+#ifndef MEMORY_START_ADDRESS
+#define MEMORY_START_ADDRESS virt_to_bus((void *)PAGE_OFFSET)
+#endif
+
+static unsigned int tlp_mps = 256;
+module_param(tlp_mps, uint, 0644);
+MODULE_PARM_DESC(tlp_mps, "Default PCIe Max_Payload_Size");
+
+/*
+ * Define EP state during host suspend
+ * 0 = EP does not power off
+ * 1 = EP power off
+ */
+#define EP_SUSPEND_MODE_RUNNING	0
+#define EP_SUSPEND_MODE_PWR_OFF	1
+static unsigned int suspend_mode = EP_SUSPEND_MODE_RUNNING;
+module_param(suspend_mode, uint, 0644);
+MODULE_PARM_DESC(suspend_mode, "Default suspend behavior");
+static unsigned int suspend_flag = 0;
+
+/* Quantenna PCIE vendor and device identifiers  */
+static struct pci_device_id qdpc_pcie_ids[] = {
+	{PCI_DEVICE(QDPC_VENDOR_ID, QDPC_DEVICE_ID),},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, qdpc_pcie_ids);
+
+static int qdpc_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id);
+static void qdpc_pcie_remove(struct pci_dev *pdev);
+static int qdpc_boot_thread(void *data);
+static void qdpc_nl_recv_msg(struct sk_buff *skb);
+int qdpc_init_netdev(struct net_device  **net_dev, struct pci_dev *pdev);
+pci_ers_result_t qdpc_pcie_slot_reset(struct pci_dev *dev);
+static void qdpc_pcie_shutdown(struct pci_dev *pdev);
+
+
+char qdpc_pcie_driver_name[] = "qdpc_host";
+
+static struct pci_error_handlers qdpc_err_hdl = {
+        .slot_reset = qdpc_pcie_slot_reset,
+};
+
+static struct pci_driver qdpc_pcie_driver = {
+	.name     = qdpc_pcie_driver_name,
+	.id_table = qdpc_pcie_ids,
+	.probe    = qdpc_pcie_probe,
+	.remove   = qdpc_pcie_remove,
+#ifdef CONFIG_QTN_PM
+	.suspend  = qdpc_pcie_suspend,
+	.resume  = qdpc_pcie_resume,
+#endif
+        .err_handler = &qdpc_err_hdl,
+	.shutdown = qdpc_pcie_shutdown,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+struct netlink_kernel_cfg qdpc_netlink_cfg = {
+	.groups   = 0,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	.flags    = 0,
+#endif
+	.input    = qdpc_nl_recv_msg,
+	.cb_mutex = NULL,
+	.bind     = NULL,
+};
+#endif
+
+struct sock *qdpc_nl_sk = NULL;
+int qdpc_clntPid = 0;
+
+unsigned int (*qdpc_pci_readl)(void *addr) = qdpc_readl;
+void (*qdpc_pci_writel)(unsigned int val, void *addr) = qdpc_writel;
+
+static int qdpc_bootpoll(struct vmac_priv *p, uint32_t state)
+{
+	while (!kthread_should_stop() && (qdpc_isbootstate(p,state) == 0)) {
+		if (qdpc_booterror(p))
+			return -1;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(QDPC_SCHED_TIMEOUT);
+	}
+	return 0;
+}
+static void booterror(qdpc_pcie_bda_t *bda)
+{
+	if (PCIE_BDA_TARGET_FWLOAD_ERR & qdpc_pci_readl(&bda->bda_flags))
+		printk("EP boot from download firmware failed!\n");
+	else if (PCIE_BDA_TARGET_FBOOT_ERR & qdpc_pci_readl(&bda->bda_flags))
+		printk("EP boot from flash failed! Please check if there is usable image in Target flash.\n");
+	else
+		printk("EP boot get in error, dba flag: 0x%x\n", qdpc_pci_readl(&bda->bda_flags));
+}
+
+static void qdpc_pci_endian_detect(struct vmac_priv *priv)
+{
+	__iomem qdpc_pcie_bda_t *bda = priv->bda;
+	volatile uint32_t pci_endian;
+
+	writel(QDPC_PCI_ENDIAN_DETECT_DATA, &bda->bda_pci_endian);
+	mmiowb();
+	writel(QDPC_PCI_ENDIAN_VALID_STATUS, &bda->bda_pci_pre_status);
+
+	while (readl(&bda->bda_pci_post_status) != QDPC_PCI_ENDIAN_VALID_STATUS) {
+		if (kthread_should_stop())
+			break;
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(QDPC_SCHED_TIMEOUT);
+	}
+
+	pci_endian = readl(&bda->bda_pci_endian);
+	if (pci_endian == QDPC_PCI_LITTLE_ENDIAN) {
+		qdpc_pci_readl = qdpc_readl;
+		qdpc_pci_writel = qdpc_writel;
+		printk("PCI memory is little endian\n");
+	} else if (pci_endian == QDPC_PCI_BIG_ENDIAN) {
+		qdpc_pci_readl = qdpc_le32_readl;
+		qdpc_pci_writel = qdpc_le32_writel;
+		printk("PCI memory is big endian\n");
+	} else {
+		qdpc_pci_readl = qdpc_readl;
+		qdpc_pci_writel = qdpc_writel;
+		printk("PCI memory endian value:%08x is invalid - using little endian\n", pci_endian);
+	}
+
+	/* Clear endian flags */
+	writel(0, &bda->bda_pci_pre_status);
+	writel(0, &bda->bda_pci_post_status);
+	writel(0, &bda->bda_pci_endian);
+}
+
+static void qdpc_pci_dma_offset_reset(struct vmac_priv *priv)
+{
+	__iomem qdpc_pcie_bda_t *bda = priv->bda;
+	uint32_t dma_offset;
+
+	/* Get EP Mapping address */
+	dma_offset = readl(&bda->bda_dma_offset);
+	if ((dma_offset & PCIE_DMA_OFFSET_ERROR_MASK) != PCIE_DMA_OFFSET_ERROR) {
+		printk("DMA offset : 0x%08x, no need to reset the value.\n", dma_offset);
+		return;
+	}
+	dma_offset &= ~PCIE_DMA_OFFSET_ERROR_MASK;
+
+	printk("EP map start addr : 0x%08x, Host memory start : 0x%08x\n",
+			dma_offset, (unsigned int)MEMORY_START_ADDRESS);
+
+	/* Reset DMA offset in bda */
+	dma_offset -= MEMORY_START_ADDRESS;
+	writel(dma_offset, &bda->bda_dma_offset);
+}
+
+static int qdpc_firmware_load(struct pci_dev *pdev, struct vmac_priv *priv, const char *name)
+{
+#define DMABLOCKSIZE	(1 * 1024 * 1024)
+#define NBLOCKS(size)  ((size)/(DMABLOCKSIZE) + (((size)%(DMABLOCKSIZE) > 0) ? 1 : 0))
+
+	int result = SUCCESS;
+	const struct firmware *fw;
+	__iomem qdpc_pcie_bda_t  *bda = priv->bda;
+
+	/* Request compressed firmware from user space */
+	if ((result = request_firmware(&fw, name, &pdev->dev)) == -ENOENT) {
+		/*
+		 * No firmware found in the firmware directory, skip firmware downloading process
+		 * boot from flash directly on target
+		 */
+		printk( "no firmware found skip fw downloading\n");
+		qdpc_pcie_posted_write((PCIE_BDA_HOST_NOFW_ERR |
+					qdpc_pci_readl(&bda->bda_flags)), &bda->bda_flags);
+		return FAILURE;
+	} else if (result == SUCCESS) {
+		uint32_t nblocks = NBLOCKS(fw->size);
+		uint32_t remaining = fw->size;
+		uint32_t count;
+		uint32_t dma_offset = qdpc_pci_readl(&bda->bda_dma_offset);
+		void *data =(void *) __get_free_pages(GFP_KERNEL | GFP_DMA,
+			get_order(DMABLOCKSIZE));
+		const uint8_t *curdata = fw->data;
+		dma_addr_t handle = 0;
+
+		if (!data) {
+			printk(KERN_ERR "Allocation failed for memory size[%u] Download firmware failed!\n", DMABLOCKSIZE);
+			release_firmware(fw);
+			qdpc_pcie_posted_write((PCIE_BDA_HOST_MEMALLOC_ERR |
+				qdpc_pci_readl(&bda->bda_flags)), &bda->bda_flags);
+			return FAILURE;
+		}
+
+		handle = pci_map_single(priv->pdev, data ,DMABLOCKSIZE, PCI_DMA_TODEVICE);
+		if (!handle) {
+			printk("Pci map for memory data block 0x%p error, Download firmware failed!\n", data);
+			free_pages((unsigned long)data, get_order(DMABLOCKSIZE));
+			release_firmware(fw);
+			qdpc_pcie_posted_write((PCIE_BDA_HOST_MEMMAP_ERR |
+				qdpc_pci_readl(&bda->bda_flags)), &bda->bda_flags);
+			return FAILURE;
+		}
+
+		qdpc_setbootstate(priv, QDPC_BDA_FW_HOST_LOAD);
+		qdpc_bootpoll(priv, QDPC_BDA_FW_EP_RDY);
+
+		/* Start loading firmware */
+		for (count = 0 ; count < nblocks; count++)
+		{
+			uint32_t size = (remaining > DMABLOCKSIZE) ? DMABLOCKSIZE : remaining;
+
+			memcpy(data, curdata, size);
+			/* flush dcache */
+			pci_dma_sync_single_for_device(priv->pdev, handle ,size, PCI_DMA_TODEVICE);
+
+			qdpc_pcie_posted_write(handle + dma_offset, &bda->bda_img);
+			qdpc_pcie_posted_write(size, &bda->bda_img_size);
+			printk("FW Data[%u]: VA:0x%p PA:0x%p Sz=%u..\n", count, (void *)curdata, (void *)handle, size);
+
+			qdpc_setbootstate(priv, QDPC_BDA_FW_BLOCK_RDY);
+			qdpc_bootpoll(priv, QDPC_BDA_FW_BLOCK_DONE);
+
+			remaining = (remaining < size) ? remaining : (remaining - size);
+			curdata += size;
+			printk("done!\n");
+		}
+
+		pci_unmap_single(priv->pdev,handle, DMABLOCKSIZE, PCI_DMA_TODEVICE);
+		/* Mark end of block */
+		qdpc_pcie_posted_write(0, &bda->bda_img);
+		qdpc_pcie_posted_write(0, &bda->bda_img_size);
+		qdpc_setbootstate(priv, QDPC_BDA_FW_BLOCK_RDY);
+		qdpc_bootpoll(priv, QDPC_BDA_FW_BLOCK_DONE);
+
+		qdpc_setbootstate(priv, QDPC_BDA_FW_BLOCK_END);
+
+		PRINT_INFO("Image. Sz:%u State:0x%x\n", (uint32_t)fw->size, qdpc_pci_readl(&bda->bda_bootstate));
+		qdpc_bootpoll(priv, QDPC_BDA_FW_LOAD_DONE);
+
+		free_pages((unsigned long)data, get_order(DMABLOCKSIZE));
+		release_firmware(fw);
+		PRINT_INFO("Image downloaded....!\n");
+	} else {
+		PRINT_ERROR("Failed to load firmware:%d\n", result);
+		return result;
+     }
+	return result;
+}
+
+static void qdpc_pcie_dev_init(struct vmac_priv *priv, struct pci_dev *pdev, struct net_device *ndev)
+{
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	priv->pdev = pdev;
+	priv->ndev = ndev;
+	pci_set_drvdata(pdev, ndev);
+}
+
+static void qdpc_tune_pcie_mps(struct pci_dev *pdev, int pos)
+{
+	struct pci_dev *parent = NULL;
+	int ppos = 0;
+	uint32_t dev_cap, pcap;
+	uint16_t dev_ctl, pctl;
+	unsigned int mps = tlp_mps;
+#define BIT_TO_MPS(m) (1 << ((m) + 7))
+
+	if (pdev->bus && pdev->bus->self) {
+		parent = pdev->bus->self;
+		if (likely(parent)) {
+			ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
+			if (ppos) {
+				pci_read_config_dword(parent, ppos + PCI_EXP_DEVCAP, &pcap);
+				pci_read_config_dword(pdev, pos + PCI_EXP_DEVCAP, &dev_cap);
+				printk(KERN_INFO "parent cap:%u, dev cap:%u\n",\
+						BIT_TO_MPS(pcap & PCI_EXP_DEVCAP_PAYLOAD), BIT_TO_MPS(dev_cap & PCI_EXP_DEVCAP_PAYLOAD));
+				mps = min(BIT_TO_MPS(dev_cap & PCI_EXP_DEVCAP_PAYLOAD), BIT_TO_MPS(pcap & PCI_EXP_DEVCAP_PAYLOAD));
+			}
+		}
+	}
+	mps = 128;
+	printk(KERN_INFO"Setting MPS to %u\n", mps);
+
+	/*
+	* Set Max_Payload_Size
+	* Max_Payload_Size_in_effect = 1 << ( ( (dev_ctl >> 5) & 0x07) + 7);
+	*/
+	mps = (((mps >> 7) - 1) << 5);
+	pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &dev_ctl);
+	dev_ctl = ((dev_ctl & ~PCI_EXP_DEVCTL_PAYLOAD) | mps);
+	pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, dev_ctl);
+
+	if (parent && ppos) {
+		pci_read_config_word(parent, pos + PCI_EXP_DEVCTL, &pctl);
+		pctl = ((pctl & ~PCI_EXP_DEVCTL_PAYLOAD) | mps);
+		pci_write_config_word(parent, pos + PCI_EXP_DEVCTL, pctl);
+	}
+}
+
+static struct net_device *g_ndev = NULL;
+static int qdpc_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct vmac_priv *priv = NULL;
+	struct net_device *ndev = NULL;
+	int result = SUCCESS;
+	int pos;
+
+	/* When system boots up, the state of pdev is not saved on the entry of this function.
+	 * Data structure pci_dev will keep in use when module is removed and re-installed.
+	 * So we can call pci_restore_sate() to recover its previous configuration space.
+	 */
+	if (pdev->state_saved == true) {
+		printk("Recovery: restore saved state\n");
+		pci_restore_state(pdev);
+	}
+
+	/* Save "fresh poweron" state including BAR address, etc. So the state can be
+	 * used for recovery next time.
+	 */
+	pci_save_state(pdev);
+
+	/* Allocate device structure */
+	if (!(ndev = vmac_alloc_ndev()))
+		return -ENOMEM;
+
+	g_ndev = ndev;
+	priv = netdev_priv(ndev);
+	qdpc_pcie_dev_init(priv, pdev, ndev);
+
+	/* allocate netlink data buffer */
+	priv->nl_buf = kmalloc(VMAC_NL_BUF_SIZE, GFP_KERNEL);
+	if (!priv->nl_buf) {
+		result = -ENOMEM;
+		goto out;
+	}
+
+	/* Check if the device has PCI express capability */
+	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	if (!pos) {
+		PRINT_ERROR(KERN_ERR "The device %x does not have PCI Express capability\n",
+	                pdev->device);
+		result = -ENOSYS;
+		goto out;
+	} else {
+		PRINT_DBG(KERN_INFO "The device %x has PCI Express capability\n", pdev->device);
+	}
+
+#ifdef CPTCFG_QUANTENNA_PCIE_HOST_MPS_FIX
+	qdpc_tune_pcie_mps(pdev, pos);
+#endif
+
+	/*  Wake up the device if it is in suspended state and allocate IO,
+	 *  memory regions and IRQ if not
+	 */
+	if (pci_enable_device(pdev)) {
+		PRINT_ERROR(KERN_ERR "Failed to initialize PCI device with device ID %x\n",
+				pdev->device);
+
+		result = -EIO;
+		goto out;
+	} else {
+		PRINT_DBG(KERN_INFO "Initialized PCI device with device ID %x\n", pdev->device);
+	}
+
+	/*
+	 * Check if the PCI device can support DMA addressing properly.
+	 * The mask gives the bits that the device can address
+	 */
+	pci_set_master(pdev);
+
+	/* Initialize PCIE layer  */
+	if (( result = qdpc_pcie_init_intr_and_mem(priv)) < 0) {
+		PRINT_DBG("Interrupt & Memory Initialization failed \n");
+		goto release_memory;
+	}
+
+	if (!!(result = vmac_net_init(pdev))) {
+		PRINT_DBG("Vmac netdev init fail\n");
+		goto free_mem_interrupt;
+	}
+
+	/* Create and start the thread to initiate the INIT Handshake*/
+	priv->init_thread = kthread_run(qdpc_boot_thread, priv, "qdpc_init_thread");
+	if (priv->init_thread == NULL) {
+		PRINT_ERROR("Init thread creation failed \n");
+		goto free_mem_interrupt;
+	}
+
+
+	/* Create netlink & register with kernel */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+	priv->nl_socket = netlink_kernel_create(&init_net,
+				QDPC_NETLINK_RPC_PCI_CLNT, &qdpc_netlink_cfg);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+	priv->nl_socket = netlink_kernel_create(&init_net,
+				QDPC_NETLINK_RPC_PCI_CLNT, THIS_MODULE, &qdpc_netlink_cfg);
+#else
+	priv->nl_socket = netlink_kernel_create(&init_net,
+				QDPC_NETLINK_RPC_PCI_CLNT, 0, qdpc_nl_recv_msg,
+				NULL, THIS_MODULE);
+#endif
+	if (priv->nl_socket) {
+		return SUCCESS;
+	}
+
+	PRINT_ERROR(KERN_ALERT "Error creating netlink socket.\n");
+	result = FAILURE;
+
+free_mem_interrupt:
+	qdpc_pcie_free_mem(pdev);
+	qdpc_free_interrupt(pdev);
+
+release_memory:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+	/* Releasing the memory region if any error occured */
+	pci_clear_master(pdev);
+#endif
+
+	pci_disable_device(pdev);
+
+out:
+	kfree(priv->nl_buf);
+	free_netdev(ndev);
+	/* Any failure in probe, so it can directly return in remove */
+	pci_set_drvdata(pdev, NULL);
+
+	return result;
+}
+
+static void qdpc_pcie_remove(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	struct vmac_priv *vmp;
+
+	if (ndev == NULL)
+		return;
+
+	vmp = netdev_priv(ndev);
+
+	vmp->ep_ready = 0;
+	if (vmp->init_thread)
+		kthread_stop(vmp->init_thread);
+	if (vmp->nl_socket)
+		netlink_kernel_release(vmp->nl_socket);
+
+	kfree(vmp->nl_buf);
+
+	vmac_clean(ndev);
+
+	qdpc_free_interrupt(pdev);
+	qdpc_pcie_free_mem(pdev);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+	pci_clear_master(pdev);
+#endif
+	pci_disable_device(pdev);
+
+	writel(TOPAZ_SET_INT(IPC_RESET_EP), (volatile void *)(vmp->ep_ipc_reg));
+	qdpc_unmap_iomem(vmp);
+
+	free_netdev(ndev);
+	g_ndev = NULL;
+
+	return;
+}
+
+static inline int qdpc_pcie_set_power_state(struct pci_dev *pdev, pci_power_t state)
+{
+	uint16_t pmcsr;
+
+	pci_read_config_word(pdev, TOPAZ_PCI_PM_CTRL_OFFSET, &pmcsr);
+
+	switch (state) {
+	case PCI_D0:
+			pci_write_config_word(pdev, TOPAZ_PCI_PM_CTRL_OFFSET,(pmcsr & ~PCI_PM_CTRL_STATE_MASK) | PCI_D0);
+		break;
+
+	case PCI_D3hot:
+		pci_write_config_word(pdev, TOPAZ_PCI_PM_CTRL_OFFSET,(pmcsr & ~PCI_PM_CTRL_STATE_MASK) | (PCI_D3hot | PCI_PM_CTRL_PME_ENABLE));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int qdpc_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	struct vmac_priv *priv;
+
+	if (ndev == NULL)
+		return -EINVAL;
+
+	priv = netdev_priv(ndev);
+	if (le32_to_cpu(*priv->ep_pmstate) == PCI_D3hot) {
+		return 0;
+	}
+
+	printk("%s start power management suspend\n", qdpc_pcie_driver_name);
+
+	/* Set ep not ready to drop packets in low power mode */
+	priv->ep_ready = 0;
+
+	ndev->flags &= ~IFF_RUNNING;
+	*priv->ep_pmstate = cpu_to_le32(PCI_D3hot);
+	barrier();
+	writel(TOPAZ_SET_INT(IPC_EP_PM_CTRL), (volatile void *)(priv->ep_ipc_reg));
+
+	msleep(100);
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	qdpc_pcie_set_power_state(pdev, PCI_D3hot);
+
+	if (suspend_mode == EP_SUSPEND_MODE_PWR_OFF)
+		suspend_flag = 1;
+
+	return 0;
+}
+
+int qdpc_pcie_resume(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	struct vmac_priv *priv;
+	int ret;
+
+	if (ndev == NULL) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	priv = netdev_priv(ndev);
+	if (le32_to_cpu(*priv->ep_pmstate) == PCI_D0) {
+		ret = 0;
+		goto out;
+	}
+
+	printk("%s start power management resume\n", qdpc_pcie_driver_name);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		PRINT_ERROR("%s: pci_enable_device failed on resume\n", __func__);
+		goto out;
+	}
+
+	pci_restore_state(pdev);
+	pdev->state_saved = true;
+	qdpc_pcie_set_power_state(pdev, PCI_D0);
+
+	{
+		*priv->ep_pmstate = cpu_to_le32(PCI_D0);
+		barrier();
+		writel(TOPAZ_SET_INT(IPC_EP_PM_CTRL), (volatile void *)(priv->ep_ipc_reg));
+
+		msleep(5000);
+	}
+
+	if ( (suspend_mode == EP_SUSPEND_MODE_PWR_OFF) && 
+	     (pdev->driver && pdev->driver->err_handler && pdev->driver->err_handler->slot_reset) ) {
+		printk("slot_reset in %s(), Device name: %s\n", __FUNCTION__, dev_name(&pdev->dev));
+		if(pdev->driver->err_handler->slot_reset(pdev) == PCI_ERS_RESULT_RECOVERED)
+			printk("Recovery OK\n");
+		else {
+			printk("Recovery Error");
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	/* Set ep_ready to resume tx traffic */
+	priv->ep_ready = 1;
+	ndev->flags |= IFF_RUNNING;
+
+out:
+	if (suspend_mode == EP_SUSPEND_MODE_PWR_OFF)
+		suspend_flag = 0;
+	return ret;
+}
+
+static int __init qdpc_init_module(void)
+{
+	int ret;
+
+	PRINT_DBG(KERN_INFO "Quantenna pcie driver initialization\n");
+
+	if (qdpc_platform_init()) {
+		PRINT_ERROR("Platform initilization failed \n");
+		ret = FAILURE;
+		return ret;
+	}
+
+	/*  Register the pci driver with device*/
+	if ((ret = pci_register_driver(&qdpc_pcie_driver)) < 0 ) {
+		PRINT_ERROR("Could not register the driver to pci : %d\n", ret);
+		ret = -ENODEV;
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit qdpc_exit_module(void)
+{
+	/* Release netlink */
+	qdpc_platform_exit();
+
+	/* Unregister the pci driver with the device */
+	pci_unregister_driver(&qdpc_pcie_driver);
+
+
+	return;
+}
+
+void qdpc_recovery_clean(struct pci_dev *pdev, struct net_device *ndev)
+{
+	struct vmac_priv *vmp;
+
+	vmp = netdev_priv(ndev);
+	vmp->ep_ready = 0;
+
+	if (vmp->init_thread) {
+		kthread_stop(vmp->init_thread);
+		vmp->init_thread = NULL;
+	}
+
+	vmac_recovery_clean(ndev);
+
+	pci_disable_device(pdev);
+
+	return;
+}
+
+int qdpc_recovery_reinit(struct pci_dev *pdev, struct net_device *ndev)
+{
+	struct vmac_priv *priv = NULL;
+
+	if (suspend_mode == EP_SUSPEND_MODE_PWR_OFF && suspend_flag)
+		suspend_flag = 0;
+	else {
+		if (pdev->state_saved == true) {
+			pci_restore_state(pdev);
+			pdev->state_saved = true;
+		} else {
+			printk("Recovery Error: No saved state\n");
+			goto out;
+		}
+	}
+
+	if (pci_enable_device(pdev)) {
+		printk("Recovery Error: Failed to enable PCI device\n");
+		goto out;
+	}
+
+	priv = netdev_priv(ndev);
+	if (vmac_recovery_init(priv, ndev) == ENOMEM) {
+		printk("Recovery Error: Not enough memory\n");
+		goto qdpc_recovery_err_0;
+	}
+
+	priv->init_thread = kthread_run(qdpc_boot_thread, priv, "qdpc_init_thread");
+	if (priv->init_thread == NULL) {
+		printk("Recovery Error: Thread creation failed \n");
+		goto qdpc_recovery_err_0;
+	}
+
+	return SUCCESS;
+
+qdpc_recovery_err_0:
+	pci_disable_device(pdev);
+out:
+	return -1;
+}
+
+static int qdpc_recovery_access_check(struct pci_dev *pdev)
+{
+	uint32_t val = 0;
+
+	pci_read_config_dword(pdev, QDPC_VENDOR_ID_OFFSET, &val);
+
+	if (val == ((QDPC_DEVICE_ID << 16) | QDPC_VENDOR_ID)) {
+		printk("%s: PCIe read access check: Pass\n", __func__);
+		return 0;
+	} else {
+		printk("%s: PCIe read access check: Fail: VENDOR_ID read error: 0x%08x\n", __func__, val);
+		return -1;
+	}
+}
+
+int qdpc_pcie_recovery(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+
+	qdpc_recovery_clean(pdev, ndev);
+
+        /* Wait EP link up. If this function is called at hardirq context where 10s
+	 * delay is not allowed, please replace with link up check at Root Complex's
+	 * status register.
+         */
+        mdelay(10000);
+
+        if (qdpc_recovery_access_check(pdev) != 0)
+                return -1;
+
+        /* Re-allocate and initialize data structure */
+        qdpc_recovery_reinit(pdev, ndev);
+
+        return 0;
+}
+
+pci_ers_result_t qdpc_pcie_slot_reset(struct pci_dev *dev)
+{
+	if (qdpc_pcie_recovery(dev) == 0)
+		return PCI_ERS_RESULT_RECOVERED;
+	else
+		return PCI_ERS_RESULT_DISCONNECT;
+}
+
+static void qdpc_pcie_shutdown(struct pci_dev *pdev)
+{
+	qdpc_pcie_remove(pdev);
+
+	return;
+}
+
+static int qdpc_bringup_fw(struct vmac_priv *priv)
+{
+	__iomem qdpc_pcie_bda_t  *bda = priv->bda;
+	uint32_t bdaflg;
+	char *fwname;
+
+	qdpc_pci_endian_detect(priv);
+	qdpc_pci_dma_offset_reset(priv);
+
+	printk("Setting HOST ready...\n");
+	qdpc_setbootstate(priv, QDPC_BDA_FW_HOST_RDY);
+	qdpc_bootpoll(priv, QDPC_BDA_FW_TARGET_RDY);
+
+	bdaflg = qdpc_pci_readl(&bda->bda_flags);
+	if ((PCIE_BDA_FLASH_PRESENT & bdaflg) && EP_BOOT_FROM_FLASH) {
+		printk("EP have fw in flash, boot from flash\n");
+		qdpc_pcie_posted_write((PCIE_BDA_FLASH_BOOT |
+			qdpc_pci_readl(&bda->bda_flags)), &bda->bda_flags);
+		qdpc_setbootstate(priv, QDPC_BDA_FW_TARGET_BOOT);
+		qdpc_bootpoll(priv, QDPC_BDA_FW_FLASH_BOOT);
+		goto fw_start;
+	}
+	bdaflg &= PCIE_BDA_XMIT_UBOOT;
+	fwname = bdaflg ? QDPC_TOPAZ_UBOOT : QDPC_TOPAZ_IMG;
+
+	qdpc_setbootstate(priv, QDPC_BDA_FW_TARGET_BOOT);
+	printk("EP FW load request...\n");
+	qdpc_bootpoll(priv, QDPC_BDA_FW_LOAD_RDY);
+
+	printk("Start download Firmware %s...\n", fwname);
+	if (qdpc_firmware_load(priv->pdev, priv, fwname)){
+		printk("Failed to download firmware.\n");
+		priv->init_thread = NULL;
+		do_exit(-1);
+	}
+
+fw_start:
+	qdpc_setbootstate(priv, QDPC_BDA_FW_START);
+	printk("Start booting EP...\n");
+	if (bdaflg != PCIE_BDA_XMIT_UBOOT) {
+		if (qdpc_bootpoll(priv,QDPC_BDA_FW_CONFIG)) {
+			booterror(bda);
+			priv->init_thread = NULL;
+			do_exit(-1);
+		}
+		printk("EP boot successful, starting config...\n");
+
+		/* Save target-side MSI address for later enable/disable irq*/
+		priv->dma_msi_imwr = readl(QDPC_BAR_VADDR(priv->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET));
+		priv->dma_msi_dummy = virt_to_bus(&priv->dma_msi_data) + qdpc_pci_readl(&bda->bda_dma_offset);
+		priv->ep_pciecfg0_val = readl(QDPC_BAR_VADDR(priv->sysctl_bar, TOPAZ_PCIE_CFG0_OFFSET));
+
+		qdpc_setbootstate(priv, QDPC_BDA_FW_RUN);
+		qdpc_bootpoll(priv,QDPC_BDA_FW_RUNNING);
+		priv->ep_ready = 1;
+	}
+
+	return (int)bdaflg;
+}
+
+static int qdpc_boot_done(struct vmac_priv *priv)
+{
+	struct net_device *ndev;
+	ndev = priv->ndev;
+
+	PRINT_INFO("Connection established with Target BBIC4 board\n");
+
+	priv->init_thread = NULL;
+	do_exit(0);
+}
+
+static int qdpc_boot_thread(void *data)
+{
+	struct vmac_priv *priv = (struct vmac_priv *)data;
+	int i;
+
+	for (i = 0; i < MAX_IMG_NUM; i++) {
+		if (qdpc_bringup_fw(priv) <= 0)
+			break;
+	}
+
+	qdpc_boot_done(priv);
+
+	return 0;
+}
+
+static void qdpc_nl_recv_msg(struct sk_buff *skb)
+{
+	struct vmac_priv *priv = netdev_priv(g_ndev);
+	struct nlmsghdr *nlh  = (struct nlmsghdr*)skb->data;
+	struct sk_buff *skb2;
+	unsigned int data_len;
+	unsigned int offset;
+	qdpc_cmd_hdr_t *cmd_hdr;
+	uint16_t rpc_type;
+
+	/* Parsing the netlink message */
+
+	PRINT_DBG(KERN_INFO "%s line %d Netlink received pid:%d, size:%d, type:%d\n",
+		__FUNCTION__, __LINE__, nlh->nlmsg_pid, nlh->nlmsg_len, nlh->nlmsg_type);
+
+	switch (nlh->nlmsg_type) {
+		case QDPC_NL_TYPE_CLNT_STR_REG:
+		case QDPC_NL_TYPE_CLNT_LIB_REG:
+			if (nlh->nlmsg_type == QDPC_NL_TYPE_CLNT_STR_REG)
+				priv->str_call_nl_pid = nlh->nlmsg_pid;
+			else
+				priv->lib_call_nl_pid = nlh->nlmsg_pid;
+			return;
+		case QDPC_NL_TYPE_CLNT_STR_REQ:
+		case QDPC_NL_TYPE_CLNT_LIB_REQ:
+			break;
+		default:
+			PRINT_DBG(KERN_INFO "%s line %d Netlink Invalid type %d\n",
+				__FUNCTION__, __LINE__, nlh->nlmsg_type);
+			return;
+	}
+
+	/*
+	 * make new skbs; Fragment if necessary.
+	 * The original skb will be freed in netlink_unicast_kernel,
+	 * we hold the new skbs until DMA transfer is done
+	 */
+	offset = sizeof(struct nlmsghdr);
+	data_len = nlh->nlmsg_len;
+
+	while (data_len > 0) {
+		unsigned int len = min_t(unsigned int, data_len, priv->ndev->mtu);
+		unsigned int skb2_len = len + sizeof(qdpc_cmd_hdr_t);
+
+		skb2 = alloc_skb(skb2_len, GFP_ATOMIC);
+		if (!skb2) {
+			printk(KERN_INFO "%s: skb alloc failed\n", __func__);
+			return;
+		}
+
+		data_len -= len;
+
+		rpc_type = nlh->nlmsg_type & QDPC_RPC_TYPE_MASK;
+		rpc_type |= (data_len > 0 ? QDPC_RPC_TYPE_FRAG : 0);
+
+		cmd_hdr = (qdpc_cmd_hdr_t *)skb2->data;
+		memcpy(cmd_hdr->dst_magic, QDPC_NETLINK_DST_MAGIC, ETH_ALEN);
+		memcpy(cmd_hdr->src_magic, QDPC_NETLINK_SRC_MAGIC, ETH_ALEN);
+		cmd_hdr->type = __constant_htons(QDPC_APP_NETLINK_TYPE);
+		cmd_hdr->len = htons((uint16_t)len);
+		cmd_hdr->rpc_type = htons(rpc_type);
+		cmd_hdr->total_len = htons((uint16_t)(nlh->nlmsg_len));
+
+		memcpy((uint8_t *)(cmd_hdr + 1), skb->data + offset, len);
+
+		offset += len;
+
+		skb_put(skb2, skb2_len);
+		skb_reset_mac_header(skb2);
+		skb_reset_network_header(skb2);
+		skb2->protocol = __constant_htons(QDPC_APP_NETLINK_TYPE);
+		skb2->dev = priv->ndev;
+
+		dev_queue_xmit(skb2);
+	}
+}
+
+module_init(qdpc_init_module);
+module_exit(qdpc_exit_module);
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h
new file mode 100644
index 0000000..3d39181
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_INIT_H_
+#define __QDPC_INIT_H_
+
+#include <asm/io.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include "topaz_vnet.h"
+
+#define QDPC_MODULE_NAME                 "qdpc_ruby"
+#define QDPC_DEV_NAME                    "qdpc_ruby"
+#define QDPC_MODULE_VERSION              "1.0"
+
+/* PCIe device information declarations */
+#define  QDPC_VENDOR_ID                   0x1bb5
+#define  QDPC_DEVICE_ID                   0x0008
+#define  QDPC_PCIE_NUM_BARS               6
+
+/* PCIe Configuration Space Defines */
+#define	QDPC_LINK_DOWN	0xffffffff /* Used to indicate link went down */
+#define	QDPC_VENDOR_ID_OFFSET	0x00
+#define	QDPC_INT_LINE_OFFSET	0x3C
+#define	QDPC_ROW_INCR_OFFSET	0x04
+#undef	QDPC_CS_DEBUG
+
+extern unsigned int (*qdpc_pci_readl)(void *addr);
+extern void (*qdpc_pci_writel)(unsigned int val, void *addr);
+
+/*
+ * End-point(EP) is little-Endian.
+ * These two macros are used for host side outbound window memory access.
+ * Outbound here is host side view-point. So memory accessed by these two macros
+ * should be on EP side.
+ * NOTE: On some platforms, outbound hardware swap(byte order swap) should be
+ * enabled for outbound memory access correctly. If enabled, Endian translation
+ * will be done by hardware, and software Endian translation should be disabled.
+ * */
+#ifdef OUTBOUND_HW_SWAP
+	#define le32_readl(x)           readl(x)
+	#define le32_writel(x, addr)    writel(x, addr)
+#else
+	#define le32_readl(x)           le32_to_cpu(readl((x)))
+	#define le32_writel(x, addr)    writel(cpu_to_le32((x)), addr)
+#endif
+
+static inline unsigned int qdpc_readl(void *addr)
+{
+	return readl(addr);
+}
+static inline void qdpc_writel(unsigned int val, void *addr)
+{
+	writel(val, addr);
+}
+static inline unsigned int qdpc_le32_readl(void *addr)
+{
+	return le32_to_cpu(readl((addr)));
+}
+static inline void qdpc_le32_writel(unsigned int val, void *addr)
+{
+	writel(cpu_to_le32((val)), addr);
+}
+
+static inline void qdpc_pcie_posted_write(uint32_t val, __iomem void *basereg)
+{
+	qdpc_pci_writel(val,basereg);
+	/* flush posted write */
+	qdpc_pci_readl(basereg);
+}
+
+static inline int qdpc_isbootstate(struct vmac_priv *p, uint32_t state) {
+	__iomem uint32_t *status = &p->bda->bda_bootstate;
+	uint32_t s = qdpc_pci_readl(status);
+	return (s == state);
+}
+static inline int qdpc_booterror(struct vmac_priv *p) {
+	__iomem uint32_t *status = &p->bda->bda_flags;
+	uint32_t s = qdpc_pci_readl(status);
+	return (s & PCIE_BDA_ERROR_MASK);
+}
+static inline void qdpc_setbootstate(struct vmac_priv *p, uint32_t state) {
+	__iomem qdpc_pcie_bda_t *bda = p->bda;
+
+	qdpc_pcie_posted_write(state, &bda->bda_bootstate);
+}
+
+/* Function prototypes */
+int qdpc_pcie_init_intr_and_mem(struct vmac_priv *priv);
+void qdpc_interrupt_target(struct vmac_priv *priv, uint32_t intr);
+void qdpc_disable_irqs(struct vmac_priv *priv);
+void qdpc_enable_irqs(struct vmac_priv *priv);
+void qdpc_free_interrupt(struct pci_dev *pdev);
+void qdpc_pcie_free_mem(struct pci_dev *pdev);
+void qdpc_init_target_buffers(void *data);
+int qdpc_send_packet(struct sk_buff *skb, struct net_device *ndev);
+void *qdpc_map_pciemem(unsigned long busaddr, size_t len);
+void qdpc_unmap_pciemem(unsigned long busaddr, void *vaddr, size_t len);
+int qdpc_unmap_iomem(struct vmac_priv *priv);
+int32_t qdpc_set_dma_mask(struct vmac_priv *priv);
+
+#endif
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_pcie.c b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_pcie.c
new file mode 100644
index 0000000..34fff2f
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_pcie.c
@@ -0,0 +1,353 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <asm/byteorder.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <asm-generic/pci-dma-compat.h>
+
+#include "qdpc_config.h"
+#include "qdpc_debug.h"
+#include "qdpc_init.h"
+#include "qdpc_regs.h"
+#include <qdpc_platform.h>
+
+static int use_msi = 1;
+module_param(use_msi, int, 0644);
+MODULE_PARM_DESC(use_msi, "Set 0 to use Legacy interrupt");
+
+static int qdpc_pcie_init_intr(struct vmac_priv *priv);
+static int qdpc_pcie_init_mem(struct vmac_priv *priv);
+static int g_msi = 1;
+int32_t qdpc_pcie_init_intr_and_mem(struct vmac_priv *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+	int result  = 0;
+
+	/*  Initialize interrupts */
+	if (( result = qdpc_pcie_init_intr(priv)) < 0) {
+		PRINT_ERROR("PCIe Interrupt Initialization failed \n");
+		return result;
+	}
+
+	/* Memory Initialization */
+	if (( result = qdpc_pcie_init_mem(priv)) < 0) {
+		PRINT_ERROR("PCIe Memory Initialization failed \n");
+		qdpc_free_interrupt(pdev);
+	}
+
+	return result;
+}
+
+static int32_t qdpc_pcie_init_intr(struct vmac_priv *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+
+	priv->msi_enabled = 0; /* Set default to use Legacy INTx interrupt */
+
+	/* Check if the device has MSI capability */
+	if (use_msi) {
+		if (!pci_enable_msi(pdev)) {
+			PRINT_INFO("PCIe MSI Interrupt Enabled\n");
+			priv->msi_enabled = 1;
+		} else {
+			PRINT_ERROR("PCIe MSI Interrupt enabling failed. Fall back to Legacy IRQ\n");
+		}
+	}
+
+	if(!priv->msi_enabled) {
+		PRINT_INFO("PCIe Legacy Interrupt Enabled\n");
+		pci_intx(pdev, 1);
+	}
+
+	return 0;
+}
+
+static bool qdpc_bar_check(struct vmac_priv *priv, qdpc_bar_t *bar)
+{
+	uint32_t offset = bar->b_offset;
+	size_t len = bar->b_len;
+	dma_addr_t busaddr = bar->b_busaddr;
+	uint8_t index = bar->b_index;
+
+	if (index > 5) {
+		printk("Invalid BAR index:%u. Must be between 0 and 5\n", index);
+		return 0;
+	}
+
+	if (!len) {
+		/* NOTE:
+		  * Do not use an implicit length such as the BAR length
+		  * if the map length is too large say > 16Mb this leaves
+		  * the implementation vulnerable to
+		  * Linux and the attack of the Silent  "S" (one between the n and u)
+		  */
+		printk("Zero length BAR\n");
+		return 0;
+	}
+
+	if (busaddr) { /*initialized BAR */
+		unsigned long bar_start =  pci_resource_start(priv->pdev , index);
+		unsigned long bar_end =  pci_resource_end(priv->pdev , index);
+
+		if (!bar_start) {
+			printk("Invalid BAR address: 0x%p.\n", (void *)busaddr);
+			return 0;
+		}
+
+		if ((busaddr - offset) != bar_start) {
+			printk("Invalid BAR offset:0x%p. BAR starts at 0x%p\n",
+				(void *)(busaddr -offset), (void *)bar_start);
+			return 0;
+		}
+		/* Check the span of the BAR including the offset + length, bar_end points to the last byte of BAR */
+		if ((busaddr + len - 1) > bar_end) {
+			printk("Invalid BAR end address:0x%p. BAR ends at 0x%p\n",
+				(void *)(busaddr + len), (void *)bar_end);
+			return 0;
+		}
+	} else { /* Unitialized bar */
+		unsigned long bar_end =  pci_resource_end(priv->pdev , index);
+		busaddr = pci_resource_start(priv->pdev , index);
+
+		if (!busaddr) {
+			printk("Invalid BAR address: 0x%p.\n", (void *)busaddr);
+			return 0;
+		}
+
+		/* Checks that offset area is within bar */
+		if ( (busaddr + offset) > bar_end) {
+			printk("Invalid BAR offset 0x%p, extends beyond end of BAR(0x%p).\n",
+				(void *)(busaddr + offset), (void *)bar_end);
+			return 0;
+		}
+
+		/* Checks that mapped area is within bar */
+		if ((busaddr + len + offset - 1) > bar_end) {
+			printk("Mapped area 0x%p, extends beyond end of BAR(0x%p).\n",
+				(void *)(busaddr + len + offset - 1), (void *)bar_end);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static qdpc_bar_t *qdpc_map_bar(struct vmac_priv *priv, qdpc_bar_t *bar,
+						uint8_t index, size_t len, uint32_t offset)
+{
+	void *vaddr = NULL;
+	dma_addr_t busaddr = 0;
+	qdpc_bar_t temp_bar;
+
+	memset(&temp_bar, 0 ,sizeof(qdpc_bar_t));
+
+	temp_bar.b_len = len;
+	temp_bar.b_offset = offset;
+	temp_bar.b_index = index;
+
+	if (!qdpc_bar_check(priv, &temp_bar)) {
+		printk("Failed bar mapping sanity check in %s\n", __FUNCTION__);
+		return NULL;
+	}
+
+	/* Reserve PCIe memory region*/
+	busaddr = pci_resource_start(priv->pdev , index) + offset;
+	if (!request_mem_region(busaddr, len , QDPC_DEV_NAME)) {
+		printk("Failed to reserve %u bytes of PCIe memory "
+			"region starting at 0x%p\n", (uint32_t)len, (void *)busaddr);
+		return NULL;
+	}
+
+	qdpc_update_hw_bar(priv->pdev, index);
+
+	vaddr = ioremap_nocache(busaddr, len);
+	if (!vaddr) {
+		printk("Failed to map %u bytes at BAR%u at bus address 0x%p.\n",
+			(uint32_t)len, index, (void *)busaddr);
+		release_mem_region(busaddr, len);
+		return NULL;
+	}
+
+	memset(&temp_bar, 0 ,sizeof(qdpc_bar_t));
+
+	bar->b_vaddr = vaddr;
+	bar->b_busaddr = busaddr;
+	bar->b_len = len;
+	bar->b_index = index;
+	bar->b_offset = offset;
+
+	printk("BAR:%u vaddr=0x%p busaddr=%p offset=%u len=%u\n",
+		bar->b_index, bar->b_vaddr, (void *)bar->b_busaddr,
+		bar->b_offset, (uint32_t)bar->b_len);
+	return bar;
+}
+
+static bool qdpc_unmap_bar(struct vmac_priv *priv, qdpc_bar_t *bar)
+{
+	if (!qdpc_bar_check(priv, bar)) {
+		PRINT_ERROR("Failed bar mapping sanity check in %s\n", __FUNCTION__);
+		return 0;
+	}
+
+	iounmap(bar->b_vaddr);
+	release_mem_region(bar->b_busaddr - bar->b_offset, bar->b_len);
+	memset(bar, 0 , sizeof(qdpc_bar_t));
+
+	return 1;
+
+}
+static void qdpc_map_epmem(struct vmac_priv *priv)
+{
+	printk("%s() Mapping epmem\n", __FUNCTION__);
+	qdpc_map_bar(priv, &priv->epmem_bar, QDPC_SHMEM_BAR,
+					pci_resource_len(priv->pdev, QDPC_SHMEM_BAR) , 0);
+
+	priv->bda =(qdpc_pcie_bda_t *)QDPC_BAR_VADDR(priv->epmem_bar, 0);
+	priv->bda->bda_rc_msi_enabled = g_msi;
+}
+
+static void qdpc_map_sysctl_regs(struct vmac_priv *priv)
+{
+	printk("%s() Mapping sysctl\n", __FUNCTION__);
+	qdpc_map_bar(priv, &priv->sysctl_bar, QDPC_SYSCTL_BAR, pci_resource_len(priv->pdev, QDPC_SYSCTL_BAR) , 0);
+}
+
+static void qdpc_map_dma_regs(struct vmac_priv *priv)
+{
+	printk("%s() Mapping dma registers\n", __FUNCTION__);
+	qdpc_map_bar(priv, &priv->dmareg_bar, QDPC_DMA_BAR, pci_resource_len(priv->pdev, QDPC_DMA_BAR), 0);
+}
+
+static void qdpc_unmap_epmem(struct vmac_priv *priv)
+{
+	printk("%s() Unmapping sysctl\n", __FUNCTION__);
+	priv->bda = NULL;
+	qdpc_unmap_bar(priv, &priv->epmem_bar);
+}
+
+static void qdpc_unmap_sysctl_regs(struct vmac_priv *priv)
+{
+	printk("%s() Unmapping sysctl\n", __FUNCTION__);
+
+	qdpc_unmap_bar(priv, &priv->sysctl_bar);
+}
+
+static void qdpc_unmap_dma_regs(struct vmac_priv *priv)
+{
+	printk("%s() Unmapping dma regs\n", __FUNCTION__);
+	qdpc_unmap_bar(priv, &priv->dmareg_bar);
+}
+
+int32_t qdpc_set_dma_mask(struct vmac_priv *priv) {
+	int result = 0;
+	uint64_t dma_mask = qdpc_pci_readl(&priv->bda->bda_dma_mask);
+
+	printk("Requested DMA mask:0x%llx\n", dma_mask);
+
+	result = pci_set_dma_mask(priv->pdev, dma_mask);
+	if (!result) {
+			result = pci_set_consistent_dma_mask(priv->pdev, dma_mask);
+			if (result) {
+				printk(" pci_set_consistent_dma_mask() error %d. Mask:0x%llx\n", result, dma_mask);
+				return 1;
+			}
+	} else {
+		printk(" pci_set_dma_mask() error %d. Mask:0x%llx\n", result, dma_mask);
+		return 1;
+	}
+
+	return 0;
+}
+static int32_t qdpc_pcie_init_mem(struct vmac_priv *priv)
+{
+	int ret = 0;
+
+	/* Map SynControl registers and Host to Endpoint interrupt registers to BAR-2 */
+	qdpc_map_sysctl_regs(priv);
+	qdpc_map_epmem(priv);
+	qdpc_map_dma_regs(priv);
+
+	return ret;
+}
+
+int qdpc_unmap_iomem(struct vmac_priv *priv)
+{
+	qdpc_unmap_dma_regs(priv);
+	qdpc_unmap_epmem(priv);
+	qdpc_unmap_sysctl_regs(priv);
+
+	return SUCCESS;
+}
+
+void qdpc_free_interrupt(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+	struct vmac_priv *priv;
+
+	if (ndev == NULL)
+		return;
+
+	priv = netdev_priv(ndev);
+	if(priv->msi_enabled)
+		pci_disable_msi(pdev);
+	else
+		pci_intx(pdev, 0);
+}
+
+void qdpc_pcie_free_mem(struct pci_dev *pdev)
+{
+	return;
+}
+
+void *qdpc_map_pciemem(unsigned long busaddr, size_t len)
+{
+	/* Reserve PCIe memory region*/
+	if (!request_mem_region(busaddr, len, QDPC_DEV_NAME)) {
+		PRINT_ERROR(KERN_ERR "Failed to reserve %u bytes of "
+			"PCIe memory region starting at 0x%lx\n", (uint32_t)len, busaddr);
+		return NULL;
+	}
+	return ioremap_nocache(busaddr, len);
+}
+
+void qdpc_unmap_pciemem(unsigned long busaddr, void *vaddr, size_t len)
+{
+	if (!vaddr || !busaddr)
+		return;
+	iounmap(vaddr);
+	release_mem_region(busaddr, len);
+}
+
+void qdpc_deassert_intx(struct vmac_priv *priv)
+{
+	void *basereg = QDPC_BAR_VADDR(priv->sysctl_bar, TOPAZ_PCIE_CFG0_OFFSET);
+
+	qdpc_pcie_posted_write(priv->ep_pciecfg0_val & ~TOPAZ_ASSERT_INTX, basereg);
+}
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_regs.h b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_regs.h
new file mode 100644
index 0000000..5325d41
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_regs.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_REGS_H__
+#define __QDPC_REGS_H__
+
+#include <linux/bitops.h>
+#include <qdpc_platform.h>
+
+#define QDPC_SYSCTL_BAR		0
+#define QDPC_SHMEM_BAR		2
+#define QDPC_DMA_BAR		3
+
+/*
+ * NOTE: Below registers are at EP but accessed and written by RC
+ * Make sure EP codes do not write them, otherwise we have race conditions
+*/
+
+/*
+ * The register is one of registers of Endpoint. Root Complex uses it
+ * to interrupt Endpoint to transmit packets.
+ */
+#define TOPAZ_IPC_OFFSET		(0x13C)
+
+/* Used to deassert Legacy INTx */
+#define TOPAZ_PCIE_CFG0_OFFSET		(0x6C)
+#define TOPAZ_ASSERT_INTX		BIT(9)
+
+/* This macro is used to set interrupt bit of register QDPC_EP_SYS_CTL_IPC4_INT */
+#define TOPAZ_SET_INT(x)		((x) | ((x) << 16))
+
+/* "DMA Write Done IMWr Address Low" register at EP side*/
+#define TOPAZ_IMWR_DONE_ADDRLO_OFFSET	(0x700 + 0x2D0)
+#define TOPAZ_IMWR_ABORT_ADDRLO_OFFSET	(0x700 + 0x2D8)
+
+/* Power management control status register */
+#define TOPAZ_PCI_PM_CTRL_OFFSET	(0x44)
+
+#endif //__QDPC_REGS_H__
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
new file mode 100644
index 0000000..08bf5ec
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
@@ -0,0 +1,1579 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/if_bridge.h>
+#include <linux/sysfs.h>
+#include <linux/pci.h>
+
+
+#include <qdpc_platform.h>
+
+#include <asm/cache.h>		/* For cache line size definitions */
+#include <asm/cacheflush.h>	/* For cache flushing functions */
+
+#include <net/netlink.h>
+
+#include "topaz_vnet.h"
+#include "qdpc_config.h"
+#include "qdpc_init.h"
+#include "qdpc_debug.h"
+#include "qdpc_regs.h"
+#include "qdpc_version.h"
+
+#define DRV_NAME	"qdpc-host"
+
+#ifndef DRV_VERSION
+#define DRV_VERSION	"1.0"
+#endif
+
+#define DRV_AUTHOR	"Quantenna Communications Inc."
+#define DRV_DESC	"PCIe virtual Ethernet port driver"
+
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESC);
+MODULE_LICENSE("GPL");
+
+#undef __sram_text
+#define __sram_text
+
+static int __sram_text vmac_rx_poll (struct napi_struct *napi, int budget);
+static int __sram_text skb2rbd_attach(struct net_device *ndev, uint16_t i, uint32_t wrap);
+static irqreturn_t vmac_interrupt(int irq, void *dev_id);
+static void vmac_tx_timeout(struct net_device *ndev);
+static int vmac_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd);
+static int vmac_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd);
+static void vmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info);
+static void free_tx_pkts(struct vmac_priv *vmp);
+static void init_tx_bd(struct vmac_priv *vmp);
+static void free_rx_skbs(struct vmac_priv *vmp);
+static int alloc_and_init_rxbuffers(struct net_device *ndev);
+static void bring_up_interface(struct net_device *ndev);
+static void shut_down_interface(struct net_device *ndev);
+static int vmac_open(struct net_device *ndev);
+static int vmac_close(struct net_device *ndev);
+static int vmac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd);
+static struct net_device_stats *vmac_get_stats(struct net_device *dev);
+#ifdef QTN_WAKEQ_SUPPORT
+static inline void vmac_try_wake_queue(struct net_device *ndev);
+static inline void vmac_try_stop_queue(struct net_device *ndev);
+#endif
+#ifdef RX_IP_HDR_REALIGN
+static uint32_t align_cnt = 0, unalign_cnt = 0;
+#endif
+
+
+#define RX_DONE_INTR_MSK	((0x1 << 6) -1)
+#define VMAC_BD_LEN		(sizeof(struct vmac_bd))
+
+#define QTN_GLOBAL_INIT_EMAC_TX_QUEUE_LEN 256
+#define VMAC_DEBUG_MODE
+/* Tx dump flag */
+#define DMP_FLG_TX_BD		(0x1 << ( 0)) /* vmac s 32 */
+#define DMP_FLG_TX_SKB		(0x1 << ( 1)) /* vmac s 33 */
+/* Rx dump flag */
+#define DMP_FLG_RX_BD		(0x1 << (16)) /* vmac s 48 */
+#define DMP_FLG_RX_SKB		(0x1 << (17)) /* vmac s 49 */
+#define DMP_FLG_RX_INT		(0x1 << (18)) /* vmac s 50 */
+
+#define SHOW_TX_BD		(16)
+#define SHOW_RX_BD		(17)
+#define SHOW_VMAC_STATS	(18)
+
+#ifndef QDPC_PLATFORM_IFPORT
+#define QDPC_PLATFORM_IFPORT 0
+#endif
+
+#define VMAC_TX_TIMEOUT		(180 * HZ)
+
+#ifdef VMAC_DEBUG_MODE
+
+#define dump_tx_bd(vmp) do { \
+		if (unlikely((vmp)->dbg_flg & DMP_FLG_TX_BD)) { \
+			txbd2str(vmp); \
+		} \
+	} while (0)
+
+#define dump_tx_pkt(vmp, data, len) do { \
+		if (unlikely(((vmp)->dbg_flg & DMP_FLG_TX_SKB))) \
+			dump_pkt(data, len, "Tx"); \
+	} while(0)
+
+#define dump_rx_bd(vmp) do { \
+		if (unlikely((vmp)->dbg_flg & DMP_FLG_RX_BD)) { \
+			rxbd2str(vmp); \
+		} \
+	} while (0)
+
+#define dump_rx_pkt(vmp, data, len) do { \
+		if (unlikely((vmp)->dbg_flg & DMP_FLG_RX_SKB)) \
+			dump_pkt(data, len, "Rx"); \
+	} while(0)
+
+#define dump_rx_int(vmp) do { \
+		if (unlikely((vmp)->dbg_flg & DMP_FLG_RX_INT)) \
+			dump_rx_interrupt(vmp); \
+	} while (0)
+
+#else
+#define dump_tx_bd(vmp)
+#define dump_tx_pkt(vmp, skb, len)
+#define dump_rx_bd(vmp)
+#define dump_rx_pkt(vmp, skb, len)
+#define dump_rx_int(vmp)
+#endif
+
+struct vmac_cfg vmaccfg = {
+	QDPC_RX_QUEUE_SIZE, QDPC_TX_QUEUE_SIZE, "wlan%d", NULL
+};
+
+static char *ethaddr = NULL;
+module_param(ethaddr, charp, S_IRUGO);
+MODULE_PARM_DESC(store, "ethaddr");
+
+#ifdef RX_IP_HDR_REALIGN
+static uint32_t rx_pkt_align = 0;
+module_param(rx_pkt_align, uint, 0644);
+MODULE_PARM_DESC(rx_pkt_align, "RX Pakcet IP header realign to 4byte boundary");
+#endif
+
+/* Alignment helper functions */
+__always_inline static unsigned long align_up_off(unsigned long val, unsigned long step)
+{
+	return (((val + (step - 1)) & (~(step - 1))) - val);
+}
+
+__always_inline static unsigned long align_down_off(unsigned long val, unsigned long step)
+{
+	return ((val) & ((step) - 1));
+}
+
+__always_inline static unsigned long align_val_up(unsigned long val, unsigned long step)
+{
+	return ((val + step - 1) & (~(step - 1)));
+}
+
+__always_inline static unsigned long align_val_down(unsigned long val, unsigned long step)
+{
+	return (val & (~(step - 1)));
+}
+
+__always_inline static void* align_buf_dma(void *addr)
+{
+	return (void*)align_val_up((unsigned long)addr, dma_get_cache_alignment());
+}
+
+__always_inline static unsigned long align_buf_dma_offset(void *addr)
+{
+	return (align_buf_dma(addr) - addr);
+}
+
+__always_inline static void* align_buf_cache(void *addr)
+{
+	return (void*)align_val_down((unsigned long)addr, dma_get_cache_alignment());
+}
+
+__always_inline static unsigned long align_buf_cache_offset(void *addr)
+{
+	return (addr - align_buf_cache(addr));
+}
+
+__always_inline static unsigned long align_buf_cache_size(void *addr, unsigned long size)
+{
+	return align_val_up(size + align_buf_cache_offset(addr), dma_get_cache_alignment());
+}
+
+/* Print the Tx Request Queue */
+static int txbd2str_range(struct vmac_priv *vmp, uint16_t s, int num)
+{
+	qdpc_pcie_bda_t *bda = vmp->bda;
+	int i;
+
+	printk("RC insert start index\t: %d\n", vmp->tx_bd_index);
+	printk("RC reclaim start index\t: %d\n", vmp->tx_reclaim_start);
+	printk("valid entries\t\t: %d\n", vmp->vmac_tx_queue_len);
+	printk("Pkt index EP handled\t: %d\n", le32_to_cpu(VMAC_REG_READ(vmp->ep_next_rx_pkt)));
+
+	printk("\t\t%8s\t%8s\t%8s\t%10s\n", "Address", "Valid", "Length", "Pkt Addr");
+
+	for (i = 0; i < num; i++) {
+		printk("\t%d\t0x%08x\t%8s\t\t%d\t0x%p\n", s, bda->request[s].addr, \
+			(bda->request[s].info & PCIE_TX_VALID_PKT) ? "Valid" : "Invalid",  \
+			bda->request[s].info & 0xffff, vmp->tx_skb[s]);
+		VMAC_INDX_INC(s, vmp->tx_bd_num);
+	}
+
+	return 0;
+}
+
+static int txbd2str(struct vmac_priv *vmp)
+{
+	uint16_t s;
+
+	s = VMAC_INDX_MINUS(vmp->tx_bd_index, 4, vmp->tx_bd_num);
+	return txbd2str_range(vmp, s, 8);
+}
+
+static int txbd2str_all(struct vmac_priv *vmp)
+{
+	return txbd2str_range(vmp, 0, vmp->tx_bd_num);
+}
+
+static int rxbd2str_range(struct vmac_priv *vmp, uint16_t s, int num)
+{
+	int i;
+	char *idxflg;
+
+	printk("rxindx\trbdaddr\t\tbuff\t\tinfo\t\trx_skb\n");
+	for (i = 0; i < num; i++) {
+		if(s == vmp->rx_bd_index)
+			idxflg = ">rbd";
+		else
+			idxflg = "";
+		printk("%2d%s\t@%p\t%08x\t%08x\t%p\n", s, idxflg,
+			&vmp->rx_bd_base[s], vmp->rx_bd_base[s].buff_addr,
+			vmp->rx_bd_base[s].buff_info, vmp->rx_skb[s]);
+
+		VMAC_INDX_INC(s, vmp->rx_bd_num);
+	}
+	return 0;
+}
+
+static int rxbd2str(struct vmac_priv *vmp)
+{
+	uint16_t s;
+	s = VMAC_INDX_MINUS(vmp->rx_bd_index, 4, vmp->rx_bd_num);
+	return rxbd2str_range(vmp, s, 8);
+}
+
+static int rxbd2str_all(struct vmac_priv *vmp)
+{
+	return rxbd2str_range(vmp, 0, vmp->rx_bd_num);
+}
+
+static int vmaccnt2str(struct vmac_priv *vmp, char *buff)
+{
+	int count;
+	count = sprintf(buff, "tx_bd_busy_cnt:\t%08x\n", vmp->tx_bd_busy_cnt);
+	count += sprintf(buff + count, "tx_stop_queue_cnt:\t%08x\n", vmp->tx_stop_queue_cnt);
+	count += sprintf(buff + count, "rx_skb_alloc_failures:\t%08x\n", vmp->rx_skb_alloc_failures);
+	count += sprintf(buff + count, "intr_cnt:\t%08x\n", vmp->intr_cnt);
+	count += sprintf(buff + count, "vmac_xmit_cnt:\t%08x\n", vmp->vmac_xmit_cnt);
+	count += sprintf(buff + count, "vmac_skb_free:\t%08x\n", vmp->vmac_skb_free);
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	count += sprintf(buff + count, "skb_recycle_cnt:\t%08x\n", vmp->skb_recycle_cnt);
+	count += sprintf(buff + count, "skb_recycle_failures:\t%08x\n", vmp->skb_recycle_failures);
+#endif
+	count += sprintf(buff + count, "vmp->txqueue_stopped=%x\n", vmp->txqueue_stopped);
+	count += sprintf(buff + count, "*vmp->txqueue_wake=%x\n", *vmp->txqueue_wake);
+#ifdef RX_IP_HDR_REALIGN
+	if(rx_pkt_align)
+		count += sprintf(buff + count, "rx iphdr aligned:%d,unalign:%d\n", align_cnt, unalign_cnt);
+#endif
+	return count;
+}
+
+static ssize_t vmac_dbg_show(struct device *dev, struct device_attribute *attr,
+						char *buff)
+{
+	struct net_device *ndev = container_of(dev, struct net_device, dev);
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	int count = 0;
+	switch (vmp->show_item) {
+	case SHOW_TX_BD: /* Print Tx Rquest Queue */
+		count = (ssize_t)txbd2str_all(vmp);
+		break;
+	case SHOW_RX_BD:/* show Rx BD */
+		count = (ssize_t)rxbd2str_all(vmp);
+		break;
+	case SHOW_VMAC_STATS:/* show vmac interrupt statistic info */
+		count = vmaccnt2str(vmp, buff);
+	default:
+		break;
+	}
+	return count;
+}
+
+static ssize_t vmac_dbg_set(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct net_device *ndev = container_of(dev, struct net_device, dev);
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	uint8_t cmd;
+
+	cmd = (uint8_t)simple_strtoul(buf, NULL, 10);
+	if (cmd < 16) {
+		switch(cmd) {
+		case 0:
+			vmp->dbg_flg = 0; /* disable all of runtime dump */
+			break;
+		case 1:
+			napi_schedule(&vmp->napi);
+			break;
+		case 2:
+			vmp->tx_bd_busy_cnt = 0;
+			vmp->intr_cnt = 0;
+			vmp->rx_skb_alloc_failures = 0;
+		default:
+			break;
+		}
+	}
+	else if (cmd < 32) /* used for vmac_dbg_show */
+		vmp->show_item = cmd;
+	else if (cmd < 64) /* used for runtime dump */
+		vmp->dbg_flg |= (0x1 << (cmd - 32));
+	else if (cmd == 64) /* enable all of runtime dump */
+		vmp->dbg_flg = -1;
+
+	return count;
+}
+static DEVICE_ATTR(dbg, S_IWUSR | S_IRUSR, vmac_dbg_show, vmac_dbg_set); /* dev_attr_dbg */
+
+static ssize_t vmac_pm_show(struct device *dev, struct device_attribute *attr,
+						char *buff)
+{
+	struct net_device *ndev = container_of(dev, struct net_device, dev);
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	int count = 0;
+
+	count += sprintf(buff + count, "PCIE Device Power State : %s\n",
+				le32_to_cpu(*vmp->ep_pmstate) == PCI_D3hot ? "D3" : "D0");
+
+	return count;
+}
+
+static ssize_t vmac_pm_set(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct net_device *ndev = container_of(dev, struct net_device, dev);
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	uint8_t cmd;
+
+	cmd = (uint8_t)simple_strtoul(buf, NULL, 10);
+
+	if (cmd == 0) {
+		qdpc_pcie_resume(vmp->pdev);
+	} else if (cmd == 1) {
+		pm_message_t state;
+		state.event = 0;
+		qdpc_pcie_suspend(vmp->pdev, state);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(pmctrl, S_IWUSR | S_IRUSR, vmac_pm_show, vmac_pm_set); /* dev_attr_pmctrl */
+
+static struct attribute *vmac_device_attrs[] = {
+	&dev_attr_dbg.attr,
+	&dev_attr_pmctrl.attr,
+	NULL,
+};
+
+static const struct attribute_group vmac_attr_group = {
+	.attrs = vmac_device_attrs,
+};
+
+#ifdef VMAC_DEBUG_MODE
+static void dump_pkt(char *data, int len, char *s)
+{
+	int i;
+
+	if (len > 128)
+		len = 128;
+	printk("%spkt start%p>\n", s, data);
+	for (i = 0; i < len;) {
+		printk("%02x ", data[i]);
+		if ((++i % 16) == 0)
+			printk("\n");
+	}
+	printk("<%spkt end\n", s);
+}
+
+static void dump_rx_interrupt(struct vmac_priv *vmp)
+{
+	printk("intr_cnt:\t%08x\n", vmp->intr_cnt);
+}
+#endif
+
+#define VMAC_BD_INT32_VAR 3
+
+static int alloc_bd_tbl(struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	uint32_t ucaddr;
+	uint32_t paddr;
+	int len;	/* Length of allocated Transmitted & Received descriptor array */
+
+	/* uint32_t is used to be updated by ep */
+	len = (vmp->tx_bd_num + vmp->rx_bd_num) * VMAC_BD_LEN + VMAC_BD_INT32_VAR * sizeof(uint32_t);
+	ucaddr = (uint32_t)pci_alloc_consistent(vmp->pdev, len, (dma_addr_t *)&paddr);
+	if (!ucaddr)
+		return -1;
+
+	memset((void *)ucaddr, 0, len);
+
+	vmp->addr_uncache = ucaddr;
+	vmp->uncache_len = len;
+
+	/* Update pointers related with Tx descriptor table */
+	vmp->tx_bd_base = (struct vmac_bd *)ucaddr;
+	vmp->paddr_tx_bd_base = paddr;
+	qdpc_pcie_posted_write(paddr, &vmp->bda->bda_rc_tx_bd_base);
+	init_tx_bd(vmp);
+	printk("Tx Descriptor table: uncache virtual addr: 0x%08x paddr: 0x%08x\n",
+		(uint32_t)vmp->tx_bd_base, paddr);
+
+	/* Update pointers related with Rx descriptor table */
+	ucaddr += vmp->tx_bd_num * VMAC_BD_LEN;
+	paddr += vmp->tx_bd_num * VMAC_BD_LEN;
+
+	vmp->rx_bd_base = (struct vmac_bd *)ucaddr;
+	qdpc_pcie_posted_write(paddr, &vmp->bda->bda_rc_rx_bd_base);
+	printk("Rx Descriptor table: uncache virtual addr: 0x%08x paddr: 0x%08x\n",
+		(uint32_t)vmp->rx_bd_base, paddr);
+
+	/* Update pointers used by EP's updating consumed packet index */
+	ucaddr += vmp->rx_bd_num * VMAC_BD_LEN;
+	paddr += vmp->rx_bd_num * VMAC_BD_LEN;
+
+	vmp->ep_next_rx_pkt = (uint32_t *)ucaddr;
+	qdpc_pcie_posted_write(paddr, &vmp->bda->bda_ep_next_pkt);
+	printk("EP_handled_idx: uncache virtual addr: 0x%08x paddr: 0x%08x\n",
+		(uint32_t)vmp->ep_next_rx_pkt, paddr);
+
+	ucaddr += sizeof(uint32_t);
+	paddr += sizeof(uint32_t);
+
+	vmp->txqueue_wake = (uint32_t *)ucaddr;
+
+	ucaddr += sizeof(uint32_t);
+	paddr += sizeof(uint32_t);
+	vmp->ep_pmstate = (uint32_t *)ucaddr;
+
+	return 0;
+}
+
+static void free_bd_tbl(struct vmac_priv *vmp)
+{
+	pci_free_consistent(vmp->pdev, vmp->uncache_len, (void *)vmp->addr_uncache,
+			vmp->paddr_tx_bd_base);
+}
+
+static int alloc_skb_desc_array(struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	uint32_t addr;
+	int len;
+
+	len = (vmp->tx_bd_num + vmp->rx_bd_num) * (sizeof(struct sk_buff *));
+	addr = (uint32_t)kzalloc(len, GFP_KERNEL);
+	if (!addr)
+		return -1;
+	vmp->tx_skb = (struct sk_buff **)addr;
+
+	addr += vmp->tx_bd_num * sizeof(struct sk_buff *);
+	vmp->rx_skb = (struct sk_buff **)addr;
+
+	return 0;
+}
+
+static void free_skb_desc_array(struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	kfree(vmp->tx_skb);
+}
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+static inline struct sk_buff *__vmac_rx_skb_freelist_pop(struct vmac_priv *vmp)
+{
+	struct sk_buff *skb = __skb_dequeue(&vmp->rx_skb_freelist);
+
+	return skb;
+}
+
+static inline int vmac_rx_skb_freelist_push(struct vmac_priv *vmp, dma_addr_t buff_addr, struct sk_buff *skb)
+{
+	unsigned long flag;
+
+	if (skb_queue_len(&vmp->rx_skb_freelist) > QTN_RX_SKB_FREELIST_MAX_SIZE) {
+		pci_unmap_single(vmp->pdev, buff_addr, skb->len, (int)DMA_BIDIRECTIONAL);
+		dev_kfree_skb(skb);
+		vmp->vmac_skb_free++;
+		return 0;
+	}
+
+	/* check for undersize skb; this should never happen, and indicates problems elsewhere */
+	if (unlikely((skb_end_pointer(skb) - skb->head) < QTN_RX_BUF_MIN_SIZE)) {
+		pci_unmap_single(vmp->pdev, buff_addr, skb->len, (int)DMA_BIDIRECTIONAL);
+		dev_kfree_skb(skb);
+		vmp->vmac_skb_free++;
+		vmp->skb_recycle_failures++;
+		return -EINVAL;
+	}
+
+	skb->len = 0;
+	skb->tail = skb->data = skb->head;
+	skb_reserve(skb, NET_SKB_PAD);
+	skb_reserve(skb, align_buf_dma_offset(skb->data));
+
+	qtn_spin_lock_bh_save(&vmp->rx_skb_freelist_lock, &flag);
+	__skb_queue_tail(&vmp->rx_skb_freelist, skb);
+	qtn_spin_unlock_bh_restore(&vmp->rx_skb_freelist_lock, &flag);
+
+	vmp->skb_recycle_cnt++;
+
+	return 0;
+}
+
+static inline void __vmac_rx_skb_freelist_refill(struct vmac_priv *vmp)
+{
+	struct sk_buff *skb = NULL;
+	int num = vmp->rx_skb_freelist_fill_level - skb_queue_len(&vmp->rx_skb_freelist);
+
+	while (num > 0) {
+		if (!(skb = dev_alloc_skb(SKB_BUF_SIZE))) {
+			vmp->rx_skb_alloc_failures++;
+			break;
+		}
+		/* Move skb->data to a cache line boundary */
+		skb_reserve(skb, align_buf_dma_offset(skb->data));
+		pci_map_single(vmp->pdev, skb->data, skb_end_pointer(skb) - skb->data, (int)DMA_FROM_DEVICE);
+		__skb_queue_tail(&vmp->rx_skb_freelist, skb);
+
+		num--;
+	}
+}
+
+static void vmac_rx_skb_freelist_purge(struct vmac_priv *vmp)
+{
+	unsigned long flag;
+
+	qtn_spin_lock_bh_save(&vmp->rx_skb_freelist_lock, &flag);
+	__skb_queue_purge(&vmp->rx_skb_freelist);
+	qtn_spin_unlock_bh_restore(&vmp->rx_skb_freelist_lock, &flag);
+}
+#endif /* QTN_SKB_RECYCLE_SUPPORT */
+
+static inline bool check_netlink_magic(qdpc_cmd_hdr_t *cmd_hdr)
+{
+	return ((memcmp(cmd_hdr->dst_magic, QDPC_NETLINK_DST_MAGIC, ETH_ALEN) == 0)
+		&& (memcmp(cmd_hdr->src_magic, QDPC_NETLINK_SRC_MAGIC, ETH_ALEN) == 0));
+}
+
+static void vmac_netlink_rx(struct net_device *ndev, void *buf, size_t len, uint16_t rpc_type, uint32_t total_len)
+{
+	struct vmac_priv *priv = netdev_priv(ndev);
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int pid = 0;
+	int frag = (rpc_type & QDPC_RPC_TYPE_FRAG_MASK);
+
+	rpc_type &= QDPC_RPC_TYPE_MASK;
+
+	if (unlikely(total_len > VMAC_NL_BUF_SIZE)) {
+		printk(KERN_INFO"%s: total length %u exceeds buffer length %u\n", __func__,
+			total_len, VMAC_NL_BUF_SIZE);
+		goto reset_nlbuf;
+	}
+
+	if (unlikely(priv->nl_len + len > total_len)) {
+		printk(KERN_INFO"%s: frag length %u exceeds total length %u\n", __func__,
+			priv->nl_len + len, total_len);
+		goto reset_nlbuf;
+	}
+
+	memcpy(priv->nl_buf + priv->nl_len, buf, len);
+	priv->nl_len += len;
+
+	if (frag)
+		return;
+
+	/* last fragment -- hand it to upper layer */
+	buf = priv->nl_buf;
+	len = priv->nl_len;
+
+	skb = nlmsg_new(len, GFP_ATOMIC);
+	if (skb == NULL) {
+		DBGPRINTF("WARNING: out of netlink SKBs\n");
+		goto reset_nlbuf;
+	}
+
+	nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, len, 0);  ;
+	memcpy(nlmsg_data(nlh), buf, len);
+	NETLINK_CB(skb).dst_group = 0;
+
+	if (rpc_type == QDPC_RPC_TYPE_STRCALL)
+		pid = priv->str_call_nl_pid;
+	else if (rpc_type == QDPC_RPC_TYPE_LIBCALL)
+		pid = priv->lib_call_nl_pid;
+
+	if (unlikely(pid == 0)) {
+		kfree_skb(skb);
+		goto reset_nlbuf;
+	}
+
+	nlmsg_unicast(priv->nl_socket, skb, pid);
+
+reset_nlbuf:
+	priv->nl_len = 0;
+}
+
+static inline void vmac_napi_schedule(struct vmac_priv *vmp)
+{
+	if (napi_schedule_prep(&vmp->napi)) {
+		disable_vmac_ints(vmp);
+		__napi_schedule(&vmp->napi);
+	}
+}
+
+#ifdef QDPC_PLATFORM_IRQ_FIXUP
+static inline int vmac_has_more_rx(struct vmac_priv *vmp)
+{
+	uint16_t i = vmp->rx_bd_index;
+	volatile struct vmac_bd *rbdp = &vmp->rx_bd_base[i];
+
+	return !(le32_to_cpu(rbdp->buff_info) & VMAC_BD_EMPTY);
+}
+static inline void vmac_irq_open_fixup(struct vmac_priv *vmp)
+{
+	vmac_napi_schedule(vmp);
+}
+/*
+ * TODO: vmac_irq_napi_fixup needs to undergo stability and
+ * especially performance test to justify its value
+*/
+static inline void vmac_irq_napi_fixup(struct vmac_priv *vmp)
+{
+	if (unlikely(vmac_has_more_rx(vmp)))
+		vmac_napi_schedule(vmp);
+}
+#else
+#define vmac_irq_open_fixup(v) do{}while(0)
+#define vmac_irq_napi_fixup(v) do{}while(0)
+#endif
+
+
+#ifdef RX_IP_HDR_REALIGN
+/*
+ * skb buffer have a pading, so skb data move less than pading is safe
+ *
+ */
+static void vmac_rx_ip_align_ahead(struct sk_buff *skb, uint32_t move_bytes)
+{
+	uint8_t *pkt_src, *pkt_dst;
+	uint8_t bytes_boundary = ((uint32_t)(skb->data)) % 4;
+	BUG_ON(bytes_boundary & 1);
+
+	/*bytes_boundary == 0 means etherheader is 4 byte aligned,
+	 *so IP header is 2(+14 ether header) byte aligned,
+	 *move whole packet 2 byte ahead for QCA NSS preference
+	*/
+
+	if(bytes_boundary == 0){
+		if(skb_headroom(skb) >= move_bytes){
+			pkt_src = skb->data;
+			pkt_dst = skb->data - move_bytes;
+
+			memmove(pkt_dst, pkt_src, skb->len);
+
+			skb->data -= move_bytes;
+			skb->tail -= move_bytes;
+		}
+		unalign_cnt++;
+	}
+	else if(bytes_boundary == 2){
+		align_cnt++;
+	}
+}
+#endif
+
+static int __sram_text vmac_rx_poll(struct napi_struct *napi, int budget)
+{
+	struct vmac_priv *vmp = container_of(napi, struct vmac_priv, napi);
+	struct net_device *ndev = vmp->ndev;
+	struct ethhdr *eth;
+	qdpc_cmd_hdr_t *cmd_hdr;
+	int processed = 0;
+	uint16_t i = vmp->rx_bd_index;
+	volatile struct vmac_bd *rbdp = &vmp->rx_bd_base[i];
+	uint32_t descw1;
+
+	while (!((descw1 = le32_to_cpu(VMAC_REG_READ(&rbdp->buff_info))) & VMAC_BD_EMPTY) && (processed < budget)) {
+		struct sk_buff *skb;
+		skb = vmp->rx_skb[i];
+		if (skb) {
+			skb_reserve(skb, VMAC_GET_OFFSET(descw1));
+			skb_put(skb, VMAC_GET_LEN(descw1));
+
+			eth = (struct ethhdr *)(skb->data);
+			if (unlikely(ntohs(eth->h_proto) == QDPC_APP_NETLINK_TYPE)) {
+				/* Double Check if it's netlink packet*/
+				cmd_hdr = (qdpc_cmd_hdr_t *)skb->data;
+				if (check_netlink_magic(cmd_hdr)) {
+					vmac_netlink_rx(ndev,
+						skb->data + sizeof(qdpc_cmd_hdr_t),
+						ntohs(cmd_hdr->len),
+						ntohs(cmd_hdr->rpc_type),
+						ntohs(cmd_hdr->total_len));
+				}
+				dev_kfree_skb(skb);
+			} else {
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+				pci_unmap_single(vmp->pdev, rbdp->buff_addr,
+					skb_end_pointer(skb) - skb->data, (int)DMA_BIDIRECTIONAL);
+#else
+				pci_unmap_single(vmp->pdev, rbdp->buff_addr,
+					skb_end_pointer(skb) - skb->data, (int)DMA_FROM_DEVICE);
+#endif /* QTN_SKB_RECYCLE_SUPPORT */
+
+#ifdef RX_IP_HDR_REALIGN
+				if (rx_pkt_align)
+					vmac_rx_ip_align_ahead(skb, 2);
+#endif
+				dump_rx_pkt(vmp, (char *)skb->data, (int)skb->len);
+
+				processed++;
+
+#ifdef CONFIG_ARCH_COMCERTO
+				extern int comcerto_wifi_rx_fastpath(struct sk_buff *skb);
+				if (comcerto_wifi_rx_fastpath(skb))
+#endif
+				{
+#ifdef CONFIG_COMCERTO_DMA_COHERENT_SKB
+					if (skb->dma_coherent) {
+						// Copy to normal memory before passing to the kernel.
+						struct sk_buff *n = skb_copy(skb, GFP_ATOMIC);
+						consume_skb(skb);
+						skb = n;
+					}
+#endif
+
+					if (skb) {
+						skb->protocol = eth_type_trans(skb, ndev);
+						netif_receive_skb(skb);
+					}
+				}
+
+				ndev->stats.rx_packets++;
+				ndev->stats.rx_bytes += VMAC_GET_LEN(descw1);
+			}
+		}
+		if ((ndev->stats.rx_packets & RX_DONE_INTR_MSK) == 0)
+			writel(TOPAZ_SET_INT(IPC_RC_RX_DONE), (volatile void *)(vmp->ep_ipc_reg));
+
+		dump_rx_bd(vmp);
+
+		ndev->last_rx = jiffies;
+
+		/*
+		 * We are done with the current buffer attached to this descriptor, so attach a new
+		 * one.
+		 */
+		if (skb2rbd_attach(ndev, i, descw1 & VMAC_BD_WRAP) == 0) {
+			if (++i >= vmp->rx_bd_num)
+				i = 0;
+			vmp->rx_bd_index = i;
+			rbdp = &vmp->rx_bd_base[i];
+		} else {
+			break;
+		}
+	}
+#ifdef QTN_WAKEQ_SUPPORT
+	vmac_try_wake_queue(ndev);
+#endif
+	if (processed < budget) {
+		napi_complete(napi);
+		enable_vmac_ints(vmp);
+		vmac_irq_napi_fixup(vmp);
+	}
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	spin_lock(&vmp->rx_skb_freelist_lock);
+	__vmac_rx_skb_freelist_refill(vmp);
+	spin_unlock(&vmp->rx_skb_freelist_lock);
+#endif
+
+	return processed;
+}
+
+static int __sram_text skb2rbd_attach(struct net_device *ndev, uint16_t rx_bd_index, uint32_t wrap)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	volatile struct vmac_bd * rbdp;
+	uint32_t buff_addr;
+	struct sk_buff *skb = NULL;
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	spin_lock(&vmp->rx_skb_freelist_lock);
+	if (unlikely(!(skb = __vmac_rx_skb_freelist_pop(vmp)))) {
+		spin_unlock(&vmp->rx_skb_freelist_lock);
+		vmp->rx_skb[rx_bd_index] = NULL;/* prevent old packet from passing the packet up */
+		return -1;
+	}
+	spin_unlock(&vmp->rx_skb_freelist_lock);
+#else
+#if defined(CONFIG_COMCERTO_ZONE_DMA_NCNB)
+	skb = __dev_alloc_skb(SKB_BUF_SIZE, GFP_ATOMIC | GFP_DMA_NCNB);
+#elif defined(CONFIG_COMCERTO_DMA_COHERENT_SKB)
+	skb = alloc_dma_coherent_skb(SKB_BUF_SIZE);
+#else
+	skb = dev_alloc_skb(SKB_BUF_SIZE);
+#endif
+	if (!skb) {
+		vmp->rx_skb_alloc_failures++;
+		vmp->rx_skb[rx_bd_index] = NULL;/* prevent old packet from passing the packet up */
+		return -1;
+	}
+#endif /* QTN_SKB_RECYCLE_SUPPORT */
+	skb->dev = ndev;
+
+	vmp->rx_skb[rx_bd_index] = skb;
+#ifndef QTN_SKB_RECYCLE_SUPPORT
+	/* Move skb->data to a cache line boundary */
+	skb_reserve(skb, align_buf_dma_offset(skb->data));
+#endif /* QTN_SKB_RECYCLE_SUPPORT */
+
+	/* Invalidate cache and map virtual address to bus address. */
+	rbdp = &vmp->rx_bd_base[rx_bd_index];
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	buff_addr = virt_to_bus(skb->data);
+#else
+	buff_addr = (uint32_t)pci_map_single(vmp->pdev, skb->data,
+				skb_end_pointer(skb) - skb->data, (int)DMA_FROM_DEVICE);
+#endif
+	rbdp->buff_addr = cpu_to_le32(buff_addr);
+
+	/* TODO: packet length, currently don't check the length */
+	rbdp->buff_info =  cpu_to_le32(VMAC_BD_EMPTY | wrap);
+
+	return 0;
+}
+
+
+static __attribute__((section(".sram.text"))) void
+vmac_tx_teardown(struct net_device *ndev, qdpc_pcie_bda_t *bda)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	volatile struct vmac_bd *tbdp;
+	uint16_t i;
+	uint32_t end_idx = le32_to_cpu(VMAC_REG_READ(vmp->ep_next_rx_pkt));
+
+	i = vmp->tx_reclaim_start;
+
+	while (i != end_idx) {
+		struct sk_buff *skb;
+		skb = vmp->tx_skb[i];
+		if (!skb)
+			break;
+		tbdp = &vmp->tx_bd_base[i];
+		ndev->stats.tx_packets++;
+
+		ndev->stats.tx_bytes +=  skb->len;
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+		vmac_rx_skb_freelist_push(vmp, (dma_addr_t)tbdp->buff_addr, skb);
+#else
+		pci_unmap_single(vmp->pdev, (dma_addr_t)tbdp->buff_addr,
+			skb->len, (int)DMA_TO_DEVICE);
+		dev_kfree_skb(skb);
+#endif /* QTN_SKB_RECYCLE_SUPPORT */
+		vmp->tx_skb[i] = NULL;
+
+		vmp->vmac_skb_free++;
+
+		vmp->vmac_tx_queue_len--;
+
+		if (++i >= vmp->tx_bd_num)
+			i = 0;
+	}
+
+	vmp->tx_reclaim_start = i;
+}
+
+#ifdef QTN_TX_SKBQ_SUPPORT
+static inline int __vmac_process_tx_skbq(struct net_device *ndev, uint32_t budget)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	struct sk_buff *skb;
+
+	while(!vmp->txqueue_stopped && (skb = __skb_dequeue(&vmp->tx_skb_queue)) != NULL) {
+		if (vmac_tx((void *)skb, ndev) != NETDEV_TX_OK) {
+			__skb_queue_head(&vmp->tx_skb_queue, skb);
+			break;
+		}
+
+		if (--budget == 0) {
+			break;
+		}
+	}
+
+	if (skb_queue_len(&vmp->tx_skb_queue) && !vmp->txqueue_stopped) {
+		tasklet_schedule(&vmp->tx_skbq_tasklet);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static int vmac_process_tx_skbq(struct net_device *ndev, uint32_t budget)
+{
+	int ret;
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	spin_lock(&vmp->tx_skbq_lock);
+	ret = __vmac_process_tx_skbq(ndev, budget);
+	spin_unlock(&vmp->tx_skbq_lock);
+
+	return ret;
+}
+
+static void __attribute__((section(".sram.text"))) vmac_tx_skbq_tasklet(unsigned long data)
+{
+	struct net_device *ndev = (struct net_device *)data;
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	vmac_process_tx_skbq(ndev, vmp->tx_skbq_tasklet_budget);
+}
+
+static int vmac_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	int ret;
+	unsigned long flag;
+
+	if (unlikely(skb_queue_len(&vmp->tx_skb_queue) >= vmp->tx_skbq_max_size)) {
+                dev_kfree_skb((void *)skb);
+		return NETDEV_TX_OK;
+	}
+
+	qtn_spin_lock_bh_save(&vmp->tx_skbq_lock, &flag);
+	__skb_queue_tail(&vmp->tx_skb_queue, skb);
+	ret = __vmac_process_tx_skbq(ndev, vmp->tx_skbq_budget);
+	qtn_spin_unlock_bh_restore(&vmp->tx_skbq_lock, &flag);
+
+	return ret;
+}
+#else
+static int vmac_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	return vmac_tx((void *)skb, ndev);
+}
+#endif
+
+void vmac_tx_drop(void *pkt_handle, struct net_device *ndev)
+{
+	struct sk_buff *skb;
+
+	{
+                skb = (struct sk_buff *)pkt_handle;
+                dev_kfree_skb((void *)skb);
+        }
+}
+
+#ifdef QTN_WAKEQ_SUPPORT
+static inline void vmac_try_stop_queue(struct net_device *ndev)
+{
+	unsigned long flags;
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	spin_lock_irqsave(&vmp->txqueue_op_lock, flags);
+
+	if (!vmp->txqueue_stopped) {
+		vmp->txqueue_stopped = 1;
+		*vmp->txqueue_wake = 0;
+		barrier();
+		writel(TOPAZ_SET_INT(IPC_RC_STOP_TX), (volatile void *)(vmp->ep_ipc_reg));
+		vmp->tx_stop_queue_cnt++;
+		netif_stop_queue(ndev);
+	}
+	spin_unlock_irqrestore(&vmp->txqueue_op_lock, flags);
+}
+
+static inline void vmac_try_wake_queue(struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&vmp->txqueue_op_lock, flags);
+	if (vmp->txqueue_stopped && *vmp->txqueue_wake) {
+
+		vmp->txqueue_stopped = 0;
+
+		netif_wake_queue(ndev);
+#ifdef QTN_TX_SKBQ_SUPPORT
+		tasklet_schedule(&vmp->tx_skbq_tasklet);
+#endif
+	}
+	spin_unlock_irqrestore(&vmp->txqueue_op_lock, flags);
+}
+#endif
+
+int __attribute__((section(".sram.text")))
+vmac_tx(void *pkt_handle, struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	uint16_t i; /* tbd index */
+	volatile struct vmac_bd *tbdp; /* Tx BD pointer */
+	int len;
+	struct sk_buff *skb;
+	uint32_t baddr;
+	qdpc_pcie_bda_t *bda = vmp->bda;
+
+	/* TODO: Under current architect, register_netdev() is called
+	before EP is ready. So an variable ep_ready is added to achieve
+	defensive programming. We need to change the code segment later */
+	if (unlikely(vmp->ep_ready == 0)) {
+		vmac_tx_drop(pkt_handle, ndev);
+		return NETDEV_TX_OK;
+	}
+
+	vmp->vmac_xmit_cnt++;
+#ifdef RC_TXDONE_TIMER
+	spin_lock(&vmp->tx_lock);
+#endif
+	/* Tear down the previous skb transmitted by DMA */
+	vmac_tx_teardown(ndev, bda);
+
+	/* Reserve one entry space to differentiate full and empty case */
+	if (vmp->vmac_tx_queue_len >= vmp->tx_bd_num - 2) {
+#ifdef QTN_WAKEQ_SUPPORT
+		vmac_try_stop_queue(ndev);
+#endif
+		if (vmp->vmac_tx_queue_len >= vmp->tx_bd_num - 1) {
+#ifdef RC_TXDONE_TIMER
+			spin_unlock(&vmp->tx_lock);
+#endif
+			vmp->tx_bd_busy_cnt++;
+			printk(KERN_ERR "%s fail to get BD\n", ndev->name);
+			return NETDEV_TX_BUSY;
+		}
+	}
+
+	i = vmp->tx_bd_index;
+
+	skb = (struct sk_buff *)pkt_handle;
+	vmp->tx_skb[i] = (struct sk_buff *)pkt_handle;
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	baddr = (uint32_t)pci_map_single(vmp->pdev, skb->data, skb->len, (int)DMA_BIDIRECTIONAL);
+#else
+	baddr = (uint32_t)pci_map_single(vmp->pdev, skb->data, skb->len, (int)DMA_TO_DEVICE);
+#endif
+	len = skb->len;
+	wmb();
+
+	/* Update local descriptor array */
+	tbdp = &vmp->tx_bd_base[i];
+	tbdp->buff_addr = baddr;
+
+	/* Update remote Request Queue */
+	VMAC_REG_WRITE(&bda->request[i].addr, (baddr));
+	VMAC_REG_WRITE(&bda->request[i].info, (len | PCIE_TX_VALID_PKT));
+
+	vmp->vmac_tx_queue_len++;
+
+	dump_tx_pkt(vmp, skb->data, skb->len);
+
+	if (++i >= vmp->tx_bd_num)
+		i = 0;
+
+	vmp->tx_bd_index = i;
+
+	dump_tx_bd(vmp);
+
+	writel(TOPAZ_SET_INT(IPC_EP_RX_PKT), (volatile void *)(vmp->ep_ipc_reg));
+
+#ifdef RC_TXDONE_TIMER
+	vmac_tx_teardown(ndev, bda);
+	mod_timer(&vmp->tx_timer, jiffies + 1);
+	spin_unlock(&vmp->tx_lock);
+#endif
+	return NETDEV_TX_OK;
+}
+
+static irqreturn_t vmac_interrupt(int irq, void *dev_id)
+{
+	struct net_device *ndev = (struct net_device *)dev_id;
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	handle_ep_rst_int(ndev);
+
+	if(!vmp->msi_enabled) {
+		/* Deassert remote INTx message */
+		qdpc_deassert_intx(vmp);
+	}
+
+	vmp->intr_cnt++;
+
+	vmac_napi_schedule(vmp);
+#ifdef QTN_WAKEQ_SUPPORT
+	vmac_try_wake_queue(ndev);
+#endif
+	dump_rx_int(vmp);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * The Tx ring has been full longer than the watchdog timeout
+ * value. The transmitter must be hung?
+ */
+inline static void vmac_tx_timeout(struct net_device *ndev)
+{
+	printk(KERN_ERR "%s: vmac_tx_timeout: ndev=%p\n", ndev->name, ndev);
+	ndev->trans_start = jiffies;
+}
+
+#ifdef RC_TXDONE_TIMER
+static void vmac_tx_buff_cleaner(struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	qdpc_pcie_bda_t *bda = vmp->bda;
+
+	spin_lock(&vmp->tx_lock);
+	vmac_tx_teardown(ndev, bda);
+
+	if (vmp->tx_skb[vmp->tx_reclaim_start] == NULL) {
+		del_timer(&vmp->tx_timer);
+	} else {
+		writel(TOPAZ_SET_INT(IPC_EP_RX_PKT), (volatile void *)(vmp->ep_ipc_reg));
+		mod_timer(&vmp->tx_timer, jiffies + 1);
+	}
+	spin_unlock(&vmp->tx_lock);
+}
+#endif
+
+/* ethtools support */
+static int vmac_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+	return -EINVAL;
+}
+
+static int vmac_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return -EINVAL;
+}
+
+static int vmac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+	return -EINVAL;
+}
+
+static void vmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	info->fw_version[0] = '\0';
+	sprintf(info->bus_info, "%s %d", DRV_NAME, vmp->mac_id);
+	info->regdump_len = 0;
+}
+
+static const struct ethtool_ops vmac_ethtool_ops = {
+	.get_settings = vmac_get_settings,
+	.set_settings = vmac_set_settings,
+	.get_drvinfo = vmac_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+};
+
+static const struct net_device_ops vmac_device_ops = {
+	.ndo_open = vmac_open,
+	.ndo_stop = vmac_close,
+	.ndo_start_xmit = vmac_xmit,
+	.ndo_do_ioctl = vmac_ioctl,
+	.ndo_tx_timeout = vmac_tx_timeout,
+	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_get_stats = vmac_get_stats,
+};
+
+struct net_device *vmac_alloc_ndev(void)
+{
+	struct net_device * ndev;
+
+        /* Allocate device structure */
+	ndev = alloc_netdev(sizeof(struct vmac_priv), vmaccfg.ifname,
+			NET_NAME_UNKNOWN, ether_setup);
+	if(!ndev)
+		printk(KERN_ERR "%s: alloc_etherdev failed\n", vmaccfg.ifname);
+
+	return ndev;
+}
+EXPORT_SYMBOL(vmac_alloc_ndev);
+
+static void eth_parse_enetaddr(const char *addr, uint8_t *enetaddr)
+{
+	char *end;
+	int i;
+
+	for (i = 0; i < 6; ++i) {
+		enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
+		if (addr)
+			addr = (*end) ? end + 1 : end;
+	}
+}
+
+
+int vmac_net_init(struct pci_dev *pdev)
+{
+	struct vmac_priv *vmp = NULL;
+	struct net_device *ndev = NULL;
+	int err = -ENOMEM;
+	__iomem qdpc_pcie_bda_t *bda;
+
+	printk(KERN_INFO"%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
+
+	ndev = (struct net_device *)pci_get_drvdata(pdev);
+	if (!ndev)
+		goto vnet_init_err_0;
+
+
+	if (ethaddr)
+		eth_parse_enetaddr(ethaddr, ndev->dev_addr);
+
+	if (!is_valid_ether_addr(ndev->dev_addr))
+		random_ether_addr(ndev->dev_addr);
+
+	ndev->netdev_ops = &vmac_device_ops;
+	ndev->tx_queue_len = QTN_GLOBAL_INIT_EMAC_TX_QUEUE_LEN;
+	ndev->ethtool_ops = &vmac_ethtool_ops;
+
+	/* Initialize private data */
+	vmp = netdev_priv(ndev);
+	vmp->pdev = pdev;
+	vmp->ndev = ndev;
+
+	vmp->pcfg = &vmaccfg;
+	vmp->tx_bd_num = vmp->pcfg->tx_bd_num;
+	vmp->rx_bd_num = vmp->pcfg->rx_bd_num;
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	spin_lock_init(&vmp->rx_skb_freelist_lock);
+	skb_queue_head_init(&vmp->rx_skb_freelist);
+	vmp->rx_skb_freelist_fill_level = QTN_RX_SKB_FREELIST_FILL_SIZE;
+	vmp->skb_recycle_cnt = 0;
+	vmp->skb_recycle_failures = 0;
+#endif
+
+	if (vmp->tx_bd_num > PCIE_RC_TX_QUEUE_LEN) {
+		printk("Error: The length of TX BD array should be no more than %d\n",
+				PCIE_RC_TX_QUEUE_LEN);
+		goto vnet_init_err_0;
+	}
+
+	vmp->ep_ipc_reg = (unsigned long)
+		QDPC_BAR_VADDR(vmp->sysctl_bar, TOPAZ_IPC_OFFSET);
+	ndev->irq = pdev->irq;
+
+	ndev->if_port = QDPC_PLATFORM_IFPORT;
+
+	ndev->watchdog_timeo = VMAC_TX_TIMEOUT;
+
+	bda = vmp->bda;
+
+	qdpc_pcie_posted_write(vmp->tx_bd_num, &bda->bda_rc_tx_bd_num);
+	qdpc_pcie_posted_write(vmp->rx_bd_num, &bda->bda_rc_rx_bd_num);
+
+	/* Allocate Tx & Rx SKB descriptor array */
+	if (alloc_skb_desc_array(ndev))
+		goto vnet_init_err_0;
+
+	/* Allocate and initialise Tx & Rx descriptor array */
+	if (alloc_bd_tbl(ndev))
+		goto vnet_init_err_1;
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	__vmac_rx_skb_freelist_refill(vmp);
+#endif
+
+	if (alloc_and_init_rxbuffers(ndev))
+		goto vnet_init_err_2;
+
+	/* Initialize NAPI */
+	netif_napi_add(ndev, &vmp->napi, vmac_rx_poll, 64);
+
+	/* Register device */
+	if ((err = register_netdev(ndev)) != 0) {
+		printk(KERN_ERR "%s: Cannot register net device, error %d\n", DRV_NAME, err);
+		goto vnet_init_err_3;
+	}
+	printk(KERN_INFO"%s: Vmac Ethernet found\n", ndev->name);
+
+	/* Add the device attributes */
+	err = sysfs_create_group(&ndev->dev.kobj, &vmac_attr_group);
+	if (err) {
+		printk(KERN_ERR "Error creating sysfs files\n");
+	}
+
+	enable_ep_rst_detection(ndev);
+
+	vmp->show_item = SHOW_VMAC_STATS;
+
+#ifdef RC_TXDONE_TIMER
+	spin_lock_init(&vmp->tx_lock);
+	init_timer(&vmp->tx_timer);
+	vmp->tx_timer.data = (unsigned long)ndev;
+	vmp->tx_timer.function = (void (*)(unsigned long))&vmac_tx_buff_cleaner;
+#endif
+	spin_lock_init(&vmp->txqueue_op_lock);
+
+#ifdef QTN_TX_SKBQ_SUPPORT
+	vmp->tx_skbq_budget = QTN_RC_TX_BUDGET;
+	vmp->tx_skbq_max_size = vmp->tx_bd_num << 4;
+	vmp->tx_skbq_tasklet_budget = QTN_RC_TX_TASKLET_BUDGET;
+	spin_lock_init(&vmp->tx_skbq_lock);
+	skb_queue_head_init(&vmp->tx_skb_queue);
+	tasklet_init(&vmp->tx_skbq_tasklet, vmac_tx_skbq_tasklet, (unsigned long)ndev);
+#endif
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	__vmac_rx_skb_freelist_refill(vmp);
+#endif
+
+	return 0;
+
+vnet_init_err_3:
+	free_rx_skbs(vmp);
+vnet_init_err_2:
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	vmac_rx_skb_freelist_purge(vmp);
+#endif
+	free_bd_tbl(vmp);
+vnet_init_err_1:
+	free_skb_desc_array(ndev);
+vnet_init_err_0:
+	return err;
+}
+EXPORT_SYMBOL(vmac_net_init);
+
+int vmac_recovery_init(struct vmac_priv *priv, struct net_device *ndev)
+{
+	int err = -ENOMEM;
+
+	qdpc_pcie_posted_write(priv->tx_bd_num, &priv->bda->bda_rc_tx_bd_num);
+	qdpc_pcie_posted_write(priv->rx_bd_num, &priv->bda->bda_rc_rx_bd_num);
+
+	if (alloc_skb_desc_array(ndev))
+		goto vnet_recovery_err_0;
+
+	if (alloc_bd_tbl(ndev))
+		goto vnet_recovery_err_1;
+
+#ifdef QTN_WAKEQ_SUPPORT
+	if (unlikely(priv->txqueue_stopped)) {
+		printk("Recovery: Wake tx queue\n");
+		*priv->txqueue_wake = 1;
+		vmac_try_wake_queue(ndev);
+	}
+#endif
+
+	if (alloc_and_init_rxbuffers(ndev))
+		goto vnet_recovery_err_2;
+
+	return SUCCESS;
+
+vnet_recovery_err_2:
+	free_bd_tbl(priv);
+vnet_recovery_err_1:
+	free_skb_desc_array(ndev);
+vnet_recovery_err_0:
+	return err;
+}
+EXPORT_SYMBOL(vmac_recovery_init);
+
+static void free_rx_skbs(struct vmac_priv *vmp)
+{
+	/* All Ethernet activity should have ceased before calling
+	 * this function
+	 */
+	uint16_t i;
+	for (i = 0; i < vmp->rx_bd_num; i++) {
+		if (vmp->rx_skb[i]) {
+			dev_kfree_skb(vmp->rx_skb[i]);
+			vmp->rx_skb[i] = 0;
+		}
+	}
+
+	vmp->rx_bd_index = 0;
+}
+
+static void free_tx_pkts(struct vmac_priv *vmp)
+{
+	/* All Ethernet activity should have ceased before calling
+	 * this function
+	 */
+	uint16_t i;
+	for (i = 0; i < vmp->tx_bd_num; i++) {
+		if (vmp->tx_skb[i]) {
+			dev_kfree_skb(vmp->tx_skb[i]);
+			vmp->tx_skb[i] = 0;
+		}
+	}
+
+	vmp->tx_bd_index = 0;
+	vmp->ep_next_rx_pkt = 0;
+	vmp->tx_reclaim_start = 0;
+	vmp->vmac_tx_queue_len = 0;
+}
+
+static void init_tx_bd(struct vmac_priv *vmp)
+{
+	uint16_t i;
+	for (i = 0; i< vmp->tx_bd_num; i++)
+		vmp->tx_bd_base[i].buff_info |= cpu_to_le32(VMAC_BD_EMPTY);
+}
+
+static int alloc_and_init_rxbuffers(struct net_device *ndev)
+{
+	uint16_t i;
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	memset((void *)vmp->rx_bd_base, 0, vmp->rx_bd_num * VMAC_BD_LEN);
+
+	/* Allocate rx buffers */
+	for (i = 0; i < vmp->rx_bd_num; i++) {
+		if (skb2rbd_attach(ndev, i, 0)) {
+			return -1;
+		}
+	}
+
+	vmp->rx_bd_base[vmp->rx_bd_num - 1].buff_info |= cpu_to_le32(VMAC_BD_WRAP);
+	return 0;
+}
+
+extern int qdpc_unmap_iomem(struct vmac_priv *priv);
+void vmac_clean(struct net_device *ndev)
+{
+	struct vmac_priv *vmp;
+
+	if (!ndev)
+		return;
+
+	vmp = netdev_priv(ndev);
+
+	device_remove_file(&ndev->dev, &dev_attr_dbg);
+
+	unregister_netdev(ndev);
+
+	free_rx_skbs(vmp);
+	free_tx_pkts(vmp);
+	free_skb_desc_array(ndev);
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	vmac_rx_skb_freelist_purge(vmp);
+#endif
+
+	disable_ep_rst_detection(ndev);
+
+	netif_napi_del(&vmp->napi);
+
+	free_bd_tbl(vmp);
+}
+
+void vmac_recovery_clean(struct net_device *ndev)
+{
+	struct vmac_priv *vmp;
+
+	vmp = netdev_priv(ndev);
+
+	free_rx_skbs(vmp);
+	free_tx_pkts(vmp);
+	free_skb_desc_array(ndev);
+	free_bd_tbl(vmp);
+}
+
+static void bring_up_interface(struct net_device *ndev)
+{
+	/* Interface will be ready to send/receive data, but will need hooking
+	 * up to the interrupts before anything will happen.
+	 */
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	enable_vmac_ints(vmp);
+}
+
+static void shut_down_interface(struct net_device *ndev)
+{
+	struct vmac_priv *vmp = netdev_priv(ndev);
+	/* Close down MAC and DMA activity and clear all data. */
+	disable_vmac_ints(vmp);
+}
+
+
+static int vmac_open(struct net_device *ndev)
+{
+	int retval = 0;
+	struct vmac_priv *vmp = netdev_priv(ndev);
+
+	bring_up_interface(ndev);
+
+	napi_enable(&vmp->napi);
+
+	/* Todo: request_irq here */
+	retval = request_irq(ndev->irq, &vmac_interrupt, 0, ndev->name, ndev);
+	if (retval) {
+		printk(KERN_ERR "%s: unable to get IRQ %d\n",
+			ndev->name, ndev->irq);
+		goto err_out;
+	}
+
+	netif_start_queue(ndev);
+
+	vmac_irq_open_fixup(vmp);
+
+	return 0;
+err_out:
+	napi_disable(&vmp->napi);
+	return retval;
+}
+
+static int vmac_close(struct net_device *ndev)
+{
+	struct vmac_priv *const vmp = netdev_priv(ndev);
+
+	napi_disable(&vmp->napi);
+
+	shut_down_interface(ndev);
+
+	netif_stop_queue(ndev);
+
+	free_irq(ndev->irq, ndev);
+
+	return 0;
+}
+
+static struct net_device_stats *vmac_get_stats(struct net_device *ndev)
+{
+	return &(ndev->stats);
+}
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h
new file mode 100644
index 0000000..928b809
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h
@@ -0,0 +1,231 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __DRIVERS_NET_TOPAZ_VNET_H
+#define __DRIVERS_NET_TOPAZ_VNET_H	1
+
+#define ETH_TX_TIMEOUT (100*HZ)
+#define MULTICAST_FILTER_LIMIT 64
+
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include <qdpc_config.h>
+#include <topaz_netcom.h>
+
+#define PROC_NAME_SIZE		(32)
+#define VMAC_BD_EMPTY		((uint32_t)0x00000001)
+#define VMAC_BD_WRAP		((uint32_t)0x00000002)
+#define VMAC_BD_MASK_LEN	((uint32_t)0xFFFF0000)
+#define VMAC_BD_MASK_OFFSET ((uint32_t)0x0000FF00)
+
+#define VMAC_GET_LEN(x)		(((x) >> 16) & 0xFFFF)
+#define VMAC_GET_OFFSET(x)	(((x) >> 8) & 0xFF)
+#define VMAC_SET_LEN(len)	(((len) & 0xFFFF) << 16)
+#define VMAC_SET_OFFSET(of)	(((of) & 0xFF) << 8)
+
+#define VMAC_INDX_MINUS(x, y, m) (((x) + (m) - (y)) % (m))
+#define VMAC_INDX_INC(index, m) do {	\
+	if (++(index) >= (m))	\
+		(index) = 0;	\
+	} while(0)
+
+/*
+ * Helper macros handling memory mapped area access
+ */
+#define VMAC_REG_TST(reg, val) ( *((volatile unsigned int *)(reg)) & (val) )
+#define VMAC_REG_SET(reg, val) { volatile unsigned int *r = (unsigned int *)(reg); *r = (*r | (val)); }
+#define VMAC_REG_CLR(reg, val) { volatile unsigned int *r = (unsigned int *)(reg); *r = (*r & ~(val)); }
+#define VMAC_REG_WRITE(reg, val) { *(volatile unsigned int *)reg = (val); }
+#define VMAC_REG_READ(reg) {*(volatile unsigned int *)(reg); }
+
+#define QTN_RC_TX_BUDGET		(16)
+#define QTN_RC_TX_TASKLET_BUDGET	(32)
+
+#define QTN_RX_SKB_FREELIST_FILL_SIZE	(1024)
+#define QTN_RX_SKB_FREELIST_MAX_SIZE	(8192)
+#define QTN_RX_BUF_MIN_SIZE		(1536)
+
+#define VMAC_NL_BUF_SIZE		USHRT_MAX
+
+typedef struct qdpc_bar {
+        void *b_vaddr; /* PCIe bar virtual address */
+        dma_addr_t b_busaddr; /* PCIe bar physical address */
+        size_t b_len; /* Bar resource length */
+        uint32_t b_offset; /* Offset from start of map */
+        uint8_t b_index; /* Bar Index */
+} qdpc_bar_t;
+
+#define QDPC_BAR_VADDR(bar, off) ((bar).b_vaddr +(off))
+
+struct vmac_cfg {
+	uint16_t rx_bd_num;
+	uint16_t tx_bd_num;
+	char ifname[PROC_NAME_SIZE];
+	struct net_device *dev;
+};
+
+#if defined(QTN_RC_ENABLE_HDP)
+enum pkt_type {
+        PKT_SKB = 0,
+        PKT_TQE
+};
+#endif
+
+struct vmac_tx_buf {
+        uint32_t handle;
+        uint16_t len;
+#if defined(QTN_RC_ENABLE_HDP)
+        uint8_t type; /* 1 payload only, 0 skb */
+        uint8_t rsv;
+#else
+	uint16_t rsv;
+#endif
+};
+
+struct vmac_priv {
+	struct sk_buff **tx_skb;/* skb having post to PCIe DMA */
+	volatile struct vmac_bd *tx_bd_base; /* Tx buffer descriptor */
+	dma_addr_t paddr_tx_bd_base; /* Physical address of Tx BD array */
+	volatile uint32_t *ep_next_rx_pkt;
+	uint16_t tx_bd_index;
+	uint16_t tx_reclaim_start;
+	uint16_t tx_bd_num;
+	uint8_t txqueue_stopped;
+	volatile uint32_t *txqueue_wake; /* shared variable with EP */
+	spinlock_t txqueue_op_lock;
+	unsigned long ep_ipc_reg;
+	uint32_t tx_bd_busy_cnt; /* tx BD unavailable */
+	uint32_t tx_stop_queue_cnt;
+#ifdef RC_TXDONE_TIMER
+	struct timer_list tx_timer;
+	spinlock_t tx_lock;
+#endif
+	uint32_t vmac_tx_queue_len;
+
+	struct sk_buff **rx_skb;
+	volatile struct vmac_bd *rx_bd_base; /* Rx buffer descriptor  */
+	uint16_t rx_bd_index;
+	uint16_t rx_bd_num;
+
+	uint32_t rx_skb_alloc_failures;
+	uint32_t intr_cnt; /* msi/legacy interrupt counter */
+	uint32_t vmac_xmit_cnt;
+	uint32_t vmac_skb_free;
+
+	struct sock *nl_socket;
+	uint32_t str_call_nl_pid;
+	uint32_t lib_call_nl_pid;
+	struct napi_struct napi;
+
+	uint32_t dbg_flg;
+
+	struct net_device *ndev;
+	struct pci_dev	*pdev;
+
+	int mac_id;
+
+	uint32_t dma_msi_imwr;
+	uint32_t dma_msi_data;
+	uint32_t dma_msi_dummy;
+	uint32_t ep_pciecfg0_val; /* used to deassert Legacy irq from RC */
+
+	/* The following members aren't related to datapath */
+	struct vmac_cfg *pcfg;
+	uint8_t show_item;
+
+	uint32_t addr_uncache;
+	uint32_t uncache_len;
+
+	struct task_struct *init_thread; /* Initialization thread */
+	uint8_t msi_enabled; /* PCIe MSI: 1 - Enabled, 0 - Disabled */
+
+	qdpc_bar_t sysctl_bar;
+	qdpc_bar_t epmem_bar;
+	qdpc_bar_t dmareg_bar;
+
+	uint32_t dma_imwr;
+
+	/* io memory pointers */
+	__iomem qdpc_pcie_bda_t *bda;
+	uint32_t ep_ready;
+
+#ifdef QTN_TX_SKBQ_SUPPORT
+	struct sk_buff_head tx_skb_queue;
+	spinlock_t tx_skbq_lock;
+	struct tasklet_struct tx_skbq_tasklet;
+	uint32_t tx_skbq_budget;
+	uint32_t tx_skbq_tasklet_budget;
+	uint32_t tx_skbq_max_size;
+#endif
+
+#ifdef QTN_SKB_RECYCLE_SUPPORT
+	struct sk_buff_head rx_skb_freelist;
+	spinlock_t rx_skb_freelist_lock;
+	uint32_t rx_skb_freelist_fill_level;
+	uint32_t skb_recycle_cnt;
+	uint32_t skb_recycle_failures;
+#endif
+
+	volatile uint32_t *ep_pmstate;
+	uint8_t *nl_buf;
+	size_t nl_len;
+};
+
+#define QTN_DISABLE_SOFTIRQ		(0xABCD)
+
+static inline void qtn_spin_lock_bh_save(spinlock_t *lock, unsigned long *flag)
+{
+	if (likely(irqs_disabled() || in_softirq())) {
+		spin_lock(lock);
+		*flag = 0;
+	} else {
+		spin_lock_bh(lock);
+		*flag = QTN_DISABLE_SOFTIRQ;
+        }
+}
+
+static inline void qtn_spin_unlock_bh_restore(spinlock_t *lock, unsigned long *flag)
+{
+	if (unlikely(*flag == QTN_DISABLE_SOFTIRQ)) {
+		*flag = 0;
+		spin_unlock_bh(lock);
+	} else {
+		spin_unlock(lock);
+	}
+}
+
+extern struct net_device *vmac_alloc_ndev(void);
+extern int vmac_net_init(struct pci_dev *pdev);
+extern int vmac_recovery_init(struct vmac_priv *priv, struct net_device *ndev);
+extern void vmac_clean(struct net_device *ndev);
+extern void vmac_recovery_clean(struct net_device *ndev);
+extern int vmac_tx(void *pkt_handle, struct net_device *ndev);
+
+#define PCIE_REG_CFG_BASE		0x0
+#define PCIE_LOGIC_PORT_CFG_BASE	(PCIE_REG_CFG_BASE + 0x700)
+#define PCIE_DMA_WR_INTR_MASK		0x2c4
+
+void vmac_pcie_edma_enable(struct vmac_priv *priv);
+void qdpc_deassert_intx(struct vmac_priv *priv);
+void qdpc_pcie_edma_enable(struct vmac_priv *priv);
+int qdpc_pcie_suspend(struct pci_dev *pdev, pm_message_t state);
+int qdpc_pcie_resume(struct pci_dev *pdev);
+#endif
diff --git a/drivers/net/wireless/quantenna/pcie2/host/intel/Makefile b/drivers/net/wireless/quantenna/pcie2/host/intel/Makefile
new file mode 100644
index 0000000..22be6ff
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/intel/Makefile
@@ -0,0 +1,33 @@
+#
+# Makefile for Intel platform
+#
+
+EXTRA_CFLAGS	+= -Wall		\
+		   -I$(src)		\
+		   -I$(src)/../../include \
+		   -I$(src)/../common
+
+EXTRA_CFLAGS	+= -DRC_TXDONE_TIMER -DQTN_WAKEQ_SUPPORT
+ifneq ($(CONFIG_HOTPLUG_PCI_PCIE),)
+EXTRA_CFLAGS    += -DPCIE_HOTPLUG_SUPPORTED
+endif
+
+KVERSION = $(shell uname -r)
+
+default: all
+
+COMMON_DIR	:= ../common
+qdpc-host-objs   := $(COMMON_DIR)/qdpc_init.o $(COMMON_DIR)/qdpc_pcie.o $(COMMON_DIR)/topaz_vnet.o qdpc_platform.o
+obj-m           :=  qdpc-host.o
+
+qdpc_host.o: $(qdpc-host-objs)
+	ld -r $^ -o $@
+
+all:
+	make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
+
+clean:
+	make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
+	rm -rf $(COMMON_DIR)/.*.cmd $(COMMON_DIR)/.tmp_versions
+	rm -rf Module.markers  modules.order *~ $(qdpc-host-objs)
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/intel/qdpc_platform.c b/drivers/net/wireless/quantenna/pcie2/host/intel/qdpc_platform.c
new file mode 100644
index 0000000..1dde009
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/intel/qdpc_platform.c
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+#include <linux/interrupt.h>
+#include <qdpc_platform.h>
+#include <topaz_vnet.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+/*
+ * Enable MSI interrupt of PCIe.
+ */
+void enable_vmac_ints(struct vmac_priv *vmp)
+{
+	enable_irq(vmp->pdev->irq);
+}
+
+/*
+ * Disable MSI interrupt of PCIe.
+ */
+void disable_vmac_ints(struct vmac_priv *vmp)
+{
+	disable_irq_nosync(vmp->pdev->irq);
+}
+
+
+/*
+ * Enable interrupt for detecting EP reset.
+ */
+void enable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Disable interrupt for detecting EP reset.
+ */
+void disable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Interrupt context for detecting EP reset.
+ * This function should do:
+ *   1. check interrupt status to see if EP reset.
+ *   2. if EP reset, handle it.
+ */
+void handle_ep_rst_int(struct net_device *ndev)
+{
+}
+
+/*
+ * PCIe driver update resource in PCI configure space after EP reset.
+ * This function should be called in such case:
+ *   1. The PCI configure space can be accessed after EP reset;
+ *   2. Kernel does not support PCIe hot-plug.
+ */
+void qdpc_update_hw_bar(struct pci_dev *pdev, uint8_t index)
+{
+	struct pci_bus_region region;
+	uint32_t addr, new;
+	int offset = PCI_BASE_ADDRESS_0 + 4 * index;
+	struct resource *res = pdev->resource + index;
+
+	if (!res->flags)
+		return;
+
+	pcibios_resource_to_bus(pdev, &region, res);
+	new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
+	pci_read_config_dword(pdev, offset, &addr);
+
+	if (addr != new) {
+		printk("PCI region %d: reset to PCI address %#llx", index, (unsigned long long)region.start);
+		pci_write_config_dword(pdev, offset, new);
+		if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
+		    (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+			printk(" (64bit address)");
+			new = region.start >> 16 >> 16;
+			pci_write_config_dword(pdev, offset + 4, new);
+		}
+		printk("\n");
+	}
+}
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/intel/qdpc_platform.h b/drivers/net/wireless/quantenna/pcie2/host/intel/qdpc_platform.h
new file mode 100644
index 0000000..4176b40
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/intel/qdpc_platform.h
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_PFDEP_H__
+#define __QDPC_PFDEP_H__
+
+#include <linux/version.h>
+
+#include <topaz_vnet.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define IOREMAP      ioremap_wc
+#else    /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) */
+#define IOREMAP      ioremap
+#endif
+
+/* IO functions */
+#ifndef readb
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#endif
+
+#ifndef readw
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#endif
+
+#ifndef readl
+#define readl(addr) (*(volatile unsigned int *) (addr))
+#endif
+
+#ifndef writeb
+#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b))
+#endif
+
+#ifndef writew
+#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b))
+#endif
+
+#ifndef writel
+#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b))
+#endif
+
+/* Bit number and mask of MSI in the interrupt mask and status register */
+#define	QDPC_INTR_MSI_BIT		0
+#define QDPC_INTR_MSI_MASK		(1 << QDPC_INTR_MSI_BIT)
+
+/* Enable MSI interrupt of PCIe */
+extern void enable_vmac_ints(struct vmac_priv *vmp);
+/* Disable MSI interrupt of PCIe */
+extern void disable_vmac_ints(struct vmac_priv *vmp);
+
+/* Enable interrupt for detecting EP reset */
+extern void enable_ep_rst_detection(struct net_device *ndev);
+/* Disable interrupt for detecting EP reset */
+extern void disable_ep_rst_detection(struct net_device *ndev);
+/* Interrupt context for detecting EP reset */
+extern void handle_ep_rst_int(struct net_device *ndev);
+
+/* PCIe driver update resource in PCI configure space after EP reset */
+extern void qdpc_update_hw_bar(struct pci_dev *pdev, uint8_t index);
+
+/* Allocated buffer size for a packet */
+#define SKB_BUF_SIZE		2048
+
+/* Transmit Queue Length */
+#define QDPC_TX_QUEUE_SIZE	180
+
+/* Receive Queue Length */
+#define QDPC_RX_QUEUE_SIZE	384
+
+/* Customer defined function	*/
+#define qdpc_platform_init()                  0
+#define qdpc_platform_exit()                  do { } while(0)
+
+#endif /* __QDPC_PFDEP_H__ */
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/mipsr2/Makefile b/drivers/net/wireless/quantenna/pcie2/host/mipsr2/Makefile
new file mode 100644
index 0000000..b6aa1f8
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/mipsr2/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for mipsr2 platform
+#
+
+EXTRA_CFLAGS	+= -Wall		\
+		   -I$(src)		\
+		   -I$(src)/../../include \
+		   -I$(src)/../common	\
+		   -D__BIG_ENDIAN
+
+EXTRA_CFLAGS    += -DQTN_TX_SKBQ_SUPPORT -DQTN_WAKEQ_SUPPORT
+PWD	:= $(shell pwd)
+
+default: all
+
+COMMON_DIR	:= ../common
+qdpc-host-objs   := $(COMMON_DIR)/qdpc_init.o $(COMMON_DIR)/qdpc_pcie.o $(COMMON_DIR)/topaz_vnet.o qdpc_platform.o
+obj-m           :=  qdpc-host.o
+
+qdpc_host.o: $(qdpc-host-objs)
+	ld -r $^ -o $@
+
+all:
+	make -C $(KERNELDIR) $(CROSS) M=$(PWD) modules
+
+
+clean:
+	rm -rf $(COMMON_DIR)/.*.cmd $(COMMON_DIR)/.tmp_versions
+	rm -rf Module.markers  Module.symvers modules.order *~ $(qdpc-host-objs) *.o *.ko *.mod.o *.mod.c
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/mipsr2/qdpc_platform.c b/drivers/net/wireless/quantenna/pcie2/host/mipsr2/qdpc_platform.c
new file mode 100644
index 0000000..2d791fe
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/mipsr2/qdpc_platform.c
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+
+#include <linux/interrupt.h>
+
+#include <qdpc_platform.h>
+#include <topaz_vnet.h>
+#include <qdpc_regs.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+/*
+ * Enable MSI interrupt of PCIe.
+ */
+void enable_vmac_ints(struct vmac_priv *vmp)
+{
+	volatile uint32_t *dma_wrd_imwr = QDPC_BAR_VADDR(vmp->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET);
+
+	writel(vmp->dma_msi_imwr, dma_wrd_imwr);
+}
+
+/*
+ * Disable MSI interrupt of PCIe.
+ */
+void disable_vmac_ints(struct vmac_priv *vmp)
+{
+	volatile uint32_t *dma_wrd_imwr = QDPC_BAR_VADDR(vmp->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET);
+	writel(vmp->dma_msi_dummy, dma_wrd_imwr);
+}
+
+
+/*
+ * Enable interrupt for detecting EP reset.
+ */
+void enable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Disable interrupt for detecting EP reset.
+ */
+void disable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Interrupt context for detecting EP reset.
+ * This function should do:
+ *   1. check interrupt status to see if EP reset.
+ *   2. if EP reset, handle it.
+ */
+void handle_ep_rst_int(struct net_device *ndev)
+{
+}
diff --git a/drivers/net/wireless/quantenna/pcie2/host/mipsr2/qdpc_platform.h b/drivers/net/wireless/quantenna/pcie2/host/mipsr2/qdpc_platform.h
new file mode 100644
index 0000000..b3f678b
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/mipsr2/qdpc_platform.h
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+#ifndef __QDPC_PFDEP_H__
+#define __QDPC_PFDEP_H__
+
+#include <linux/version.h>
+
+#include <topaz_vnet.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define IOREMAP      ioremap_wc
+#else    /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) */
+#define IOREMAP      ioremap
+#endif
+
+/* IO functions */
+#ifndef readb
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#endif
+
+#ifndef readw
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#endif
+
+#ifndef readl
+#define readl(addr) (*(volatile unsigned int *) (addr))
+#endif
+
+#ifndef writeb
+#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b))
+#endif
+
+#ifndef writew
+#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b))
+#endif
+
+#ifndef writel
+#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b))
+#endif
+
+#ifndef virt_to_bus
+#define virt_to_bus virt_to_phys
+#endif
+
+/* Bit number and mask of MSI in the interrupt mask and status register */
+#define	QDPC_INTR_MSI_BIT		0
+#define QDPC_INTR_MSI_MASK		(1 << QDPC_INTR_MSI_BIT)
+
+/* Enable MSI interrupt of PCIe */
+extern void enable_vmac_ints(struct vmac_priv *vmp);
+/* Disable MSI interrupt of PCIe */
+extern void disable_vmac_ints(struct vmac_priv *vmp);
+
+/* Enable interrupt for detecting EP reset */
+extern void enable_ep_rst_detection(struct net_device *ndev);
+/* Disable interrupt for detecting EP reset */
+extern void disable_ep_rst_detection(struct net_device *ndev);
+/* Interrupt context for detecting EP reset */
+extern void handle_ep_rst_int(struct net_device *ndev);
+
+/* Allocated buffer size for a packet */
+#define SKB_BUF_SIZE		2048
+
+/* Transmit Queue Length */
+#define QDPC_TX_QUEUE_SIZE	180
+
+/* Receive Queue Length */
+#define QDPC_RX_QUEUE_SIZE	384
+
+/* Customer defined function	*/
+#define qdpc_platform_init()                  0
+#define qdpc_platform_exit()                  do { } while(0)
+
+/* PCIe driver update resource in PCI configure space after EP reset */
+#define qdpc_update_hw_bar(pdev, index)       do { } while(0)
+
+/* TODO: If MSI IRQ-loss issue can be fixed, remove macro below */
+/*#define QDPC_PLATFORM_IRQ_FIXUP*/
+
+#endif /* __QDPC_PFDEP_H__ */
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile b/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile
new file mode 100644
index 0000000..82815c8
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile
@@ -0,0 +1,70 @@
+#
+# Makefile for Quantenna RC paltform
+#
+#
+
+EXTRA_CFLAGS	+= -Wall -Werror -Wno-unknown-pragmas \
+		   -I$(src)		\
+		   -I$(src)/../../include \
+		   -I../drivers/include/shared \
+		   -I../drivers/include/kernel \
+		   -I$(src)/../common
+
+EXTRA_CFLAGS    += -mlong-calls -DQTN_WAKEQ_SUPPORT
+
+ifeq ($(board_config),topaz_host_realign_config)
+EXTRA_CFLAGS    += -DQTN_BYTEALIGN
+endif
+
+ ifneq ($(CONFIG_HOTPLUG_PCI_PCIE),)
+ EXTRA_CFLAGS    += -DPCIE_HOTPLUG_SUPPORTED
+ endif
+
+ifeq (${PCIE_HOST_CRUMBS},1)
+EXTRA_CFLAGS += -finstrument-functions
+endif
+
+#EXTRA_CFLAGS	+= -DDEBUG
+
+ifneq ($(KERNELRELEASE),)
+COMMON_DIR	:= ../common
+TQE_DIR_TO_WORK := ../../tqe
+TQE_DIR_TO_LINUX:= ../drivers/pcie2/tqe
+EXTRA_CFLAGS += -I.
+ifeq ($(CONFIG_TOPAZ_DBDC_HOST), y)
+qdpc-host-objs	+= $(if $(wildcard $(TQE_DIR_TO_LINUX)), $(TQE_DIR_TO_WORK)/topaz_qfp.o)
+else
+qdpc-host-objs	+= $(COMMON_DIR)/qdpc_init.o $(COMMON_DIR)/qdpc_pcie.o $(COMMON_DIR)/topaz_vnet.o qdpc_platform.o
+endif
+
+qdpc-host-objs  += $(if $(wildcard $(TQE_DIR_TO_LINUX)), $(TQE_DIR_TO_WORK)/topaz_pcie_tqe.o)
+qdpc-host-objs  += qdpc_dspload.o
+
+obj-m           :=  qdpc-host.o
+
+else
+
+KERNELDIR	?= ../../../../linux
+INSTALL		= INSTALL_MOD_PATH=../linux/modules
+CROSS		= ARCH=arc CROSS_COMPILE=/usr/local/ARC/gcc/bin/arc-linux-uclibc-
+PWD		:= $(shell pwd)
+
+default:
+	$(MAKE) -C $(KERNELDIR) $(CROSS) M=$(PWD) modules
+
+install:
+	$(MAKE) -C $(KERNELDIR) $(CROSS) $(INSTALL) M=$(PWD) modules_install
+
+endif
+
+clean:
+	rm -rf *.o  *~  core  .depend  .*.cmd  *.ko  *.mod.c  .tmp_versions  Module.symvers  modules.order
+	rm -rf $(COMMON_DIR)/.*.cmd $(COMMON_DIR)/.tmp_versions $(COMMON_DIR)/*.o
+
+depend .depend dep:
+	$(CC) $(CFLAGS) -M *.c > .depend
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_dspload.c b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_dspload.c
new file mode 100644
index 0000000..7c08910
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_dspload.c
@@ -0,0 +1,169 @@
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/syscalls.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <asm/io.h>
+#include <qtn/registers.h>
+#include <qtn/shared_params.h>
+#include <qtn/topaz_fwt_sw.h>
+#include "qdpc_dspload.h"
+
+static inline unsigned long
+qdpc_dsp_to_host_addr(unsigned long dsp_addr)
+{
+        void *ret = bus_to_virt(dsp_addr);
+        if (RUBY_BAD_VIRT_ADDR == ret) {
+                panic("Converting out of range DSP address 0x%lx to host address\n", dsp_addr);
+        }
+        return virt_to_phys(ret);
+}
+
+static char* qdpc_dsp_read(struct file *file, int buflen)
+{
+	char *p = NULL;
+
+	if (!file)
+		return NULL;
+
+	p = kmalloc(buflen*sizeof(unsigned char), GFP_ATOMIC);
+	if (p)
+		file->f_op->read(file, p, buflen, &file->f_pos);
+
+	return p;
+}
+
+static int qdpc_dsp_install_firmware(struct file *file, u32 *dsp_start_addr)
+{
+        Elf32_Ehdr *ehdr;
+        Elf32_Phdr *phdr, *phdr_o;
+        u8* vaddr;
+        int i, buflen;
+	char *pdata = NULL;
+	int e_phnum = 0;
+
+	buflen = sizeof(Elf32_Ehdr);
+	pdata = qdpc_dsp_read(file, buflen);
+	if (!pdata) {
+		return -1;
+	}
+        ehdr = (Elf32_Ehdr *)pdata;
+	e_phnum = ehdr->e_phnum;
+	kfree(pdata);
+
+	buflen = e_phnum * sizeof(Elf32_Phdr);
+	pdata = qdpc_dsp_read(file, buflen);
+	if (!pdata) {
+		return -1;
+	}
+	phdr = (Elf32_Phdr *)pdata;
+	phdr_o = (Elf32_Phdr *)pdata;
+
+        for(i = 0; i < e_phnum; i++, phdr++)
+        {
+		pdata = qdpc_dsp_read(file, phdr->p_filesz);
+		if (!pdata) {
+			return -1;
+		}
+
+                /* Skip blocks for DSP X/Y memory */
+                if ((phdr->p_vaddr >= RUBY_DSP_XYMEM_BEGIN) && (phdr->p_vaddr <= RUBY_DSP_XYMEM_END)) {
+                        if (pdata)
+				kfree(pdata);
+			continue;
+                }
+                unsigned long p_muc = qdpc_dsp_to_host_addr(phdr->p_vaddr);
+                        printk("p_vaddr in ELF header is %p, "
+                                "remapping to 0x%lx\n", (void *)phdr->p_vaddr, p_muc);
+                /* Copy segment to right location */
+                vaddr = ioremap_nocache(p_muc, phdr->p_memsz);
+
+                /* Copy data */
+                memcpy(vaddr, pdata, phdr->p_filesz);
+                /* Clear BSS */
+                memset(vaddr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
+
+                iounmap(vaddr);
+		if (pdata)
+			kfree(pdata);
+        }
+
+	if (phdr_o)
+		kfree(phdr_o);
+
+        *dsp_start_addr = ehdr->e_entry;
+
+        return(0);
+}
+
+static void hal_dsp_start(u32 dsp_start_addr)
+{
+#ifdef CONFIG_ARCH_ARC
+        /* Check that we can start this address */
+        if (dsp_start_addr & ((1 << RUBY_SYS_CTL_DSP_REMAP_SHIFT) - 1)) {
+                panic("DSP address 0x%x cannot be used as entry point\n", (unsigned)dsp_start_addr);
+        }
+        /* Tells DSP from which address start execution */
+        writel(RUBY_SYS_CTL_DSP_REMAP_VAL(dsp_start_addr), RUBY_SYS_CTL_DSP_REMAP);
+#else
+        /* Swap upper and lower half words for DSP instruction */
+        dsp_start_addr = ((dsp_start_addr >> 16) & 0xFFFF) | (dsp_start_addr << 16);
+
+        /* Push the jump instr and location into the mbx */
+        *(volatile u32*)IO_ADDRESS(UMS_REGS_MB + UMS_MBX_DSP_PUSH)
+                = DSP_JUMP_INSTR_SWAP;
+        *(volatile u32*)IO_ADDRESS(UMS_REGS_MB + UMS_MBX_DSP_PUSH)
+                = dsp_start_addr;
+#endif
+}
+
+void hal_enable_dsp(void)
+{
+#ifdef CONFIG_ARCH_ARC
+        const unsigned long reset = RUBY_SYS_CTL_RESET_DSP_ALL;
+
+        qtn_txbf_lhost_init();
+
+        writel(reset, RUBY_SYS_CTL_CPU_VEC_MASK);
+        writel(reset, RUBY_SYS_CTL_CPU_VEC);
+        writel(0, RUBY_SYS_CTL_CPU_VEC_MASK);
+#else
+        /* Bring the DSP out of reset */
+        *(volatile u32 *)IO_ADDRESS(SYS_RESET_VECTOR_MASK) = DSP_RESET;
+        *(volatile u32 *)IO_ADDRESS(SYS_RESET_VECTOR) = DSP_RESET;
+#endif
+}
+
+int qdpc_dsp_open(void)
+{
+	struct	file *file = NULL;
+	mm_segment_t fs;
+	u32 dsp_start_addr = 0;
+
+	file = filp_open(QDCP_DSP_FILE_NAME, O_RDONLY, 0);
+	if(IS_ERR(file)) {
+		printk("error occured while opening file %s, exiting...\n", QDCP_DSP_FILE_NAME);
+		return -1;
+	}
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	qdpc_dsp_install_firmware(file, &dsp_start_addr);
+
+        hal_dsp_start(dsp_start_addr);
+        hal_enable_dsp();
+
+	filp_close(file, NULL);
+	set_fs(fs);
+
+	return 0;
+}
diff --git a/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_dspload.h b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_dspload.h
new file mode 100644
index 0000000..883fb8c
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_dspload.h
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_DSP_H__
+#define __QDPC_DSP_H__
+
+#include <linux/version.h>
+
+#include <topaz_vnet.h>
+
+#include <qtn/topaz_tqe_cpuif.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define IOREMAP     ioremap_nocache
+#else   /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) */
+#define IOREMAP     ioremap
+#endif
+
+#define QTN_TXBF_MUC_TO_DSP_MBOX_INT            (0)
+#define QTN_TXBF_DSP_TO_HOST_MBOX_INT           (0)
+
+#define QDCP_DSP_FILE_NAME "/etc/firmware/rdsp_driver.0.bin"
+
+RUBY_INLINE void
+qtn_txbf_lhost_init(void)
+{
+#if CONFIG_USE_SPI1_FOR_IPC
+        /* Initialize SPI controller, keep IRQ disabled */
+        qtn_mproc_sync_mem_write(RUBY_SPI1_SPCR,
+                RUBY_SPI1_SPCR_SPE | RUBY_SPI1_SPCR_MSTR |
+                RUBY_SPI1_SPCR_SPR(0));
+        qtn_mproc_sync_mem_write(RUBY_SPI1_SPER,
+                RUBY_SPI1_SPER_ESPR(0));
+#else
+        /* Ack, and keep IRQ disabled */
+        qtn_mproc_sync_mem_write(RUBY_SYS_CTL_D2L_INT,
+                qtn_mproc_sync_mem_read(RUBY_SYS_CTL_D2L_INT));
+        qtn_mproc_sync_mem_write(RUBY_SYS_CTL_D2L_INT_MASK,
+                ~(1 << QTN_TXBF_DSP_TO_HOST_MBOX_INT));
+#endif
+}
+
+extern int qdpc_dsp_open(void);
+
+#endif /* __QDPC_PFDEP_H__ */
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_platform.c b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_platform.c
new file mode 100644
index 0000000..25aff26
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_platform.c
@@ -0,0 +1,360 @@
+/**
+ * Copyright (c) 2012-2013 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+#include <qdpc_platform.h>
+#include <topaz_vnet.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <asm/gpio.h>
+#include <../drivers/pcie2/host/common/qdpc_regs.h>
+
+/* WPS button event reported to user space process */
+typedef enum {
+	MODE_LED_INIT = 0,
+	MODE_LED_EXIT,
+	MODE_LED_FLASH,
+} MODE_LED_OPS;
+#define MODE_LED_GPIO 6
+
+typedef enum {
+	WPS_BUTTON_NONE_EVENT = 0,
+	WPS_BUTTON_WIRELESS_EVENT,
+	WPS_BUTTON_DBGDUMP_EVENT,
+	WPS_BUTTON_INVALIDE_EVENT
+} WPS_Button_Event;
+#define WPS_BUTTON_VALID(e) (WPS_BUTTON_NONE_EVENT < (e) && (e) < WPS_BUTTON_INVALIDE_EVENT)
+
+#define WPS_BUTTON_GPIO 4
+#define QDPC_WPS_BUTTON_ACTIVE_LEVEL 0
+#define WPS_BUTTON_TIMER_INTERVAL ((3 * HZ) / 10) /* timer interval */
+
+/*
+* Queue of processes who access wps_button file
+*/
+DECLARE_WAIT_QUEUE_HEAD(WPS_Button_WaitQ);
+
+static WPS_Button_Event wps_button_event = WPS_BUTTON_NONE_EVENT;
+struct timer_list qdpc_wps_button_timer;
+static u32 qdpc_wps_button_last_level = ~QDPC_WPS_BUTTON_ACTIVE_LEVEL;
+static u32 qdpc_wps_button_down_jiffies = 0; /* records the jiffies when button down, back to 0 after button released */
+
+static int vmac_rst_rc_en = 1;
+struct work_struct detect_ep_rst_work;
+
+void enable_vmac_ints(struct vmac_priv *vmp)
+{
+	uint32_t temp = readl(QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+
+	if(vmp->msi_enabled) {
+		temp |= BIT(10); /* MSI */
+	} else {
+		temp |= BIT(11); /* Legacy INTx */
+	}
+	writel(temp, QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+}
+
+void disable_vmac_ints(struct vmac_priv *vmp)
+{
+	uint32_t temp = readl(QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+
+	if(vmp->msi_enabled) {
+		temp &= ~BIT(10); /* MSI */
+	} else {
+		temp &= ~BIT(11); /* Legacy INTx */
+	}
+	writel(temp, QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+}
+
+static ssize_t vmac_reset_get(struct device *dev, struct device_attribute *attr, char *buf)
+{
+        return sprintf(buf, "%u\n", vmac_rst_rc_en);
+}
+
+static ssize_t vmac_reset_set(struct device *dev,
+        struct device_attribute *attr, const char *buf, size_t count)
+{
+        uint8_t cmd;
+
+        cmd = (uint8_t)simple_strtoul(buf, NULL, 10);
+	if (cmd == 0)
+		vmac_rst_rc_en = 0;
+	else
+		vmac_rst_rc_en = 1;
+
+        return count;
+}
+DEVICE_ATTR(enable_reset, S_IWUSR | S_IRUSR, vmac_reset_get, vmac_reset_set);
+
+static void detect_ep_rst(struct work_struct *data)
+{
+	kernel_restart(NULL);
+}
+
+void enable_ep_rst_detection(struct net_device *ndev)
+{
+        uint32_t temp = readl(QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+
+        temp |= QDPC_INTR_EP_RST_MASK;
+        writel(temp, QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+
+	device_create_file(&ndev->dev, &dev_attr_enable_reset);
+	INIT_WORK(&detect_ep_rst_work, detect_ep_rst);
+}
+
+void disable_ep_rst_detection(struct net_device *ndev)
+{
+        uint32_t temp = readl(QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+
+        temp &= ~QDPC_INTR_EP_RST_MASK;
+        writel(temp, QDPC_RC_SYS_CTL_PCIE_INT_MASK);
+
+	device_remove_file(&ndev->dev, &dev_attr_enable_reset);
+}
+
+void handle_ep_rst_int(struct net_device *ndev)
+{
+	uint32_t status = readl(QDPC_RC_SYS_CTL_PCIE_INT_STAT);
+
+	if ((status & QDPC_INTR_EP_RST_MASK) == 0)
+		return;
+
+	/* Clear pending interrupt */
+	writel(QDPC_INTR_EP_RST_MASK, QDPC_RC_SYS_CTL_PCIE_INT_STAT);
+
+	printk("Detected reset of Endpoint\n");
+
+	if (vmac_rst_rc_en == 1) {
+		netif_stop_queue(ndev);
+		schedule_work(&detect_ep_rst_work);
+	}
+}
+
+static void qdpc_mode_led(struct net_device *ndev, MODE_LED_OPS op)
+{
+	static int inited = 0;
+	static u32 led_bk = 0;
+
+	switch(op) {
+	case MODE_LED_INIT:
+		if (gpio_request(MODE_LED_GPIO, ndev->name) < 0)
+			printk(KERN_INFO "%s: Failed to request GPIO%d for GPIO reset\n",
+			       ndev->name, MODE_LED_GPIO);
+
+		led_bk = gpio_get_value(MODE_LED_GPIO);
+		gpio_direction_output(MODE_LED_GPIO, led_bk);
+		inited = 1;
+
+		break;
+
+	case MODE_LED_EXIT:
+		if (inited) {
+			gpio_set_value(MODE_LED_GPIO, led_bk);
+			gpio_free(MODE_LED_GPIO);
+			inited = 0;
+		}
+
+		break;
+
+	case MODE_LED_FLASH:
+		if (inited)
+			gpio_set_value(MODE_LED_GPIO, ~gpio_get_value(MODE_LED_GPIO) & 0x01);
+
+		break;
+	}
+}
+
+
+static void qdpc_wps_button_event_wakeup(struct net_device *ndev, WPS_Button_Event event)
+{
+	struct vmac_priv *priv = netdev_priv(ndev);
+
+	if (!WPS_BUTTON_VALID(event))
+		return;
+
+	/* notify local watcher */
+	wps_button_event = event;
+	wake_up_all(&WPS_Button_WaitQ);
+
+	/* notify ep the offline dbg info, if ep is ready*/
+	if (priv->ep_ready && event == WPS_BUTTON_DBGDUMP_EVENT)
+		writel(TOPAZ_SET_INT(IPC_OFFLINE_DBG), priv->ep_ipc_reg);
+}
+
+static ssize_t qdpc_wps_button_read(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buff)
+{
+	int i = 0;
+
+	/* As usual, this read is always blocked untill wps button is pressed
+	 * so increase the module reference to prevent it being unload during
+	 * blocking read
+	 */
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+
+	/* wait for valid WPS button event */
+	wait_event_interruptible(WPS_Button_WaitQ, WPS_BUTTON_VALID(wps_button_event));
+
+	/* read back empty string in signal wakeup case */
+	for (i = 0; i < _NSIG_WORDS; i++) {
+		if (current->pending.signal.sig[i] & ~current->blocked.sig[i]) {
+			module_put(THIS_MODULE);
+			return 0;
+		}
+	}
+
+	sprintf(buff, "%d\n", wps_button_event);
+
+	/* after new event been handled, reset to none event */
+	wps_button_event = WPS_BUTTON_NONE_EVENT;
+
+	module_put(THIS_MODULE);
+
+	return strlen(buff);
+}
+
+static ssize_t qdpc_wps_button_write(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf,
+				     size_t count)
+{
+	int input = 0;
+	struct net_device *ndev;
+
+	input = simple_strtoul(buf, NULL, 10);
+
+	ndev = (struct net_device*)dev_get_drvdata(dev);
+
+	switch (input) {
+	case 1:
+		qdpc_mode_led(ndev, MODE_LED_INIT);
+
+		qdpc_mode_led(ndev, MODE_LED_FLASH);
+		msleep(300);
+		qdpc_mode_led(ndev, MODE_LED_FLASH);
+		msleep(300);
+		qdpc_mode_led(ndev, MODE_LED_FLASH);
+		msleep(300);
+
+		qdpc_mode_led(ndev, MODE_LED_EXIT);
+
+		break;
+	default:
+		printk(KERN_INFO "WPS button: unknow cmd (%d)\n", input);
+	}
+
+	return count;
+}
+
+DEVICE_ATTR(wps_button, S_IWUSR | S_IRUSR, qdpc_wps_button_read, qdpc_wps_button_write); /* dev_attr_wps_button */
+
+static void qdpc_wps_button_device_file_create(struct net_device *ndev)
+{
+	device_create_file(&(ndev->dev), &dev_attr_wps_button);
+}
+
+
+static void qdpc_wps_polling_button_notifier(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	u32 current_level;
+
+	current_level = gpio_get_value(WPS_BUTTON_GPIO);
+
+	/* records the falling edge jiffies */
+	if ((current_level == QDPC_WPS_BUTTON_ACTIVE_LEVEL)
+	    && (qdpc_wps_button_last_level != QDPC_WPS_BUTTON_ACTIVE_LEVEL)) {
+
+		qdpc_mode_led(dev, MODE_LED_INIT);
+		qdpc_wps_button_down_jiffies = jiffies;
+	}
+
+	/* at rising edge */
+	if ((current_level != QDPC_WPS_BUTTON_ACTIVE_LEVEL)
+	    && (qdpc_wps_button_last_level == QDPC_WPS_BUTTON_ACTIVE_LEVEL)) {
+
+		/* WPS button event is rising triggered -- when button
+		 * being changed from active to inactive level.
+		 *
+		 * Different press time trigger different event
+		 */
+		if ((jiffies - qdpc_wps_button_down_jiffies) >= 10 * HZ) {
+
+			/* wakeup the event waiting processes */
+			qdpc_wps_button_event_wakeup(dev, WPS_BUTTON_DBGDUMP_EVENT);
+
+			printk(KERN_INFO "WPS: button long press polling at %u\n", (unsigned int) jiffies);
+		} else {
+			/* wakeup the event waiting processes */
+			qdpc_wps_button_event_wakeup(dev, WPS_BUTTON_WIRELESS_EVENT);
+
+			printk(KERN_INFO "WPS: button short press polling at %u\n", (unsigned int) jiffies);
+		}
+
+		/* back to 0 after rising edge */
+		qdpc_wps_button_down_jiffies = 0;
+		qdpc_mode_led(dev, MODE_LED_EXIT);
+	}
+
+	/* after button down more than 10s, begin change the mode led's state to notify user to release button */
+	if (qdpc_wps_button_down_jiffies != 0 && ((jiffies - qdpc_wps_button_down_jiffies) >= 10 * HZ)) {
+		qdpc_mode_led(dev, MODE_LED_FLASH);
+	}
+
+	/* Restart the timer */
+	mod_timer(&qdpc_wps_button_timer, jiffies + WPS_BUTTON_TIMER_INTERVAL);
+
+	qdpc_wps_button_last_level = current_level;
+
+	return;
+}
+
+int qdpc_wps_button_init(struct net_device *dev)
+{
+	/*
+	 * Set up timer to poll the button.
+	 * Request the GPIO resource and export it for userspace
+	 */
+	if (gpio_request(WPS_BUTTON_GPIO, dev->name) < 0)
+		printk(KERN_INFO "%s: Failed to request GPIO%d for GPIO reset\n",
+		       dev->name, WPS_BUTTON_GPIO);
+
+	init_timer(&qdpc_wps_button_timer);
+	qdpc_wps_button_timer.function = qdpc_wps_polling_button_notifier;
+	qdpc_wps_button_timer.data = (unsigned long)dev;
+	qdpc_wps_button_timer.expires = jiffies + WPS_BUTTON_TIMER_INTERVAL;
+	add_timer(&qdpc_wps_button_timer);
+
+	/* creeate the device file for user space use */
+	qdpc_wps_button_device_file_create(dev);
+
+	return 0;
+}
+
+void qdpc_wps_button_exit(void)
+{
+	del_timer(&qdpc_wps_button_timer);
+}
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_platform.h b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_platform.h
new file mode 100644
index 0000000..0598b58
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/qdpc_platform.h
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_PFDEP_H__
+#define __QDPC_PFDEP_H__
+
+#include <linux/version.h>
+
+#include <topaz_vnet.h>
+
+#include <qtn/topaz_tqe_cpuif.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define IOREMAP     ioremap_nocache
+#else   /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) */
+#define IOREMAP     ioremap
+#endif
+
+/* IO functions */
+#ifndef readb
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#endif
+
+#ifndef readw
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#endif
+
+#ifndef readl
+#define readl(addr) (*(volatile unsigned int *) (addr))
+#endif
+
+#ifndef writeb
+#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b))
+#endif
+
+#ifndef writew
+#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b))
+#endif
+
+#ifndef writel
+#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b))
+#endif
+
+/*
+ * Interrupt
+ */
+/* Interrupt Mask and Status Reigster */
+#define QDPC_RC_SYS_CTL_BASE		0xe0000000
+#define QDPC_RC_SYS_CTL_PCIE_INT_MASK	(QDPC_RC_SYS_CTL_BASE + 0xC0)
+#define QDPC_RC_SYS_CTL_PCIE_INT_STAT	(QDPC_RC_SYS_CTL_BASE + 0x17C)
+
+/* Bit number and mask of MSI in the interrupt mask and status register */
+#define	QDPC_INTR_MSI_BIT		12
+#define QDPC_INTR_MSI_MASK		(1 << QDPC_INTR_MSI_BIT)
+
+/* Bit number and mask of EP-reset-detect Interrupt in the mask and status register */
+#define QDPC_INTR_EP_RST_BIT		3
+#define QDPC_INTR_EP_RST_MASK		(1 << QDPC_INTR_EP_RST_BIT)
+
+extern void enable_vmac_ints(struct vmac_priv *vmp);
+extern void disable_vmac_ints(struct vmac_priv *vmp);
+
+extern void enable_ep_rst_detection(struct net_device *ndev);
+extern void disable_ep_rst_detection(struct net_device *ndev);
+extern void handle_ep_rst_int(struct net_device *ndev);
+
+extern int qdpc_wps_button_init(struct net_device *dev);
+extern void qdpc_wps_button_exit(void);
+
+/* Allocated buffer size for a packet */
+#define SKB_BUF_SIZE		RX_BUF_SIZE
+
+/* Transmit Queue Length */
+#if defined(QTN_BYTEALIGN)
+#define QDPC_TX_QUEUE_SIZE	180
+#else
+#define QDPC_TX_QUEUE_SIZE	200
+#endif
+
+/* Receive Queue Length */
+#define QDPC_RX_QUEUE_SIZE	384
+
+/* SDP requires packets show up at Lhost */
+#define QDPC_PLATFORM_IFPORT	TOPAZ_TQE_LHOST_PORT
+
+/* Customer defined function	*/
+#define qdpc_platform_init()                  0
+#define qdpc_platform_exit()                  do { } while(0)
+
+/* PCIe driver update resource in PCI configure space after EP reset */
+#define qdpc_update_hw_bar(pdev, index)       do { } while(0)
+
+#endif /* __QDPC_PFDEP_H__ */
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/st/Makefile b/drivers/net/wireless/quantenna/pcie2/host/st/Makefile
new file mode 100644
index 0000000..008ccac
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/st/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for ST platform
+#
+
+EXTRA_CFLAGS	+= -Wall		\
+		   -I$(src)		\
+		   -I$(src)/../../include \
+		   -I$(src)/../common
+
+EXTRA_CFLAGS	+= -DRC_TXDONE_TIMER -DQTN_WAKEQ_SUPPORT
+
+default: all
+
+COMMON_DIR	:= ../common
+qdpc-host-objs   := $(COMMON_DIR)/qdpc_init.o $(COMMON_DIR)/qdpc_pcie.o $(COMMON_DIR)/topaz_vnet.o qdpc_platform.o
+obj-m           :=  qdpc-host.o
+
+qdpc_host.o: $(qdpc-host-objs)
+	ld -r $^ -o $@
+
+all:
+	make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
+
+clean:
+	make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
+	rm -rf $(COMMON_DIR)/.*.cmd $(COMMON_DIR)/.tmp_versions
+	rm -rf Module.markers  modules.order *~ $(qdpc-host-objs)
+
diff --git a/drivers/net/wireless/quantenna/pcie2/host/st/qdpc_platform.c b/drivers/net/wireless/quantenna/pcie2/host/st/qdpc_platform.c
new file mode 100644
index 0000000..85fd1b3
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/st/qdpc_platform.c
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+
+#include <linux/interrupt.h>
+
+#include <qdpc_platform.h>
+#include <topaz_vnet.h>
+#include <qdpc_regs.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+/*
+ * Enable MSI interrupt of PCIe.
+ */
+void enable_vmac_ints(struct vmac_priv *vmp)
+{
+	volatile uint32_t *dma_wrd_imwr = QDPC_BAR_VADDR(vmp->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET);
+	writel(vmp->dma_msi_imwr, dma_wrd_imwr);
+}
+
+/*
+ * Disable MSI interrupt of PCIe.
+ */
+void disable_vmac_ints(struct vmac_priv *vmp)
+{
+	volatile uint32_t *dma_wrd_imwr = QDPC_BAR_VADDR(vmp->dmareg_bar, TOPAZ_IMWR_DONE_ADDRLO_OFFSET);
+	writel(vmp->dma_msi_dummy, dma_wrd_imwr);
+}
+
+/*
+ * Enable interrupt for detecting EP reset.
+ */
+void enable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Disable interrupt for detecting EP reset.
+ */
+void disable_ep_rst_detection(struct net_device *ndev)
+{
+}
+
+/*
+ * Interrupt context for detecting EP reset.
+ * This function should do:
+ *   1. check interrupt status to see if EP reset.
+ *   2. if EP reset, handle it.
+ */
+void handle_ep_rst_int(struct net_device *ndev)
+{
+}
diff --git a/drivers/net/wireless/quantenna/pcie2/host/st/qdpc_platform.h b/drivers/net/wireless/quantenna/pcie2/host/st/qdpc_platform.h
new file mode 100644
index 0000000..c0cc77b
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/st/qdpc_platform.h
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+/*
+ * Platform dependant implement. Customer needs to modify this file.
+ */
+#ifndef __QDPC_PFDEP_H__
+#define __QDPC_PFDEP_H__
+
+#include <linux/version.h>
+
+#include <topaz_vnet.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define IOREMAP      ioremap_wc
+#else    /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) */
+#define IOREMAP      ioremap
+#endif
+
+/* IO functions */
+#ifndef readb
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#endif
+
+#ifndef readw
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#endif
+
+#ifndef readl
+#define readl(addr) (*(volatile unsigned int *) (addr))
+#endif
+
+#ifndef writeb
+#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b))
+#endif
+
+#ifndef writew
+#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b))
+#endif
+
+#ifndef writel
+#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b))
+#endif
+
+#ifndef virt_to_bus
+#define virt_to_bus virt_to_phys
+#endif
+
+/* Bit number and mask of MSI in the interrupt mask and status register */
+#define	QDPC_INTR_MSI_BIT		0
+#define QDPC_INTR_MSI_MASK		(1 << QDPC_INTR_MSI_BIT)
+
+/* Enable MSI interrupt of PCIe */
+extern void enable_vmac_ints(struct vmac_priv *vmp);
+/* Disable MSI interrupt of PCIe */
+extern void disable_vmac_ints(struct vmac_priv *vmp);
+
+/* Enable interrupt for detecting EP reset */
+extern void enable_ep_rst_detection(struct net_device *ndev);
+/* Disable interrupt for detecting EP reset */
+extern void disable_ep_rst_detection(struct net_device *ndev);
+/* Interrupt context for detecting EP reset */
+extern void handle_ep_rst_int(struct net_device *ndev);
+
+/* Allocated buffer size for a packet */
+#define SKB_BUF_SIZE		2048
+
+/* Transmit Queue Length */
+#define QDPC_TX_QUEUE_SIZE	180
+
+/* Receive Queue Length */
+#define QDPC_RX_QUEUE_SIZE	384
+
+/* Customer defined function	*/
+#define qdpc_platform_init()                  0
+#define qdpc_platform_exit()                  do { } while(0)
+
+/* PCIe driver update resource in PCI configure space after EP reset */
+#define qdpc_update_hw_bar(pdev, index)       do { } while(0)
+
+/* TODO: If IRQ-loss issue can be fixed, remove macro below */
+#define QDPC_PLATFORM_IRQ_FIXUP
+
+#endif /* __QDPC_PFDEP_H__ */
+
diff --git a/drivers/net/wireless/quantenna/pcie2/include/qdpc_config.h b/drivers/net/wireless/quantenna/pcie2/include/qdpc_config.h
new file mode 100644
index 0000000..ed14e77
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_config.h
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_CONFIG_H__
+#define __QDPC_CONFIG_H__
+
+#include "ruby_pcie_bda.h"
+#define QDPC_MAC_ADDR_SIZE	6
+
+/*
+ * Using Type/Length field for checking if data packet or
+ * netlink packet(call_qcsapi remote interface).
+ * Using 0x0601 as netlink packet type and MAC magic number(Quantenna OUI)
+ * to distinguish netlink packet
+ */
+#define QDPC_APP_NETLINK_TYPE 0x0601
+#define QDPC_NETLINK_DST_MAGIC "\x00\x26\x86\x00\x00\x00"
+#define QDPC_NETLINK_SRC_MAGIC "\x00\x26\x86\x00\x00\x00"
+
+#define QDPC_RPC_TYPE_MASK	0x0f00
+#define QDPC_RPC_TYPE_STRCALL	0x0100
+#define QDPC_RPC_TYPE_LIBCALL	0x0200
+#define QDPC_RPC_TYPE_FRAG_MASK	0x8000
+#define QDPC_RPC_TYPE_FRAG	0x8000
+
+ /* Used on RC side */
+#define QDPC_NETLINK_RPC_PCI_CLNT	31
+#define QDPC_NL_TYPE_CLNT_STR_REG	(QDPC_RPC_TYPE_STRCALL | 0x0010)
+#define QDPC_NL_TYPE_CLNT_STR_REQ	(QDPC_RPC_TYPE_STRCALL | 0x0011)
+#define QDPC_NL_TYPE_CLNT_LIB_REG	(QDPC_RPC_TYPE_LIBCALL | 0x0010)
+#define QDPC_NL_TYPE_CLNT_LIB_REQ	(QDPC_RPC_TYPE_LIBCALL | 0x0011)
+
+ /* Used on EP side */
+#define QDPC_NETLINK_RPC_PCI_SVC	31
+#define QDPC_NL_TYPE_SVC_STR_REG	(QDPC_RPC_TYPE_STRCALL | 0x0010)
+#define QDPC_NL_TYPE_SVC_STR_REQ	(QDPC_RPC_TYPE_STRCALL | 0x0011)
+#define QDPC_NL_TYPE_SVC_LIB_REG	(QDPC_RPC_TYPE_LIBCALL | 0x0010)
+#define QDPC_NL_TYPE_SVC_LIB_REQ	(QDPC_RPC_TYPE_LIBCALL | 0x0011)
+
+typedef struct qdpc_cmd_hdr {
+	uint8_t dst_magic[ETH_ALEN];
+	uint8_t src_magic[ETH_ALEN];
+	__be16 type;
+	__be16 len;
+	__be16 rpc_type;
+	__be16 total_len;
+} qdpc_cmd_hdr_t;
+
+
+#endif /* __QDPC_CONFIG_H__ */
diff --git a/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h b/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h
new file mode 100644
index 0000000..dc63a1b
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __QDPC_DEBUG_H__
+#define __QDPC_DEBUG_H__
+
+/* Debug macros */
+#define SUCCESS         0
+#define FAILURE        -1
+
+#ifdef  DEBUG
+#define PRINT_DBG(format, ...)           printk(KERN_DEBUG format, ##__VA_ARGS__)
+#else
+#define PRINT_DBG(format, ...)           do { } while(0);
+#endif
+
+#define PRINT_ERROR(format, ...)         printk(KERN_ERR format, ##__VA_ARGS__)
+#define PRINT_INFO(format, ...)          printk(KERN_INFO format, ##__VA_ARGS__)
+
+#ifdef DBGFMT
+#undef DBGFMT
+#endif
+
+#ifdef DBGARG
+#undef DBGARG
+#endif
+
+#ifdef DBGPRINTF
+#undef DBGPRINTF
+#endif
+
+#define DBGFMT	"%s-%d: "
+#define DBGARG	__func__, __LINE__
+
+#define DBGPRINTF(fmt, ...)					\
+	do {								\
+		if(printk_ratelimit()) {				\
+			printk(DBGFMT fmt, DBGARG, ##__VA_ARGS__);	\
+		}							\
+	} while(0)
+
+
+#ifdef DEBUG
+#define qdpc_print_dump(str_, buf_, len_)	\
+{					\
+	u32 i = 0;			\
+	printk("%s\n", str_);		\
+	printk("0x%04X : ", i*8);	\
+	for (i=0; i < (u32)(len_); i++) {	\
+		if (i && ((i%8) == 0)) {	\
+			printk( "%s", "\n");	\
+			printk("0x%04X : ", (i));\
+		}				\
+		printk("%02x ", (buf_)[i]);	\
+	}					\
+	printk("\n%s\n", str_);			\
+}
+#else
+#define qdpc_print_dump(str_, buf_, len_)
+#endif
+#endif
diff --git a/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h b/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
new file mode 100644
index 0000000..3e81707
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
@@ -0,0 +1 @@
+#define DRV_VERSION "v37.4.0.46"
diff --git a/drivers/net/wireless/quantenna/pcie2/include/ruby_pcie_bda.h b/drivers/net/wireless/quantenna/pcie2/include/ruby_pcie_bda.h
new file mode 100644
index 0000000..89091ae
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/ruby_pcie_bda.h
@@ -0,0 +1,136 @@
+/*

+ * (C) Copyright 2011 Quantenna Communications Inc.

+ *

+ * See file CREDITS for list of people who contributed to this

+ * project.

+ *

+ * 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 Free Software Foundation; either version 2 of

+ * the License, or (at your option) any later version.

+ *

+ * This program is distributed in the hope that it will be useful,

+ * but WITHOUT ANY WARRANTY; without even the implied warranty of

+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

+ * GNU General Public License for more details.

+ *

+ * You should have received a copy of the GNU General Public License

+ * along with this program; if not, write to the Free Software

+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,

+ * MA 02111-1307 USA

+ */

+

+/*

+ * Header file which describes Ruby PCI Express Boot Data Area

+ * Has to be used by both kernel and bootloader.

+ */

+

+#ifndef RUBY_PCIE_BDA_H

+#define RUBY_PCIE_BDA_H

+

+/* Area mapped by via the BAR visible to the host */

+#define RUBY_PCIE_BDA_ADDR		CONFIG_ARC_PCIE_BASE

+#define RUBY_PCIE_BDA_SIZE		CONFIG_ARC_PCIE_SIZE

+

+#define RUBY_BDA_VADDR			(RUBY_PCIE_BDA_ADDR + 0x80000000)

+

+

+#define QDPC_PCIE_BDA_VERSION	0x1000

+

+#define QDPC_BDA_PCIE_INIT		0x01

+#define QDPC_BDA_PCIE_RDY		0x02

+#define QDPC_BDA_FW_LOAD_RDY		0x03

+#define QDPC_BDA_FW_LOAD_DONE		0x04

+#define QDPC_BDA_FW_START		0x05

+#define QDPC_BDA_FW_RUN			0x06

+#define QDPC_BDA_FW_HOST_RDY		0x07

+#define QDPC_BDA_FW_TARGET_RDY		0x11

+#define QDPC_BDA_FW_TARGET_BOOT		0x12

+#define QDPC_BDA_FW_FLASH_BOOT		0x13

+#define QDPC_BDA_FW_HOST_LOAD		0x08

+#define QDPC_BDA_FW_BLOCK_DONE		0x09

+#define QDPC_BDA_FW_BLOCK_RDY		0x0A

+#define QDPC_BDA_FW_EP_RDY		0x0B

+#define QDPC_BDA_FW_BLOCK_END		0x0C

+#define QDPC_BDA_FW_CONFIG		0x0D

+#define QDPC_BDA_FW_RUNNING		0x0E

+

+#define QDPC_BDA_PCIE_FAIL		0x82

+#define QDPC_BDA_FW_LOAD_FAIL		0x85

+

+

+#define PCIE_BDA_RCMODE                 BIT(1)

+#define PCIE_BDA_MSI                    BIT(2)

+#define PCIE_BDA_BAR64                  BIT(3)

+#define PCIE_BDA_FLASH_PRESENT          BIT(4)  /* Tell the Host if EP have flash contain firmware */

+#define PCIE_BDA_FLASH_BOOT             BIT(5)  /* Tell TARGET to boot from flash */

+#define PCIE_BDA_XMIT_UBOOT             BIT(6) /* EP ask for u-boot.bin */

+#define PCIE_BDA_TARGET_FBOOT_ERR       BIT(8)  /* TARGET flash boot failed */

+#define PCIE_BDA_TARGET_FWLOAD_ERR      BIT(9)  /* TARGET firmware load failed */

+#define PCIE_BDA_HOST_NOFW_ERR          BIT(12) /* Host not find any firmware */

+#define PCIE_BDA_HOST_MEMALLOC_ERR      BIT(13) /* Host malloc firmware download memory block failed */

+#define PCIE_BDA_HOST_MEMMAP_ERR        BIT(14) /* Host pci map download memory block failed */

+#define PCIE_BDA_VER(x)                 (((x) >> 4) & 0xFF)

+#define PCIE_BDA_ERROR_MASK             0xFF00  /* take the second 8 bits as error flag */

+

+#define PCIE_DMA_OFFSET_ERROR		0xFFFF

+#define PCIE_DMA_OFFSET_ERROR_MASK	0xFFFF

+

+#define PCIE_BDA_NAMELEN		32

+

+#define QDPC_PCI_ENDIAN_DETECT_DATA	0x12345678

+#define QDPC_PCI_ENDIAN_REVERSE_DATA	0x78563412

+

+#define QDPC_PCI_ENDIAN_VALID_STATUS	0x3c3c3c3c

+#define QDPC_PCI_ENDIAN_INVALID_STATUS	0

+

+#define QDPC_PCI_LITTLE_ENDIAN		0

+#define	QDPC_PCI_BIG_ENDIAN		0xffffffff

+

+#define QDPC_SCHED_TIMEOUT		(HZ / 20)

+

+#define PCIE_DMA_ISSUE_LOG_NUM		128

+

+#define PCIE_RC_TX_QUEUE_LEN		256

+#define PCIE_TX_VALID_PKT		0x80000000

+#define PCIE_PKT_LEN_MASK		0xffff

+

+struct vmac_pkt_info {

+	uint32_t addr;

+	uint32_t info;

+};

+

+typedef struct qdpc_pcie_bda {

+	uint16_t	bda_len;			/* Size of BDA block */

+	uint16_t	bda_version;			/* BDA version */

+	uint32_t	bda_bootstate;			/* Boot state of device */

+	uint32_t	bda_dma_mask;			/* Number of addressable DMA bits */

+	uint32_t	bda_dma_offset;			/* HW specific offset for DMA engine */

+	uint32_t	bda_flags;

+	uint32_t	bda_img;			/* Current load image block */

+	uint32_t	bda_img_size;			/* Current load image block size */

+	uint32_t	bda_ep2h_irqstatus;		/* Added here to allow boot loader to use irqs if desired */

+	uint32_t	bda_h2ep_irqstatus;		/* Added here to allow boot loader to use irqs if desired */

+	uint32_t	bda_msi_addr;

+	uint8_t		reserved1[56];			/* Reserve 56 bytes to make it compatible with older version */

+	uint32_t	bda_flashsz;

+	char		bda_boardname[PCIE_BDA_NAMELEN];

+	uint32_t	bda_pci_pre_status;		/* PCI endian check previous status */

+	uint32_t	bda_pci_endian;			/* Check pci memory endian format */

+	uint32_t	bda_pci_post_status;		/* PCI endian check post status */

+	int32_t		bda_h2ep_txd_budget;		/* txdone replenish budget for ep */

+	int32_t		bda_ep2h_txd_budget;		/* txdone replenish budget for host */

+	uint32_t	bda_rc_rx_bd_base;		/* EP rx buffer descriptors base address */

+	uint32_t	bda_rc_rx_bd_num;

+	uint32_t	bda_rc_tx_bd_base;		/* RC rx buffer descriptors base address */

+	uint32_t	bda_rc_tx_bd_num;

+	uint8_t		bda_ep_link_state;

+	uint8_t		bda_rc_link_state;

+	uint8_t		bda_rc_msi_enabled;

+	uint8_t		reserved2;

+        uint32_t        bda_ep_next_pkt;		/* A pointer to RC's memory specifying next packet to be handled by EP */

+	struct vmac_pkt_info request[PCIE_RC_TX_QUEUE_LEN];

+} qdpc_pcie_bda_t;

+

+#endif

+

diff --git a/drivers/net/wireless/quantenna/pcie2/include/topaz_netcom.h b/drivers/net/wireless/quantenna/pcie2/include/topaz_netcom.h
new file mode 100644
index 0000000..4eb3b56
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/topaz_netcom.h
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2012-2012 Quantenna Communications, Inc.
+ * All rights reserved.
+ *
+ * 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 Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **/
+
+#ifndef __DRIVES_NET_TOPAZ_NETCOM_H
+#define __DRIVES_NET_TOPAZ_NETCOM_H
+
+#define IPC_BIT_EP_RX_PKT	(0)
+#define IPC_BIT_RESET_EP	(1)
+#define IPC_BIT_RC_STOP_TX	(2)
+#define IPC_BIT_RC_RX_DONE	(3)
+#define IPC_BIT_EP_PM_CTRL	(4)
+#define IPC_BIT_OFFLINE_DBG	(5)
+
+#define IPC_EP_RX_PKT		(BIT(IPC_BIT_EP_RX_PKT))
+#define IPC_RESET_EP		(BIT(IPC_BIT_RESET_EP))
+#define IPC_RC_STOP_TX		(BIT(IPC_BIT_RC_STOP_TX))
+#define IPC_RC_RX_DONE		(BIT(IPC_BIT_RC_RX_DONE))
+#define IPC_EP_PM_CTRL		(BIT(IPC_BIT_EP_PM_CTRL))
+#define IPC_OFFLINE_DBG		(BIT(IPC_BIT_OFFLINE_DBG))
+
+#define TQE_NAPI_SCHED		(0x3)
+#define TQE_ENABLE_INTR		(0x1)
+
+struct vmac_bd {
+	uint32_t buff_addr;
+	uint32_t buff_info;
+};
+
+struct vmac_rx_buf {
+	uint32_t baddr;
+	uint16_t offset;
+	uint16_t len;
+};
+
+#endif /* __DRIVES_NET_TOPAZ_NETCOM_H */
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 355919e..27727cb 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -691,10 +691,12 @@
 		struct oob_data *oob_data;
 		u8 bdaddr_type;
 
+#ifndef CONFIG_BRUNO	/* GFRM200 rejects these bits */
 		if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
 			local_dist |= SMP_DIST_LINK_KEY;
 			remote_dist |= SMP_DIST_LINK_KEY;
 		}
+#endif
 
 		if (hcon->dst_type == ADDR_LE_DEV_PUBLIC)
 			bdaddr_type = BDADDR_LE_PUBLIC;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 29eddf2..0a9b49d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2000,7 +2000,7 @@
 		 * the  frames sent while scanning on other channel will be
 		 * lost)
 		 */
-		if (sdata->u.ap.beacon &&
+		if (0 && sdata->u.ap.beacon &&
 		    (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
 		     !(req->flags & NL80211_SCAN_FLAG_AP)))
 			return -EOPNOTSUPP;
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index de04e85..f4576d4 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -715,6 +715,8 @@
 }
 #endif
 
+u32 default_agg_timeout = 5000;
+
 static void add_files(struct ieee80211_sub_if_data *sdata)
 {
 	if (!sdata->vif.debugfs_dir)
@@ -725,6 +727,8 @@
 	DEBUGFS_ADD(txpower);
 	DEBUGFS_ADD(user_power_level);
 	DEBUGFS_ADD(ap_power_level);
+	debugfs_create_u32("default_agg_timeout", 0600, sdata->vif.debugfs_dir,
+		&default_agg_timeout);
 
 	if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
 		add_common_files(sdata);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index a39512f..f885a86 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -370,6 +370,11 @@
 	DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments);
 	DEBUGFS_ADD_COUNTER(tx_filtered, status_stats.filtered);
 
+	DEBUGFS_ADD_COUNTER(last_tx, last_tx);
+	DEBUGFS_ADD_COUNTER(last_ack, last_ack);
+	DEBUGFS_ADD_COUNTER(last_noack, last_noack);
+	DEBUGFS_ADD_COUNTER(last_rx, last_rx);
+
 	if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
 		debugfs_create_x32("driver_buffered_tids", 0400,
 				   sta->debugfs.dir,
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 6b0e0e6..292fb9a 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -671,6 +671,8 @@
 	}
 }
 
+extern u32 default_agg_timeout;
+
 static void
 minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
 {
@@ -691,7 +693,7 @@
 	if (likely(sta->ampdu_mlme.tid_tx[tid]))
 		return;
 
-	ieee80211_start_tx_ba_session(pubsta, tid, 5000);
+	ieee80211_start_tx_ba_session(pubsta, tid, default_agg_timeout);
 }
 
 static void
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 02b5427..2622081 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2147,12 +2147,58 @@
 
 	if (skb) {
 		/* deliver to local stack */
-		skb->protocol = eth_type_trans(skb, dev);
-		memset(skb->cb, 0, sizeof(skb->cb));
-		if (rx->napi)
-			napi_gro_receive(rx->napi, skb);
-		else
-			netif_receive_skb(skb);
+#ifdef CPTCFG_ATH10K_USE_COMCERTO_WIFI_FASTPATH
+		extern int comcerto_wifi_rx_fastpath(struct sk_buff *skb);
+		int slowpath = 1;
+		if (!comcerto_wifi_rx_fastpath(skb)) {
+			/* fastpath handled this packet */
+			slowpath = 0;
+		}
+#ifdef CPTCFG_ATH10K_USE_DMA_COHERENT_SKB
+		else {
+			if (skb->dma_coherent) {
+				/*
+				 * We must never inject DMA coherent skb heads
+				 * into the kernel which is why we copy the skb
+				 * into normal memory. The reason we must avoid
+				 * DMA coherent memory is that struct
+				 * skb_shared_info is stored at the end of the
+				 * skb head. This struct has a field named
+				 * dataref which is defiend as atomic_t, and
+				 * ARM prohibits atomic operations on memory
+				 * that is not normal. In practice,
+				 * __skb_clone() hangs at
+				 * atomic_inc(&(skb_shinfo(skb)->dataref));
+				 * when it tries to operate on DMA coherent
+				 * (non-cachable) memory.
+				 *
+				 * One of the situations where this code path
+				 * is executed is when we receive multicast
+				 * frames. comcerto_wifi_rx_fastpath() always
+				 * rejects multicast frames which is why we end
+				 * up here. The bridge code might have to clone
+				 * these frames because they need to be copied
+				 * to multiple output ports. Hence, we need to
+				 * move the data buffer into normal memory. */
+				struct sk_buff *n = skb_copy(skb, GFP_ATOMIC);
+				consume_skb(skb);
+				if (!n)
+					slowpath = 0;
+				else
+					skb = n;
+			}
+		}
+#endif
+		if (slowpath)
+#endif
+		{
+			skb->protocol = eth_type_trans(skb, dev);
+			memset(skb->cb, 0, sizeof(skb->cb));
+			if (rx->napi)
+				napi_gro_receive(rx->napi, skb);
+			else
+				netif_receive_skb(skb);
+		}
 	}
 
 	if (xmit_skb) {
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 6a0adec..e7c7cf0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -242,11 +242,28 @@
  */
 void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 {
+	struct timespec ts_last_tx, ts_last_ack, ts_last_noack, ts_last_rx;
+
 	if (sta->rate_ctrl)
 		rate_control_free_sta(sta);
 
 	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
+
+	jiffies_to_timespec(sta->last_tx - INITIAL_JIFFIES, &ts_last_tx);
+	jiffies_to_timespec(sta->last_ack - INITIAL_JIFFIES, &ts_last_ack);
+	jiffies_to_timespec(sta->last_noack - INITIAL_JIFFIES, &ts_last_noack);
+	jiffies_to_timespec(sta->last_rx - INITIAL_JIFFIES, &ts_last_rx);
+
+	printk(KERN_DEBUG "sta_info_free: %pM "
+	       "last_tx %lu.%06lu last_ack %lu.%06lu "
+	       "last_noack %lu.%06lu last_rcv %lu.%06lu\n",
+	       sta->sta.addr,
+	       ts_last_tx.tv_sec, ts_last_tx.tv_nsec / 1000,
+	       ts_last_ack.tv_sec, ts_last_ack.tv_nsec / 1000,
+	       ts_last_noack.tv_sec, ts_last_noack.tv_nsec / 1000,
+	       ts_last_rx.tv_sec, ts_last_rx.tv_nsec / 1000);
+
 	if (sta->sta.txq[0])
 		kfree(to_txq_info(sta->sta.txq[0]));
 	kfree(rcu_dereference_raw(sta->sta.rates));
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index ab6c19e..2709ad9 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -264,17 +264,18 @@
  *
  * This struct is small enough so that the common case (maximum crypto
  * header length of 8 like for CCMP/GCMP) fits into a single 64-byte
- * cache line.
+ * cache line. Moved rcu_head after key to prevent the compiler from 
+ * adding padding. 
+ * 
  */
 struct ieee80211_fast_tx {
 	struct ieee80211_key *key;
+	struct rcu_head rcu_head;
 	u8 hdr_len;
 	u8 sa_offs, da_offs, pn_offs;
 	u8 band;
 	u8 hdr[30 + 2 + IEEE80211_FAST_XMIT_MAX_IV +
 	       sizeof(rfc1042_header)];
-
-	struct rcu_head rcu_head;
 };
 
 /**
@@ -494,6 +495,10 @@
 	enum ieee80211_smps_mode known_smps_mode;
 	const struct ieee80211_cipher_scheme *cipher_scheme;
 
+	unsigned long last_rx;
+	unsigned long last_tx;
+	unsigned long last_ack;
+	unsigned long last_noack;
 	u8 reserved_tid;
 
 	struct cfg80211_chan_def tdls_chandef;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 6101deb..c5942cb 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -875,6 +875,12 @@
 				ieee80211_lost_packet(sta, info);
 			}
 		}
+
+		if (acked) {
+			sta->last_ack = jiffies;
+		} else {
+			sta->last_noack = jiffies;
+		}
 	}
 
 	rcu_read_unlock();
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index b88889e..c5881b4 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1320,6 +1320,7 @@
 
 static bool ieee80211_tx_frags(struct ieee80211_local *local,
 			       struct ieee80211_vif *vif,
+			       struct sta_info *privsta,
 			       struct ieee80211_sta *sta,
 			       struct sk_buff_head *skbs,
 			       bool txpending)
@@ -1380,6 +1381,9 @@
 		}
 		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
+		if (privsta)
+			privsta->last_tx = jiffies;
+
 		info->control.vif = vif;
 
 		__skb_unlink(skb, skbs);
@@ -1445,7 +1449,7 @@
 		break;
 	}
 
-	result = ieee80211_tx_frags(local, vif, pubsta, skbs,
+	result = ieee80211_tx_frags(local, vif, sta, pubsta, skbs,
 				    txpending);
 
 	ieee80211_tpt_led_trig_tx(local, fc, led_len);
@@ -2841,7 +2845,7 @@
 				     struct ieee80211_sub_if_data, u.ap);
 
 	__skb_queue_tail(&tx.skbs, skb);
-	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
+	ieee80211_tx_frags(local, &sdata->vif, sta, &sta->sta, &tx.skbs, false);
 	return true;
 }
 
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 6232a12..98f72a2 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -1,17 +1,847 @@
-#
-# This file is a placeholder to prevent accidental build breakage if someone
-# enables CPTCFG_CFG80211_INTERNAL_REGDB.  Almost no one actually needs to
-# enable that build option.
-#
-# You should be using CRDA instead.  It is even better if you use the CRDA
-# package provided by your distribution, since they will probably keep it
-# up-to-date on your behalf.
-#
-# If you _really_ intend to use CPTCFG_CFG80211_INTERNAL_REGDB then you will
-# need to replace this file with one containing appropriately formatted
-# regulatory rules that cover the regulatory domains you will be using.  Your
-# best option is to extract the db.txt file from the wireless-regdb git
-# repository:
-#
-#   git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
-#
+# This is the world regulatory domain
+country 00:
+	(2402 - 2472 @ 40), (20)
+	# Channel 12 - 13.
+	(2457 - 2482 @ 40), (20), NO-IR
+	# Channel 14. Only JP enables this and for 802.11b only
+	(2474 - 2494 @ 20), (20), NO-IR
+	# Channel 36 - 48
+	(5170 - 5250 @ 80), (20), NO-IR
+	# NB: 5260 MHz - 5700 MHz requies DFS
+	# Channel 149 - 165
+	(5735 - 5835 @ 80), (20), NO-IR
+	# IEEE 802.11ad (60GHz), channels 1..3
+	(57240 - 63720 @ 2160), (0)
+
+
+country AD:
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country AE: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country AL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20.00)
+	(5250 - 5330 @ 80), (20.00), DFS
+	(5490 - 5710 @ 80), (27.00), DFS
+
+country AM: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (18)
+	(5250 - 5330 @ 80), (18), DFS
+
+country AN: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country AR: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country AT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country AU:
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5710 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country AW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country AZ: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (18)
+	(5250 - 5330 @ 80), (18), DFS
+
+country BA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country BB: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country BD: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
+country BE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country BG: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country BH: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5735 - 5835 @ 80), (20)
+
+country BL:
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 40), (18)
+	(5250 - 5330 @ 40), (18), DFS
+
+country BN: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5735 - 5835 @ 80), (20)
+
+country BO: DFS-JP
+	(2402 - 2482 @ 40), (30)
+	(5735 - 5835 @ 80), (30)
+
+country BR: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country BY: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country BZ: DFS-JP
+	(2402 - 2482 @ 40), (30)
+	(5735 - 5835 @ 80), (30)
+
+country CA: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country CH: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country CL: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5735 - 5835 @ 80), (20)
+
+country CN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+	# 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm
+	# ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
+	(57240 - 59400 @ 2160), (28)
+	(59400 - 63720 @ 2160), (44)
+	(63720 - 65880 @ 2160), (28)
+
+country CO: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country CR: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country CY: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf
+# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf
+# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is
+# implemented.
+country CZ: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (200 mW), NO-OUTDOOR
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR
+	(5470 - 5725 @ 80), (500 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from
+# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf
+# For the 5GHz range also see
+# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf
+# The values have been reduced by a factor of 2 (3db) for non TPC devices
+# (in other words: devices with TPC can use twice the tx power of this table).
+# Note that the docs do not require TPC for 5150--5250; the reduction to
+# 100mW thus is not strictly required -- however the conservative 100mW
+# limit is used here as the non-interference with radar and satellite
+# apps relies on the attenuation by the building walls only in the
+# absence of DFS; the neighbour countries have 100mW limit here as well.
+
+country DE: DFS-ETSI
+	# entries 279004 and 280006
+	(2400 - 2483.5 @ 40), (100 mW)
+	# entry 303005, 304002 and 305002
+	(5150 - 5350 @ 80), (100 mW), NO-OUTDOOR
+	# entries 308002, 309001 and 310003
+	(5470 - 5725 @ 80), (500 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country DK: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country DO: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country DZ: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170.000 - 5250.000 @ 80.000), (23.00)
+	(5250.000 - 5330.000 @ 80.000), (23.00), DFS
+	(5490.000 - 5670.000 @ 80.000), (23.00), DFS
+
+country EC: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country EE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country EG: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+
+country ES: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5250 @ 80), (100 mW), NO-OUTDOOR
+	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR
+	(5470 - 5725 @ 80), (500 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country FI: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country FR: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (18)
+	(5250 - 5330 @ 80), (18), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GB: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GD: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country GR: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country GT: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country GU: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country HN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country HK:
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5710 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country HR: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country HT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country HU: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country ID: DFS-JP
+	# ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5815 @ 80), (23)
+
+country IE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country IL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5150 - 5350 @ 80), (200 mW), NO-OUTDOOR
+
+country IN: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5735 - 5835 @ 80), (20)
+
+country IS: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country IR: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
+country IT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country JM: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country JP: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(2474 - 2494 @ 20), (20), NO-OFDM
+	(4910 - 4990 @ 40), (23)
+	(5030 - 5090 @ 40), (23)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 160), (23), DFS
+
+country JO: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23)
+	(5735 - 5835 @ 80), (23)
+
+country KE: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23)
+	(5490 - 5570 @ 80), (30), DFS
+	(5735 - 5775 @ 40), (23)
+
+country KH: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country KP: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5630 @ 80), (30), DFS
+	(5735 - 5815 @ 80), (30)
+
+country KR: DFS-JP
+	(2402 - 2482 @ 20), (20)
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (30), DFS
+	(5735 - 5815 @ 80), (30)
+
+country KW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+
+country KZ:
+	(2402 - 2482 @ 40), (20)
+
+country LB: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country LI: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country LK: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country LT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country LU: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country LV: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country MC: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country MA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+
+country MO:
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 40), (23)
+	(5250 - 5330 @ 40), (23), DFS
+	(5735 - 5835 @ 40), (30)
+
+country MK: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country MT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country MY: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country MX: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country NL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5330 @ 80), (20), NO-OUTDOOR
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country NO: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country NP: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5735 - 5835 @ 80), (20)
+
+country NZ: DFS-FCC
+	(2402 - 2482 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country OM: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country PA: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country PE: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country PG: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country PH: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country PK: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
+country PL: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country PT: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country PR: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country QA: DFS-JP
+	(2402 - 2482 @ 40), (20)
+	(5735 - 5835 @ 80), (30)
+
+country RO: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+
+# Source:
+# http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf
+country RS: DFS-ETSI
+	(2400 - 2483.5 @ 40), (100 mW)
+	(5150 - 5350 @ 40), (200 mW), NO-OUTDOOR
+	(5470 - 5725 @ 20), (1000 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country RU: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5650 - 5730 @ 80), (30), DFS
+	(5735 - 5835 @ 80), (30)
+
+country RW: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country SA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country SE: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country SG: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country SI: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country SK: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country SV: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country SY:
+	(2402 - 2482 @ 40), (20)
+
+country TW: DFS-JP
+	(2402 - 2472 @ 40), (30)
+	(5270 - 5330 @ 40), (17), DFS
+	(5490 - 5590 @ 80), (30), DFS
+	(5650 - 5710 @ 40), (30), DFS
+	(5735 - 5835 @ 80), (30)
+
+country TH: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country TT: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country TN: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+
+country TR: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+ 
+# Source:
+# #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874
+# #1174 / 23 Oct 2008: http://www.nkrz.gov.ua/uk/activities/ruling/1225269361
+# (appendix 8)
+# Listed 5GHz range is a lowest common denominator for all related
+# rules in the referenced laws. Such a range is used because of
+# disputable definitions there.
+country UA: DFS-ETSI
+	(2400 - 2483.5 @ 40), (20), NO-OUTDOOR
+	(5150 - 5350 @ 40), (20), NO-OUTDOOR
+	(5490 - 5670 @ 80), (20), DFS
+	(5735 - 5835 @ 80), (20)
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country US: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5490 - 5590 @ 80), (20), DFS
+	(5650 - 5710 @ 40), (30), DFS
+	(5735 - 5835 @ 80), (30)
+	# 60g band
+	# reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
+	# channels 1,2,3, EIRP=40dBm(43dBm peak)
+	(57240 - 63720 @ 2160), (40)
+
+country UY: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country UZ: DFS-FCC
+	(2402 - 2472 @ 40), (30)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country VE: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (23), DFS
+	(5735 - 5835 @ 80), (30)
+
+country VN: DFS-FCC
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (17)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
+	(5735 - 5835 @ 80), (30)
+
+country YE:
+	(2402 - 2482 @ 40), (20)
+
+country ZA: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
+country ZW: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (20)
+	(5250 - 5330 @ 80), (20), DFS
+	(5490 - 5710 @ 80), (27), DFS
+
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
index baf2426..37be759 100644
--- a/net/wireless/genregdb.awk
+++ b/net/wireless/genregdb.awk
@@ -66,6 +66,7 @@
 
 	sub(/\(/, "", power)
 	sub(/\),/, "", power)
+	sub(/\)/, "", power)
 	sub(/\),/, "", units)
 	sub(/\)/, "", units)
 
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 9277042..a84606e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -749,6 +749,7 @@
 {
 	unsigned int dscp;
 	unsigned char vlan_priority;
+	return 0;
 
 	/* skb->priority values from 256->263 are magic values to
 	 * directly indicate a specific 802.1d priority.  This is used