qdpc-host: import from Quantenna SDK v37.3.1.25.
quantenna-sdk-v37.3.1.25.tar.gz/
drivers/pcie2 -> drivers/net/wireless/quantenna/pcie2
Change-Id: Ia22e5ab07cc4917c7da6de5ac9e1a78c0c50b995
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..c2e6e30
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
@@ -0,0 +1,882 @@
+/**
+ * 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);
+ }
+
+ qdpc_tune_pcie_mps(pdev, pos);
+
+ /* 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);
+
+ if (qdpc_set_dma_mask(priv)){
+ printk("Failed to map DMA mask.\n");
+ priv->init_thread = NULL;
+ do_exit(-1);
+ }
+
+ 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..86fe579
--- /dev/null
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
@@ -0,0 +1,1495 @@
+/**
+ * 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, "host%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);
+
+ skb->protocol = eth_type_trans(skb, ndev);
+ processed++;
+
+
+ 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
+ if (!(skb = dev_alloc_skb(SKB_BUF_SIZE))) {
+ 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, 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, 10);
+
+ /* 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, ®ion, 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 */