From db62b9a804b465f5050438eb06151c99c625ec9a Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:16 +0200 Subject: [PATCH] usb: dwc2: host: don't use dma_alloc_coherent with irqs disabled Align buffer must be allocated using kmalloc since irqs are disabled. Coherency is handled through dma_map_single which can be used with irqs disabled. Reviewed-by: Julius Werner Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 13 +++++++-- drivers/usb/dwc2/hcd_intr.c | 53 ++++++++++++++++++++++++++---------- drivers/usb/dwc2/hcd_queue.c | 10 ++++--- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index d72557cfc052..745230d0d8b3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -719,9 +719,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* 3072 = 3 max-size Isoc packets */ buf_size = 3072; - qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, - &qh->dw_align_buf_dma, - GFP_ATOMIC); + qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); if (!qh->dw_align_buf) return -ENOMEM; qh->dw_align_buf_size = buf_size; @@ -746,6 +744,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, } } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, + qh->dw_align_buf, qh->dw_align_buf_size, + chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = (dma_addr_t)NULL; + return -EINVAL; + } + chan->align_buf = qh->dw_align_buf_dma; return 0; } diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 6927bba86245..6ea8eb6899f4 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -466,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, } /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && xfer_length && chan->ep_is_in) { + if (chan->align_buf && xfer_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, xfer_length); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -555,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -584,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } /* Skip whole frame */ @@ -926,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); } @@ -1155,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, + xfer_length); } urb->actual_length += xfer_length; diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 63207dc3cb22..9b5c36256627 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - if (hsotg->core_params->dma_desc_enable > 0) + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_qh_free_ddma(hsotg, qh); - else if (qh->dw_align_buf) - dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, - qh->dw_align_buf, qh->dw_align_buf_dma); + } else { + /* kfree(NULL) is safe */ + kfree(qh->dw_align_buf); + qh->dw_align_buf_dma = (dma_addr_t)0; + } kfree(qh); } -- 2.20.1