From c095f06560a0efacc7a34ea4e7f1e69c1faab0cf Mon Sep 17 00:00:00 2001
From: "Suren A. Chilingaryan" <csa@dside.dyndns.org>
Date: Mon, 11 Jul 2011 01:37:54 +0200
Subject: IRQ support in NWL DMA engine

---
 driver/common.h    |  2 +-
 driver/config.h    |  2 +-
 driver/int.c       | 71 +++++++++++++-----------------------------------------
 driver/ioctl.c     | 26 +++++++++++++++-----
 driver/pciDriver.h |  9 +++++--
 5 files changed, 46 insertions(+), 64 deletions(-)

(limited to 'driver')

diff --git a/driver/common.h b/driver/common.h
index 6036b0c..3bc27d8 100644
--- a/driver/common.h
+++ b/driver/common.h
@@ -60,7 +60,7 @@ typedef struct  {
 	struct list_head umem_list;			/* List of 'umem_list_entry's associated with this device */
 	atomic_t umem_count;				/* id for next umem entry */
 
-	
+	int msi_mode;
 } pcidriver_privdata_t;
 
 
diff --git a/driver/config.h b/driver/config.h
index a2b6946..c217afd 100644
--- a/driver/config.h
+++ b/driver/config.h
@@ -3,7 +3,7 @@
 /*******************************/
 
 /* Debug messages */
-#define DEBUG
+//#define DEBUG
 
 /* Enable/disable IRQ handling */
 #define ENABLE_IRQ
diff --git a/driver/int.c b/driver/int.c
index 285aa0e..5dbcb7f 100644
--- a/driver/int.c
+++ b/driver/int.c
@@ -162,6 +162,10 @@ int pcidriver_probe_irq(pcidriver_privdata_t *privdata)
 		return 0;
 	}
 
+	/* Enable interrupts using MSI mode */
+	if (!pci_enable_msi(privdata->pdev)) 
+		privdata->msi_mode = 1;
+	
 	/* register interrupt handler */
 	if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) {
 		mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n");
@@ -184,6 +188,11 @@ void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
 	/* Release the IRQ handler */
 	if (privdata->irq_enabled != 0)
 		free_irq(privdata->pdev->irq, privdata);
+	
+	if (privdata->msi_mode) {
+		pci_disable_msi(privdata->pdev);
+		privdata->msi_mode = 0;
+	}
 
 	pcidriver_irq_unmap_bars(privdata);
 }
@@ -206,29 +215,6 @@ void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
 	}
 }
 
