summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-mem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/xhci-mem.c')
-rw-r--r--drivers/usb/host/xhci-mem.c245
1 files changed, 109 insertions, 136 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index d698095fc88d..bd745a0f2f78 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -484,6 +484,35 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
kfree(ctx);
}
+struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
+ gfp_t flags)
+{
+ struct xhci_container_ctx *ctx;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+
+ ctx = kzalloc_node(sizeof(*ctx), flags, dev_to_node(dev));
+ if (!ctx)
+ return NULL;
+
+ ctx->size = GET_PORT_BW_ARRAY_SIZE;
+
+ ctx->bytes = dma_pool_zalloc(xhci->port_bw_pool, flags, &ctx->dma);
+ if (!ctx->bytes) {
+ kfree(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ dma_pool_free(xhci->port_bw_pool, ctx->bytes, ctx->dma);
+ kfree(ctx);
+}
+
struct xhci_input_control_ctx *xhci_get_input_control_ctx(
struct xhci_container_ctx *ctx)
{
@@ -1802,10 +1831,10 @@ xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
*/
if (ir->ir_set) {
tmp = readl(&ir->ir_set->erst_size);
- tmp &= ERST_SIZE_MASK;
+ tmp &= ~ERST_SIZE_MASK;
writel(tmp, &ir->ir_set->erst_size);
- xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue);
+ xhci_update_erst_dequeue(xhci, ir, true);
}
}
@@ -1848,6 +1877,11 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
return;
}
+ /*
+ * Cleanup secondary interrupter to ensure there are no pending events.
+ * This also updates event ring dequeue pointer back to the start.
+ */
+ xhci_skip_sec_intr_events(xhci, ir->event_ring, ir);
intr_num = ir->intr_num;
xhci_remove_interrupter(xhci, ir);
@@ -1907,6 +1941,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Freed small stream array pool");
+ dma_pool_destroy(xhci->port_bw_pool);
+ xhci->port_bw_pool = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Freed xhci port bw array pool");
+
dma_pool_destroy(xhci->medium_streams_pool);
xhci->medium_streams_pool = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2282,37 +2321,25 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
return ir;
}
-static int
-xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
- unsigned int intr_num)
+void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num)
{
+ struct xhci_interrupter *ir;
u64 erst_base;
u32 erst_size;
- if (intr_num >= xhci->max_interrupters) {
- xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
- intr_num, xhci->max_interrupters);
- return -EINVAL;
- }
-
- if (xhci->interrupters[intr_num]) {
- xhci_warn(xhci, "Interrupter %d\n already set up", intr_num);
- return -EINVAL;
- }
-
- xhci->interrupters[intr_num] = ir;
+ ir = xhci->interrupters[intr_num];
ir->intr_num = intr_num;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
/* 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_SIZE_MASK;
erst_size |= ir->event_ring->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;
+ erst_base &= ~ERST_BASE_ADDRESS_MASK;
+ erst_base |= ir->erst.erst_dma_addr & ERST_BASE_ADDRESS_MASK;
if (xhci->quirks & XHCI_WRITE_64_HI_LO)
hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
else
@@ -2320,20 +2347,19 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
-
- return 0;
}
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
- u32 imod_interval)
+ u32 imod_interval, unsigned int intr_num)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir;
unsigned int i;
int err = -ENOSPC;
- if (!xhci->interrupters || xhci->max_interrupters <= 1)
+ if (!xhci->interrupters || xhci->max_interrupters <= 1 ||
+ intr_num >= xhci->max_interrupters)
return NULL;
ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
@@ -2341,15 +2367,23 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
return NULL;
spin_lock_irq(&xhci->lock);
-
- /* Find available secondary interrupter, interrupter 0 is reserved for primary */
- for (i = 1; i < xhci->max_interrupters; i++) {
- if (xhci->interrupters[i] == NULL) {
- err = xhci_add_interrupter(xhci, ir, i);
- break;
+ if (!intr_num) {
+ /* Find available secondary interrupter, interrupter 0 is reserved for primary */
+ for (i = 1; i < xhci->max_interrupters; i++) {
+ if (!xhci->interrupters[i]) {
+ xhci->interrupters[i] = ir;
+ xhci_add_interrupter(xhci, i);
+ err = 0;
+ break;
+ }
+ }
+ } else {
+ if (!xhci->interrupters[intr_num]) {
+ xhci->interrupters[intr_num] = ir;
+ xhci_add_interrupter(xhci, intr_num);
+ err = 0;
}
}
-
spin_unlock_irq(&xhci->lock);
if (err) {
@@ -2359,78 +2393,32 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
return NULL;
}
- err = xhci_set_interrupter_moderation(ir, imod_interval);
- if (err)
- xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n",
- i, imod_interval);
+ xhci_set_interrupter_moderation(ir, imod_interval);
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
- i, xhci->max_interrupters);
+ ir->intr_num, xhci->max_interrupters);
return ir;
}
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
-static void xhci_hcd_page_size(struct xhci_hcd *xhci)
-{
- u32 page_size;
-
- page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK;
- if (!is_power_of_2(page_size)) {
- xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size);
- /* Fallback to 4K page size, since that's common */
- page_size = 1;
- }
-
- xhci->page_size = page_size << 12;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK",
- xhci->page_size >> 10);
-}
-
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
- struct xhci_interrupter *ir;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
dma_addr_t dma;
- unsigned int val, val2;
- u64 val_64;
- u32 temp;
- int i;
-
- INIT_LIST_HEAD(&xhci->cmd_list);
-
- /* init command timeout work */
- INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
- init_completion(&xhci->cmd_ring_stop_completion);
-
- xhci_hcd_page_size(xhci);
-
- /*
- * Program the Number of Device Slots Enabled field in the CONFIG
- * register with the max value of slots the HC can handle.
- */
- val = HCS_MAX_SLOTS(readl(&xhci->cap_regs->hcs_params1));
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// xHC can handle at most %d device slots.", val);
- val2 = readl(&xhci->op_regs->config_reg);
- val |= (val2 & ~HCS_SLOTS_MASK);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Setting Max device slots reg = 0x%x.", val);
- writel(val, &xhci->op_regs->config_reg);
/*
* xHCI section 5.4.6 - Device Context array must be
* "physically contiguous and 64-byte (cache line) aligned".
*/
- xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
- flags);
+ xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags);
if (!xhci->dcbaa)
goto fail;
+
xhci->dcbaa->dma = dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Device context base array address = 0x%pad (DMA), %p (virt)",
- &xhci->dcbaa->dma, xhci->dcbaa);
- xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr);
+ "Device context base array address = 0x%pad (DMA), %p (virt)",
+ &xhci->dcbaa->dma, xhci->dcbaa);
/*
* Initialize the ring segment pool. The ring must be a contiguous
@@ -2446,92 +2434,77 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
else
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
+ if (!xhci->segment_pool)
+ goto fail;
/* See Table 46 and Note on Figure 55 */
- xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
- 2112, 64, xhci->page_size);
- if (!xhci->segment_pool || !xhci->device_pool)
+ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, 2112, 64,
+ xhci->page_size);
+ if (!xhci->device_pool)
goto fail;
- /* Linear stream context arrays don't have any boundary restrictions,
+ /*
+ * Linear stream context arrays don't have any boundary restrictions,
* and only need to be 16-byte aligned.
*/
- xhci->small_streams_pool =
- dma_pool_create("xHCI 256 byte stream ctx arrays",
- dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
- xhci->medium_streams_pool =
- dma_pool_create("xHCI 1KB stream ctx arrays",
- dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
- /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
- * will be allocated with dma_alloc_coherent()
+ xhci->small_streams_pool = dma_pool_create("xHCI 256 byte stream ctx arrays",
+ dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
+ if (!xhci->small_streams_pool)
+ goto fail;
+
+ /*
+ * Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE will be
+ * allocated with dma_alloc_coherent().
*/
- if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
+ xhci->medium_streams_pool = dma_pool_create("xHCI 1KB stream ctx arrays",
+ dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
+ if (!xhci->medium_streams_pool)
+ goto fail;
+
+ /*
+ * refer to xhci rev1_2 protocol 5.3.3 max ports is 255.
+ * refer to xhci rev1_2 protocol 6.4.3.14 port bandwidth buffer need
+ * to be 16-byte aligned.
+ */
+ xhci->port_bw_pool = dma_pool_create("xHCI 256 port bw ctx arrays",
+ dev, GET_PORT_BW_ARRAY_SIZE, 16, 0);
+ if (!xhci->port_bw_pool)
goto fail;
/* Set up the command ring to have one segments for now. */
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags);
if (!xhci->cmd_ring)
goto fail;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Allocated command ring at %p", xhci->cmd_ring);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
- &xhci->cmd_ring->first_seg->dma);
- /* Set the address in the Command Ring Control register */
- val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
- (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) |
- xhci->cmd_ring->cycle_state;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Setting command ring address to 0x%016llx", val_64);
- xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
+ &xhci->cmd_ring->first_seg->dma);
- /* Reserve one command ring TRB for disabling LPM.
+ /*
+ * Reserve one command ring TRB for disabling LPM.
* Since the USB core grabs the shared usb_bus bandwidth mutex before
* disabling LPM, we only need to reserve one TRB for all devices.
*/
xhci->cmd_ring_reserved_trbs++;
- val = readl(&xhci->cap_regs->db_off);
- val &= DBOFF_MASK;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Doorbell array is located at offset 0x%x from cap regs base addr",
- val);
- xhci->dba = (void __iomem *) xhci->cap_regs + val;
-
/* Allocate and set up primary interrupter 0 with an event ring. */
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Allocating primary event ring");
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring");
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
flags, dev_to_node(dev));
-
- ir = xhci_alloc_interrupter(xhci, 0, flags);
- if (!ir)
+ if (!xhci->interrupters)
goto fail;
- if (xhci_add_interrupter(xhci, ir, 0))
+ xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags);
+ if (!xhci->interrupters[0])
goto fail;
- ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
-
- for (i = 0; i < MAX_HC_SLOTS; i++)
- xhci->devs[i] = NULL;
-
if (scratchpad_alloc(xhci, flags))
goto fail;
+
if (xhci_setup_port_arrays(xhci, flags))
goto fail;
- /* Enable USB 3.0 device notifications for function remote wake, which
- * is necessary for allowing USB 3.0 devices to do remote wakeup from
- * U3 (device suspend).
- */
- temp = readl(&xhci->op_regs->dev_notification);
- temp &= ~DEV_NOTE_MASK;
- temp |= DEV_NOTE_FWAKE;
- writel(temp, &xhci->op_regs->dev_notification);
-
return 0;
fail: