diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 74 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 106 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 10 |
4 files changed, 155 insertions, 40 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bad0d1f9a41d..6afe32381209 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -37,7 +37,9 @@ * "All components of all Command and Transfer TRBs shall be initialized to '0'" */ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, - unsigned int cycle_state, gfp_t flags) + unsigned int cycle_state, + unsigned int max_packet, + gfp_t flags) { struct xhci_segment *seg; dma_addr_t dma; @@ -53,6 +55,14 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, return NULL; } + if (max_packet) { + seg->bounce_buf = kzalloc(max_packet, flags | GFP_DMA); + if (!seg->bounce_buf) { + dma_pool_free(xhci->segment_pool, seg->trbs, dma); + kfree(seg); + return NULL; + } + } /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ if (cycle_state == 0) { for (i = 0; i < TRBS_PER_SEGMENT; i++) @@ -70,6 +80,7 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma); seg->trbs = NULL; } + kfree(seg->bounce_buf); kfree(seg); } @@ -317,11 +328,11 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring, static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_segment **first, struct xhci_segment **last, unsigned int num_segs, unsigned int cycle_state, - enum xhci_ring_type type, gfp_t flags) + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { struct xhci_segment *prev; - prev = xhci_segment_alloc(xhci, cycle_state, flags); + prev = xhci_segment_alloc(xhci, cycle_state, max_packet, flags); if (!prev) return -ENOMEM; num_segs--; @@ -330,7 +341,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, while (num_segs > 0) { struct xhci_segment *next; - next = xhci_segment_alloc(xhci, cycle_state, flags); + next = xhci_segment_alloc(xhci, cycle_state, max_packet, flags); if (!next) { prev = *first; while (prev) { @@ -360,7 +371,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, */ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, unsigned int cycle_state, - enum xhci_ring_type type, gfp_t flags) + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { struct xhci_ring *ring; int ret; @@ -370,13 +381,15 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, return NULL; ring->num_segs = num_segs; + ring->bounce_buf_len = max_packet; INIT_LIST_HEAD(&ring->td_list); ring->type = type; if (num_segs == 0) return ring; ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, - &ring->last_seg, num_segs, cycle_state, type, flags); + &ring->last_seg, num_segs, cycle_state, type, + max_packet, flags); if (ret) goto fail; @@ -470,7 +483,8 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, ring->num_segs : num_segs_needed; ret = xhci_alloc_segments_for_ring(xhci, &first, &last, - num_segs, ring->cycle_state, ring->type, flags); + num_segs, ring->cycle_state, ring->type, + ring->bounce_buf_len, flags); if (ret) return -ENOMEM; @@ -652,7 +666,8 @@ struct xhci_ring *xhci_stream_id_to_ring( */ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, - unsigned int num_streams, gfp_t mem_flags) + unsigned int num_streams, + unsigned int max_packet, gfp_t mem_flags) { struct xhci_stream_info *stream_info; u32 cur_stream; @@ -704,9 +719,11 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, * and add their segment DMA addresses to the radix tree. * Stream 0 is reserved. */ + for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, mem_flags); + xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, max_packet, + mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -1003,7 +1020,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, 0, flags); if (!dev->eps[0].ring) goto fail; @@ -1434,22 +1451,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, return -EINVAL; ring_type = usb_endpoint_type(&ep->desc); - /* Set up the endpoint ring */ - virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 2, 1, ring_type, mem_flags); - if (!virt_dev->eps[ep_index].new_ring) { - /* Attempt to use the ring cache */ - if (virt_dev->num_rings_cached == 0) - return -ENOMEM; - virt_dev->num_rings_cached--; - virt_dev->eps[ep_index].new_ring = - virt_dev->ring_cache[virt_dev->num_rings_cached]; - virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL; - xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring, - 1, ring_type); - } - virt_dev->eps[ep_index].skip = false; - ep_ring = virt_dev->eps[ep_index].new_ring; /* * Get values to fill the endpoint context, mostly from ep descriptor. @@ -1479,6 +1480,23 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2)) mult = 0; + /* Set up the endpoint ring */ + virt_dev->eps[ep_index].new_ring = + xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); + if (!virt_dev->eps[ep_index].new_ring) { + /* Attempt to use the ring cache */ + if (virt_dev->num_rings_cached == 0) + return -ENOMEM; + virt_dev->num_rings_cached--; + virt_dev->eps[ep_index].new_ring = + virt_dev->ring_cache[virt_dev->num_rings_cached]; + virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL; + xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring, + 1, ring_type); + } + virt_dev->eps[ep_index].skip = false; + ep_ring = virt_dev->eps[ep_index].new_ring; + /* Fill the endpoint context */ ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | EP_INTERVAL(interval) | @@ -2409,7 +2427,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, 0, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2454,7 +2472,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - flags); + 0, flags); if (!xhci->event_ring) goto fail; if (xhci_check_trb_in_td_math(xhci) < 0) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 81bb8bfdb7e6..b2c861eeeece 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -66,6 +66,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/dma-mapping.h> #include "xhci.h" #include "xhci-trace.h" #include "xhci-mtk.h" @@ -626,6 +627,31 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, } } +void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, struct xhci_ring *ring, + struct xhci_td *td) +{ + struct device *dev = xhci_to_hcd(xhci)->self.controller; + struct xhci_segment *seg = td->bounce_seg; + struct urb *urb = td->urb; + + if (!seg || !urb) + return; + + if (usb_urb_dir_out(urb)) { + dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, + DMA_TO_DEVICE); + return; + } + + /* for in tranfers we need to copy the data from bounce to sg */ + sg_pcopy_from_buffer(urb->sg, urb->num_mapped_sgs, seg->bounce_buf, + seg->bounce_len, seg->bounce_offs); + dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, + DMA_FROM_DEVICE); + seg->bounce_len = 0; + seg->bounce_offs = 0; +} + /* * When we get a command completion for a Stop Endpoint Command, we need to * unlink any cancelled TDs from the ring. There are two ways to do that: @@ -745,6 +771,8 @@ remove_finished_td: /* Doesn't matter what we pass for status, since the core will * just overwrite it (because the URB has been unlinked). */ + if (ep_ring && cur_td->bounce_seg) + xhci_unmap_td_bounce_buffer(xhci, ep_ring, cur_td); xhci_giveback_urb_in_irq(xhci, cur_td, 0); /* Stop processing the cancelled list if the watchdog timer is @@ -767,6 +795,9 @@ static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring) list_del_init(&cur_td->td_list); if (!list_empty(&cur_td->cancelled_td_list)) list_del_init(&cur_td->cancelled_td_list); + + if (cur_td->bounce_seg) + xhci_unmap_td_bounce_buffer(xhci, ring, cur_td); xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN); } } @@ -1865,6 +1896,10 @@ td_cleanup: urb = td->urb; urb_priv = urb->hcpriv; + /* if a bounce buffer was used to align this td then unmap it */ + if (td->bounce_seg) + xhci_unmap_td_bounce_buffer(xhci, ep_ring, td); + /* Do one last check of the actual transfer length. * If the host controller said we transferred more data than the buffer * length, urb->actual_length will be a very big number (since it's @@ -3116,11 +3151,14 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, return (total_packet_count - ((transferred + trb_buff_len) / maxp)); } + static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, - u32 *trb_buff_len) + u32 *trb_buff_len, struct xhci_segment *seg) { + struct device *dev = xhci_to_hcd(xhci)->self.controller; unsigned int unalign; unsigned int max_pkt; + u32 new_buff_len; max_pkt = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); unalign = (enqd_len + *trb_buff_len) % max_pkt; @@ -3129,11 +3167,48 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, if (unalign == 0) return 0; + xhci_dbg(xhci, "Unaligned %d bytes, buff len %d\n", + unalign, *trb_buff_len); + /* is the last nornal TRB alignable by splitting it */ if (*trb_buff_len > unalign) { *trb_buff_len -= unalign; + xhci_dbg(xhci, "split align, new buff len %d\n", *trb_buff_len); return 0; } + + /* + * We want enqd_len + trb_buff_len to sum up to a number aligned to + * number which is divisible by the endpoint's wMaxPacketSize. IOW: + * (size of currently enqueued TRBs + remainder) % wMaxPacketSize == 0. + */ + new_buff_len = max_pkt - (enqd_len % max_pkt); + + if (new_buff_len > (urb->transfer_buffer_length - enqd_len)) + new_buff_len = (urb->transfer_buffer_length - enqd_len); + + /* create a max max_pkt sized bounce buffer pointed to by last trb */ + if (usb_urb_dir_out(urb)) { + sg_pcopy_to_buffer(urb->sg, urb->num_mapped_sgs, + seg->bounce_buf, new_buff_len, enqd_len); + seg->bounce_dma = dma_map_single(dev, seg->bounce_buf, + max_pkt, DMA_TO_DEVICE); + } else { + seg->bounce_dma = dma_map_single(dev, seg->bounce_buf, + max_pkt, DMA_FROM_DEVICE); + } + + if (dma_mapping_error(dev, seg->bounce_dma)) { + /* try without aligning. Some host controllers survive */ + xhci_warn(xhci, "Failed mapping bounce buffer, not aligning\n"); + return 0; + } + *trb_buff_len = new_buff_len; + seg->bounce_len = new_buff_len; + seg->bounce_offs = enqd_len; + + xhci_dbg(xhci, "Bounce align, new buff len %d\n", *trb_buff_len); + return 1; } @@ -3152,9 +3227,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, unsigned int num_trbs; unsigned int start_cycle, num_sgs = 0; unsigned int enqd_len, block_len, trb_buff_len, full_len; - int ret; + int sent_len, ret; u32 field, length_field, remainder; - u64 addr; + u64 addr, send_addr; ring = xhci_urb_to_transfer_ring(xhci, urb); if (!ring) @@ -3194,6 +3269,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, */ start_trb = &ring->enqueue->generic; start_cycle = ring->cycle_state; + send_addr = addr; /* Queue the TRBs, even if they are zero-length */ for (enqd_len = 0; enqd_len < full_len; enqd_len += trb_buff_len) { @@ -3222,10 +3298,16 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (last_trb(xhci, ring, ring->enq_seg, ring->enqueue + 1)) { if (xhci_align_td(xhci, urb, enqd_len, - &trb_buff_len)) - xhci_dbg(xhci, "TRB align fail\n"); + &trb_buff_len, + ring->enq_seg)) { + send_addr = ring->enq_seg->bounce_dma; + /* assuming TD won't span 2 segs */ + td->bounce_seg = ring->enq_seg; + } } - } else { + } + if (enqd_len + trb_buff_len >= full_len) { + field &= ~TRB_CHAIN; field |= TRB_IOC; more_trbs_coming = false; td->last_trb = ring->enqueue; @@ -3244,23 +3326,27 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, TRB_INTR_TARGET(0); queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt, - lower_32_bits(addr), - upper_32_bits(addr), + lower_32_bits(send_addr), + upper_32_bits(send_addr), length_field, field); addr += trb_buff_len; - block_len -= trb_buff_len; + sent_len = trb_buff_len; - if (sg && block_len == 0) { + while (sg && sent_len >= block_len) { /* New sg entry */ --num_sgs; + sent_len -= block_len; if (num_sgs != 0) { sg = sg_next(sg); block_len = sg_dma_len(sg); addr = (u64) sg_dma_address(sg); + addr += sent_len; } } + block_len -= sent_len; + send_addr = addr; } if (need_zero_pkt) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f2f9518c53ab..9da98321d8e6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3139,6 +3139,7 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_input_control_ctx *ctrl_ctx; unsigned int ep_index; unsigned int num_stream_ctxs; + unsigned int max_packet; unsigned long flags; u32 changed_ep_bitmask = 0; @@ -3212,9 +3213,11 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, for (i = 0; i < num_eps; i++) { ep_index = xhci_get_endpoint_index(&eps[i]->desc); + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&eps[i]->desc)); vdev->eps[ep_index].stream_info = xhci_alloc_stream_info(xhci, num_stream_ctxs, - num_streams, mem_flags); + num_streams, + max_packet, mem_flags); if (!vdev->eps[ep_index].stream_info) goto cleanup; /* Set maxPstreams in endpoint context and update deq ptr to diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b0b8d0f8791a..b2c1dc5dc0f3 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1347,6 +1347,11 @@ struct xhci_segment { /* private to HCD */ struct xhci_segment *next; dma_addr_t dma; + /* Max packet sized bounce buffer for td-fragmant alignment */ + dma_addr_t bounce_dma; + void *bounce_buf; + unsigned int bounce_offs; + unsigned int bounce_len; }; struct xhci_td { @@ -1356,6 +1361,7 @@ struct xhci_td { struct xhci_segment *start_seg; union xhci_trb *first_trb; union xhci_trb *last_trb; + struct xhci_segment *bounce_seg; /* actual_length of the URB has already been set */ bool urb_length_set; }; @@ -1405,6 +1411,7 @@ struct xhci_ring { unsigned int num_segs; unsigned int num_trbs_free; unsigned int num_trbs_free_temp; + unsigned int bounce_buf_len; enum xhci_ring_type type; bool last_td_was_short; struct radix_tree_root *trb_address_map; @@ -1807,7 +1814,8 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, unsigned int ep_index); struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, - unsigned int num_streams, gfp_t flags); + unsigned int num_streams, + unsigned int max_packet, gfp_t flags); void xhci_free_stream_info(struct xhci_hcd *xhci, struct xhci_stream_info *stream_info); void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, |