thunderbolt: Add support for end-to-end flow control
authorMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 19 Oct 2020 16:15:20 +0000 (19:15 +0300)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Wed, 11 Nov 2020 07:20:16 +0000 (10:20 +0300)
USB4 spec defines end-to-end (E2E) flow control that can be used between
hosts to prevent overflow of a RX ring. We previously had this partially
implemented but that code was removed with commit 53f13319d131
("thunderbolt: Get rid of E2E workaround") with the idea that we add it
back properly if there ever is need. Now that we are going to add DMA
traffic test driver (in subsequent patches) this can be useful.

For this reason we modify tb_ring_alloc_rx/tx() so that they accept
RING_FLAG_E2E and configure the hardware ring accordingly. The RX side
also requires passing TX HopID (e2e_tx_hop) used in the credit grant
packets.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Yehezkel Bernat <YehezkelShB@gmail.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/thunderbolt.c
drivers/thunderbolt/ctl.c
drivers/thunderbolt/nhi.c
include/linux/thunderbolt.h

index 3160443..d7b5f87 100644 (file)
@@ -866,7 +866,7 @@ static int tbnet_open(struct net_device *dev)
        eof_mask = BIT(TBIP_PDF_FRAME_END);
 
        ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE,
-                               RING_FLAG_FRAME, sof_mask, eof_mask,
+                               RING_FLAG_FRAME, 0, sof_mask, eof_mask,
                                tbnet_start_poll, net);
        if (!ring) {
                netdev_err(dev, "failed to allocate Rx ring\n");
index 9894b8f..1d86e27 100644 (file)
@@ -628,8 +628,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
        if (!ctl->tx)
                goto err;
 
-       ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff,
-                               0xffff, NULL, NULL);
+       ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff,
+                                  0xffff, NULL, NULL);
        if (!ctl->rx)
                goto err;
 
index 3f79baa..a69bc6b 100644 (file)
@@ -483,7 +483,7 @@ err_unlock:
 
 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,
+                                    int e2e_tx_hop, u16 sof_mask, u16 eof_mask,
                                     void (*start_poll)(void *),
                                     void *poll_data)
 {
@@ -506,6 +506,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
        ring->is_tx = transmit;
        ring->size = size;
        ring->flags = flags;
+       ring->e2e_tx_hop = e2e_tx_hop;
        ring->sof_mask = sof_mask;
        ring->eof_mask = eof_mask;
        ring->head = 0;
@@ -550,7 +551,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, NULL, NULL);
+       return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, 0, NULL, NULL);
 }
 EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
 
@@ -560,6 +561,7 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
  * @hop: HopID (ring) to allocate. Pass %-1 for automatic allocation.
  * @size: Number of entries in the ring
  * @flags: Flags for the ring
+ * @e2e_tx_hop: Transmit HopID when E2E is enabled in @flags
  * @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
@@ -568,10 +570,11 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
  * @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, int e2e_tx_hop,
+                                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, e2e_tx_hop, sof_mask, eof_mask,
                             start_poll, poll_data);
 }
 EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
@@ -618,6 +621,31 @@ void tb_ring_start(struct tb_ring *ring)
                ring_iowrite32options(ring, sof_eof_mask, 4);
                ring_iowrite32options(ring, flags, 0);
        }
+
+       /*
+        * Now that the ring valid bit is set we can configure E2E if
+        * enabled for the ring.
+        */
+       if (ring->flags & RING_FLAG_E2E) {
+               if (!ring->is_tx) {
+                       u32 hop;
+
+                       hop = ring->e2e_tx_hop << REG_RX_OPTIONS_E2E_HOP_SHIFT;
+                       hop &= REG_RX_OPTIONS_E2E_HOP_MASK;
+                       flags |= hop;
+
+                       dev_dbg(&ring->nhi->pdev->dev,
+                               "enabling E2E for %s %d with TX HopID %d\n",
+                               RING_TYPE(ring), ring->hop, ring->e2e_tx_hop);
+               } else {
+                       dev_dbg(&ring->nhi->pdev->dev, "enabling E2E for %s %d\n",
+                               RING_TYPE(ring), ring->hop);
+               }
+
+               flags |= RING_FLAG_E2E_FLOW_CONTROL;
+               ring_iowrite32options(ring, flags, 0);
+       }
+
        ring_interrupt_active(ring, true);
        ring->running = true;
 err:
index a844fd5..034dccf 100644 (file)
@@ -481,6 +481,8 @@ struct tb_nhi {
  * @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise.
  * @vector: MSI-X vector number the ring uses (only set if @irq is > 0)
  * @flags: Ring specific flags
+ * @e2e_tx_hop: Transmit HopID when E2E is enabled. Only applicable to
+ *             RX ring. For TX ring this should be set to %0.
  * @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
@@ -504,6 +506,7 @@ struct tb_ring {
        int irq;
        u8 vector;
        unsigned int flags;
+       int e2e_tx_hop;
        u16 sof_mask;
        u16 eof_mask;
        void (*start_poll)(void *data);
@@ -514,6 +517,8 @@ struct tb_ring {
 #define RING_FLAG_NO_SUSPEND   BIT(0)
 /* Configure the ring to be in frame mode */
 #define RING_FLAG_FRAME                BIT(1)
+/* Enable end-to-end flow control */
+#define RING_FLAG_E2E          BIT(2)
 
 struct ring_frame;
 typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled);
@@ -562,7 +567,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, int e2e_tx_hop,
+                                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);