qdpc-host: import from Quantenna SDK v37.4.0.38.

quantenna-sdk-v37.4.0.38.tar.gz/
  drivers/pcie2 -> drivers/net/wireless/quantenna/pcie2

Change-Id: I539b4d0a476e50c7435458ca73841c1e8cdd7439
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
index c2e6e30..a648be0 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
@@ -34,6 +34,7 @@
 #include <linux/firmware.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/delay.h>
 
 #include "qdpc_config.h"
 #include "qdpc_debug.h"
@@ -68,15 +69,14 @@
 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
+pci_ers_result_t qdpc_pcie_slot_reset(struct pci_dev *dev);
 
 char qdpc_pcie_driver_name[] = "qdpc_host";
 
+static struct pci_error_handlers qdpc_err_hdl = {
+        .slot_reset = qdpc_pcie_slot_reset,
+};
+
 static struct pci_driver qdpc_pcie_driver = {
 	.name     = qdpc_pcie_driver_name,
 	.id_table = qdpc_pcie_ids,
@@ -86,6 +86,7 @@
 	.suspend  = qdpc_pcie_suspend,
 	.resume  = qdpc_pcie_resume,
 #endif
+        .err_handler = &qdpc_err_hdl,
 };
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
@@ -335,6 +336,20 @@
 	int result = SUCCESS;
 	int pos;
 
+	/* When system boots up, the state of pdev is not saved on the entry of this function.
+	 * Data structure pci_dev will keep in use when module is removed and re-installed.
+	 * So we can call pci_restore_sate() to recover its previous configuration space.
+	 */
+	if (pdev->state_saved == true) {
+		printk("Recovery: restore saved state\n");
+		pci_restore_state(pdev);
+	}
+
+	/* Save "fresh poweron" state including BAR address, etc. So the state can be
+	 * used for recovery next time.
+	 */
+	pci_save_state(pdev);
+
 	/* Allocate device structure */
 	if (!(ndev = vmac_alloc_ndev()))
 		return -ENOMEM;
@@ -451,6 +466,7 @@
 
 	vmp = netdev_priv(ndev);
 
+	vmp->ep_ready = 0;
 	if (vmp->init_thread)
 		kthread_stop(vmp->init_thread);
 	if (vmp->nl_socket)
@@ -589,10 +605,6 @@
 		return ret;
 	}
 
-#ifndef PCIE_HOTPLUG_SUPPORTED
-	link_monitor_thread = kthread_run(link_monitor, NULL, "link_monitor");
-#endif
-
 	return ret;
 }
 
@@ -601,11 +613,6 @@
 	/* 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);
 
@@ -613,99 +620,105 @@
 	return;
 }
 
-static inline bool is_pcie_linkup(struct pci_dev *pdev)
+void qdpc_recovery_clean(struct pci_dev *pdev, struct net_device *ndev)
 {
-	uint32_t cs = 0;
+	struct vmac_priv *vmp;
 
-	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;
+	vmp = netdev_priv(ndev);
+	vmp->ep_ready = 0;
+
+	if (vmp->init_thread) {
+		kthread_stop(vmp->init_thread);
+		vmp->init_thread = NULL;
 	}
 
-	return false;
+	vmac_recovery_clean(ndev);
+
+	pci_disable_device(pdev);
+
+	return;
 }
 
-static inline void qdpc_pcie_print_config_space(struct pci_dev *pdev)
+int qdpc_recovery_reinit(struct pci_dev *pdev, struct net_device *ndev)
 {
-	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);
+	if (pdev->state_saved == true) {
+		pci_restore_state(pdev);
+		pdev->state_saved = true;
+	} else {
+		printk("Recovery Error: No saved state\n");
+		goto out;
+	}
 
-		ndev = g_ndev;
-		priv = netdev_priv(ndev);
-		bda = priv->bda;
-		pdev = priv->pdev;
+	if (pci_enable_device(pdev)) {
+		printk("Recovery Error: Failed to enable PCI device\n");
+		goto out;
+	}
 
-#ifdef QDPC_CS_DEBUG
-		qdpc_pcie_print_config_space(pdev);
-		msleep(5000);
-#endif
+	priv = netdev_priv(ndev);
+	if (vmac_recovery_init(priv, ndev) == ENOMEM) {
+		printk("Recovery Error: Not enough memory\n");
+		goto qdpc_recovery_err_0;
+	}
 
-		/* Check if reset to EP occurred */
-		while (!pci_read_config_dword(pdev, QDPC_VENDOR_ID_OFFSET, &cs)) {
+	priv->init_thread = kthread_run(qdpc_boot_thread, priv, "qdpc_init_thread");
+	if (priv->init_thread == NULL) {
+		printk("Recovery Error: Thread creation failed \n");
+		goto qdpc_recovery_err_0;
+	}
 
-			if (kthread_should_stop())
-				do_exit(0);
+	return SUCCESS;
 
-			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);
+qdpc_recovery_err_0:
+	pci_disable_device(pdev);
+out:
+	return -1;
 }
