Merge Quantenna SDK v37.4.0.46.

Change-Id: I6f8c6f5a56ba623b38c02ef6512b8c8618ca621f
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 2c7d63b..09c8112 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/qdpc_init.c
@@ -56,6 +56,18 @@
 module_param(tlp_mps, uint, 0644);
 MODULE_PARM_DESC(tlp_mps, "Default PCIe Max_Payload_Size");
 
+/*
+ * Define EP state during host suspend
+ * 0 = EP does not power off
+ * 1 = EP power off
+ */
+#define EP_SUSPEND_MODE_RUNNING	0
+#define EP_SUSPEND_MODE_PWR_OFF	1
+static unsigned int suspend_mode = EP_SUSPEND_MODE_RUNNING;
+module_param(suspend_mode, uint, 0644);
+MODULE_PARM_DESC(suspend_mode, "Default suspend behavior");
+static unsigned int suspend_flag = 0;
+
 /* Quantenna PCIE vendor and device identifiers  */
 static struct pci_device_id qdpc_pcie_ids[] = {
 	{PCI_DEVICE(QDPC_VENDOR_ID, QDPC_DEVICE_ID),},
@@ -70,6 +82,8 @@
 static void qdpc_nl_recv_msg(struct sk_buff *skb);
 int qdpc_init_netdev(struct net_device  **net_dev, struct pci_dev *pdev);
 pci_ers_result_t qdpc_pcie_slot_reset(struct pci_dev *dev);
+static void qdpc_pcie_shutdown(struct pci_dev *pdev);
+
 
 char qdpc_pcie_driver_name[] = "qdpc_host";
 
@@ -87,6 +101,7 @@
 	.resume  = qdpc_pcie_resume,
 #endif
         .err_handler = &qdpc_err_hdl,
+	.shutdown = qdpc_pcie_shutdown,
 };
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
@@ -487,7 +502,7 @@
 #endif
 	pci_disable_device(pdev);
 
-	writel(TOPAZ_SET_INT(IPC_RESET_EP), vmp->ep_ipc_reg);
+	writel(TOPAZ_SET_INT(IPC_RESET_EP), (volatile void *)(vmp->ep_ipc_reg));
 	qdpc_unmap_iomem(vmp);
 
 	free_netdev(ndev);
@@ -539,13 +554,16 @@
 	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);
+	writel(TOPAZ_SET_INT(IPC_EP_PM_CTRL), (volatile void *)(priv->ep_ipc_reg));
 
 	msleep(100);
 	pci_save_state(pdev);
 	pci_disable_device(pdev);
 	qdpc_pcie_set_power_state(pdev, PCI_D3hot);
 
+	if (suspend_mode == EP_SUSPEND_MODE_PWR_OFF)
+		suspend_flag = 1;
+
 	return 0;
 }
 
@@ -555,12 +573,15 @@
 	struct vmac_priv *priv;
 	int ret;
 
-	if (ndev == NULL)
-		return -EINVAL;
+	if (ndev == NULL) {
+		ret = -EINVAL;
+		goto out;
+	}
 
 	priv = netdev_priv(ndev);
 	if (le32_to_cpu(*priv->ep_pmstate) == PCI_D0) {
-		return 0;
+		ret = 0;
+		goto out;
 	}
 
 	printk("%s start power management resume\n", qdpc_pcie_driver_name);
@@ -568,25 +589,41 @@
 	ret = pci_enable_device(pdev);
 	if (ret) {
 		PRINT_ERROR("%s: pci_enable_device failed on resume\n", __func__);
-		return ret;
+		goto out;
 	}
 
 	pci_restore_state(pdev);
+	pdev->state_saved = true;
 	qdpc_pcie_set_power_state(pdev, PCI_D0);
 
 	{
 		*priv->ep_pmstate = cpu_to_le32(PCI_D0);
 		barrier();
-		writel(TOPAZ_SET_INT(IPC_EP_PM_CTRL), priv->ep_ipc_reg);
+		writel(TOPAZ_SET_INT(IPC_EP_PM_CTRL), (volatile void *)(priv->ep_ipc_reg));
 
 		msleep(5000);
 	}
 
