From: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Date: Mon, 2 May 2016 10:16:44 +0530
Subject: xhci: fix missing keys from keyboard

Current handling of the keyboard polling was very slow and
keys were getting dropped. Done following for fixing this:

* Use multiple buffers per TRB
* Allocate buffers in xhci according to the number of TRBS.

This reduces the delay of key? processing by getting rid of wait in
the polling routine.

Reported-by: Dinar Valeev <k0da@opensuse.org>
Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Tested-by: Dinar Valeev <k0da@opensuse.org>
Tested-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Bruce Rogers <brogers@suse.com>
---
 lib/libusb/usb-xhci.c | 51 +++++++++++++++++++++++++++++++++----------
 lib/libusb/usb-xhci.h |  2 ++
 2 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/roms/SLOF/lib/libusb/usb-xhci.c b/roms/SLOF/lib/libusb/usb-xhci.c
index 858cd12f9069df81ab1b2a62a9c7..b04e070dcd7c11ba22bfeb0f19c5 100644
--- a/roms/SLOF/lib/libusb/usb-xhci.c
+++ b/roms/SLOF/lib/libusb/usb-xhci.c
@@ -238,7 +238,7 @@ static uint64_t xhci_poll_event(struct xhci_hcd *xhcd,
 	flags = le32_to_cpu(event->flags);
 
 	dprintf("Reading from event ptr %p %08x\n", event, flags);
-	time = SLOF_GetTimer() + USB_TIMEOUT;
+	time = SLOF_GetTimer() + ((event_type == XHCI_POLL_NO_WAIT)? 0: USB_TIMEOUT);
 
 	while ((flags & TRB_CYCLE_STATE) != xhcd->ering.cycle_state) {
 		mb();
@@ -1147,11 +1147,36 @@ static inline void *xhci_get_trb(struct xhci_seg *seg)
 	return (void *)enq;
 }
 
+static inline void *xhci_get_trb_deq(struct xhci_seg *seg)
+{
+	uint64_t deq_next, deq;
+	int index;
+
+	deq = seg->deq;
+	deq_next = deq + XHCI_TRB_SIZE;
+	index = (deq - (uint64_t)seg->trbs) / XHCI_TRB_SIZE + 1;
+	dprintf("%s: deq %llx, deq_next %llx index %x\n", __func__, deq, deq_next, index);
+	/* TRBs being a cyclic buffer, here we cycle back to beginning. */
+	if (index == (seg->size - 1)) {
+		dprintf("%s: rounding \n", __func__);
+		seg->deq = (uint64_t)seg->trbs;
+	}
+	else {
+		seg->deq = deq_next;
+	}
+	return (void *)deq;
+}
+
 static uint64_t xhci_get_trb_phys(struct xhci_seg *seg, uint64_t trb)
 {
 	return seg->trbs_dma + (trb - (uint64_t)seg->trbs);
 }
 
+static uint32_t xhci_trb_get_index(struct xhci_seg *seg, struct xhci_transfer_trb *trb)
+{
+	return trb - (struct xhci_transfer_trb *)seg->trbs;
+}
+
 static int usb_kb = false;
 static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys,
 			void *data, int datalen)
@@ -1331,9 +1356,9 @@ static int xhci_get_pipe_intr(struct usb_pipe *pipe,
 		xhci_init_seg(seg, XHCI_EVENT_TRBS_SIZE, TYPE_BULK);
 	}
 
-	xpipe->buf = buf;
-	xpipe->buf_phys = SLOF_dma_map_in(buf, len, false);
-	xpipe->buflen = len;
+	xpipe->buflen = pipe->mps * XHCI_INTR_TRBS_SIZE/(sizeof(struct xhci_transfer_trb));
+	xpipe->buf = SLOF_dma_alloc(xpipe->buflen);
+	xpipe->buf_phys = SLOF_dma_map_in(xpipe->buf, xpipe->buflen, false);
 
 	ctrl = xhci_get_control_ctx(&xdev->in_ctx);
 	x_epno = xhci_get_epno(pipe);
@@ -1349,7 +1374,8 @@ static int xhci_get_pipe_intr(struct usb_pipe *pipe,
 	xpipe->seg = seg;
 
 	trb = xhci_get_trb(seg);
-	fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps);
+	buf = (char *)(xpipe->buf_phys + xhci_trb_get_index(seg, trb) * pipe->mps);
+	fill_normal_trb(trb, (void *)buf, pipe->mps);
 	return true;
 }
 
@@ -1411,6 +1437,7 @@ static void xhci_put_pipe(struct usb_pipe *pipe)
 	} else if (pipe->type == USB_EP_TYPE_INTR) {
 		xpipe = xhci_pipe_get_xpipe(pipe);
 		SLOF_dma_map_out(xpipe->buf_phys, xpipe->buf, xpipe->buflen);
+		SLOF_dma_free(xpipe->buf, xpipe->buflen);
 		xpipe->seg = NULL;
 	}
 	if (xhcd->end)
@@ -1448,26 +1475,26 @@ static int xhci_poll_intr(struct usb_pipe *pipe, uint8_t *data)
 	if (usb_kb == true) {
 		/* This event was consumed by bulk transfer */
 		usb_kb = false;
+		xhci_get_trb_deq(seg);
 		goto skip_poll;
 	}
-	buf = xpipe->buf;
-	memset(buf, 0, 8);
 
-	mb();
 	/* Ring the doorbell - x_epno */
 	dbr = xhcd->db_regs;
 	write_reg32(&dbr->db[xdev->slot_id], x_epno);
-	if (!xhci_poll_event(xhcd, 0)) {
-		printf("poll intr failed\n");
+	if (!xhci_poll_event(xhcd, XHCI_POLL_NO_WAIT)) {
 		return 0;
 	}
 	mb();
+	trb = xhci_get_trb_deq(seg);
+	buf = xpipe->buf + xhci_trb_get_index(seg, trb) * pipe->mps;
 	memcpy(data, buf, 8);
+	memset(buf, 0, 8);
 
 skip_poll:
 	trb = xhci_get_trb(seg);
-	fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps);
-	mb();
+	buf = (uint8_t *)(xpipe->buf_phys + xhci_trb_get_index(seg, trb) * pipe->mps);
+	fill_normal_trb(trb, (void *)buf, pipe->mps);
 	return ret;
 }
 
diff --git a/roms/SLOF/lib/libusb/usb-xhci.h b/roms/SLOF/lib/libusb/usb-xhci.h
index 3fc7e7889ba7297e6b733ddf53c4..8172a371f983cc7dd0ea12dcb1e5 100644
--- a/roms/SLOF/lib/libusb/usb-xhci.h
+++ b/roms/SLOF/lib/libusb/usb-xhci.h
@@ -269,6 +269,8 @@ struct xhci_seg {
 #define XHCI_INTR_TRBS_SIZE    4096
 #define XHCI_ERST_NUM_SEGS     1
 
+#define XHCI_POLL_NO_WAIT      1
+
 #define XHCI_MAX_BULK_SIZE    0xF000
 
 struct xhci_erst_entry {
