Staging: sxg: Make SXG driver use MSI-X interrupts if possible

Make Sahara SXG driver use MSI-X interrupts instead of line based interrupts
if possible. In case of problems in getting MSI-X vectors or MSI-X not being
supported, driver will fall back to use previous line based interrupts.

Signed-off-by: LinSysSoft Sahara Team <saharaproj@linsyssoft.com>
Signed-off-by: Mithlesh Thukral <mithlesh@linsyssoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/staging/sxg/sxg.c b/drivers/staging/sxg/sxg.c
index 75c4982..287edb7 100644
--- a/drivers/staging/sxg/sxg.c
+++ b/drivers/staging/sxg/sxg.c
@@ -132,6 +132,9 @@
 void sxg_unmap_resources(struct adapter_t *adapter);
 void sxg_free_mcast_addrs(struct adapter_t *adapter);
 void sxg_collect_statistics(struct adapter_t *adapter);
+static int sxg_register_interrupt(struct adapter_t *adapter);
+static void sxg_remove_isr(struct adapter_t *adapter);
+static irqreturn_t sxg_isr(int irq, void *dev_id);
 
 #define XXXTODO 0
 
@@ -251,6 +254,131 @@
 static struct sxg_trace_buffer *SxgTraceBuffer = NULL;
 
 /*
+ * MSI Related API's
+ */
+int sxg_register_intr(struct adapter_t *adapter);
+int sxg_enable_msi_x(struct adapter_t *adapter);
+int sxg_add_msi_isr(struct adapter_t *adapter);
+void sxg_remove_msix_isr(struct adapter_t *adapter);
+int sxg_set_interrupt_capability(struct adapter_t *adapter);
+
+int sxg_set_interrupt_capability(struct adapter_t *adapter)
+{
+	int ret;
+
+	ret = sxg_enable_msi_x(adapter);
+	if (ret != STATUS_SUCCESS) {
+		adapter->msi_enabled = FALSE;
+		DBG_ERROR("sxg_set_interrupt_capability MSI-X Disable\n");
+	} else {
+		adapter->msi_enabled = TRUE;
+		DBG_ERROR("sxg_set_interrupt_capability MSI-X Enable\n");
+	}
+	return ret;
+}
+
+int sxg_register_intr(struct adapter_t *adapter)
+{
+	int ret = 0;
+
+	if (adapter->msi_enabled) {
+		ret = sxg_add_msi_isr(adapter);
+	}
+	else {
+		DBG_ERROR("MSI-X Enable Failed. Using Pin INT\n");
+		ret = sxg_register_interrupt(adapter);
+		if (ret != STATUS_SUCCESS) {
+			DBG_ERROR("sxg_register_interrupt Failed\n");
+		}
+	}
+	return ret;
+}
+
+int sxg_enable_msi_x(struct adapter_t *adapter)
+{
+	int ret;
+
+	adapter->nr_msix_entries = 1;
+	adapter->msi_entries =  kmalloc(adapter->nr_msix_entries *
+					sizeof(struct msix_entry),GFP_KERNEL);
+	if (!adapter->msi_entries) {
+		DBG_ERROR("%s:MSI Entries memory allocation Failed\n",__func__);
+		return -ENOMEM;
+	}
+	memset(adapter->msi_entries, 0, adapter->nr_msix_entries *
+		sizeof(struct msix_entry));
+
+	ret = pci_enable_msix(adapter->pcidev, adapter->msi_entries,
+				adapter->nr_msix_entries);
+	if (ret) {
+		DBG_ERROR("Enabling MSI-X with %d vectors failed\n",
+				adapter->nr_msix_entries);
+		/*Should try with less vector returned.*/
+		kfree(adapter->msi_entries);
+		return STATUS_FAILURE; /*MSI-X Enable failed.*/
+	}
+	return (STATUS_SUCCESS);
+}
+
+int sxg_add_msi_isr(struct adapter_t *adapter)
+{
+	int ret,i;
+
+	if (!adapter->intrregistered) {
+		for (i=0; i<adapter->nr_msix_entries; i++) {
+			ret = request_irq (adapter->msi_entries[i].vector,
+					sxg_isr,
+					IRQF_SHARED,
+					adapter->netdev->name,
+					adapter->netdev);
+			if (ret) {
+				DBG_ERROR("sxg: MSI-X request_irq (%s) "
+					"FAILED [%x]\n", adapter->netdev->name,
+					 ret);
+				return (ret);
+			}
+		}
+	}
+	adapter->msi_enabled = TRUE;
+	adapter->intrregistered = 1;
+	adapter->IntRegistered = TRUE;
+	return (STATUS_SUCCESS);
+}
+
+void sxg_remove_msix_isr(struct adapter_t *adapter)
+{
+	int i,vector;
+	struct net_device *netdev = adapter->netdev;
+
+	for(i=0; i< adapter->nr_msix_entries;i++)
+	{
+		vector = adapter->msi_entries[i].vector;
+		DBG_ERROR("%s : Freeing IRQ vector#%d\n",__FUNCTION__,vector);
+		free_irq(vector,netdev);
+	}
+}
+
+
+static void sxg_remove_isr(struct adapter_t *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	if (adapter->msi_enabled)
+		sxg_remove_msix_isr(adapter);
+	else
+		free_irq(adapter->netdev->irq, netdev);
+}
+
+void sxg_reset_interrupt_capability(struct adapter_t *adapter)
+{
+	if (adapter->msi_enabled) {
+		pci_disable_msix(adapter->pcidev);
+		kfree(adapter->msi_entries);
+		adapter->msi_entries = NULL;
+	}
+	return;
+}
+
+/*
  * sxg_download_microcode
  *
  * Download Microcode to Sahara adapter
@@ -462,7 +590,7 @@
 	/* Windows tells us how many CPUs it plans to use for */
 	/* RSS */
 	RssIds = SXG_RSS_CPU_COUNT(adapter);
