linux-arm-msm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support
@ 2016-10-21 16:37 Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 1/4] dmaengine: qcom_hidma: make pending_tre_count atomic Sinan Kaya
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Sinan Kaya @ 2016-10-21 16:37 UTC (permalink / raw)
  To: dmaengine-u79uwXL29TY76Z2rM5mHXA, timur-sgV2jX0FEOL9JmXXK+q4OQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, cov-sgV2jX0FEOL9JmXXK+q4OQ,
	vinod.koul-ral2JQCrhuEAvxtiuMwx3w, jcm-H+wXaHxf7aLQT0dZR+AlfA
  Cc: agross-sgV2jX0FEOL9JmXXK+q4OQ, arnd-r2nGTMty4D4,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sinan Kaya

The new version of the HW supports MSI interrupts instead of wired
interrupts. The MSI interrupts are especially useful for the guest machine
execution. The wired interrupts usually trap to the hypervisor and then are
relayed to the actual interrupt.

The MSI interrupts can be directly fed into the interrupt controller.

Adding a new OF compat string (qcom,hidma-1.1) and ACPI string (QCOM8062)
to distinguish newer HW from the older ones.

v7:
* rebase on top of Vinod's topic/qcom
* drop first 6 patches as they are already applied

v6:
http://www.spinics.net/lists/devicetree/msg146700.html
* rebase 4.9 kernel

v5:
http://www.spinics.net/lists/arm-kernel/msg537014.html
* dmaengine: qcom_hidma: add MSI support for interrupts
** Return MSI interrupts before calling platform_msi_domain_free_irqs.
Also cleanup MSI interrupts on the error path.
** Free the legacy IRQ only if MSI is disabled
* add dmaengine: qcom_hidma: break completion processing on error
in order to break the completions if an error is observed while servicing
completed work.
* drop dmaengine: qcom_hidma: make error and success path common
as the success path assumes that we'll get the number of notifications for
the
jobs queued. This is not true under error conditions.
* simplify dmaengine: qcom_hidma: protect common data structures. We just
need to protect the TRE processed offset. It is the variable that keeps
track
of outstanding requests.

v4:
http://www.spinics.net/lists/devicetree/msg144563.html
* device tree binding update to refer to msi.txt

v3:
* day 0 fix for when ACPI is not compiled in
* https://www.spinics.net/lists/arm-kernel/msg532179.html

v2:
https://patchwork.kernel.org/patch/9326399/
* Documentation update for DT bindings
* Rebased to slave-next
* Dropped dmaengine: qcom_hidma: eliminate processed variables. Replaced it
  with dmaengine: qcom_hidma: protect common data structures

v1:
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/444167.html
* initial implementation

Sinan Kaya (4):
  dmaengine: qcom_hidma: make pending_tre_count atomic
  dmaengine: qcom_hidma: bring out interrupt cause
  dmaengine: qcom_hidma: protect common data structures
  dmaengine: qcom_hidma: add MSI support for interrupts

 drivers/dma/qcom/hidma.c     | 143 +++++++++++++++++++++++++++++++++++++++++--
 drivers/dma/qcom/hidma.h     |   4 +-
 drivers/dma/qcom/hidma_dbg.c |   3 +-
 drivers/dma/qcom/hidma_ll.c  | 127 ++++++++++++++++++--------------------
 4 files changed, 200 insertions(+), 77 deletions(-)