-/**
- *
- * Acknowledge the interrupt by ACKing the interrupt generator.
- *
- * @returns true if the channel was acknowledged and the interrupt handler is done
- *
- */
-static bool check_acknowlegde_channel(pcidriver_privdata_t *privdata, int interrupt,
-				      int channel, volatile unsigned int *bar)
-{
-	if (!(bar[ABB_INT_STAT] & interrupt))
-		return false;
-
-	bar[ABB_INT_ENABLE] &= !interrupt;
-	if (interrupt == ABB_INT_IG)
-		bar[ABB_IG_CTRL] = ABB_IG_ACK;
-
-        /* Wake up the waiting loop in ioctl.c:ioctl_wait_interrupt() */
-	atomic_inc(&(privdata->irq_outstanding[channel]));
-	wake_up_interruptible(&(privdata->irq_queues[channel]));
-	return true;
-}
-
 /**
  *
  * Acknowledges the receival of an interrupt to the card.
@@ -241,38 +227,15 @@ static bool check_acknowlegde_channel(pcidriver_privdata_t *privdata, int interr
  */
 static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
 {
-	volatile unsigned int *bar;
+	int channel = 0;
+//	volatile unsigned int *bar;
+//	bar = privdata->bars_kmapped[0];
+//	mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]);
 
-	/* TODO: add subvendor / subsystem ids */
-	/* FIXME: guillermo: which ones? all? */
-
-	/* Test if we have to handle this interrupt */
-	return false;	// The device is not supported any more
-
-	/* Acknowledge the device */
-	/* this is for ABB / wenxue DMA engine */
-	bar = privdata->bars_kmapped[0];
-
-	mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]);
-
-	if (check_acknowlegde_channel(privdata, ABB_INT_CH0, ABB_IRQ_CH0, bar))
-		return true;
-
-	if (check_acknowlegde_channel(privdata, ABB_INT_CH1, ABB_IRQ_CH1, bar))
-		return true;
-
-	if (check_acknowlegde_channel(privdata, ABB_INT_IG, ABB_IRQ_IG, bar))
-		return true;
-
-        if (check_acknowlegde_channel(privdata, ABB_INT_CH0_TIMEOUT, ABB_IRQ_CH0, bar))
-                return true;
-
-        if (check_acknowlegde_channel(privdata, ABB_INT_CH1_TIMEOUT, ABB_IRQ_CH1, bar))
-                return true;
-
-	mod_info_dbg("err: interrupt registers. ISR: %x, IER: %x\n", bar[ ABB_INT_STAT ], bar[ ABB_INT_ENABLE ] );
-
-	return false;
+	atomic_inc(&(privdata->irq_outstanding[channel]));
+	wake_up_interruptible(&(privdata->irq_queues[channel]));
+	
+	return true;
 }
 
 /**
diff --git a/driver/ioctl.c b/driver/ioctl.c
index 64985e8..0059833 100644
--- a/driver/ioctl.c
+++ b/driver/ioctl.c
@@ -335,18 +335,23 @@ static int ioctl_umem_sync(pcidriver_privdata_t *privdata, unsigned long arg)
 static int ioctl_wait_interrupt(pcidriver_privdata_t *privdata, unsigned long arg)
 {
 #ifdef ENABLE_IRQ
+	int ret;
+	unsigned long timeout;
 	unsigned int irq_source;
-	int temp;
+	unsigned long temp = 0;
 
-	if (arg >= PCIDRIVER_INT_MAXSOURCES)
+	READ_FROM_USER(interrupt_wait_t, irq_handle);
+
+	irq_source = irq_handle.source;
+
+	if (irq_source >= PCIDRIVER_INT_MAXSOURCES)
 		return -EFAULT;						/* User tried to overrun the IRQ_SOURCES array */
 
-	irq_source = arg;
+	timeout = jiffies + (irq_handle.timeout * HZ / 1000000);
 
 	/* Thanks to Joern for the correction and tips! */
 	/* done this way to avoid wrong behaviour (endless loop) of the compiler in AMD platforms */
-	temp=1;
-	while (temp) {
+	do {
 		/* We wait here with an interruptible timeout. This will be interrupted
                  * by int.c:check_acknowledge_channel() as soon as in interrupt for
                  * the specified source arrives. */
@@ -355,8 +360,17 @@ static int ioctl_wait_interrupt(pcidriver_privdata_t *privdata, unsigned long ar
 		if (atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source])) )
 			atomic_inc( &(privdata->irq_outstanding[irq_source]) );
 		else
-			temp =0;
+			temp = 1;
+	} while ((!temp)&&(jiffies < timeout));
+	
+	if ((temp)&&(irq_handle.count)) {
+	    while (!atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source]))) temp++;
+	    atomic_inc( &(privdata->irq_outstanding[irq_source]) );
 	}
+	
+	irq_handle.count = temp;
+
+	WRITE_TO_USER(interrupt_wait_t, irq_handle);
 
 	return 0;
 #else
diff --git a/driver/pciDriver.h b/driver/pciDriver.h
index 94c98e7..2704ab5 100644
--- a/driver/pciDriver.h
+++ b/driver/pciDriver.h
@@ -64,8 +64,8 @@
 #define PCIE_ML605_DEVICE_ID 0x04a0
 
 /* Identifies the PCI-E IPE Camera */
-#define PCIE_IPECAMERA_DEVICE_ID 0x6081
-//#define PCIE_IPECAMERA_DEVICE_ID 0x6018
+//#define PCIE_IPECAMERA_DEVICE_ID 0x6081
+#define PCIE_IPECAMERA_DEVICE_ID 0x6018
 
 
 /* Possible values for ioctl commands */
@@ -133,6 +133,11 @@ typedef struct {
 	int dir;
 } kmem_sync_t;
 
+typedef struct {
+    unsigned long count;
+    unsigned long timeout;	// microseconds
+    unsigned int source;
+} interrupt_wait_t;
 
 typedef struct {
 	int size;
-- 
cgit v1.2.3