thunderbolt: Add polling mode for rings
authorMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 2 Oct 2017 10:38:41 +0000 (13:38 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 2 Oct 2017 18:24:41 +0000 (11:24 -0700)
In order to support things like networking over Thunderbolt cable, there
needs to be a way to switch the ring to a mode where it can be polled
with the interrupt masked. We implement such mode so that the caller can
allocate a ring by passing pointer to a function that is then called
when an interrupt is triggered. Completed frames can be fetched using
tb_ring_poll() and the interrupt can be re-enabled when the caller is
finished with polling by using tb_ring_poll_complete().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Michael Jamet <michael.jamet@intel.com>
Reviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/thunderbolt/ctl.c
drivers/thunderbolt/nhi.c
include/linux/thunderbolt.h

index dd10789e1dbb359e4e4507a3d9c9bdaa9bac6c8f..d079dbba2c03694c01ffb9ab7a9526eac92da6c5 100644 (file)
@@ -619,7 +619,7 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
                goto err;
 
        ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff,
-                               0xffff);
+                               0xffff, NULL, NULL);
        if (!ctl->rx)
                goto err;
 
index 0b3c0640048b8953068d08edddf1aa8d73ca85cc..af0a80ddf5948c232950b5e72b5652327cde4b4a 100644 (file)
@@ -252,7 +252,8 @@ invoke_callback:
                 * Do not hold on to it.
                 */
                list_del_init(&frame->list);
-               frame->callback(ring, frame, canceled);
+               if (frame->callback)
+                       frame->callback(ring, frame, canceled);
        }
 }
 
@@ -273,11 +274,106 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
 }
 EXPORT_SYMBOL_GPL(__tb_ring_enqueue);
 
+/**
+ * tb_ring_poll() - Poll one completed frame from the ring
+ * @ring: Ring to poll
+ *
+ * This function can be called when @start_poll callback of the @ring
+ * has been called. It will read one completed frame from the ring and
+ * return it to the caller. Returns %NULL if there is no more completed
+ * frames.
+ */
+struct ring_frame *tb_ring_poll(struct tb_ring *ring)
+{
+       struct ring_frame *frame = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ring->lock, flags);
+       if (!ring->running)
+               goto unlock;
+       if (ring_empty(ring))
+               goto unlock;
+
+       if (ring->descriptors[ring->tail].flags & RING_DESC_COMPLETED) {
+               frame = list_first_entry(&ring->in_flight, typeof(*frame),
+                                        list);
+               list_del_init(&frame->list);
+
+               if (!ring->is_tx) {
+                       frame->size = ring->descriptors[ring->tail].length;
+                       frame->eof = ring->descriptors[ring->tail].eof;
+                       frame->sof = ring->descriptors[ring->tail].sof;
+                       frame->flags = ring->descriptors[ring->tail].flags;
+               }
+
+               ring->tail = (ring->tail + 1) % ring->size;
+       }
+
+unlock:
+       spin_unlock_irqrestore(&ring->lock, flags);
+       return frame;
+}
+EXPORT_SYMBOL_GPL(tb_ring_poll);
+
+static void __ring_interrupt_mask(struct tb_ring *ring, bool mask)
+{
+       int idx = ring_interrupt_index(ring);
+       int reg = REG_RING_INTERRUPT_BASE + idx / 32 * 4;
+       int bit = idx % 32;
+       u32 val;
+
+       val = ioread32(ring->nhi->iobase + reg);
+       if (mask)
+               val &= ~BIT(bit);
+       else
+               val |= BIT(bit);
+       iowrite32(val, ring->nhi->iobase + reg);
+}
+
+/* Both @nhi->lock and @ring->lock should be held */
+static void __ring_interrupt(struct tb_ring *ring)
+{
+       if (!ring->running)
+               return;
+
+       if (ring->start_poll) {
+               __ring_interrupt_mask(ring, false);
+               ring->start_poll(ring->poll_data);
+       } else {
+               schedule_work(&ring->work);
+       }
+}
+
+/**
+ * tb_ring_poll_complete() - Re-start interrupt for the ring
+ * @ring: Ring to re-start the interrupt
+ *
+ * This will re-start (unmask) the ring interrupt once the user is done
+ * with polling.
+ */
+void tb_ring_poll_complete(struct tb_ring *ring)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ring->nhi->lock, flags);
+       spin_lock(&ring->lock);
+       if (ring->start_poll)
+               __ring_interrupt_mask(ring, false);
+       spin_unlock(&ring->lock);
+       spin_unlock_irqrestore(&ring->nhi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
+
 static irqreturn_t ring_msix(int irq, void *data)
 {
        struct tb_ring *ring = data;
 
-       schedule_work(&ring->work);
+       spin_lock(&ring->nhi->lock);
+       spin_lock(&ring->lock);
+       __ring_interrupt(ring);
+       spin_unlock(&ring->lock);
+       spin_unlock(&ring->nhi->lock);
+
        return IRQ_HANDLED;
 }
 
@@ -317,7 +413,9 @@ static void ring_release_msix(struct tb_ring *ring)
 
 static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
                                     bool transmit, unsigned int flags,
-                                    u16 sof_mask, u16 eof_mask)
+                                    u16 sof_mask, u16 eof_mask,
+                                    void (*start_poll)(void *),
+                                    void *poll_data)
 {
        struct tb_ring *ring = NULL;
        dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n",
@@ -346,6 +444,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
        ring->head = 0;
        ring->tail = 0;
        ring->running = false;
+       ring->start_poll = start_poll;
+       ring->poll_data = poll_data;
 
        ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev,
                        size * sizeof(*ring->descriptors),
@@ -399,7 +499,7 @@ err_free_ring:
 struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
                                 unsigned int flags)
 {
-       return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0);
+       return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL);
 }
 EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
 
