Merge 71147efd68 ("usb: xhci: fix loss of data on Cadence xHC") into android14-6.1-lts

Steps on the way to 6.1.113

Resolves merge conflicts in:
	drivers/usb/host/xhci-mem.c
	drivers/usb/host/xhci.h

Change-Id: I423af7c6bba0aaeb56dacbed4059f2fc0d0e1de5
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2024-11-10 12:30:33 +00:00
7 changed files with 230 additions and 285 deletions

View File

@@ -62,7 +62,9 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
.resume_quirk = xhci_cdns3_resume_quirk, .resume_quirk = xhci_cdns3_resume_quirk,
}; };
static const struct xhci_plat_priv xhci_plat_cdnsp_xhci; static const struct xhci_plat_priv xhci_plat_cdnsp_xhci = {
.quirks = XHCI_CDNS_SCTX_QUIRK,
};
static int __cdns_host_init(struct cdns *cdns) static int __cdns_host_init(struct cdns *cdns)
{ {

View File

@@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
"command-ring", "command-ring",
xhci->debugfs_root); xhci->debugfs_root);
xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring,
"event-ring", "event-ring",
xhci->debugfs_root); xhci->debugfs_root);

View File

@@ -1902,19 +1902,44 @@ int xhci_alloc_erst(struct xhci_hcd *xhci,
} }
EXPORT_SYMBOL_GPL(xhci_alloc_erst); EXPORT_SYMBOL_GPL(xhci_alloc_erst);
void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) static void
xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
{ {
size_t size;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
size_t erst_size;
u64 tmp64;
u32 tmp;
size = sizeof(struct xhci_erst_entry) * (erst->num_entries); if (!ir)
if (erst->entries) return;
dma_free_coherent(dev, size,
erst->entries, erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries);
erst->erst_dma_addr); if (ir->erst.entries)
erst->entries = NULL; dma_free_coherent(dev, erst_size,
ir->erst.entries,
ir->erst.erst_dma_addr);
ir->erst.entries = NULL;
/*
* Clean out interrupter registers except ERSTBA. Clearing either the
* low or high 32 bits of ERSTBA immediately causes the controller to
* dereference the partially cleared 64 bit address, causing IOMMU error.
*/
tmp = readl(&ir->ir_set->erst_size);
tmp &= ERST_SIZE_MASK;
writel(tmp, &ir->ir_set->erst_size);
tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
tmp64 &= (u64) ERST_PTR_MASK;
xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
/* free interrrupter event ring */
if (ir->event_ring)
xhci_ring_free(xhci, ir->event_ring);
ir->event_ring = NULL;
kfree(ir);
} }
EXPORT_SYMBOL_GPL(xhci_free_erst);
static struct xhci_device_context_array *xhci_vendor_alloc_dcbaa( static struct xhci_device_context_array *xhci_vendor_alloc_dcbaa(
struct xhci_hcd *xhci, gfp_t flags) struct xhci_hcd *xhci, gfp_t flags)
@@ -1941,12 +1966,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
cancel_delayed_work_sync(&xhci->cmd_timer); cancel_delayed_work_sync(&xhci->cmd_timer);
xhci_free_erst(xhci, &xhci->erst); xhci_free_interrupter(xhci, xhci->interrupter);
xhci->interrupter = NULL;
if (xhci->event_ring) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");
xhci_ring_free(xhci, xhci->event_ring);
xhci->event_ring = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring");
if (xhci->cmd_ring) if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring); xhci_ring_free(xhci, xhci->cmd_ring);
@@ -2035,177 +2057,18 @@ no_bw:
xhci->usb3_rhub.bus_state.bus_suspended = 0; xhci->usb3_rhub.bus_state.bus_suspended = 0;
} }
static int xhci_test_trb_in_td(struct xhci_hcd *xhci, static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
struct xhci_segment *input_seg,
union xhci_trb *start_trb,
union xhci_trb *end_trb,
dma_addr_t input_dma,
struct xhci_segment *result_seg,
char *test_name, int test_number)
{
unsigned long long start_dma;
unsigned long long end_dma;
struct xhci_segment *seg;
start_dma = xhci_trb_virt_to_dma(input_seg, start_trb);
end_dma = xhci_trb_virt_to_dma(input_seg, end_trb);
seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false);
if (seg != result_seg) {
xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n",
test_name, test_number);
xhci_warn(xhci, "Tested TRB math w/ seg %p and "
"input DMA 0x%llx\n",
input_seg,
(unsigned long long) input_dma);
xhci_warn(xhci, "starting TRB %p (0x%llx DMA), "
"ending TRB %p (0x%llx DMA)\n",
start_trb, start_dma,
end_trb, end_dma);
xhci_warn(xhci, "Expected seg %p, got seg %p\n",
result_seg, seg);
trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma,
true);
return -1;
}
return 0;
}
/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */
int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
{
struct {
dma_addr_t input_dma;
struct xhci_segment *result_seg;
} simple_test_vector [] = {
/* A zeroed DMA field should fail */
{ 0, NULL },
/* One TRB before the ring start should fail */
{ xhci->event_ring->first_seg->dma - 16, NULL },
/* One byte before the ring start should fail */
{ xhci->event_ring->first_seg->dma - 1, NULL },
/* Starting TRB should succeed */
{ xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg },
/* Ending TRB should succeed */
{ xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16,
xhci->event_ring->first_seg },
/* One byte after the ring end should fail */
{ xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL },
/* One TRB after the ring end should fail */
{ xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL },
/* An address of all ones should fail */
{ (dma_addr_t) (~0), NULL },
};
struct {
struct xhci_segment *input_seg;
union xhci_trb *start_trb;
union xhci_trb *end_trb;
dma_addr_t input_dma;
struct xhci_segment *result_seg;
} complex_test_vector [] = {
/* Test feeding a valid DMA address from a different ring */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = xhci->event_ring->first_seg->trbs,
.end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
.input_dma = xhci->cmd_ring->first_seg->dma,
.result_seg = NULL,
},
/* Test feeding a valid end TRB from a different ring */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = xhci->event_ring->first_seg->trbs,
.end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
.input_dma = xhci->cmd_ring->first_seg->dma,
.result_seg = NULL,
},
/* Test feeding a valid start and end TRB from a different ring */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = xhci->cmd_ring->first_seg->trbs,
.end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
.input_dma = xhci->cmd_ring->first_seg->dma,
.result_seg = NULL,
},
/* TRB in this ring, but after this TD */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = &xhci->event_ring->first_seg->trbs[0],
.end_trb = &xhci->event_ring->first_seg->trbs[3],
.input_dma = xhci->event_ring->first_seg->dma + 4*16,
.result_seg = NULL,
},
/* TRB in this ring, but before this TD */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = &xhci->event_ring->first_seg->trbs[3],
.end_trb = &xhci->event_ring->first_seg->trbs[6],
.input_dma = xhci->event_ring->first_seg->dma + 2*16,
.result_seg = NULL,
},
/* TRB in this ring, but after this wrapped TD */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
.end_trb = &xhci->event_ring->first_seg->trbs[1],
.input_dma = xhci->event_ring->first_seg->dma + 2*16,
.result_seg = NULL,
},
/* TRB in this ring, but before this wrapped TD */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
.end_trb = &xhci->event_ring->first_seg->trbs[1],
.input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16,
.result_seg = NULL,
},
/* TRB not in this ring, and we have a wrapped TD */
{ .input_seg = xhci->event_ring->first_seg,
.start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
.end_trb = &xhci->event_ring->first_seg->trbs[1],
.input_dma = xhci->cmd_ring->first_seg->dma + 2*16,
.result_seg = NULL,
},
};
unsigned int num_tests;
int i, ret;
num_tests = ARRAY_SIZE(simple_test_vector);
for (i = 0; i < num_tests; i++) {
ret = xhci_test_trb_in_td(xhci,
xhci->event_ring->first_seg,
xhci->event_ring->first_seg->trbs,
&xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
simple_test_vector[i].input_dma,
simple_test_vector[i].result_seg,
"Simple", i);
if (ret < 0)
return ret;
}
num_tests = ARRAY_SIZE(complex_test_vector);
for (i = 0; i < num_tests; i++) {
ret = xhci_test_trb_in_td(xhci,
complex_test_vector[i].input_seg,
complex_test_vector[i].start_trb,
complex_test_vector[i].end_trb,
complex_test_vector[i].input_dma,
complex_test_vector[i].result_seg,
"Complex", i);
if (ret < 0)
return ret;
}
xhci_dbg(xhci, "TRB math tests passed.\n");
return 0;
}
EXPORT_SYMBOL_GPL(xhci_check_trb_in_td_math);
static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
{ {
u64 temp; u64 temp;
dma_addr_t deq; dma_addr_t deq;
deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg,
xhci->event_ring->dequeue); ir->event_ring->dequeue);
if (!deq) if (!deq)
xhci_warn(xhci, "WARN something wrong with SW event ring " xhci_warn(xhci, "WARN something wrong with SW event ring "
"dequeue ptr.\n"); "dequeue ptr.\n");
/* Update HC event ring dequeue pointer */ /* Update HC event ring dequeue pointer */
temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
temp &= ERST_PTR_MASK; temp &= ERST_PTR_MASK;
/* Don't clear the EHB bit (which is RW1C) because /* Don't clear the EHB bit (which is RW1C) because
* there might be more events to service. * there might be more events to service.
@@ -2215,7 +2078,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
"// Write event ring dequeue pointer, " "// Write event ring dequeue pointer, "
"preserving EHB bit"); "preserving EHB bit");
xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
&xhci->ir_set->erst_dequeue); &ir->ir_set->erst_dequeue);
} }
static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
@@ -2499,6 +2362,71 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
return 0; return 0;
} }
static struct xhci_interrupter *
xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_interrupter *ir;
u64 erst_base;
u32 erst_size;
int ret;
if (intr_num > xhci->max_interrupters) {
xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n",
intr_num, xhci->max_interrupters);
return NULL;
}
if (xhci->interrupter) {
xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num);
return NULL;
}
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
if (!ir)
return NULL;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
0, flags);
if (!ir->event_ring) {
xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num);
goto fail_ir;
}
ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags);
if (ret) {
xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num);
goto fail_ev;
}
/* set ERST count with the number of entries in the segment table */
erst_size = readl(&ir->ir_set->erst_size);
erst_size &= ERST_SIZE_MASK;
erst_size |= ERST_NUM_SEGS;
writel(erst_size, &ir->ir_set->erst_size);
erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
erst_base &= ERST_BASE_RSVDP;
erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP;
if (xhci->quirks & XHCI_WRITE_64_HI_LO)
hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
else
xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
return ir;
fail_ev:
xhci_ring_free(xhci, ir->event_ring);
fail_ir:
kfree(ir);
return NULL;
}
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{ {
dma_addr_t dma; dma_addr_t dma;
@@ -2506,7 +2434,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
unsigned int val, val2; unsigned int val, val2;
u64 val_64; u64 val_64;
u32 page_size, temp; u32 page_size, temp;
int i, ret; int i;
INIT_LIST_HEAD(&xhci->cmd_list); INIT_LIST_HEAD(&xhci->cmd_list);
@@ -2629,48 +2557,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
" from cap regs base addr", val); " from cap regs base addr", val);
xhci->dba = (void __iomem *) xhci->cap_regs + val; xhci->dba = (void __iomem *) xhci->cap_regs + val;
/* Set ir_set to interrupt register set 0 */ /* Set ir_set to interrupt register set 0 */
xhci->ir_set = &xhci->run_regs->ir_set[0];
/* /* allocate and set up primary interrupter with an event ring. */
* Event ring setup: Allocate a normal ring, but also setup xhci_dbg_trace(xhci, trace_xhci_dbg_init,
* the event ring segment table (ERST). Section 4.9.3. "Allocating primary event ring");
*/ xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); if (!xhci->interrupter)
xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
0, flags);
if (!xhci->event_ring)
goto fail; goto fail;
if (xhci_check_trb_in_td_math(xhci) < 0)
goto fail;
ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags);
if (ret)
goto fail;
/* set ERST count with the number of entries in the segment table */
val = readl(&xhci->ir_set->erst_size);
val &= ERST_SIZE_MASK;
val |= ERST_NUM_SEGS;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"// Write ERST size = %i to ir_set 0 (some bits preserved)",
val);
writel(val, &xhci->ir_set->erst_size);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"// Set ERST entries to point to event ring.");
/* set the segment table base address */
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"// Set ERST base address for ir_set 0 = 0x%llx",
(unsigned long long)xhci->erst.erst_dma_addr);
val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base);
val_64 &= ERST_PTR_MASK;
val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK);
xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base);
/* Set the event ring dequeue address */
xhci_set_hc_event_deq(xhci);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Wrote ERST address to ir_set 0.");
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;