-#endif
+
+static int qdpc_recovery_access_check(struct pci_dev *pdev)
+{
+	uint32_t val = 0;
+
+	pci_read_config_dword(pdev, QDPC_VENDOR_ID_OFFSET, &val);
+
+	if (val == ((QDPC_DEVICE_ID << 16) | QDPC_VENDOR_ID)) {
+		printk("%s: PCIe read access check: Pass\n", __func__);
+		return 0;
+	} else {
+		printk("%s: PCIe read access check: Fail: VENDOR_ID read error: 0x%08x\n", __func__, val);
+		return -1;
+	}
+}
+
+int qdpc_pcie_recovery(struct pci_dev *pdev)
+{
+	struct net_device *ndev = pci_get_drvdata(pdev);
+
+	qdpc_recovery_clean(pdev, ndev);
+
+        /* Wait EP link up. If this function is called at hardirq context where 10s
+	 * delay is not allowed, please replace with link up check at Root Complex's
+	 * status register.
+         */
+        mdelay(10000);
+
+        if (qdpc_recovery_access_check(pdev) != 0)
+                return -1;
+
+        /* Re-allocate and initialize data structure */
+        qdpc_recovery_reinit(pdev, ndev);
+
+        return 0;
+}
+
+pci_ers_result_t qdpc_pcie_slot_reset(struct pci_dev *dev)
+{
+	if (qdpc_pcie_recovery(dev) == 0)
+		return PCI_ERS_RESULT_RECOVERED;
+	else
+		return PCI_ERS_RESULT_DISCONNECT;
+}
 
 static int qdpc_bringup_fw(struct vmac_priv *priv)
 {
@@ -780,11 +793,6 @@
 
 	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);
 }
@@ -872,6 +880,9 @@
 		offset += len;
 
 		skb_put(skb2, skb2_len);
+		skb_reset_mac_header(skb2);
+		skb_reset_network_header(skb2);
+		skb2->protocol = __constant_htons(QDPC_APP_NETLINK_TYPE);
 		skb2->dev = priv->ndev;
 
 		dev_queue_xmit(skb2);
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h
index 6d3c825..3d39181 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.h
@@ -35,7 +35,6 @@
 #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
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
index 86fe579..29835d9 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
@@ -75,7 +75,7 @@
 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 free_tx_pkts(struct vmac_priv *vmp);
 static void init_tx_bd(struct vmac_priv *vmp);
 static void free_rx_skbs(struct vmac_priv *vmp);
 static int alloc_and_init_rxbuffers(struct net_device *ndev);
@@ -84,6 +84,7 @@
 static int vmac_open(struct net_device *ndev);
 static int vmac_close(struct net_device *ndev);
 static int vmac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd);
+static struct net_device_stats *vmac_get_stats(struct net_device *dev);
 #ifdef QTN_WAKEQ_SUPPORT
 static inline void vmac_try_wake_queue(struct net_device *ndev);
 static inline void vmac_try_stop_queue(struct net_device *ndev);
@@ -447,6 +448,7 @@
 
 	/* Update pointers related with Tx descriptor table */
 	vmp->tx_bd_base = (struct vmac_bd *)ucaddr;
+	vmp->paddr_tx_bd_base = paddr;
 	qdpc_pcie_posted_write(paddr, &vmp->bda->bda_rc_tx_bd_base);
 	init_tx_bd(vmp);
 	printk("Tx Descriptor table: uncache virtual addr: 0x%08x paddr: 0x%08x\n",
@@ -485,7 +487,7 @@
 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));
+			vmp->paddr_tx_bd_base);
 }
 
 static int alloc_skb_desc_array(struct net_device *ndev)
@@ -1193,6 +1195,7 @@
 	.ndo_do_ioctl = vmac_ioctl,
 	.ndo_tx_timeout = vmac_tx_timeout,
 	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_get_stats = vmac_get_stats,
 };
 
 struct net_device *vmac_alloc_ndev(void)
@@ -1353,6 +1356,41 @@
 }
 EXPORT_SYMBOL(vmac_net_init);
 