+	if ( (suspend_mode == EP_SUSPEND_MODE_PWR_OFF) && 
+	     (pdev->driver && pdev->driver->err_handler && pdev->driver->err_handler->slot_reset) ) {
+		printk("slot_reset in %s(), Device name: %s\n", __FUNCTION__, dev_name(&pdev->dev));
+		if(pdev->driver->err_handler->slot_reset(pdev) == PCI_ERS_RESULT_RECOVERED)
+			printk("Recovery OK\n");
+		else {
+			printk("Recovery Error");
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
 	/* Set ep_ready to resume tx traffic */
 	priv->ep_ready = 1;
 	ndev->flags |= IFF_RUNNING;
 
-	return 0;
+out:
+	if (suspend_mode == EP_SUSPEND_MODE_PWR_OFF)
+		suspend_flag = 0;
+	return ret;
 }
 
 static int __init qdpc_init_module(void)
@@ -646,12 +683,16 @@
 {
 	struct vmac_priv *priv = NULL;
 
-	if (pdev->state_saved == true) {
-		pci_restore_state(pdev);
-		pdev->state_saved = true;
-	} else {
-		printk("Recovery Error: No saved state\n");
-		goto out;
+	if (suspend_mode == EP_SUSPEND_MODE_PWR_OFF && suspend_flag)
+		suspend_flag = 0;
+	else {
+		if (pdev->state_saved == true) {
+			pci_restore_state(pdev);
+			pdev->state_saved = true;
+		} else {
+			printk("Recovery Error: No saved state\n");
+			goto out;
+		}
 	}
 
 	if (pci_enable_device(pdev)) {
@@ -723,6 +764,13 @@
 		return PCI_ERS_RESULT_DISCONNECT;
 }
 
+static void qdpc_pcie_shutdown(struct pci_dev *pdev)
+{
+	qdpc_pcie_remove(pdev);
+
+	return;
+}
+
 static int qdpc_bringup_fw(struct vmac_priv *priv)
 {
 	__iomem qdpc_pcie_bda_t  *bda = priv->bda;
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 d21f7bf..08bf5ec 100644
--- a/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
+++ b/drivers/net/wireless/quantenna/pcie2/host/common/topaz_vnet.c
@@ -791,7 +791,7 @@
 			}
 		}
 		if ((ndev->stats.rx_packets & RX_DONE_INTR_MSK) == 0)
-			writel(TOPAZ_SET_INT(IPC_RC_RX_DONE), vmp->ep_ipc_reg);
+			writel(TOPAZ_SET_INT(IPC_RC_RX_DONE), (volatile void *)(vmp->ep_ipc_reg));
 
 		dump_rx_bd(vmp);
 
@@ -893,12 +893,11 @@
 	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;
-
+		tbdp = &vmp->tx_bd_base[i];
 		ndev->stats.tx_packets++;
 
 		ndev->stats.tx_bytes +=  skb->len;
@@ -1013,7 +1012,7 @@
 		vmp->txqueue_stopped = 1;
 		*vmp->txqueue_wake = 0;
 		barrier();
-		writel(TOPAZ_SET_INT(IPC_RC_STOP_TX), vmp->ep_ipc_reg);
+		writel(TOPAZ_SET_INT(IPC_RC_STOP_TX), (volatile void *)(vmp->ep_ipc_reg));
 		vmp->tx_stop_queue_cnt++;
 		netif_stop_queue(ndev);
 	}
@@ -1111,7 +1110,7 @@
 
 	dump_tx_bd(vmp);
 
-	writel(TOPAZ_SET_INT(IPC_EP_RX_PKT), vmp->ep_ipc_reg);
+	writel(TOPAZ_SET_INT(IPC_EP_RX_PKT), (volatile void *)(vmp->ep_ipc_reg));
 
 #ifdef RC_TXDONE_TIMER
 	vmac_tx_teardown(ndev, bda);
@@ -1166,7 +1165,7 @@
 	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);
+		writel(TOPAZ_SET_INT(IPC_EP_RX_PKT), (volatile void *)(vmp->ep_ipc_reg));
 		mod_timer(&vmp->tx_timer, jiffies + 1);
 	}
 	spin_unlock(&vmp->tx_lock);
diff --git a/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h b/drivers/net/wireless/quantenna/pcie2/include/qdpc_version.h
index 3b01b8e..3e81707 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.4.0.38"
+#define DRV_VERSION "v37.4.0.46"