-	IsrCount = adapter->MsiEnabled ? RssIds : 1;
+	IsrCount = adapter->msi_enabled ? RssIds : 1;
 
 	DBG_ERROR("%s Setup the spinlocks\n", __func__);
 
@@ -950,6 +1078,9 @@
 	netdev->get_stats = sxg_get_stats;
 	netdev->set_multicast_list = sxg_mcast_set_list;
 	SET_ETHTOOL_OPS(netdev, &sxg_nic_ethtool_ops);
+	err = sxg_set_interrupt_capability(adapter);
+	if (err != STATUS_SUCCESS)
+		DBG_ERROR("Cannot enable MSI-X capability\n");
 
 	strcpy(netdev->name, "eth%d");
 	/*  strcpy(netdev->name, pci_name(pcidev)); */
@@ -1025,7 +1156,6 @@
 		  adapter, adapter->InterruptsEnabled, 0, 0);
 	/* For now, RSS is disabled with line based interrupts */
 	ASSERT(adapter->RssEnabled == FALSE);
-	ASSERT(adapter->MsiEnabled == FALSE);
 	/* Turn off interrupts by writing to the icr register. */
 	WRITE_REG(adapter->UcodeRegs[0].Icr, SXG_ICR(0, SXG_ICR_DISABLE), TRUE);
 
@@ -1053,7 +1183,6 @@
 		  adapter, adapter->InterruptsEnabled, 0, 0);
 	/* For now, RSS is disabled with line based interrupts */
 	ASSERT(adapter->RssEnabled == FALSE);
-	ASSERT(adapter->MsiEnabled == FALSE);
 	/* Turn on interrupts by writing to the icr register. */
 	WRITE_REG(adapter->UcodeRegs[0].Icr, SXG_ICR(0, SXG_ICR_ENABLE), TRUE);
 
@@ -1154,7 +1283,6 @@
 		  adapter, adapter->IsrCopy[0], 0, 0);
 	/* For now, RSS is disabled with line based interrupts */
 	ASSERT(adapter->RssEnabled == FALSE);
-	ASSERT(adapter->MsiEnabled == FALSE);
 
 	adapter->IsrCopy[0] = adapter->Isr[0];
 	adapter->Isr[0] = 0;
@@ -1197,7 +1325,6 @@
 		netif_rx_complete(napi);
 		WRITE_REG(adapter->UcodeRegs[0].Isr, 0, TRUE);
 	}
-
 	return work_done;
 }
 
@@ -1845,7 +1972,6 @@
 		adapter->intrregistered = 1;
 		adapter->IntRegistered = TRUE;
 		/* Disable RSS with line-based interrupts */