View File

@@ -75,6 +75,9 @@
#define PCI_DEVICE_ID_ASMEDIA_2142_XHCI 0x2142 #define PCI_DEVICE_ID_ASMEDIA_2142_XHCI 0x2142
#define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242 #define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242
#define PCI_DEVICE_ID_CADENCE 0x17CD
#define PCI_DEVICE_ID_CADENCE_SSP 0x0200
static const char hcd_name[] = "xhci_hcd"; static const char hcd_name[] = "xhci_hcd";
static struct hc_driver __read_mostly xhci_pci_hc_driver; static struct hc_driver __read_mostly xhci_pci_hc_driver;
@@ -352,6 +355,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
} }
if (pdev->vendor == PCI_DEVICE_ID_CADENCE &&
pdev->device == PCI_DEVICE_ID_CADENCE_SSP)
xhci->quirks |= XHCI_CDNS_SCTX_QUIRK;
/* xHC spec requires PCI devices to support D3hot and D3cold */ /* xHC spec requires PCI devices to support D3hot and D3cold */
if (xhci->hci_version >= 0x120) if (xhci->hci_version >= 0x120)
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;

View File

@@ -1385,6 +1385,20 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
struct xhci_stream_ctx *ctx = struct xhci_stream_ctx *ctx =
&ep->stream_info->stream_ctx_array[stream_id]; &ep->stream_info->stream_ctx_array[stream_id];
deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
/*
* Cadence xHCI controllers store some endpoint state
* information within Rsvd0 fields of Stream Endpoint
* context. This field is not cleared during Set TR
* Dequeue Pointer command which causes XDMA to skip
* over transfer ring and leads to data loss on stream
* pipe.
* To fix this issue driver must clear Rsvd0 field.
*/
if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) {
ctx->reserved[0] = 0;
ctx->reserved[1] = 0;
}
} else { } else {
deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
} }
@@ -1865,7 +1879,8 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
} }
static void handle_port_status(struct xhci_hcd *xhci, static void handle_port_status(struct xhci_hcd *xhci,
union xhci_trb *event) struct xhci_interrupter *ir,
union xhci_trb *event)
{ {
struct usb_hcd *hcd; struct usb_hcd *hcd;
u32 port_id; u32 port_id;
@@ -1888,7 +1903,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
if ((port_id <= 0) || (port_id > max_ports)) { if ((port_id <= 0) || (port_id > max_ports)) {
xhci_warn(xhci, "Port change event with invalid port ID %d\n", xhci_warn(xhci, "Port change event with invalid port ID %d\n",
port_id); port_id);
inc_deq(xhci, xhci->event_ring); inc_deq(xhci, ir->event_ring);
return; return;
} }
@@ -2018,7 +2033,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
cleanup: cleanup:
/* Update event ring dequeue pointer before dropping the lock */ /* Update event ring dequeue pointer before dropping the lock */
inc_deq(xhci, xhci->event_ring); inc_deq(xhci, ir->event_ring);
/* Don't make the USB core poll the roothub if we got a bad port status /* Don't make the USB core poll the roothub if we got a bad port status
* change event. Besides, at that point we can't tell which roothub * change event. Besides, at that point we can't tell which roothub
@@ -2579,7 +2594,8 @@ finish_td:
* At this point, the host controller is probably hosed and should be reset. * At this point, the host controller is probably hosed and should be reset.
*/ */
static int handle_tx_event(struct xhci_hcd *xhci, static int handle_tx_event(struct xhci_hcd *xhci,
struct xhci_transfer_event *event) struct xhci_interrupter *ir,
struct xhci_transfer_event *event)
{ {
struct xhci_virt_ep *ep; struct xhci_virt_ep *ep;
struct xhci_ring *ep_ring; struct xhci_ring *ep_ring;
@@ -2965,7 +2981,7 @@ cleanup:
* processing missed tds. * processing missed tds.
*/ */
if (!handling_skipped_tds) if (!handling_skipped_tds)
inc_deq(xhci, xhci->event_ring); inc_deq(xhci, ir->event_ring);
/* /*
* If ep->skip is set, it means there are missed tds on the * If ep->skip is set, it means there are missed tds on the
@@ -2980,8 +2996,8 @@ cleanup:
err_out: err_out:
xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
(unsigned long long) xhci_trb_virt_to_dma( (unsigned long long) xhci_trb_virt_to_dma(
xhci->event_ring->deq_seg, ir->event_ring->deq_seg,
xhci->event_ring->dequeue), ir->event_ring->dequeue),
lower_32_bits(le64_to_cpu(event->buffer)), lower_32_bits(le64_to_cpu(event->buffer)),
upper_32_bits(le64_to_cpu(event->buffer)), upper_32_bits(le64_to_cpu(event->buffer)),
le32_to_cpu(event->transfer_len), le32_to_cpu(event->transfer_len),
@@ -2995,7 +3011,7 @@ err_out:
* Returns >0 for "possibly more events to process" (caller should call again), * Returns >0 for "possibly more events to process" (caller should call again),
* otherwise 0 if done. In future, <0 returns should indicate error code. * otherwise 0 if done. In future, <0 returns should indicate error code.
*/ */
static int xhci_handle_event(struct xhci_hcd *xhci) static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
{ {
union xhci_trb *event; union xhci_trb *event;
int update_ptrs = 1; int update_ptrs = 1;
@@ -3003,18 +3019,18 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
int ret; int ret;
/* Event ring hasn't been allocated yet. */ /* Event ring hasn't been allocated yet. */
if (!xhci->event_ring || !xhci->event_ring->dequeue) { if (!ir || !ir->event_ring || !ir->event_ring->dequeue) {
xhci_err(xhci, "ERROR event ring not ready\n"); xhci_err(xhci, "ERROR interrupter not ready\n");
return -ENOMEM; return -ENOMEM;
} }
event = xhci->event_ring->dequeue; event = ir->event_ring->dequeue;
/* Does the HC or OS own the TRB? */ /* Does the HC or OS own the TRB? */
if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
xhci->event_ring->cycle_state) ir->event_ring->cycle_state)
return 0; return 0;
trace_xhci_handle_event(xhci->event_ring, &event->generic); trace_xhci_handle_event(ir->event_ring, &event->generic);
/* /*
* Barrier between reading the TRB_CYCLE (valid) flag above and any * Barrier between reading the TRB_CYCLE (valid) flag above and any
@@ -3029,11 +3045,11 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
handle_cmd_completion(xhci, &event->event_cmd); handle_cmd_completion(xhci, &event->event_cmd);
break; break;
case TRB_PORT_STATUS: case TRB_PORT_STATUS:
handle_port_status(xhci, event); handle_port_status(xhci, ir, event);
update_ptrs = 0; update_ptrs = 0;
break; break;
case TRB_TRANSFER: case TRB_TRANSFER:
ret = handle_tx_event(xhci, &event->trans_event); ret = handle_tx_event(xhci, ir, &event->trans_event);
if (ret >= 0) if (ret >= 0)
update_ptrs = 0; update_ptrs = 0;
break; break;
@@ -3057,7 +3073,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
if (update_ptrs) if (update_ptrs)
/* Update SW event ring dequeue pointer */ /* Update SW event ring dequeue pointer */
inc_deq(xhci, xhci->event_ring); inc_deq(xhci, ir->event_ring);
/* Are there more items on the event ring? Caller will call us again to /* Are there more items on the event ring? Caller will call us again to
* check. * check.
@@ -3071,16 +3087,17 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
* - To avoid "Event Ring Full Error" condition * - To avoid "Event Ring Full Error" condition
*/ */
static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
union xhci_trb *event_ring_deq) struct xhci_interrupter *ir,
union xhci_trb *event_ring_deq)
{ {
u64 temp_64; u64 temp_64;
dma_addr_t deq; dma_addr_t deq;
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
/* If necessary, update the HW's version of the event ring deq ptr. */ /* If necessary, update the HW's version of the event ring deq ptr. */
if (event_ring_deq != xhci->event_ring->dequeue) { if (event_ring_deq != ir->event_ring->dequeue) {
deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg,
xhci->event_ring->dequeue); ir->event_ring->dequeue);
if (deq == 0) if (deq == 0)
xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n");
/* /*
@@ -3098,7 +3115,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
/* Clear the event handler busy flag (RW1C) */ /* Clear the event handler busy flag (RW1C) */
temp_64 |= ERST_EHB; temp_64 |= ERST_EHB;
xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue);
} }
/* /*
@@ -3110,6 +3127,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
{ {
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
union xhci_trb *event_ring_deq; union xhci_trb *event_ring_deq;
struct xhci_interrupter *ir;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
u64 temp_64; u64 temp_64;
u32 status; u32 status;
@@ -3142,11 +3160,13 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
status |= STS_EINT; status |= STS_EINT;
writel(status, &xhci->op_regs->status); writel(status, &xhci->op_regs->status);
/* This is the handler of the primary interrupter */
ir = xhci->interrupter;
if (!hcd->msi_enabled) { if (!hcd->msi_enabled) {
u32 irq_pending; u32 irq_pending;
irq_pending = readl(&xhci->ir_set->irq_pending); irq_pending = readl(&ir->ir_set->irq_pending);
irq_pending |= IMAN_IP; irq_pending |= IMAN_IP;
writel(irq_pending, &xhci->ir_set->irq_pending); writel(irq_pending, &ir->ir_set->irq_pending);
} }
if (xhci->xhc_state & XHCI_STATE_DYING || if (xhci->xhc_state & XHCI_STATE_DYING ||
@@ -3156,22 +3176,22 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
/* Clear the event handler busy flag (RW1C); /* Clear the event handler busy flag (RW1C);
* the event ring should be empty. * the event ring should be empty.
*/ */
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
xhci_write_64(xhci, temp_64 | ERST_EHB, xhci_write_64(xhci, temp_64 | ERST_EHB,
&xhci->ir_set->erst_dequeue); &ir->ir_set->erst_dequeue);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
goto out; goto out;
} }
event_ring_deq = xhci->event_ring->dequeue; event_ring_deq = ir->event_ring->dequeue;
/* FIXME this should be a delayed service routine /* FIXME this should be a delayed service routine
* that clears the EHB. * that clears the EHB.
*/ */
while (xhci_handle_event(xhci) > 0) { while (xhci_handle_event(xhci, ir) > 0) {
if (event_loop++ < TRBS_PER_SEGMENT / 2) if (event_loop++ < TRBS_PER_SEGMENT / 2)
continue; continue;
xhci_update_erst_dequeue(xhci, event_ring_deq); xhci_update_erst_dequeue(xhci, ir, event_ring_deq);
event_ring_deq = xhci->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 (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN)
@@ -3180,7 +3200,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
event_loop = 0; event_loop = 0;
} }
xhci_update_erst_dequeue(xhci, event_ring_deq); xhci_update_erst_dequeue(xhci, ir, event_ring_deq);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
out: out:

View File

@@ -619,6 +619,7 @@ static int xhci_init(struct usb_hcd *hcd)
static int xhci_run_finished(struct xhci_hcd *xhci) static int xhci_run_finished(struct xhci_hcd *xhci)
{ {
struct xhci_interrupter *ir = xhci->interrupter;
unsigned long flags; unsigned long flags;
u32 temp; u32 temp;
@@ -634,8 +635,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
writel(temp, &xhci->op_regs->command); writel(temp, &xhci->op_regs->command);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter");
temp = readl(&xhci->ir_set->irq_pending); temp = readl(&ir->ir_set->irq_pending);
writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending);
if (xhci_start(xhci)) { if (xhci_start(xhci)) {
xhci_halt(xhci); xhci_halt(xhci);
@@ -671,7 +672,7 @@ int xhci_run(struct usb_hcd *hcd)
u64 temp_64; u64 temp_64;
int ret; int ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir = xhci->interrupter;
/* Start the xHCI host controller running only after the USB 2.0 roothub /* Start the xHCI host controller running only after the USB 2.0 roothub
* is setup. * is setup.
*/ */
@@ -686,17 +687,17 @@ int xhci_run(struct usb_hcd *hcd)
if (ret) if (ret)
return ret; return ret;
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
temp_64 &= ~ERST_PTR_MASK; temp_64 &= ~ERST_PTR_MASK;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"ERST deq = 64'h%0lx", (long unsigned int) temp_64); "ERST deq = 64'h%0lx", (long unsigned int) temp_64);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"// Set the interrupt modulation register"); "// Set the interrupt modulation register");
temp = readl(&xhci->ir_set->irq_control); temp = readl(&ir->ir_set->irq_control);
temp &= ~ER_IRQ_INTERVAL_MASK; temp &= ~ER_IRQ_INTERVAL_MASK;
temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
writel(temp, &xhci->ir_set->irq_control); writel(temp, &ir->ir_set->irq_control);
if (xhci->quirks & XHCI_NEC_HOST) { if (xhci->quirks & XHCI_NEC_HOST) {
struct xhci_command *command; struct xhci_command *command;
@@ -775,8 +776,8 @@ static void xhci_stop(struct usb_hcd *hcd)
"// Disabling event ring interrupts"); "// Disabling event ring interrupts");
temp = readl(&xhci->op_regs->status); temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
temp = readl(&xhci->ir_set->irq_pending); temp = readl(&xhci->interrupter->ir_set->irq_pending);
writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
xhci_mem_cleanup(xhci); xhci_mem_cleanup(xhci);
@@ -838,28 +839,36 @@ EXPORT_SYMBOL_GPL(xhci_shutdown);
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void xhci_save_registers(struct xhci_hcd *xhci) static void xhci_save_registers(struct xhci_hcd *xhci)
{ {
struct xhci_interrupter *ir = xhci->interrupter;
xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.command = readl(&xhci->op_regs->command);
xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification);
xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg);
xhci->s3.erst_size = readl(&xhci->ir_set->erst_size);
xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); if (!ir)
xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); return;
xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending);
xhci->s3.irq_control = readl(&xhci->ir_set->irq_control); ir->s3_erst_size = readl(&ir->ir_set->erst_size);
ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
ir->s3_irq_control = readl(&ir->ir_set->irq_control);
} }
static void xhci_restore_registers(struct xhci_hcd *xhci) static void xhci_restore_registers(struct xhci_hcd *xhci)
{ {
struct xhci_interrupter *ir = xhci->interrupter;
writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.command, &xhci->op_regs->command);
writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg);
writel(xhci->s3.erst_size, &xhci->ir_set->erst_size); writel(ir->s3_erst_size, &ir->ir_set->erst_size);
xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending); writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
writel(xhci->s3.irq_control, &xhci->ir_set->irq_control); writel(ir->s3_irq_control, &ir->ir_set->irq_control);
} }
static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
@@ -1226,8 +1235,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
xhci_dbg(xhci, "// Disabling event ring interrupts\n"); xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = readl(&xhci->op_regs->status); temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
temp = readl(&xhci->ir_set->irq_pending); temp = readl(&xhci->interrupter->ir_set->irq_pending);
writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending);
xhci_dbg(xhci, "cleaning up memory\n"); xhci_dbg(xhci, "cleaning up memory\n");
xhci_mem_cleanup(xhci); xhci_mem_cleanup(xhci);
@@ -5456,6 +5465,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (xhci->hci_version > 0x100) if (xhci->hci_version > 0x100)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
/* xhci-plat or xhci-pci might have set max_interrupters already */
if ((!xhci->max_interrupters) ||
xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1))
xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1);
xhci->quirks |= quirks; xhci->quirks |= quirks;
get_quirks(dev, xhci); get_quirks(dev, xhci);