-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH V7 1/4] dmaengine: qcom_hidma: make pending_tre_count atomic
  2016-10-21 16:37 [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support Sinan Kaya
@ 2016-10-21 16:37 ` Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 2/4] dmaengine: qcom_hidma: bring out interrupt cause Sinan Kaya
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Sinan Kaya @ 2016-10-21 16:37 UTC (permalink / raw)
  To: dmaengine, timur, devicetree, cov, vinod.koul, jcm
  Cc: agross, arnd, linux-arm-msm, linux-arm-kernel, Sinan Kaya,
	Dan Williams, Andy Shevchenko, linux-kernel

Getting ready for the MSI interrupts. The pending_tre_count is used
in the interrupt handler to make sure all outstanding requests are
serviced.

The driver will allocate 11 MSI interrupts. Each MSI interrupt can be
assigned to a different CPU. Then, we have a race condition for common
variables as they share the same interrupt handler with a different
cause bit and they can potentially be executed in parallel. Making this
variable atomic so that it can be updated from multiple processor
contexts.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma.h     |  2 +-
 drivers/dma/qcom/hidma_dbg.c |  3 ++-
 drivers/dma/qcom/hidma_ll.c  | 13 ++++++-------
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h
index de61764..181f7e0 100644
--- a/drivers/dma/qcom/hidma.h
+++ b/drivers/dma/qcom/hidma.h
@@ -59,7 +59,7 @@ struct hidma_lldev {
 	void __iomem *evca;		/* Event Channel address          */
 	struct hidma_tre
 		**pending_tre_list;	/* Pointers to pending TREs	  */
-	s32 pending_tre_count;		/* Number of TREs pending	  */
+	atomic_t pending_tre_count;	/* Number of TREs pending	  */
 
 	void *tre_ring;			/* TRE ring			  */
 	dma_addr_t tre_dma;		/* TRE ring to be shared with HW  */
diff --git a/drivers/dma/qcom/hidma_dbg.c b/drivers/dma/qcom/hidma_dbg.c
index 3d83b99..3bdcb80 100644
--- a/drivers/dma/qcom/hidma_dbg.c
+++ b/drivers/dma/qcom/hidma_dbg.c
@@ -74,7 +74,8 @@ static void hidma_ll_devstats(struct seq_file *s, void *llhndl)
 	seq_printf(s, "tre_ring_handle=%pap\n", &lldev->tre_dma);
 	seq_printf(s, "tre_ring_size = 0x%x\n", lldev->tre_ring_size);
 	seq_printf(s, "tre_processed_off = 0x%x\n", lldev->tre_processed_off);
-	seq_printf(s, "pending_tre_count=%d\n", lldev->pending_tre_count);
+	seq_printf(s, "pending_tre_count=%d\n",
+			atomic_read(&lldev->pending_tre_count));
 	seq_printf(s, "evca=%p\n", lldev->evca);
 	seq_printf(s, "evre_ring=%p\n", lldev->evre_ring);
 	seq_printf(s, "evre_ring_handle=%pap\n", &lldev->evre_dma);
diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index e605c90..114409e 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -218,10 +218,9 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
 	 * Keep track of pending TREs that SW is expecting to receive
 	 * from HW. We got one now. Decrement our counter.
 	 */
-	lldev->pending_tre_count--;
-	if (lldev->pending_tre_count < 0) {
+	if (atomic_dec_return(&lldev->pending_tre_count) < 0) {
 		dev_warn(lldev->dev, "tre count mismatch on completion");
-		lldev->pending_tre_count = 0;
+		atomic_set(&lldev->pending_tre_count, 0);
 	}
 
 	spin_unlock_irqrestore(&lldev->lock, flags);
@@ -328,7 +327,7 @@ void hidma_cleanup_pending_tre(struct hidma_lldev *lldev, u8 err_info,
 	u32 tre_read_off;
 
 	tre_iterator = lldev->tre_processed_off;
-	while (lldev->pending_tre_count) {
+	while (atomic_read(&lldev->pending_tre_count)) {
 		if (hidma_post_completed(lldev, tre_iterator, err_info,
 					 err_code))
 			break;
@@ -555,7 +554,7 @@ void hidma_ll_queue_request(struct hidma_lldev *lldev, u32 tre_ch)
 	tre->err_code = 0;
 	tre->err_info = 0;
 	tre->queued = 1;
-	lldev->pending_tre_count++;
+	atomic_inc(&lldev->pending_tre_count);
 	lldev->tre_write_offset = (lldev->tre_write_offset + HIDMA_TRE_SIZE)
 					% lldev->tre_ring_size;
 	spin_unlock_irqrestore(&lldev->lock, flags);
@@ -650,7 +649,7 @@ int hidma_ll_setup(struct hidma_lldev *lldev)
 	u32 val;
 	u32 nr_tres = lldev->nr_tres;
 
-	lldev->pending_tre_count = 0;
+	atomic_set(&lldev->pending_tre_count, 0);
 	lldev->tre_processed_off = 0;
 	lldev->evre_processed_off = 0;
 	lldev->tre_write_offset = 0;
@@ -831,7 +830,7 @@ int hidma_ll_uninit(struct hidma_lldev *lldev)
 	tasklet_kill(&lldev->task);
 	memset(lldev->trepool, 0, required_bytes);
 	lldev->trepool = NULL;
-	lldev->pending_tre_count = 0;
+	atomic_set(&lldev->pending_tre_count, 0);
 	lldev->tre_write_offset = 0;
 
 	rc = hidma_ll_reset(lldev);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH V7 2/4] dmaengine: qcom_hidma: bring out interrupt cause
  2016-10-21 16:37 [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 1/4] dmaengine: qcom_hidma: make pending_tre_count atomic Sinan Kaya
@ 2016-10-21 16:37 ` Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 3/4] dmaengine: qcom_hidma: protect common data structures Sinan Kaya
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Sinan Kaya @ 2016-10-21 16:37 UTC (permalink / raw)
  To: dmaengine, timur, devicetree, cov, vinod.koul, jcm
  Cc: agross, arnd, linux-arm-msm, linux-arm-kernel, Sinan Kaya,
	Dan Williams, linux-kernel

Bring out the interrupt cause to the top level so that MSI interrupts
can be hooked at a later stage.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma_ll.c | 62 ++++++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 29 deletions(-)

diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 114409e..9193f46 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -418,12 +418,24 @@ static int hidma_ll_reset(struct hidma_lldev *lldev)
  * requests traditionally to the destination, this concept does not apply
  * here for this HW.
  */
-irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
+static void hidma_ll_int_handler_internal(struct hidma_lldev *lldev, int cause)
 {
-	struct hidma_lldev *lldev = arg;
-	u32 status;
-	u32 enable;
-	u32 cause;
+	if (cause & HIDMA_ERR_INT_MASK) {
+		dev_err(lldev->dev, "error 0x%x, disabling...\n",
+				cause);
+
+		/* Clear out pending interrupts */
+		writel(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+
+		/* No further submissions. */
+		hidma_ll_disable(lldev);
+
+		/* Driver completes the txn and intimates the client.*/
+		hidma_cleanup_pending_tre(lldev, 0xFF,
+					  HIDMA_EVRE_STATUS_ERROR);
+
+		return;
+	}
 
 	/*
 	 * Fine tuned for this HW...
@@ -432,35 +444,28 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
 	 * read and write accessors are used for performance reasons due to
 	 * interrupt delivery guarantees. Do not copy this code blindly and
 	 * expect that to work.
+	 *
+	 * Try to consume as many EVREs as possible.
 	 */
+	hidma_handle_tre_completion(lldev);
+
+	/* We consumed TREs or there are pending TREs or EVREs. */
+	writel_relaxed(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+}
+
+irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
+{
+	struct hidma_lldev *lldev = arg;
+	u32 status;
+	u32 enable;
+	u32 cause;
+
 	status = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_STAT_REG);
 	enable = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_EN_REG);
 	cause = status & enable;
 
 	while (cause) {
-		if (cause & HIDMA_ERR_INT_MASK) {
-			dev_err(lldev->dev, "error 0x%x, disabling...\n",
-					cause);
-
-			/* Clear out pending interrupts */
-			writel(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
-
-			/* No further submissions. */
-			hidma_ll_disable(lldev);
-
-			/* Driver completes the txn and intimates the client.*/
-			hidma_cleanup_pending_tre(lldev, 0xFF,
-						  HIDMA_EVRE_STATUS_ERROR);
-			goto out;
-		}
-
-		/*
-		 * Try to consume as many EVREs as possible.
-		 */
-		hidma_handle_tre_completion(lldev);
-
-		/* We consumed TREs or there are pending TREs or EVREs. */
-		writel_relaxed(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+		hidma_ll_int_handler_internal(lldev, cause);
 
 		/*
 		 * Another interrupt might have arrived while we are
@@ -471,7 +476,6 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
 		cause = status & enable;
 	}
 
-out:
 	return IRQ_HANDLED;
 }
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH V7 3/4] dmaengine: qcom_hidma: protect common data structures
  2016-10-21 16:37 [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 1/4] dmaengine: qcom_hidma: make pending_tre_count atomic Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 2/4] dmaengine: qcom_hidma: bring out interrupt cause Sinan Kaya
@ 2016-10-21 16:37 ` Sinan Kaya
  2016-10-21 16:37 ` [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts Sinan Kaya
       [not found] ` <1477067879-23750-1-git-send-email-okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  4 siblings, 0 replies; 10+ messages in thread
From: Sinan Kaya @ 2016-10-21 16:37 UTC (permalink / raw)
  To: dmaengine, timur, devicetree, cov, vinod.koul, jcm
  Cc: agross, arnd, linux-arm-msm, linux-arm-kernel, Sinan Kaya,
	Dan Williams, linux-kernel

When MSI interrupts are supported, error and the transfer interrupt can
come from multiple processor contexts.

Each error interrupt is an MSI interrupt. If the channel is disabled by
the first error interrupt, the remaining error interrupts will gracefully
return in the interrupt handler.

If an error is observed while servicing the completions in success case,
the posting of the completions will be aborted as soon as channel disabled
state is observed. The error interrupt handler will take it from there and
finish the remaining completions. We don't want to create multiple success
and error messages to be delivered to the client in mixed order.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma_ll.c | 44 +++++++++++---------------------------------
 1 file changed, 11 insertions(+), 33 deletions(-)

diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 9193f46..7fe43af 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -198,13 +198,16 @@ static void hidma_ll_tre_complete(unsigned long arg)
 	}
 }
 
-static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
-				u8 err_info, u8 err_code)
+static int hidma_post_completed(struct hidma_lldev *lldev, u8 err_info,
+				u8 err_code)
 {
 	struct hidma_tre *tre;
 	unsigned long flags;
+	u32 tre_iterator;
 
 	spin_lock_irqsave(&lldev->lock, flags);
+
+	tre_iterator = lldev->tre_processed_off;
 	tre = lldev->pending_tre_list[tre_iterator / HIDMA_TRE_SIZE];
 	if (!tre) {
 		spin_unlock_irqrestore(&lldev->lock, flags);
@@ -223,6 +226,9 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
 		atomic_set(&lldev->pending_tre_count, 0);
 	}
 
+	HIDMA_INCREMENT_ITERATOR(tre_iterator, HIDMA_TRE_SIZE,
+				 lldev->tre_ring_size);
+	lldev->tre_processed_off = tre_iterator;
 	spin_unlock_irqrestore(&lldev->lock, flags);
 
 	tre->err_info = err_info;
@@ -244,13 +250,11 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
 static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
 {
 	u32 evre_ring_size = lldev->evre_ring_size;
-	u32 tre_ring_size = lldev->tre_ring_size;
 	u32 err_info, err_code, evre_write_off;
-	u32 tre_iterator, evre_iterator;
+	u32 evre_iterator;
 	u32 num_completed = 0;
 
 	evre_write_off = readl_relaxed(lldev->evca + HIDMA_EVCA_WRITE_PTR_REG);
-	tre_iterator = lldev->tre_processed_off;
 	evre_iterator = lldev->evre_processed_off;
 
 	if ((evre_write_off > evre_ring_size) ||
@@ -273,12 +277,9 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
 		err_code =
 		    (cfg >> HIDMA_EVRE_CODE_BIT_POS) & HIDMA_EVRE_CODE_MASK;
 
-		if (hidma_post_completed(lldev, tre_iterator, err_info,
-					 err_code))
+		if (hidma_post_completed(lldev, err_info, err_code))
 			break;
 
-		HIDMA_INCREMENT_ITERATOR(tre_iterator, HIDMA_TRE_SIZE,
-					 tre_ring_size);
 		HIDMA_INCREMENT_ITERATOR(evre_iterator, HIDMA_EVRE_SIZE,
 					 evre_ring_size);
 
@@ -302,16 +303,10 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
 	if (num_completed) {
 		u32 evre_read_off = (lldev->evre_processed_off +
 				     HIDMA_EVRE_SIZE * num_completed);
-		u32 tre_read_off = (lldev->tre_processed_off +
-				    HIDMA_TRE_SIZE * num_completed);
-
 		evre_read_off = evre_read_off % evre_ring_size;
-		tre_read_off = tre_read_off % tre_ring_size;
-
 		writel(evre_read_off, lldev->evca + HIDMA_EVCA_DOORBELL_REG);
 
 		/* record the last processed tre offset */
-		lldev->tre_processed_off = tre_read_off;
 		lldev->evre_processed_off = evre_read_off;
 	}
 
@@ -321,27 +316,10 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
 void hidma_cleanup_pending_tre(struct hidma_lldev *lldev, u8 err_info,
 			       u8 err_code)
 {
-	u32 tre_iterator;
-	u32 tre_ring_size = lldev->tre_ring_size;
-	int num_completed = 0;
-	u32 tre_read_off;
-
-	tre_iterator = lldev->tre_processed_off;
 	while (atomic_read(&lldev->pending_tre_count)) {
-		if (hidma_post_completed(lldev, tre_iterator, err_info,
-					 err_code))
+		if (hidma_post_completed(lldev, err_info, err_code))
 			break;
-		HIDMA_INCREMENT_ITERATOR(tre_iterator, HIDMA_TRE_SIZE,
-					 tre_ring_size);
-		num_completed++;
 	}
-	tre_read_off = (lldev->tre_processed_off +
-			HIDMA_TRE_SIZE * num_completed);
-
-	tre_read_off = tre_read_off % tre_ring_size;
-
-	/* record the last processed tre offset */
-	lldev->tre_processed_off = tre_read_off;
 }
 
 static int hidma_ll_reset(struct hidma_lldev *lldev)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts
  2016-10-21 16:37 [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support Sinan Kaya
                   ` (2 preceding siblings ...)
  2016-10-21 16:37 ` [PATCH V7 3/4] dmaengine: qcom_hidma: protect common data structures Sinan Kaya
@ 2016-10-21 16:37 ` Sinan Kaya
  2016-10-21 19:11   ` Andy Shevchenko
       [not found] ` <1477067879-23750-1-git-send-email-okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  4 siblings, 1 reply; 10+ messages in thread
From: Sinan Kaya @ 2016-10-21 16:37 UTC (permalink / raw)
  To: dmaengine, timur, devicetree, cov, vinod.koul, jcm
  Cc: agross, arnd, linux-arm-msm, linux-arm-kernel, Sinan Kaya,
	Dan Williams, Andy Shevchenko, Dave Jiang, Lars-Peter Clausen,
	linux-kernel

The interrupts can now be delivered as platform MSI interrupts on newer
platforms. The code looks for a new OF and ACPI strings in order to enable
the functionality.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma.c    | 143 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/dma/qcom/hidma.h    |   2 +
 drivers/dma/qcom/hidma_ll.c |   8 +++
 3 files changed, 147 insertions(+), 6 deletions(-)

diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c
index e244e10..d5e7991 100644
--- a/drivers/dma/qcom/hidma.c
+++ b/drivers/dma/qcom/hidma.c
@@ -56,6 +56,7 @@
 #include <linux/irq.h>
 #include <linux/atomic.h>
 #include <linux/pm_runtime.h>
+#include <linux/msi.h>
 
 #include "../dmaengine.h"
 #include "hidma.h"
@@ -70,6 +71,7 @@
 #define HIDMA_ERR_INFO_SW			0xFF
 #define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE	0x0
 #define HIDMA_NR_DEFAULT_DESC			10
+#define HIDMA_MSI_INTS				11
 
 static inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev)
 {
@@ -553,6 +555,15 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
 	return hidma_ll_inthandler(chirq, lldev);
 }
 
+static irqreturn_t hidma_chirq_handler_msi(int chirq, void *arg)
+{
+	struct hidma_lldev **lldevp = arg;
+	struct hidma_dev *dmadev = to_hidma_dev_from_lldev(lldevp);
+
+	return hidma_ll_inthandler_msi(chirq, *lldevp,
+				       1 << (chirq - dmadev->msi_virqbase));
+}
+
 static ssize_t hidma_show_values(struct device *dev,
 				 struct device_attribute *attr, char *buf)
 {
@@ -590,6 +601,104 @@ static int hidma_create_sysfs_entry(struct hidma_dev *dev, char *name,
 	return device_create_file(dev->ddev.dev, attrs);
 }
 
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+static void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	struct device *dev = msi_desc_to_dev(desc);
+	struct hidma_dev *dmadev = dev_get_drvdata(dev);
+
+	if (!desc->platform.msi_index) {
+		writel(msg->address_lo, dmadev->dev_evca + 0x118);
+		writel(msg->address_hi, dmadev->dev_evca + 0x11C);
+		writel(msg->data, dmadev->dev_evca + 0x120);
+	}
+}
+#endif
+
+static void hidma_free_msis(struct hidma_dev *dmadev)
+{
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+	struct device *dev = dmadev->ddev.dev;
+	struct msi_desc *desc;
+
+	/* free allocated MSI interrupts above */
+	for_each_msi_entry(desc, dev)
+		devm_free_irq(dev, desc->irq, &dmadev->lldev);
+
+	platform_msi_domain_free_irqs(dev);
+#endif
+}
+
+static int hidma_request_msi(struct hidma_dev *dmadev,
+			     struct platform_device *pdev)
+{
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+	int rc;
+	struct msi_desc *desc;
+	struct msi_desc *failed_desc = NULL;
+
+	rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
+					    hidma_write_msi_msg);
+	if (rc)
+		return rc;
+
+	for_each_msi_entry(desc, &pdev->dev) {
+		if (!desc->platform.msi_index)
+			dmadev->msi_virqbase = desc->irq;
+
+		rc = devm_request_irq(&pdev->dev, desc->irq,
+				       hidma_chirq_handler_msi,
+				       0, "qcom-hidma-msi",
+				       &dmadev->lldev);
+		if (rc) {
+			failed_desc = desc;
+			break;
+		}
+	}
+
+	if (rc) {
+		/* free allocated MSI interrupts above */
+		for_each_msi_entry(desc, &pdev->dev) {
+			if (desc == failed_desc)
+				break;
+			devm_free_irq(&pdev->dev, desc->irq,
+				      &dmadev->lldev);
+		}
+	} else {
+		/* Add callback to free MSIs on teardown */
+		hidma_ll_setup_irq(dmadev->lldev, true);
+
+	}
+	if (rc)
+		dev_warn(&pdev->dev,
+			 "failed to request MSI irq, falling back to wired IRQ\n");
+	return rc;
+#else
+	return -EINVAL;
+#endif
+}
+
+static bool hidma_msi_capable(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	const char *of_compat;
+	int ret = -EINVAL;
+
+	if (!adev || acpi_disabled) {
+		ret = device_property_read_string(dev, "compatible",
+						  &of_compat);
+		if (ret)
+			return false;
+
+		ret = strcmp(of_compat, "qcom,hidma-1.1");
+	} else {
+#ifdef CONFIG_ACPI
+		ret = strcmp(acpi_device_hid(adev), "QCOM8062");
+#endif
+	}
+	return ret == 0;
+}
+
 static int hidma_probe(struct platform_device *pdev)
 {
 	struct hidma_dev *dmadev;
@@ -599,6 +708,7 @@ static int hidma_probe(struct platform_device *pdev)
 	void __iomem *evca;
 	void __iomem *trca;
 	int rc;
+	bool msi;
 
 	pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
 	pm_runtime_use_autosuspend(&pdev->dev);
@@ -660,6 +770,12 @@ static int hidma_probe(struct platform_device *pdev)
 	dmadev->ddev.device_terminate_all = hidma_terminate_all;
 	dmadev->ddev.copy_align = 8;
 
+	/*
+	 * Determine the MSI capability of the platform. Old HW doesn't
+	 * support MSI.
+	 */
+	msi = hidma_msi_capable(&pdev->dev);
+
 	device_property_read_u32(&pdev->dev, "desc-count",
 				 &dmadev->nr_descriptors);
 
@@ -688,10 +804,17 @@ static int hidma_probe(struct platform_device *pdev)
 		goto dmafree;
 	}
 
-	rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler, 0,
-			      "qcom-hidma", dmadev->lldev);
-	if (rc)
-		goto uninit;
+	platform_set_drvdata(pdev, dmadev);
+	if (msi)
+		rc = hidma_request_msi(dmadev, pdev);
+
+	if (!msi || rc) {
+		hidma_ll_setup_irq(dmadev->lldev, false);
+		rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler,
+				      0, "qcom-hidma", dmadev->lldev);
+		if (rc)
+			goto uninit;
+	}
 
 	INIT_LIST_HEAD(&dmadev->ddev.channels);
 	rc = hidma_chan_init(dmadev, 0);
@@ -707,12 +830,14 @@ static int hidma_probe(struct platform_device *pdev)
 	hidma_debug_init(dmadev);
 	hidma_create_sysfs_entry(dmadev, "chid", S_IRUGO);
 	dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n");
-	platform_set_drvdata(pdev, dmadev);
 	pm_runtime_mark_last_busy(dmadev->ddev.dev);
 	pm_runtime_put_autosuspend(dmadev->ddev.dev);
 	return 0;
 
 uninit:
+	if (msi)
+		hidma_free_msis(dmadev);
+
 	hidma_debug_uninit(dmadev);
 	hidma_ll_uninit(dmadev->lldev);
 dmafree:
@@ -730,7 +855,11 @@ static int hidma_remove(struct platform_device *pdev)
 
 	pm_runtime_get_sync(dmadev->ddev.dev);
 	dma_async_device_unregister(&dmadev->ddev);
-	devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev);
+	if (!dmadev->lldev->msi_support)
+		devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev);
+	else
+		hidma_free_msis(dmadev);
+
 	tasklet_kill(&dmadev->task);
 	hidma_debug_uninit(dmadev);
 	hidma_ll_uninit(dmadev->lldev);
@@ -746,12 +875,14 @@ static int hidma_remove(struct platform_device *pdev)
 #if IS_ENABLED(CONFIG_ACPI)
 static const struct acpi_device_id hidma_acpi_ids[] = {
 	{"QCOM8061"},
+	{"QCOM8062"},
 	{},
 };
 #endif
 
 static const struct of_device_id hidma_match[] = {
 	{.compatible = "qcom,hidma-1.0",},
+	{.compatible = "qcom,hidma-1.1",},
 	{},
 };
 MODULE_DEVICE_TABLE(of, hidma_match);
diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h
index 181f7e0..05f8ba4 100644
--- a/drivers/dma/qcom/hidma.h
+++ b/drivers/dma/qcom/hidma.h
@@ -115,6 +115,7 @@ struct hidma_dev {
 	int				irq;
 	int				chidx;
 	u32				nr_descriptors;
+	int				msi_virqbase;
 
 	struct hidma_lldev		*lldev;
 	void				__iomem *dev_trca;
@@ -153,6 +154,7 @@ struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels,
 			u8 chidx);
 int hidma_ll_uninit(struct hidma_lldev *llhndl);
 irqreturn_t hidma_ll_inthandler(int irq, void *arg);
+irqreturn_t hidma_ll_inthandler_msi(int irq, void *arg, int cause);
 void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info,
 				u8 err_code);
 int hidma_debug_init(struct hidma_dev *dmadev);
diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 7fe43af..6645bdf 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -457,6 +457,14 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
 	return IRQ_HANDLED;
 }
 
+irqreturn_t hidma_ll_inthandler_msi(int chirq, void *arg, int cause)
+{
+	struct hidma_lldev *lldev = arg;
+
+	hidma_ll_int_handler_internal(lldev, cause);
+	return IRQ_HANDLED;
+}
+
 int hidma_ll_enable(struct hidma_lldev *lldev)
 {
 	u32 val;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts
  2016-10-21 16:37 ` [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts Sinan Kaya
@ 2016-10-21 19:11   ` Andy Shevchenko
  2016-10-24  2:55     ` Sinan Kaya
  0 siblings, 1 reply; 10+ messages in thread
From: Andy Shevchenko @ 2016-10-21 19:11 UTC (permalink / raw)
  To: Sinan Kaya
  Cc: dmaengine, Timur Tabi, devicetree, Christopher Covington,
	Vinod Koul, Jon Masters, Andy Gross, Arnd Bergmann,
	linux-arm-msm, linux-arm Mailing List, Dan Williams, Dave Jiang,
	Lars-Peter Clausen, linux-kernel

On Fri, Oct 21, 2016 at 7:37 PM, Sinan Kaya <okaya@codeaurora.org> wrote:
> The interrupts can now be delivered as platform MSI interrupts on newer
> platforms. The code looks for a new OF and ACPI strings in order to enable
> the functionality.

> +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
> +static void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
> +{
> +       struct device *dev = msi_desc_to_dev(desc);
> +       struct hidma_dev *dmadev = dev_get_drvdata(dev);
> +
> +       if (!desc->platform.msi_index) {
> +               writel(msg->address_lo, dmadev->dev_evca + 0x118);
> +               writel(msg->address_hi, dmadev->dev_evca + 0x11C);
> +               writel(msg->data, dmadev->dev_evca + 0x120);
> +       }
> +}
> +#endif
> +
> +static void hidma_free_msis(struct hidma_dev *dmadev)
> +{
> +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN

Perhaps one #ifdef and two definitions of functions?

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts
  2016-10-21 19:11   ` Andy Shevchenko
@ 2016-10-24  2:55     ` Sinan Kaya
       [not found]       ` <36163853-ac4a-e146-0c1b-eaf42e8b234d-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Sinan Kaya @ 2016-10-24  2:55 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: dmaengine, Timur Tabi, devicetree, Christopher Covington,
	Vinod Koul, Jon Masters, Andy Gross, Arnd Bergmann,
	linux-arm-msm, linux-arm Mailing List, Dan Williams, Dave Jiang,
	Lars-Peter Clausen, linux-kernel

On 10/21/2016 12:11 PM, Andy Shevchenko wrote:
>> +static void hidma_free_msis(struct hidma_dev *dmadev)
>> > +{
>> > +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
> Perhaps one #ifdef and two definitions of functions?

I don't think it will make a difference. I'll have to move
#ifdef around the caller of hidma_free_msis instead which
I think is uglier.

The hidma_write_msi_msg function gets called only when 
CONFIG_GENERIC_MSI_IRQ_DOMAIN is defined. If I don't put 
this around the function definition, I get unused function
warning from the compiler. This is the reason why preprocessor
definition is outside of the function definition.


-- 
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts
       [not found]       ` <36163853-ac4a-e146-0c1b-eaf42e8b234d-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-10-24  7:30         ` Andy Shevchenko
       [not found]           ` <CAHp75VettN=-S+iqKQbqixRe2BM=AiS26VGM+8yLophbkc+Z8g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Andy Shevchenko @ 2016-10-24  7:30 UTC (permalink / raw)
  To: Sinan Kaya
  Cc: dmaengine, Timur Tabi, devicetree, Christopher Covington,
	Vinod Koul, Jon Masters, Andy Gross, Arnd Bergmann,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, linux-arm Mailing List,
	Dan Williams, Dave Jiang, Lars-Peter Clausen,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Mon, Oct 24, 2016 at 5:55 AM, Sinan Kaya <okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
> On 10/21/2016 12:11 PM, Andy Shevchenko wrote:
>>> +static void hidma_free_msis(struct hidma_dev *dmadev)
>>> > +{
>>> > +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
>> Perhaps one #ifdef and two definitions of functions?
>
> I don't think it will make a difference. I'll have to move
> #ifdef around the caller of hidma_free_msis instead which
> I think is uglier.
>
> The hidma_write_msi_msg function gets called only when
> CONFIG_GENERIC_MSI_IRQ_DOMAIN is defined. If I don't put
> this around the function definition, I get unused function
> warning from the compiler. This is the reason why preprocessor
> definition is outside of the function definition.

I am talking about something like below:

#ifdef UGLY_DEFINE
myfunc_a()
{
}

myfunc_b()
{
}
#else
static inline myfunc_a() {}
static inline myfunc_b() {}
#endif


There is another way as well, namely use of IS_ENABLED(), IS_BUILTIN()
macros (I don't remember how exactly second one is spelt).

-- 
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts
       [not found]           ` <CAHp75VettN=-S+iqKQbqixRe2BM=AiS26VGM+8yLophbkc+Z8g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2016-10-24 11:07             ` okaya-sgV2jX0FEOL9JmXXK+q4OQ
  0 siblings, 0 replies; 10+ messages in thread
From: okaya-sgV2jX0FEOL9JmXXK+q4OQ @ 2016-10-24 11:07 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: dmaengine, Timur Tabi, devicetree, Christopher Covington,
	Vinod Koul, Jon Masters, Andy Gross, Arnd Bergmann,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, linux-arm Mailing List,
	Dan Williams, Dave Jiang, Lars-Peter Clausen,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dmaengine-owner-u79uwXL29TY76Z2rM5mHXA

On 2016-10-24 03:30, Andy Shevchenko wrote:
> On Mon, Oct 24, 2016 at 5:55 AM, Sinan Kaya <okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> 
> wrote:
>> On 10/21/2016 12:11 PM, Andy Shevchenko wrote:
>>>> +static void hidma_free_msis(struct hidma_dev *dmadev)
>>>> > +{
>>>> > +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
>>> Perhaps one #ifdef and two definitions of functions?
>> 
>> I don't think it will make a difference. I'll have to move
>> #ifdef around the caller of hidma_free_msis instead which
>> I think is uglier.
>> 
>> The hidma_write_msi_msg function gets called only when
>> CONFIG_GENERIC_MSI_IRQ_DOMAIN is defined. If I don't put
>> this around the function definition, I get unused function
>> warning from the compiler. This is the reason why preprocessor
>> definition is outside of the function definition.
> 
> I am talking about something like below:
> 
> #ifdef UGLY_DEFINE
> myfunc_a()
> {
> }
> 
> myfunc_b()
> {
> }
> #else
> static inline myfunc_a() {}
> static inline myfunc_b() {}
> #endif
> 
> 
> There is another way as well, namely use of IS_ENABLED(), IS_BUILTIN()
> macros (I don't remember how exactly second one is spelt).


This was my initial approach. I was asked to remove the stub functions. 
So, I did it.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support
       [not found] ` <1477067879-23750-1-git-send-email-okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-11-03 13:17   ` Koul, Vinod
  0 siblings, 0 replies; 10+ messages in thread
From: Koul, Vinod @ 2016-11-03 13:17 UTC (permalink / raw)
  To: dmaengine-u79uwXL29TY76Z2rM5mHXA, timur-sgV2jX0FEOL9JmXXK+q4OQ,
	okaya-sgV2jX0FEOL9JmXXK+q4OQ, cov-sgV2jX0FEOL9JmXXK+q4OQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, jcm-H+wXaHxf7aLQT0dZR+AlfA
  Cc: agross-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

On Fri, 2016-10-21 at 12:37 -0400, Sinan Kaya wrote:
> The new version of the HW supports MSI interrupts instead of wired
> interrupts. The MSI interrupts are especially useful for the guest
> machine
> execution. The wired interrupts usually trap to the hypervisor and
> then are
> relayed to the actual interrupt.
> 
> The MSI interrupts can be directly fed into the interrupt controller.
> 
> Adding a new OF compat string (qcom,hidma-1.1) and ACPI string
> (QCOM8062)
> to distinguish newer HW from the older ones.

Applied, thanks

-- 
~Vinod
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-11-03 13:17 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-21 16:37 [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support Sinan Kaya
2016-10-21 16:37 ` [PATCH V7 1/4] dmaengine: qcom_hidma: make pending_tre_count atomic Sinan Kaya
2016-10-21 16:37 ` [PATCH V7 2/4] dmaengine: qcom_hidma: bring out interrupt cause Sinan Kaya
2016-10-21 16:37 ` [PATCH V7 3/4] dmaengine: qcom_hidma: protect common data structures Sinan Kaya
2016-10-21 16:37 ` [PATCH V7 4/4] dmaengine: qcom_hidma: add MSI support for interrupts Sinan Kaya
2016-10-21 19:11   ` Andy Shevchenko
2016-10-24  2:55     ` Sinan Kaya
     [not found]       ` <36163853-ac4a-e146-0c1b-eaf42e8b234d-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-10-24  7:30         ` Andy Shevchenko
     [not found]           ` <CAHp75VettN=-S+iqKQbqixRe2BM=AiS26VGM+8yLophbkc+Z8g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-10-24 11:07             ` okaya-sgV2jX0FEOL9JmXXK+q4OQ
     [not found] ` <1477067879-23750-1-git-send-email-okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-11-03 13:17   ` [PATCH V7 0/4] dmaengine: qcom_hidma: add MSI interrupt support Koul, Vinod

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).