-		adapter->MsiEnabled = FALSE;
 		adapter->RssEnabled = FALSE;
 		DBG_ERROR("sxg: %s AllocAdaptRsrcs adapter[%p] dev->irq[%x]\n",
 			  __func__, adapter, adapter->netdev->irq);
@@ -1919,9 +2045,9 @@
 		}
 		DBG_ERROR("\n");
 	}
-	status = sxg_register_interrupt(adapter);
+	status = sxg_register_intr(adapter);
 	if (status != STATUS_SUCCESS) {
-		DBG_ERROR("sxg_if_init: sxg_register_interrupt FAILED %x\n",
+		DBG_ERROR("sxg_if_init: sxg_register_intr FAILED %x\n",
 			  status);
 		sxg_deregister_interrupt(adapter);
 		return (status);
@@ -2081,8 +2207,8 @@
         SXG_ENABLE_ALL_INTERRUPTS(adapter);
 
 	netif_carrier_on(dev);
-        spin_unlock_irqrestore(&sxg_global.driver_lock, sxg_global.flags);
         sxg_register_interrupt(adapter);
+	spin_unlock_irqrestore(&sxg_global.driver_lock, sxg_global.flags);
 	return (STATUS_SUCCESS);
 
 }
@@ -2099,6 +2225,7 @@
 
 	/* Deallocate Resources */
 	unregister_netdev(dev);
+	sxg_reset_interrupt_capability(adapter);
 	sxg_free_resources(adapter);
 
 	ASSERT(adapter);
@@ -2133,7 +2260,7 @@
 	unsigned long flags;
 
 	RssIds = SXG_RSS_CPU_COUNT(adapter);
-	IsrCount = adapter->MsiEnabled ? RssIds : 1;
+	IsrCount = adapter->msi_enabled ? RssIds : 1;
 
 	napi_disable(&adapter->napi);
 	spin_lock_irqsave(&sxg_global.driver_lock, sxg_global.flags);
@@ -2147,9 +2274,6 @@
 	DBG_ERROR("sxg: %s (%s) set adapter[%p] state to ADAPT_DOWN(%d)\n",
 		  __func__, dev->name, adapter, adapter->state);
 
-	DBG_ERROR("sxg: %s (%s) EXIT\n", __func__, dev->name);
-	DBG_ERROR("sxg: %s EXIT\n", __func__);
-
 	/* Disable interrupts */
 	SXG_DISABLE_ALL_INTERRUPTS(adapter);
 
@@ -2196,11 +2320,13 @@
 	memset(adapter->XmtRingZeroIndex, 0, sizeof(u32));
 	spin_unlock_irqrestore(&adapter->XmtZeroLock, flags);
 
-
 	for (i = 0; i < SXG_MAX_RSS; i++) {
 		adapter->NextEvent[i] = 0;
 	}
 	atomic_set(&adapter->pending_allocations, 0);
+	adapter->intrregistered = 0;
+	sxg_remove_isr(adapter);
+	DBG_ERROR("sxg: %s (%s) EXIT\n", __FUNCTION__, dev->name);
 	return (STATUS_SUCCESS);
 }
 
@@ -3451,9 +3577,8 @@
 void sxg_free_resources(struct adapter_t *adapter)
 {
 	u32 RssIds, IsrCount;
-	struct net_device *netdev = adapter->netdev;
 	RssIds = SXG_RSS_CPU_COUNT(adapter);
-	IsrCount = adapter->MsiEnabled ? RssIds : 1;
+	IsrCount = adapter->msi_enabled ? RssIds : 1;
 
 	if (adapter->BasicAllocations == FALSE) {
 		/*
@@ -3463,9 +3588,6 @@
 		return;
 	}
 
-	/* Free Irq */
-	free_irq(adapter->netdev->irq, netdev);
-
 	if (!(IsListEmpty(&adapter->AllRcvBlocks))) {
 		sxg_free_rcvblocks(adapter);
 	}
@@ -3877,7 +3999,7 @@
 		  adapter, 0, 0, 0);
 
 	RssIds = 1;		/*  XXXTODO  SXG_RSS_CPU_COUNT(adapter); */
-	IsrCount = adapter->MsiEnabled ? RssIds : 1;
+	IsrCount = adapter->msi_enabled ? RssIds : 1;
 
 	/*
 	 * Sanity check SXG_UCODE_REGS structure definition to