@@ -411,11 +511,17 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
  * @flags: Flags for the ring
  * @sof_mask: Mask of PDF values that start a frame
  * @eof_mask: Mask of PDF values that end a frame
+ * @start_poll: If not %NULL the ring will call this function when an
+ *             interrupt is triggered and masked, instead of callback
+ *             in each Rx frame.
+ * @poll_data: Optional data passed to @start_poll
  */
 struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
-                                unsigned int flags, u16 sof_mask, u16 eof_mask)
+                                unsigned int flags, u16 sof_mask, u16 eof_mask,
+                                void (*start_poll)(void *), void *poll_data)
 {
-       return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask);
+       return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask,
+                            start_poll, poll_data);
 }
 EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
 
@@ -556,6 +662,7 @@ void tb_ring_free(struct tb_ring *ring)
                dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n",
                         RING_TYPE(ring), ring->hop);
        }
+       spin_unlock_irq(&ring->nhi->lock);
 
        ring_release_msix(ring);
 
@@ -572,7 +679,6 @@ void tb_ring_free(struct tb_ring *ring)
                 RING_TYPE(ring),
                 ring->hop);
 
-       spin_unlock_irq(&ring->nhi->lock);
        /**
         * ring->work can no longer be scheduled (it is scheduled only
         * by nhi_interrupt_work, ring_stop and ring_msix). Wait for it
@@ -682,8 +788,10 @@ static void nhi_interrupt_work(struct work_struct *work)
                                 hop);
                        continue;
                }
-               /* we do not check ring->running, this is done in ring->work */
-               schedule_work(&ring->work);
+
+               spin_lock(&ring->lock);
+               __ring_interrupt(ring);
+               spin_unlock(&ring->lock);
        }
        spin_unlock_irq(&nhi->lock);
 }
index d59e3f9a35c47a8bc4ab4983ffc3936afffb6b9e..36925e3aec7c5c4ab72b4398e0e7b2ee2262f531 100644 (file)
@@ -446,6 +446,9 @@ struct tb_nhi {
  * @flags: Ring specific flags
  * @sof_mask: Bit mask used to detect start of frame PDF
  * @eof_mask: Bit mask used to detect end of frame PDF
+ * @start_poll: Called when ring interrupt is triggered to start
+ *             polling. Passing %NULL keeps the ring in interrupt mode.
+ * @poll_data: Data passed to @start_poll
  */
 struct tb_ring {
        spinlock_t lock;
@@ -466,6 +469,8 @@ struct tb_ring {
        unsigned int flags;
        u16 sof_mask;
        u16 eof_mask;
+       void (*start_poll)(void *data);
+       void *poll_data;
 };
 
 /* Leave ring interrupt enabled on suspend */
@@ -499,7 +504,7 @@ enum ring_desc_flags {
 /**
  * struct ring_frame - For use with ring_rx/ring_tx
  * @buffer_phy: DMA mapped address of the frame
- * @callback: Callback called when the frame is finished
+ * @callback: Callback called when the frame is finished (optional)
  * @list: Frame is linked to a queue using this
  * @size: Size of the frame in bytes (%0 means %4096)
  * @flags: Flags for the frame (see &enum ring_desc_flags)
@@ -522,8 +527,8 @@ struct ring_frame {
 struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
                                 unsigned int flags);
 struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
-                                unsigned int flags, u16 sof_mask,
-                                u16 eof_mask);
+                                unsigned int flags, u16 sof_mask, u16 eof_mask,
+                                void (*start_poll)(void *), void *poll_data);
 void tb_ring_start(struct tb_ring *ring);
 void tb_ring_stop(struct tb_ring *ring);
 void tb_ring_free(struct tb_ring *ring);
@@ -535,8 +540,8 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame);
  * @ring: Ring to enqueue the frame
  * @frame: Frame to enqueue
  *
- * @frame->buffer, @frame->buffer_phy and @frame->callback have to be set. The
- * buffer must contain at least %TB_FRAME_SIZE bytes.
+ * @frame->buffer, @frame->buffer_phy have to be set. The buffer must
+ * contain at least %TB_FRAME_SIZE bytes.
  *
  * @frame->callback will be invoked with @frame->size, @frame->flags,
  * @frame->eof, @frame->sof set once the frame has been received.
@@ -557,8 +562,8 @@ static inline int tb_ring_rx(struct tb_ring *ring, struct ring_frame *frame)
  * @ring: Ring the enqueue the frame
  * @frame: Frame to enqueue
  *
- * @frame->buffer, @frame->buffer_phy, @frame->callback, @frame->size,
- * @frame->eof and @frame->sof have to be set.
+ * @frame->buffer, @frame->buffer_phy, @frame->size, @frame->eof and
+ * @frame->sof have to be set.
  *
  * @frame->callback will be invoked with once the frame has been transmitted.
  *
@@ -573,4 +578,8 @@ static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame)
        return __tb_ring_enqueue(ring, frame);
 }
 
+/* Used only when the ring is in polling mode */
+struct ring_frame *tb_ring_poll(struct tb_ring *ring);
+void tb_ring_poll_complete(struct tb_ring *ring);
+
 #endif /* THUNDERBOLT_H_ */