diff options
| author | Mark Kettenis <kettenis@openbsd.org> | 2023-01-21 20:27:55 +0100 | 
|---|---|---|
| committer | Tom Rini <trini@konsulko.com> | 2023-01-27 14:47:58 -0500 | 
| commit | ba1efb3d2494e8ceb0ff405642f44a955f8d8e1b (patch) | |
| tree | 9785b311bfffb082a65d4ec99248e940a7a77ada /drivers/usb/host/xhci-mem.c | |
| parent | 862b4a0f32ae395dac147542c3336ae8a0c47d69 (diff) | |
usb: xhci: Implement DMA mapping
An XHCI controller that sits behind an IOMMU needs to map and unmap
its memory buffers to do DMA.  Implement this by inroducing new
xhci_dma_map() and xhci_dma_unmap() helper functions.  The
xhci_dma_map() function replaces the existing xhci_virt_to_bus()
function in the sense that it returns the bus address in the case
of simple address translation in the absence of an IOMMU.  The
xhci_bus_to_virt() function is eliminated by storing the CPU
address of the allocated scratchpad memory in struct xhci_ctrl.
Signed-off-by: Mark Kettenis <kettenis@openbsd.org>
Reviewed-by: Marek Vasut <marex@denx.de>
Diffstat (limited to 'drivers/usb/host/xhci-mem.c')
| -rw-r--r-- | drivers/usb/host/xhci-mem.c | 84 | 
1 files changed, 53 insertions, 31 deletions
| diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 21cd03b662a..72b75306265 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -64,8 +64,9 @@ void xhci_inval_cache(uintptr_t addr, u32 len)   * @param ptr	pointer to "segement" to be freed   * Return: none   */ -static void xhci_segment_free(struct xhci_segment *seg) +static void xhci_segment_free(struct xhci_ctrl *ctrl, struct xhci_segment *seg)  { +	xhci_dma_unmap(ctrl, seg->dma, SEGMENT_SIZE);  	free(seg->trbs);  	seg->trbs = NULL; @@ -78,7 +79,7 @@ static void xhci_segment_free(struct xhci_segment *seg)   * @param ptr	pointer to "ring" to be freed   * Return: none   */ -static void xhci_ring_free(struct xhci_ring *ring) +static void xhci_ring_free(struct xhci_ctrl *ctrl, struct xhci_ring *ring)  {  	struct xhci_segment *seg;  	struct xhci_segment *first_seg; @@ -89,10 +90,10 @@ static void xhci_ring_free(struct xhci_ring *ring)  	seg = first_seg->next;  	while (seg != first_seg) {  		struct xhci_segment *next = seg->next; -		xhci_segment_free(seg); +		xhci_segment_free(ctrl, seg);  		seg = next;  	} -	xhci_segment_free(first_seg); +	xhci_segment_free(ctrl, first_seg);  	free(ring);  } @@ -105,12 +106,20 @@ static void xhci_ring_free(struct xhci_ring *ring)   */  static void xhci_scratchpad_free(struct xhci_ctrl *ctrl)  { +	struct xhci_hccr *hccr = ctrl->hccr; +	int num_sp; +  	if (!ctrl->scratchpad)  		return; +	num_sp = HCS_MAX_SCRATCHPAD(xhci_readl(&hccr->cr_hcsparams2)); +	xhci_dma_unmap(ctrl, ctrl->scratchpad->sp_array[0], +		       num_sp * ctrl->page_size); +	xhci_dma_unmap(ctrl, ctrl->dcbaa->dev_context_ptrs[0], +		       num_sp * sizeof(u64));  	ctrl->dcbaa->dev_context_ptrs[0] = 0; -	free(xhci_bus_to_virt(ctrl, le64_to_cpu(ctrl->scratchpad->sp_array[0]))); +	free(ctrl->scratchpad->scratchpad);  	free(ctrl->scratchpad->sp_array);  	free(ctrl->scratchpad);  	ctrl->scratchpad = NULL; @@ -122,8 +131,10 @@ static void xhci_scratchpad_free(struct xhci_ctrl *ctrl)   * @param ptr	pointer to "xhci_container_ctx" to be freed   * Return: none   */ -static void xhci_free_container_ctx(struct xhci_container_ctx *ctx) +static void xhci_free_container_ctx(struct xhci_ctrl *ctrl, +				    struct xhci_container_ctx *ctx)  { +	xhci_dma_unmap(ctrl, ctx->dma, ctx->size);  	free(ctx->bytes);  	free(ctx);  } @@ -153,12 +164,12 @@ static void xhci_free_virt_devices(struct xhci_ctrl *ctrl)  		for (i = 0; i < 31; ++i)  			if (virt_dev->eps[i].ring) -				xhci_ring_free(virt_dev->eps[i].ring); +				xhci_ring_free(ctrl, virt_dev->eps[i].ring);  		if (virt_dev->in_ctx) -			xhci_free_container_ctx(virt_dev->in_ctx); +			xhci_free_container_ctx(ctrl, virt_dev->in_ctx);  		if (virt_dev->out_ctx) -			xhci_free_container_ctx(virt_dev->out_ctx); +			xhci_free_container_ctx(ctrl, virt_dev->out_ctx);  		free(virt_dev);  		/* make sure we are pointing to NULL */ @@ -174,11 +185,15 @@ static void xhci_free_virt_devices(struct xhci_ctrl *ctrl)   */  void xhci_cleanup(struct xhci_ctrl *ctrl)  { -	xhci_ring_free(ctrl->event_ring); -	xhci_ring_free(ctrl->cmd_ring); +	xhci_ring_free(ctrl, ctrl->event_ring); +	xhci_ring_free(ctrl, ctrl->cmd_ring);  	xhci_scratchpad_free(ctrl);  	xhci_free_virt_devices(ctrl); +	xhci_dma_unmap(ctrl, ctrl->erst.erst_dma_addr, +		       sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);  	free(ctrl->erst.entries); +	xhci_dma_unmap(ctrl, ctrl->dcbaa->dma, +		       sizeof(struct xhci_device_context_array));  	free(ctrl->dcbaa);  	memset(ctrl, '\0', sizeof(struct xhci_ctrl));  } @@ -218,15 +233,13 @@ static void xhci_link_segments(struct xhci_ctrl *ctrl, struct xhci_segment *prev  			       struct xhci_segment *next, bool link_trbs)  {  	u32 val; -	u64 val_64 = 0;  	if (!prev || !next)  		return;  	prev->next = next;  	if (link_trbs) { -		val_64 = xhci_virt_to_bus(ctrl, next->trbs);  		prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = -			cpu_to_le64(val_64); +			cpu_to_le64(next->dma);  		/*  		 * Set the last TRB in the segment to @@ -273,7 +286,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring)   * @param	none   * Return: pointer to the newly allocated SEGMENT   */ -static struct xhci_segment *xhci_segment_alloc(void) +static struct xhci_segment *xhci_segment_alloc(struct xhci_ctrl *ctrl)  {  	struct xhci_segment *seg; @@ -281,6 +294,7 @@ static struct xhci_segment *xhci_segment_alloc(void)  	BUG_ON(!seg);  	seg->trbs = xhci_malloc(SEGMENT_SIZE); +	seg->dma = xhci_dma_map(ctrl, seg->trbs, SEGMENT_SIZE);  	seg->next = NULL; @@ -314,7 +328,7 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_ctrl *ctrl, unsigned int num_segs,  	if (num_segs == 0)  		return ring; -	ring->first_seg = xhci_segment_alloc(); +	ring->first_seg = xhci_segment_alloc(ctrl);  	BUG_ON(!ring->first_seg);  	num_segs--; @@ -323,7 +337,7 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_ctrl *ctrl, unsigned int num_segs,  	while (num_segs > 0) {  		struct xhci_segment *next; -		next = xhci_segment_alloc(); +		next = xhci_segment_alloc(ctrl);  		BUG_ON(!next);  		xhci_link_segments(ctrl, prev, next, link_trbs); @@ -372,7 +386,8 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl)  	if (!scratchpad->sp_array)  		goto fail_sp2; -	val_64 = xhci_virt_to_bus(ctrl, scratchpad->sp_array); +	val_64 = xhci_dma_map(ctrl, scratchpad->sp_array, +			      num_sp * sizeof(u64));  	ctrl->dcbaa->dev_context_ptrs[0] = cpu_to_le64(val_64);  	xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[0], @@ -386,16 +401,18 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl)  	}  	BUG_ON(i == 16); -	page_size = 1 << (i + 12); -	buf = memalign(page_size, num_sp * page_size); +	ctrl->page_size = 1 << (i + 12); +	buf = memalign(ctrl->page_size, num_sp * ctrl->page_size);  	if (!buf)  		goto fail_sp3; -	memset(buf, '\0', num_sp * page_size); -	xhci_flush_cache((uintptr_t)buf, num_sp * page_size); +	memset(buf, '\0', num_sp * ctrl->page_size); +	xhci_flush_cache((uintptr_t)buf, num_sp * ctrl->page_size); +	scratchpad->scratchpad = buf; +	val_64 = xhci_dma_map(ctrl, buf, num_sp * ctrl->page_size);  	for (i = 0; i < num_sp; i++) { -		val_64 = xhci_virt_to_bus(ctrl, buf + i * page_size);  		scratchpad->sp_array[i] = cpu_to_le64(val_64); +		val_64 += ctrl->page_size;  	}  	xhci_flush_cache((uintptr_t)scratchpad->sp_array, @@ -437,6 +454,7 @@ static struct xhci_container_ctx  		ctx->size += CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams));  	ctx->bytes = xhci_malloc(ctx->size); +	ctx->dma = xhci_dma_map(ctrl, ctx->bytes, ctx->size);  	return ctx;  } @@ -487,7 +505,7 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)  	/* Allocate endpoint 0 ring */  	virt_dev->eps[0].ring = xhci_ring_alloc(ctrl, 1, true); -	byte_64 = xhci_virt_to_bus(ctrl, virt_dev->out_ctx->bytes); +	byte_64 = virt_dev->out_ctx->dma;  	/* Point to output device context in dcbaa. */  	ctrl->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(byte_64); @@ -523,15 +541,16 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,  		return -ENOMEM;  	} -	val_64 = xhci_virt_to_bus(ctrl, ctrl->dcbaa); +	ctrl->dcbaa->dma = xhci_dma_map(ctrl, ctrl->dcbaa, +				sizeof(struct xhci_device_context_array));  	/* Set the pointer in DCBAA register */ -	xhci_writeq(&hcor->or_dcbaap, val_64); +	xhci_writeq(&hcor->or_dcbaap, ctrl->dcbaa->dma);  	/* Command ring control pointer register initialization */  	ctrl->cmd_ring = xhci_ring_alloc(ctrl, 1, true);  	/* Set the address in the Command Ring Control register */ -	trb_64 = xhci_virt_to_bus(ctrl, ctrl->cmd_ring->first_seg->trbs); +	trb_64 = ctrl->cmd_ring->first_seg->dma;  	val_64 = xhci_readq(&hcor->or_crcr);  	val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |  		(trb_64 & (u64) ~CMD_RING_RSVD_BITS) | @@ -555,6 +574,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,  	ctrl->event_ring = xhci_ring_alloc(ctrl, ERST_NUM_SEGS, false);  	ctrl->erst.entries = xhci_malloc(sizeof(struct xhci_erst_entry) *  					 ERST_NUM_SEGS); +	ctrl->erst.erst_dma_addr = xhci_dma_map(ctrl, ctrl->erst.entries, +			sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);  	ctrl->erst.num_entries = ERST_NUM_SEGS; @@ -562,7 +583,7 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,  			val < ERST_NUM_SEGS;  			val++) {  		struct xhci_erst_entry *entry = &ctrl->erst.entries[val]; -		trb_64 = xhci_virt_to_bus(ctrl, seg->trbs); +		trb_64 = seg->dma;  		entry->seg_addr = cpu_to_le64(trb_64);  		entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);  		entry->rsvd = 0; @@ -571,7 +592,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,  	xhci_flush_cache((uintptr_t)ctrl->erst.entries,  			 ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); -	deq = xhci_virt_to_bus(ctrl, ctrl->event_ring->dequeue); +	deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg, +				   ctrl->event_ring->dequeue);  	/* Update HC event ring dequeue pointer */  	xhci_writeq(&ctrl->ir_set->erst_dequeue, @@ -586,7 +608,7 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,  	/* this is the event ring segment table pointer */  	val_64 = xhci_readq(&ctrl->ir_set->erst_base);  	val_64 &= ERST_PTR_MASK; -	val_64 |= xhci_virt_to_bus(ctrl, ctrl->erst.entries) & ~ERST_PTR_MASK; +	val_64 |= ctrl->erst.erst_dma_addr & ~ERST_PTR_MASK;  	xhci_writeq(&ctrl->ir_set->erst_base, val_64); @@ -849,7 +871,7 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,  	/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */  	ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3)); -	trb_64 = xhci_virt_to_bus(ctrl, virt_dev->eps[0].ring->first_seg->trbs); +	trb_64 = virt_dev->eps[0].ring->first_seg->dma;  	ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state);  	/* | 
