Merge "Implement mwifiex_cfg80211_get_station for UAP mode."
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 2ff5602..2987857 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -304,5 +304,6 @@
 source "drivers/net/wireless/mwifiex/Kconfig"
 source "drivers/net/wireless/cw1200/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
+source "drivers/net/wireless/quantenna/Kconfig"
 
 endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index ed7b41a..98acb8d 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -60,3 +60,5 @@
 
 obj-$(CPTCFG_CW1200)	+= cw1200/
 obj-$(CPTCFG_RSI_91X)	+= rsi/
+
+obj-$(CPTCFG_QUANTENNA_PCIE_HOST) += quantenna/pcie2/host/arm/
diff --git a/drivers/net/wireless/quantenna/Kconfig b/drivers/net/wireless/quantenna/Kconfig
new file mode 100644
index 0000000..73c81ad
--- /dev/null
+++ b/drivers/net/wireless/quantenna/Kconfig
@@ -0,0 +1,6 @@
+menu "Quantenna drivers"
+	config QUANTENNA_PCIE_HOST
+		tristate "PCIe host"
+		default n
+		depends on m
+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..5dfe587
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
@@ -0,0 +1,874 @@
+/**
+ * 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 "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 1
+
+#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");
+
+/* 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);
+
+static bool is_ep_reset = false;
+#ifndef PCIE_HOTPLUG_SUPPORTED
+static int link_monitor(void *data);
+static struct task_struct *link_monitor_thread = NULL;
+#endif
+
+char qdpc_pcie_driver_name[] = "qdpc_host";
+
+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
+};
+
+#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));
+			}
+		}
+	}
+	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;
+
+	/* 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);
+	}
+
+	/*  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);
+
+	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), 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), priv->ep_ipc_reg);
+
+	msleep(100);
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	qdpc_pcie_set_power_state(pdev, PCI_D3hot);
+
+	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)
+		return -EINVAL;
+
+	priv = netdev_priv(ndev);
+	if (le32_to_cpu(*priv->ep_pmstate) == PCI_D0) {
+		return 0;
+	}
+
+	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__);
+		return ret;
+	}
+
+	pci_restore_state(pdev);
+	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), priv->ep_ipc_reg);
+
+		msleep(5000);
+	}
+
+	/* Set ep_ready to resume tx traffic */
+	priv->ep_ready = 1;
+	ndev->flags |= IFF_RUNNING;
+
+	return 0;
+}
+
+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;
+	}
+
+#ifndef PCIE_HOTPLUG_SUPPORTED
+	link_monitor_thread = kthread_run(link_monitor, NULL, "link_monitor");
+#endif
+
+	return ret;
+}
+
+static void __exit qdpc_exit_module(void)
+{
+	/* Release netlink */
+	qdpc_platform_exit();
+
+#ifndef PCIE_HOTPLUG_SUPPORTED
+	kthread_stop(link_monitor_thread);
+	link_monitor_thread = NULL;
+#endif
+
+	/* Unregister the pci driver with the device */
+	pci_unregister_driver(&qdpc_pcie_driver);
+
+
+	return;
+}
+
+static inline bool is_pcie_linkup(struct pci_dev *pdev)
+{
+	uint32_t cs = 0;
+
+	pci_read_config_dword(pdev, QDPC_VENDOR_ID_OFFSET, &cs);
+	if (cs == QDPC_LINK_UP) {
+		msleep(10000);
+		printk("%s: PCIe link up!\n", __func__);
+		return true;
+	}
+
+	return false;
+}
+
+static inline void qdpc_pcie_print_config_space(struct pci_dev *pdev)
+{
+	int i = 0;
+	uint32_t cs = 0;
+
+	/* Read PCIe configuration space header */
+	for (i = QDPC_VENDOR_ID_OFFSET; i <= QDPC_INT_LINE_OFFSET; i += QDPC_ROW_INCR_OFFSET) {
+		pci_read_config_dword(pdev, i, &cs);
+		printk("%s: pdev:0x%p config_space offset:0x%02x value:0x%08x\n", __func__, pdev, i, cs);
+	}
+	printk("\n");
+}
+
+static inline void qdpc_pcie_check_link(struct pci_dev *pdev, struct vmac_priv *priv)
+{
+	__iomem qdpc_pcie_bda_t *bda = priv->bda;
+	uint32_t cs = 0;
+
+	pci_read_config_dword(pdev, QDPC_VENDOR_ID_OFFSET, &cs);
+	/* Endian value will be all 1s if link went down */
+	if (readl(&bda->bda_pci_endian) == QDPC_LINK_DOWN) {
+		is_ep_reset = true;
+		printk("Reset detected\n");
+	}
+}
+
+#ifndef PCIE_HOTPLUG_SUPPORTED
+static int link_monitor(void *data)
+{
+	struct net_device *ndev = NULL;
+	struct vmac_priv *priv = NULL;
+	__iomem qdpc_pcie_bda_t *bda = NULL;
+	struct pci_dev *pdev = NULL;
+	uint32_t cs = 0;
+
+	set_current_state(TASK_RUNNING);
+	while (!kthread_should_stop()) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		set_current_state(TASK_RUNNING);
+
+		ndev = g_ndev;
+		priv = netdev_priv(ndev);
+		bda = priv->bda;
+		pdev = priv->pdev;
+
+#ifdef QDPC_CS_DEBUG
+		qdpc_pcie_print_config_space(pdev);
+		msleep(5000);
+#endif
+
+		/* Check if reset to EP occurred */
+		while (!pci_read_config_dword(pdev, QDPC_VENDOR_ID_OFFSET, &cs)) {
+
+			if (kthread_should_stop())
+				do_exit(0);
+
+			qdpc_pcie_check_link(pdev, priv);
+			if (is_ep_reset) {
+				is_ep_reset = false;
+				qdpc_pcie_remove(pdev);
+				printk("%s: Attempting to recover from EP reset\n", __func__);
+				break;
+			}
+			msleep(500);
+		}
+
+		while(!is_pcie_linkup(pdev)) {
+		}
+
+#ifdef QDPC_CS_DEBUG
+		qdpc_pcie_print_config_space(pdev);
+#endif
+
+		qdpc_pcie_probe(pdev, NULL);
+    }
+    do_exit(0);
+}
+#endif
+
+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");
+
+#ifndef PCIE_HOTPLUG_SUPPORTED
+	if (link_monitor_thread)
+		wake_up_process(link_monitor_thread);
+#endif
+
+	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);
+		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..6d3c825
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h
@@ -0,0 +1,119 @@
+/**
+ * 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_UP	((QDPC_DEVICE_ID << 16) | QDPC_VENDOR_ID) /* Used to indicate CS is valid and link is up */
+#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..d04ec67
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
@@ -0,0 +1,1505 @@
+/**
+ * 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_skbs(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);
+#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;
+	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,
+			qdpc_pci_readl(&vmp->bda->bda_rc_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
+				{
+					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), 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
+#ifdef CONFIG_ARCH_COMCERTO
+	if (!(skb = __dev_alloc_skb(SKB_BUF_SIZE, GFP_ATOMIC | GFP_DMA_NCNB))) {
+#else
+	if (!(skb = dev_alloc_skb(SKB_BUF_SIZE))) {
+#endif
+		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) {
+		tbdp = &vmp->tx_bd_base[i];
+		struct sk_buff *skb;
+		skb = vmp->tx_skb[i];
+		if (!skb)
+			break;
+
+		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), 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, bus_to_virt(baddr), 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), 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), 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,
+};
+
+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;
+	SET_ETHTOOL_OPS(ndev, &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);
+
+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;
+		}
+	}
+}
+
+static void free_tx_skbs(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;
+		}
+	}
+}
+
+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_skbs(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);
+}
+
+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;
+}
+
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..170d163
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h
@@ -0,0 +1,228 @@
+/**
+ * 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 */
+	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 void vmac_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..e39e8de
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile
@@ -0,0 +1,67 @@
+#
+# 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.
+qdpc-host-objs   := $(COMMON_DIR)/topaz_vnet.o $(COMMON_DIR)/qdpc_init.o \
+		$(COMMON_DIR)/qdpc_pcie.o qdpc_platform.o
+
+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..302e8d2
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h
@@ -0,0 +1,65 @@
+/**
+ * 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__)
+
+#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..c004ae6
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
@@ -0,0 +1 @@
+#define DRV_VERSION "v37.3.1.25"
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 */