+int vmac_recovery_init(struct vmac_priv *priv, struct net_device *ndev)
+{
+	int err = -ENOMEM;
+
+	qdpc_pcie_posted_write(priv->tx_bd_num, &priv->bda->bda_rc_tx_bd_num);
+	qdpc_pcie_posted_write(priv->rx_bd_num, &priv->bda->bda_rc_rx_bd_num);
+
+	if (alloc_skb_desc_array(ndev))
+		goto vnet_recovery_err_0;
+
+	if (alloc_bd_tbl(ndev))
+		goto vnet_recovery_err_1;
+
+#ifdef QTN_WAKEQ_SUPPORT
+	if (unlikely(priv->txqueue_stopped)) {
+		printk("Recovery: Wake tx queue\n");
+		*priv->txqueue_wake = 1;
+		vmac_try_wake_queue(ndev);
+	}
+#endif
+
+	if (alloc_and_init_rxbuffers(ndev))
+		goto vnet_recovery_err_2;
+
+	return SUCCESS;
+
+vnet_recovery_err_2:
+	free_bd_tbl(priv);
+vnet_recovery_err_1:
+	free_skb_desc_array(ndev);
+vnet_recovery_err_0:
+	return err;
+}
+EXPORT_SYMBOL(vmac_recovery_init);
+
 static void free_rx_skbs(struct vmac_priv *vmp)
 {
 	/* All Ethernet activity should have ceased before calling
@@ -1365,9 +1403,11 @@
 			vmp->rx_skb[i] = 0;
 		}
 	}
+
+	vmp->rx_bd_index = 0;
 }
 
-static void free_tx_skbs(struct vmac_priv *vmp)
+static void free_tx_pkts(struct vmac_priv *vmp)
 {
 	/* All Ethernet activity should have ceased before calling
 	 * this function
@@ -1379,6 +1419,11 @@
 			vmp->tx_skb[i] = 0;
 		}
 	}
+
+	vmp->tx_bd_index = 0;
+	vmp->ep_next_rx_pkt = 0;
+	vmp->tx_reclaim_start = 0;
+	vmp->vmac_tx_queue_len = 0;
 }
 
 static void init_tx_bd(struct vmac_priv *vmp)
@@ -1421,7 +1466,7 @@
 	unregister_netdev(ndev);
 
 	free_rx_skbs(vmp);
-	free_tx_skbs(vmp);
+	free_tx_pkts(vmp);
 	free_skb_desc_array(ndev);
 #ifdef QTN_SKB_RECYCLE_SUPPORT
 	vmac_rx_skb_freelist_purge(vmp);
@@ -1434,6 +1479,18 @@
 	free_bd_tbl(vmp);
 }
 
+void vmac_recovery_clean(struct net_device *ndev)
+{
+	struct vmac_priv *vmp;
+
+	vmp = netdev_priv(ndev);
+
+	free_rx_skbs(vmp);
+	free_tx_pkts(vmp);
+	free_skb_desc_array(ndev);
+	free_bd_tbl(vmp);
+}
+
 static void bring_up_interface(struct net_device *ndev)
 {
 	/* Interface will be ready to send/receive data, but will need hooking
@@ -1493,3 +1550,7 @@
 	return 0;
 }
 
+static struct net_device_stats *vmac_get_stats(struct net_device *ndev)
+{
+	return &(ndev->stats);
+}
diff --git a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h
index 170d163..928b809 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.h
@@ -103,6 +103,7 @@
 struct vmac_priv {
 	struct sk_buff **tx_skb;/* skb having post to PCIe DMA */
 	volatile struct vmac_bd *tx_bd_base; /* Tx buffer descriptor */
+	dma_addr_t paddr_tx_bd_base; /* Physical address of Tx BD array */
 	volatile uint32_t *ep_next_rx_pkt;
 	uint16_t tx_bd_index;
 	uint16_t tx_reclaim_start;
@@ -213,7 +214,9 @@
 
 extern struct net_device *vmac_alloc_ndev(void);
 extern int vmac_net_init(struct pci_dev *pdev);
+extern int vmac_recovery_init(struct vmac_priv *priv, struct net_device *ndev);
 extern void vmac_clean(struct net_device *ndev);
+extern void vmac_recovery_clean(struct net_device *ndev);
 extern int vmac_tx(void *pkt_handle, struct net_device *ndev);
 
 #define PCIE_REG_CFG_BASE		0x0
diff --git a/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile b/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile
index e39e8de..82815c8 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile
+++ b/drivers/net/wireless/quantenna/pcie2/host/quantenna/Makefile
@@ -31,8 +31,11 @@
 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
+ifeq ($(CONFIG_TOPAZ_DBDC_HOST), y)
+qdpc-host-objs	+= $(if $(wildcard $(TQE_DIR_TO_LINUX)), $(TQE_DIR_TO_WORK)/topaz_qfp.o)
+else
+qdpc-host-objs	+= $(COMMON_DIR)/qdpc_init.o $(COMMON_DIR)/qdpc_pcie.o $(COMMON_DIR)/topaz_vnet.o qdpc_platform.o
+endif
 
 qdpc-host-objs  += $(if $(wildcard $(TQE_DIR_TO_LINUX)), $(TQE_DIR_TO_WORK)/topaz_pcie_tqe.o)
 qdpc-host-objs  += qdpc_dspload.o
diff --git a/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h b/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h
index 302e8d2..dc63a1b 100644
--- a/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_debug.h
@@ -33,6 +33,18 @@
 #define PRINT_ERROR(format, ...)         printk(KERN_ERR format, ##__VA_ARGS__)
 #define PRINT_INFO(format, ...)          printk(KERN_INFO format, ##__VA_ARGS__)
 
+#ifdef DBGFMT
+#undef DBGFMT
+#endif
+
+#ifdef DBGARG
+#undef DBGARG
+#endif
+
+#ifdef DBGPRINTF
+#undef DBGPRINTF
+#endif
+
 #define DBGFMT	"%s-%d: "
 #define DBGARG	__func__, __LINE__
 
diff --git a/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h b/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
index c004ae6..3b01b8e 100644
--- a/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
+++ b/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
@@ -1 +1 @@
-#define DRV_VERSION "v37.3.1.25"
+#define DRV_VERSION "v37.4.0.38"