Commit becbd202 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: make isoc_bei_interval variable interrupter specific.

isoc_bei_interval is used to balance how often completed isochronous
events cause interrupts. If interval is too large then the event ring
may fill up before the completed isoc TRBs are handled.

isoc_bei_interval is tuned based on how full the event ring is.

isoc_bei_interval variable needs to be per interrupter as
with several interrupters each one has its own event ring.

move isoc_bei_interval variable to the interrupter structure.

if a secondary interrupter does not care about this feature then
keep isoc_bei_interval 0.
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarWesley Cheng <quic_wcheng@quicinc.com>
Link: https://lore.kernel.org/r/20240217001017.29969-4-quic_wcheng@quicinc.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ace21625
...@@ -2533,7 +2533,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ...@@ -2533,7 +2533,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
if (xhci_add_interrupter(xhci, ir, 0)) if (xhci_add_interrupter(xhci, ir, 0))
goto fail; goto fail;
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
/* /*
* XXX: Might need to set the Interrupter Moderation Register to * XXX: Might need to set the Interrupter Moderation Register to
......
...@@ -3153,8 +3153,8 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) ...@@ -3153,8 +3153,8 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
event_ring_deq = ir->event_ring->dequeue; event_ring_deq = ir->event_ring->dequeue;
/* ring is half-full, force isoc trbs to interrupt more often */ /* ring is half-full, force isoc trbs to interrupt more often */
if (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN) if (ir->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN)
xhci->isoc_bei_interval = xhci->isoc_bei_interval / 2; ir->isoc_bei_interval = ir->isoc_bei_interval / 2;
event_loop = 0; event_loop = 0;
} }
...@@ -4022,7 +4022,8 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, ...@@ -4022,7 +4022,8 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
} }
/* Check if we should generate event interrupt for a TD in an isoc URB */ /* Check if we should generate event interrupt for a TD in an isoc URB */
static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i) static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i,
struct xhci_interrupter *ir)
{ {
if (xhci->hci_version < 0x100) if (xhci->hci_version < 0x100)
return false; return false;
...@@ -4033,8 +4034,8 @@ static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i) ...@@ -4033,8 +4034,8 @@ static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i)
* If AVOID_BEI is set the host handles full event rings poorly, * If AVOID_BEI is set the host handles full event rings poorly,
* generate an event at least every 8th TD to clear the event ring * generate an event at least every 8th TD to clear the event ring
*/ */
if (i && xhci->quirks & XHCI_AVOID_BEI) if (i && ir->isoc_bei_interval && xhci->quirks & XHCI_AVOID_BEI)
return !!(i % xhci->isoc_bei_interval); return !!(i % ir->isoc_bei_interval);
return true; return true;
} }
...@@ -4043,6 +4044,7 @@ static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i) ...@@ -4043,6 +4044,7 @@ static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i)
static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index) struct urb *urb, int slot_id, unsigned int ep_index)
{ {
struct xhci_interrupter *ir;
struct xhci_ring *ep_ring; struct xhci_ring *ep_ring;
struct urb_priv *urb_priv; struct urb_priv *urb_priv;
struct xhci_td *td; struct xhci_td *td;
...@@ -4060,6 +4062,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -4060,6 +4062,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
xep = &xhci->devs[slot_id]->eps[ep_index]; xep = &xhci->devs[slot_id]->eps[ep_index];
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
ir = xhci->interrupters[0];
num_tds = urb->number_of_packets; num_tds = urb->number_of_packets;
if (num_tds < 1) { if (num_tds < 1) {
...@@ -4147,7 +4150,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -4147,7 +4150,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
td->last_trb = ep_ring->enqueue; td->last_trb = ep_ring->enqueue;
td->last_trb_seg = ep_ring->enq_seg; td->last_trb_seg = ep_ring->enq_seg;
field |= TRB_IOC; field |= TRB_IOC;
if (trb_block_event_intr(xhci, num_tds, i)) if (trb_block_event_intr(xhci, num_tds, i, ir))
field |= TRB_BEI; field |= TRB_BEI;
} }
/* Calculate TRB length */ /* Calculate TRB length */
......
...@@ -1433,6 +1433,7 @@ struct xhci_interrupter { ...@@ -1433,6 +1433,7 @@ struct xhci_interrupter {
struct xhci_intr_reg __iomem *ir_set; struct xhci_intr_reg __iomem *ir_set;
unsigned int intr_num; unsigned int intr_num;
bool ip_autoclear; bool ip_autoclear;
u32 isoc_bei_interval;
/* For interrupter registers save and restore over suspend/resume */ /* For interrupter registers save and restore over suspend/resume */
u32 s3_irq_pending; u32 s3_irq_pending;
u32 s3_irq_control; u32 s3_irq_control;
...@@ -1505,7 +1506,6 @@ struct xhci_hcd { ...@@ -1505,7 +1506,6 @@ struct xhci_hcd {
u8 isoc_threshold; u8 isoc_threshold;
/* imod_interval in ns (I * 250ns) */ /* imod_interval in ns (I * 250ns) */
u32 imod_interval; u32 imod_interval;
u32 isoc_bei_interval;
int event_ring_max; int event_ring_max;
/* 4KB min, 128MB max */ /* 4KB min, 128MB max */
int page_size; int page_size;
......
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