提交 9fb1e654 编写于 作者: M Mika Westerberg 提交者: David S. Miller

thunderbolt: Add support for frame mode

When high-speed DMA paths are used to transfer arbitrary data over a
Thunderbolt link, DMA rings should be in frame mode instead of raw mode.
The latter is used by the control channel (ring 0). In frame mode each
data frame can hold up to 4kB payload.

This patch modifies the DMA ring code to allow configuring a ring to be
in frame mode by passing a new flag (RING_FLAG_FRAME) to the ring when
it is allocated. In addition there might be need to enable end-to-end
(E2E) workaround for the ring to prevent losing Rx frames in certain
situations. We add another flag (RING_FLAG_E2E) that can be used for
this purpose.

This code is based on the work done by Amir Levy and Michael Jamet.
Signed-off-by: NMichael Jamet <michael.jamet@intel.com>
Signed-off-by: NMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: NYehezkel Bernat <yehezkel.bernat@intel.com>
Reviewed-by: NAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 8c6bba10
...@@ -618,7 +618,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) ...@@ -618,7 +618,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
if (!ctl->tx) if (!ctl->tx)
goto err; goto err;
ctl->rx = ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND); ctl->rx = ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff,
0xffff);
if (!ctl->rx) if (!ctl->rx)
goto err; goto err;
......
...@@ -21,6 +21,12 @@ ...@@ -21,6 +21,12 @@
#define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring") #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring")
/*
* Used to enable end-to-end workaround for missing RX packets. Do not
* use this ring for anything else.
*/
#define RING_E2E_UNUSED_HOPID 2
/* /*
* Minimal number of vectors when we use MSI-X. Two for control channel * Minimal number of vectors when we use MSI-X. Two for control channel
* Rx/Tx and the rest four are for cross domain DMA paths. * Rx/Tx and the rest four are for cross domain DMA paths.
...@@ -229,23 +235,6 @@ static void ring_work(struct work_struct *work) ...@@ -229,23 +235,6 @@ static void ring_work(struct work_struct *work)
frame->eof = ring->descriptors[ring->tail].eof; frame->eof = ring->descriptors[ring->tail].eof;
frame->sof = ring->descriptors[ring->tail].sof; frame->sof = ring->descriptors[ring->tail].sof;
frame->flags = ring->descriptors[ring->tail].flags; frame->flags = ring->descriptors[ring->tail].flags;
if (frame->sof != 0)
dev_WARN(&ring->nhi->pdev->dev,
"%s %d got unexpected SOF: %#x\n",
RING_TYPE(ring), ring->hop,
frame->sof);
/*
* known flags:
* raw not enabled, interupt not set: 0x2=0010
* raw enabled: 0xa=1010
* raw not enabled: 0xb=1011
* partial frame (>MAX_FRAME_SIZE): 0xe=1110
*/
if (frame->flags != 0xa)
dev_WARN(&ring->nhi->pdev->dev,
"%s %d got unexpected flags: %#x\n",
RING_TYPE(ring), ring->hop,
frame->flags);
} }
ring->tail = (ring->tail + 1) % ring->size; ring->tail = (ring->tail + 1) % ring->size;
} }
...@@ -321,12 +310,17 @@ static void ring_release_msix(struct tb_ring *ring) ...@@ -321,12 +310,17 @@ static void ring_release_msix(struct tb_ring *ring)
} }
static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size, static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
bool transmit, unsigned int flags) bool transmit, unsigned int flags,
u16 sof_mask, u16 eof_mask)
{ {
struct tb_ring *ring = NULL; struct tb_ring *ring = NULL;
dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n",
transmit ? "TX" : "RX", hop, size); transmit ? "TX" : "RX", hop, size);
/* Tx Ring 2 is reserved for E2E workaround */
if (transmit && hop == RING_E2E_UNUSED_HOPID)
return NULL;
mutex_lock(&nhi->lock); mutex_lock(&nhi->lock);
if (hop >= nhi->hop_count) { if (hop >= nhi->hop_count) {
dev_WARN(&nhi->pdev->dev, "invalid hop: %d\n", hop); dev_WARN(&nhi->pdev->dev, "invalid hop: %d\n", hop);
...@@ -353,6 +347,8 @@ static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ...@@ -353,6 +347,8 @@ static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
ring->is_tx = transmit; ring->is_tx = transmit;
ring->size = size; ring->size = size;
ring->flags = flags; ring->flags = flags;
ring->sof_mask = sof_mask;
ring->eof_mask = eof_mask;
ring->head = 0; ring->head = 0;
ring->tail = 0; ring->tail = 0;
ring->running = false; ring->running = false;
...@@ -384,13 +380,13 @@ static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ...@@ -384,13 +380,13 @@ static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags) unsigned int flags)
{ {
return ring_alloc(nhi, hop, size, true, flags); return ring_alloc(nhi, hop, size, true, flags, 0, 0);
} }
struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags) unsigned int flags, u16 sof_mask, u16 eof_mask)
{ {
return ring_alloc(nhi, hop, size, false, flags); return ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask);
} }
/** /**
...@@ -400,6 +396,9 @@ struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, ...@@ -400,6 +396,9 @@ struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
*/ */
void ring_start(struct tb_ring *ring) void ring_start(struct tb_ring *ring)
{ {
u16 frame_size;
u32 flags;
mutex_lock(&ring->nhi->lock); mutex_lock(&ring->nhi->lock);
mutex_lock(&ring->lock); mutex_lock(&ring->lock);
if (ring->nhi->going_away) if (ring->nhi->going_away)
...@@ -411,18 +410,39 @@ void ring_start(struct tb_ring *ring) ...@@ -411,18 +410,39 @@ void ring_start(struct tb_ring *ring)
dev_info(&ring->nhi->pdev->dev, "starting %s %d\n", dev_info(&ring->nhi->pdev->dev, "starting %s %d\n",
RING_TYPE(ring), ring->hop); RING_TYPE(ring), ring->hop);
if (ring->flags & RING_FLAG_FRAME) {
/* Means 4096 */
frame_size = 0;
flags = RING_FLAG_ENABLE;
} else {
frame_size = TB_FRAME_SIZE;
flags = RING_FLAG_ENABLE | RING_FLAG_RAW;
}
if (ring->flags & RING_FLAG_E2E && !ring->is_tx) {
u32 hop;
/*
* In order not to lose Rx packets we enable end-to-end
* workaround which transfers Rx credits to an unused Tx
* HopID.
*/
hop = RING_E2E_UNUSED_HOPID << REG_RX_OPTIONS_E2E_HOP_SHIFT;
hop &= REG_RX_OPTIONS_E2E_HOP_MASK;
flags |= hop | RING_FLAG_E2E_FLOW_CONTROL;
}
ring_iowrite64desc(ring, ring->descriptors_dma, 0); ring_iowrite64desc(ring, ring->descriptors_dma, 0);
if (ring->is_tx) { if (ring->is_tx) {
ring_iowrite32desc(ring, ring->size, 12); ring_iowrite32desc(ring, ring->size, 12);
ring_iowrite32options(ring, 0, 4); /* time releated ? */ ring_iowrite32options(ring, 0, 4); /* time releated ? */
ring_iowrite32options(ring, ring_iowrite32options(ring, flags, 0);
RING_FLAG_ENABLE | RING_FLAG_RAW, 0);
} else { } else {
ring_iowrite32desc(ring, u32 sof_eof_mask = ring->sof_mask << 16 | ring->eof_mask;
(TB_FRAME_SIZE << 16) | ring->size, 12);
ring_iowrite32options(ring, 0xffffffff, 4); /* SOF EOF mask */ ring_iowrite32desc(ring, (frame_size << 16) | ring->size, 12);
ring_iowrite32options(ring, ring_iowrite32options(ring, sof_eof_mask, 4);
RING_FLAG_ENABLE | RING_FLAG_RAW, 0); ring_iowrite32options(ring, flags, 0);
} }
ring_interrupt_active(ring, true); ring_interrupt_active(ring, true);
ring->running = true; ring->running = true;
......
...@@ -56,6 +56,8 @@ struct tb_nhi { ...@@ -56,6 +56,8 @@ struct tb_nhi {
* @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise. * @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) * @vector: MSI-X vector number the ring uses (only set if @irq is > 0)
* @flags: Ring specific flags * @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
*/ */
struct tb_ring { struct tb_ring {
struct mutex lock; struct mutex lock;
...@@ -74,10 +76,16 @@ struct tb_ring { ...@@ -74,10 +76,16 @@ struct tb_ring {
int irq; int irq;
u8 vector; u8 vector;
unsigned int flags; unsigned int flags;
u16 sof_mask;
u16 eof_mask;
}; };
/* Leave ring interrupt enabled on suspend */ /* Leave ring interrupt enabled on suspend */
#define RING_FLAG_NO_SUSPEND BIT(0) #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; struct ring_frame;
typedef void (*ring_cb)(struct tb_ring*, struct ring_frame*, bool canceled); typedef void (*ring_cb)(struct tb_ring*, struct ring_frame*, bool canceled);
...@@ -100,7 +108,7 @@ struct ring_frame { ...@@ -100,7 +108,7 @@ struct ring_frame {
struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags); unsigned int flags);
struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags); unsigned int flags, u16 sof_mask, u16 eof_mask);
void ring_start(struct tb_ring *ring); void ring_start(struct tb_ring *ring);
void ring_stop(struct tb_ring *ring); void ring_stop(struct tb_ring *ring);
void ring_free(struct tb_ring *ring); void ring_free(struct tb_ring *ring);
......
...@@ -77,6 +77,8 @@ struct ring_desc { ...@@ -77,6 +77,8 @@ struct ring_desc {
* ..: unknown * ..: unknown
*/ */
#define REG_RX_OPTIONS_BASE 0x29800 #define REG_RX_OPTIONS_BASE 0x29800
#define REG_RX_OPTIONS_E2E_HOP_MASK GENMASK(22, 12)
#define REG_RX_OPTIONS_E2E_HOP_SHIFT 12
/* /*
* three bitfields: tx, rx, rx overflow * three bitfields: tx, rx, rx overflow
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册