View File

@@ -17,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/android_kabi.h> #include <linux/android_kabi.h>
/* Code sharing between pci-quirks and xhci hcd */ /* Code sharing between pci-quirks and xhci hcd */
@@ -514,6 +515,9 @@ struct xhci_intr_reg {
/* Preserve bits 16:31 of erst_size */ /* Preserve bits 16:31 of erst_size */
#define ERST_SIZE_MASK (0xffff << 16) #define ERST_SIZE_MASK (0xffff << 16)
/* erst_base bitmasks */
#define ERST_BASE_RSVDP (GENMASK_ULL(5, 0))
/* erst_dequeue bitmasks */ /* erst_dequeue bitmasks */
/* Dequeue ERST Segment Index (DESI) - Segment number (or alias) /* Dequeue ERST Segment Index (DESI) - Segment number (or alias)
* where the current dequeue pointer lies. This is an optional HW hint. * where the current dequeue pointer lies. This is an optional HW hint.
@@ -1697,11 +1701,6 @@ struct s3_save {
u32 dev_nt; u32 dev_nt;
u64 dcbaa_ptr; u64 dcbaa_ptr;
u32 config_reg; u32 config_reg;
u32 irq_pending;
u32 irq_control;
u32 erst_size;
u64 erst_base;
u64 erst_dequeue;
}; };
/* Use for lpm */ /* Use for lpm */
@@ -1728,7 +1727,18 @@ struct xhci_bus_state {
struct completion u3exit_done[USB_MAXCHILDREN]; struct completion u3exit_done[USB_MAXCHILDREN];
}; };
struct xhci_interrupter {
struct xhci_ring *event_ring;
struct xhci_erst erst;
struct xhci_intr_reg __iomem *ir_set;
unsigned int intr_num;
/* For interrupter registers save and restore over suspend/resume */
u32 s3_irq_pending;
u32 s3_irq_control;
u32 s3_erst_size;
u64 s3_erst_base;
u64 s3_erst_dequeue;
};
/* /*
* It can take up to 20 ms to transition from RExit to U0 on the * It can take up to 20 ms to transition from RExit to U0 on the
* Intel Lynx Point LP xHCI host. * Intel Lynx Point LP xHCI host.
@@ -1771,8 +1781,6 @@ struct xhci_hcd {
struct xhci_op_regs __iomem *op_regs; struct xhci_op_regs __iomem *op_regs;
struct xhci_run_regs __iomem *run_regs; struct xhci_run_regs __iomem *run_regs;
struct xhci_doorbell_array __iomem *dba; struct xhci_doorbell_array __iomem *dba;
/* Our HCD's current interrupter register set */
struct xhci_intr_reg __iomem *ir_set;
/* Cached register copies of read-only HC data */ /* Cached register copies of read-only HC data */
__u32 hcs_params1; __u32 hcs_params1;
@@ -1787,7 +1795,7 @@ struct xhci_hcd {
u8 sbrn; u8 sbrn;
u16 hci_version; u16 hci_version;
u8 max_slots; u8 max_slots;
u8 max_interrupters; u16 max_interrupters;
u8 max_ports; u8 max_ports;
u8 isoc_threshold; u8 isoc_threshold;
/* imod_interval in ns (I * 250ns) */ /* imod_interval in ns (I * 250ns) */
@@ -1807,6 +1815,7 @@ struct xhci_hcd {
struct reset_control *reset; struct reset_control *reset;
/* data structures */ /* data structures */
struct xhci_device_context_array *dcbaa; struct xhci_device_context_array *dcbaa;
struct xhci_interrupter *interrupter;
struct xhci_ring *cmd_ring; struct xhci_ring *cmd_ring;
unsigned int cmd_ring_state; unsigned int cmd_ring_state;
#define CMD_RING_STATE_RUNNING (1 << 0) #define CMD_RING_STATE_RUNNING (1 << 0)
@@ -1817,8 +1826,7 @@ struct xhci_hcd {
struct delayed_work cmd_timer; struct delayed_work cmd_timer;
struct completion cmd_ring_stop_completion; struct completion cmd_ring_stop_completion;
struct xhci_command *current_cmd; struct xhci_command *current_cmd;
struct xhci_ring *event_ring;
struct xhci_erst erst;
/* Scratchpad */ /* Scratchpad */
struct xhci_scratchpad *scratchpad; struct xhci_scratchpad *scratchpad;
@@ -1914,6 +1922,8 @@ struct xhci_hcd {
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46) #define XHCI_ZHAOXIN_HOST BIT_ULL(46)
#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
unsigned int num_active_eps; unsigned int num_active_eps;
unsigned int limit_active_eps; unsigned int limit_active_eps;
@@ -2107,7 +2117,6 @@ int xhci_alloc_erst(struct xhci_hcd *xhci,
gfp_t flags); gfp_t flags);
void xhci_initialize_ring_info(struct xhci_ring *ring, void xhci_initialize_ring_info(struct xhci_ring *ring,
unsigned int cycle_state); unsigned int cycle_state);
void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst);
void xhci_free_endpoint_ring(struct xhci_hcd *xhci, void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev, struct xhci_virt_device *virt_dev,
unsigned int ep_index); unsigned int ep_index);