Commit 4ffe722e authored by Mika Westerberg's avatar Mika Westerberg Committed by David S. Miller

thunderbolt: Add polling mode for rings

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: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarMichael Jamet <michael.jamet@intel.com>
Reviewed-by: default avatarYehezkel Bernat <yehezkel.bernat@intel.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 59120e06
...@@ -619,7 +619,7 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) ...@@ -619,7 +619,7 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
goto err; goto err;
ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff,
0xffff); 0xffff, NULL, NULL);
if (!ctl->rx) if (!ctl->rx)
goto err; goto err;
......
...@@ -252,7 +252,8 @@ static void ring_work(struct work_struct *work) ...@@ -252,7 +252,8 @@ static void ring_work(struct work_struct *work)
* Do not hold on to it. * Do not hold on to it.
*/ */
list_del_init(&frame->list); 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) ...@@ -273,11 +274,106 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
} }
EXPORT_SYMBOL_GPL(__tb_ring_enqueue); 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) static irqreturn_t ring_msix(int irq, void *data)
{ {
struct tb_ring *ring = 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; return IRQ_HANDLED;
} }
...@@ -317,7 +413,9 @@ static void ring_release_msix(struct tb_ring *ring) ...@@ -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, static struct tb_ring *tb_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) u16 sof_mask, u16 eof_mask,
void (*start_poll)(void *),
void *poll_data)
{ {
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",
...@@ -346,6 +444,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ...@@ -346,6 +444,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
ring->head = 0; ring->head = 0;
ring->tail = 0; ring->tail = 0;
ring->running = false; ring->running = false;
ring->start_poll = start_poll;
ring->poll_data = poll_data;
ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev, ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev,
size * sizeof(*ring->descriptors), size * sizeof(*ring->descriptors),
...@@ -399,7 +499,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ...@@ -399,7 +499,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags) 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); EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
...@@ -411,11 +511,17 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); ...@@ -411,11 +511,17 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
* @flags: Flags for the ring * @flags: Flags for the ring
* @sof_mask: Mask of PDF values that start a frame * @sof_mask: Mask of PDF values that start a frame
* @eof_mask: Mask of PDF values that end 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, 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); EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
...@@ -556,6 +662,7 @@ void tb_ring_free(struct tb_ring *ring) ...@@ -556,6 +662,7 @@ void tb_ring_free(struct tb_ring *ring)
dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n", dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n",
RING_TYPE(ring), ring->hop); RING_TYPE(ring), ring->hop);
} }
spin_unlock_irq(&ring->nhi->lock);
ring_release_msix(ring); ring_release_msix(ring);
...@@ -572,7 +679,6 @@ void tb_ring_free(struct tb_ring *ring) ...@@ -572,7 +679,6 @@ void tb_ring_free(struct tb_ring *ring)
RING_TYPE(ring), RING_TYPE(ring),
ring->hop); ring->hop);
spin_unlock_irq(&ring->nhi->lock);
/** /**
* ring->work can no longer be scheduled (it is scheduled only * ring->work can no longer be scheduled (it is scheduled only
* by nhi_interrupt_work, ring_stop and ring_msix). Wait for it * 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) ...@@ -682,8 +788,10 @@ static void nhi_interrupt_work(struct work_struct *work)
hop); hop);
continue; 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); spin_unlock_irq(&nhi->lock);
} }
......
...@@ -446,6 +446,9 @@ struct tb_nhi { ...@@ -446,6 +446,9 @@ struct tb_nhi {
* @flags: Ring specific flags * @flags: Ring specific flags
* @sof_mask: Bit mask used to detect start of frame PDF * @sof_mask: Bit mask used to detect start of frame PDF
* @eof_mask: Bit mask used to detect end 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 { struct tb_ring {
spinlock_t lock; spinlock_t lock;
...@@ -466,6 +469,8 @@ struct tb_ring { ...@@ -466,6 +469,8 @@ struct tb_ring {
unsigned int flags; unsigned int flags;
u16 sof_mask; u16 sof_mask;
u16 eof_mask; u16 eof_mask;
void (*start_poll)(void *data);
void *poll_data;
}; };
/* Leave ring interrupt enabled on suspend */ /* Leave ring interrupt enabled on suspend */
...@@ -499,7 +504,7 @@ enum ring_desc_flags { ...@@ -499,7 +504,7 @@ enum ring_desc_flags {
/** /**
* struct ring_frame - For use with ring_rx/ring_tx * struct ring_frame - For use with ring_rx/ring_tx
* @buffer_phy: DMA mapped address of the frame * @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 * @list: Frame is linked to a queue using this
* @size: Size of the frame in bytes (%0 means %4096) * @size: Size of the frame in bytes (%0 means %4096)
* @flags: Flags for the frame (see &enum ring_desc_flags) * @flags: Flags for the frame (see &enum ring_desc_flags)
...@@ -522,8 +527,8 @@ struct ring_frame { ...@@ -522,8 +527,8 @@ struct ring_frame {
struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags); unsigned int flags);
struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags, u16 sof_mask, unsigned int flags, u16 sof_mask, u16 eof_mask,
u16 eof_mask); void (*start_poll)(void *), void *poll_data);
void tb_ring_start(struct tb_ring *ring); void tb_ring_start(struct tb_ring *ring);
void tb_ring_stop(struct tb_ring *ring); void tb_ring_stop(struct tb_ring *ring);
void tb_ring_free(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); ...@@ -535,8 +540,8 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame);
* @ring: Ring to enqueue the frame * @ring: Ring to enqueue the frame
* @frame: Frame to enqueue * @frame: Frame to enqueue
* *
* @frame->buffer, @frame->buffer_phy and @frame->callback have to be set. The * @frame->buffer, @frame->buffer_phy have to be set. The buffer must
* buffer must contain at least %TB_FRAME_SIZE bytes. * contain at least %TB_FRAME_SIZE bytes.
* *
* @frame->callback will be invoked with @frame->size, @frame->flags, * @frame->callback will be invoked with @frame->size, @frame->flags,
* @frame->eof, @frame->sof set once the frame has been received. * @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) ...@@ -557,8 +562,8 @@ static inline int tb_ring_rx(struct tb_ring *ring, struct ring_frame *frame)
* @ring: Ring the enqueue the frame * @ring: Ring the enqueue the frame
* @frame: Frame to enqueue * @frame: Frame to enqueue
* *
* @frame->buffer, @frame->buffer_phy, @frame->callback, @frame->size, * @frame->buffer, @frame->buffer_phy, @frame->size, @frame->eof and
* @frame->eof and @frame->sof have to be set. * @frame->sof have to be set.
* *
* @frame->callback will be invoked with once the frame has been transmitted. * @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) ...@@ -573,4 +578,8 @@ static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame)
return __tb_ring_enqueue(ring, 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_ */ #endif /* THUNDERBOLT_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment