On Thu, Nov 22, 2018 at 10:29:48AM +0000, Russell King - ARM Linux wrote: > On Tue, Nov 20, 2018 at 11:04:06PM +0200, Aaro Koskinen wrote: > > I had switched to PIO mode in 2015 since the WARNs about legacy DMA > > API were too annoying and flooding the console. And now that I tried > > using DMA again with g_ether, it doesn't work anymore. The device get's > > recognized on host side, but no traffic goes through. Switching back to > > PIO makes it to work again. > > A solution to that would be to do what the warning message says, and > update the driver to the DMAengine API. Here's a partial conversion (not even build tested) - it only supports OUT transfers with dmaengine at the moment. There's at least one thing that doesn't make sense - the driver apparently can transfer more than req->length bytes, surely if it does that, it's a serious problem - shouldn't it be noisy about that? Also we can't deal with the omap_set_dma_dest_burst_mode() setting - DMAengine always uses a 64 byte burst, but udc wants a smaller burst setting. Does this matter? I've kept the old code for reference (and the driver will fall back if we can't get a dmaengine channel.) I haven't been through and checked that we result in the channel setup largely the same either. There will be one major difference - UDC uses element sync, where an element is 16bits, ep->ep.maxpacket/2 in a frame, and "packets" frames. DMAengine is using frame sync, with a 16bit element, one element in a frame, and packets*ep->ep.maxpacket/2 frames. This should be functionally equivalent but I'd like confirmation of that. I'm also not sure about this: if (cpu_is_omap15xx()) end++; in dma_dest_len() - is that missing from the omap-dma driver? It looks like a work-around for some problem on OMAP15xx, but I can't make sense about why it's in the UDC driver rather than the legacy DMA driver. I'm also confused by: end |= start & (0xffff << 16); also in dma_dest_len() - omap_get_dma_dst_pos() returns in the high 16 bits the full address: if (dma_omap1()) offset |= (p->dma_read(CDSA, lch) & 0xFFFF0000); so if the address crosses a 64k physical address boundary, surely orring in the start address is wrong? The more I look at dma_dest_len(), the more I wonder whether that and dma_src_len() are anywhere near correct, and whether that is a source of breakage for Aaro. As I've already said, I've no way to test this on hardware. Please review and let me know whether I missed anything on the OUT handling path. Fixing the IN path is going to be a bit more head-scratching. drivers/usb/gadget/udc/omap_udc.c | 154 +++++++++++++++++++++++++++++--------- drivers/usb/gadget/udc/omap_udc.h | 2 + 2 files changed, 120 insertions(+), 36 deletions(-) diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 3a16431da321..a37e1d2f0f3e 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -204,6 +204,7 @@ static int omap_ep_enable(struct usb_ep *_ep, ep->dma_channel = 0; ep->has_dma = 0; ep->lch = -1; + ep->dma = NULL; use_ep(ep, UDC_EP_SEL); omap_writew(udc->clr_halt, UDC_CTRL); ep->ackwait = 0; @@ -576,21 +577,49 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) static void next_out_dma(struct omap_ep *ep, struct omap_req *req) { unsigned packets = req->req.length - req->req.actual; - int dma_trigger = 0; + struct dma_async_tx_descriptor *tx; + struct dma_chan *dma = ep->dma; + dma_cookie_t cookie; u16 w; - /* set up this DMA transfer, enable the fifo, start */ - packets /= ep->ep.maxpacket; - packets = min(packets, (unsigned)UDC_RXN_TC + 1); + packets = min_t(unsigned, packets / ep->ep.maxpacket, UDC_RXN_TC + 1); req->dma_bytes = packets * ep->ep.maxpacket; - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, - ep->ep.maxpacket >> 1, packets, - OMAP_DMA_SYNC_ELEMENT, - dma_trigger, 0); - omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, - 0, 0); - ep->dma_counter = omap_get_dma_dst_pos(ep->lch); + + if (dma) { + struct dma_slave_config cfg = { + .direction = DMA_DEV_TO_MEM, + .src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTE, + /* + * DMAengine uses frame sync mode, setting maxburst=1 + * is equivalent to element sync mode. + */ + .src_maxburst = 1, + .src_addr = UDC_DATA_DMA, + }; + + if (WARN_ON(dmaengine_slave_config(dma, &cfg))) + return; + + tx = dmaengine_prep_slave_single(dma, + req->req.dma + req->req.actual, + req->dma_bytes, + DMA_DEV_TO_MEM, 0); + if (WARN_ON(!tx)) + return; + } else { + int dma_trigger = 0; + + /* set up this DMA transfer, enable the fifo, start */ + /* dt = S16, cen = ep->ep.maxpacket / 2, cfn = packets */ + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, + ep->ep.maxpacket >> 1, packets, + OMAP_DMA_SYNC_ELEMENT, + dma_trigger, 0); + omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, + 0, 0); + ep->dma_counter = omap_get_dma_dst_pos(ep->lch); + } omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel)); w = omap_readw(UDC_DMA_IRQ_EN); @@ -599,7 +628,15 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) omap_writew(ep->bEndpointAddress & 0xf, UDC_EP_NUM); omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); - omap_start_dma(ep->lch); + if (dma) { + cookie = dmaengine_submit(tx); + if (WARN_ON(dma_submit_error(cookie))) + return; + ep->dma_cookie = cookie; + dma_async_issue_pending(dma); + } else { + omap_start_dma(ep->lch); + } } static void @@ -607,21 +644,39 @@ finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one) { u16 count, w; - if (status == 0) - ep->dma_counter = (u16) (req->req.dma + req->req.actual); - count = dma_dest_len(ep, req->req.dma + req->req.actual); + if (ep->dma) { + struct dma_tx_state state; + + dmaengine_tx_status(ep->dma, ep->dma_cookie, &state); + + count = req->dma_bytes - state.residual; + } else { + if (status == 0) + ep->dma_counter = (u16) (req->req.dma + req->req.actual); + count = dma_dest_len(ep, req->req.dma + req->req.actual); + } + count += req->req.actual; if (one) count--; + + /* + * FIXME: Surely if count > req->req.length, something has gone + * seriously wrong and we've scribbled over memory we should not... + * so surely we should be a WARN_ON() at the very least? + */ if (count <= req->req.length) req->req.actual = count; - if (count != req->dma_bytes || status) - omap_stop_dma(ep->lch); - + if (count != req->dma_bytes || status) { + if (ep->dma) + dmaengine_terminate_async(ep->dma); + else + omap_stop_dma(ep->lch); /* if this wasn't short, request may need another transfer */ - else if (req->req.actual < req->req.length) + } else if (req->req.actual < req->req.length) { return; + } /* rx completion */ w = omap_readw(UDC_DMA_IRQ_EN); @@ -709,6 +764,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ep->dma_channel = 0; ep->lch = -1; + ep->dma = NULL; if (channel == 0 || channel > 3) { if ((reg & 0x0f00) == 0) channel = 3; @@ -742,26 +798,44 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) 0, 0); } } else { + struct dma_chan *dma; + dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel; - status = omap_request_dma(dma_channel, - ep->ep.name, dma_error, ep, &ep->lch); - if (status == 0) { + + dma = __dma_request_channel(&mask, omap_dma_filter_fn, + (void *)dma_channel); + if (dma) { + ep->dma = dma; omap_writew(reg, UDC_RXDMA_CFG); - /* TIPB */ - omap_set_dma_src_params(ep->lch, - OMAP_DMA_PORT_TIPB, - OMAP_DMA_AMODE_CONSTANT, - UDC_DATA_DMA, - 0, 0); - /* EMIFF or SDRC */ - omap_set_dma_dest_burst_mode(ep->lch, - OMAP_DMA_DATA_BURST_4); - omap_set_dma_dest_data_pack(ep->lch, 1); + } else { + status = omap_request_dma(dma_channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { + omap_writew(reg, UDC_RXDMA_CFG); + /* TIPB */ + omap_set_dma_src_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + UDC_DATA_DMA, + 0, 0); + /* EMIFF or SDRC */ + /* + * not ok - CSDP_DST_BURST_64 selected, but this selects + * CSDP_DST_BURST_16 on omap2+ and CSDP_DST_BURST_32 on + * omap1. + */ + omap_set_dma_dest_burst_mode(ep->lch, + OMAP_DMA_DATA_BURST_4); + /* ok - CSDP_DST_PACKED set for dmaengine */ + omap_set_dma_dest_data_pack(ep->lch, 1); + } } } - if (status) + if (d->dma) { + ep->has_dma = 1; + } else if (status) { ep->dma_channel = 0; - else { + } else { ep->has_dma = 1; omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); @@ -777,6 +851,10 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) if (status) DBG("%s no dma channel: %d%s\n", ep->ep.name, status, restart ? " (restart)" : ""); + else if (d->dma) + DBG("%s claimed %cxdma%d dmaengine %s%s\n", ep->ep.name, + is_in ? 't' : 'r', ep->dma_channel - 1, + dma_chan_name(d->dma), restart ? " (restart)" : ""); else DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name, is_in ? 't' : 'r', @@ -850,9 +928,13 @@ static void dma_channel_release(struct omap_ep *ep) if (req) finish_out_dma(ep, req, -ECONNRESET, 0); } - omap_free_dma(ep->lch); + if (ep->dma) + dma_release_channel(ep->dma); + else + omap_free_dma(ep->lch); ep->dma_channel = 0; ep->lch = -1; + ep->dma = NULL; /* has_dma still set, till endpoint is fully quiesced */ } diff --git a/drivers/usb/gadget/udc/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h index 00f9e608e755..68857ae8d763 100644 --- a/drivers/usb/gadget/udc/omap_udc.h +++ b/drivers/usb/gadget/udc/omap_udc.h @@ -153,6 +153,8 @@ struct omap_ep { u8 dma_channel; u16 dma_counter; int lch; + struct dma_chan *dma; + dma_cookie_t dma_cookie; struct omap_udc *udc; struct timer_list timer; }; -- RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up According to speedtest.net: 11.9Mbps down 500kbps up