Patchwork dmaengine: ti: omap-dma: Configure LCH_TYPE for OMAP1

login
register
mail settings
Submitter Russell King - ARM Linux
Date Nov. 23, 2018, 11:27 p.m.
Message ID <20181123232746.GA18875@n2100.armlinux.org.uk>
Download mbox | patch
Permalink /patch/664073/
State New
Headers show

Comments

Russell King - ARM Linux - Nov. 23, 2018, 11:27 p.m.
On Fri, Nov 23, 2018 at 04:16:59PM +0000, Russell King - ARM Linux wrote:
> Hi Peter,
> 
> Here's the patch, which should now support IN as well as OUT.
> Completely untested, as mentioned before.

Now compile tested...

 drivers/usb/gadget/udc/omap_udc.c | 291 ++++++++++++++++++--------------------
 drivers/usb/gadget/udc/omap_udc.h |   3 +-
 2 files changed, 137 insertions(+), 157 deletions(-)

Patch

diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 3a16431da321..dd85476ec234 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -28,6 +28,7 @@ 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
+#include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/clk.h>
 #include <linux/err.h>
@@ -203,7 +204,7 @@  static int omap_ep_enable(struct usb_ep *_ep,
 	/* set endpoint to initial state */
 	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;
@@ -468,43 +469,6 @@  static int read_fifo(struct omap_ep *ep, struct omap_req *req)
 
 /*-------------------------------------------------------------------------*/
 
-static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
-{
-	dma_addr_t	end;
-
-	/* IN-DMA needs this on fault/cancel paths, so 15xx misreports
-	 * the last transfer's bytecount by more than a FIFO's worth.
-	 */
-	if (cpu_is_omap15xx())
-		return 0;
-
-	end = omap_get_dma_src_pos(ep->lch);
-	if (end == ep->dma_counter)
-		return 0;
-
-	end |= start & (0xffff << 16);
-	if (end < start)
-		end += 0x10000;
-	return end - start;
-}
-
-static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start)
-{
-	dma_addr_t	end;
-
-	end = omap_get_dma_dst_pos(ep->lch);
-	if (end == ep->dma_counter)
-		return 0;
-
-	end |= start & (0xffff << 16);
-	if (cpu_is_omap15xx())
-		end++;
-	if (end < start)
-		end += 0x10000;
-	return end - start;
-}
-
-
 /* Each USB transfer request using DMA maps to one or more DMA transfers.
  * When DMA completion isn't request completion, the UDC continues with
  * the next DMA transfer for that USB transfer.
@@ -512,34 +476,53 @@  static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start)
 
 static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
 {
-	u16		txdma_ctrl, w;
-	unsigned	length = req->req.length - req->req.actual;
-	const int	sync_mode = cpu_is_omap15xx()
-				? OMAP_DMA_SYNC_FRAME
-				: OMAP_DMA_SYNC_ELEMENT;
-	int		dma_trigger = 0;
+	struct dma_async_tx_descriptor *tx;
+	struct dma_chan *dma = ep->dma;
+	dma_cookie_t cookie;
+	unsigned burst, length;
+	u16 txdma_ctrl, w;
+	struct dma_slave_config omap_udc_in_cfg = {
+		.direction = DMA_MEM_TO_DEV,
+		.dst_addr = UDC_DATA_DMA,
+	};
+
+	length = req->req.length - req->req.actual;
 
 	/* measure length in either bytes or packets */
-	if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
-			|| (cpu_is_omap15xx() && length < ep->maxpacket)) {
+	if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC) ||
+	    (cpu_is_omap15xx() && length < ep->maxpacket)) {
+		omap_udc_in_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
 		txdma_ctrl = UDC_TXN_EOT | length;
-		omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
-				length, 1, sync_mode, dma_trigger, 0);
+		burst = length;
 	} else {
-		length = min(length / ep->maxpacket,
-				(unsigned) UDC_TXN_TSC + 1);
+		omap_udc_in_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+		length = min_t(unsigned, length / ep->maxpacket,
+		               UDC_TXN_TSC + 1);
 		txdma_ctrl = length;
-		omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
-				ep->ep.maxpacket >> 1, length, sync_mode,
-				dma_trigger, 0);
 		length *= ep->maxpacket;
+		burst = ep->ep.maxpacket >> 1;
 	}
-	omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF,
-		OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
-		0, 0);
 
-	omap_start_dma(ep->lch);
-	ep->dma_counter = omap_get_dma_src_pos(ep->lch);
+	if (!cpu_is_omap15xx())
+		burst = 1;
+
+	omap_udc_in_cfg.dst_maxburst = burst;
+
+	if (WARN_ON(dmaengine_slave_config(dma, &omap_udc_in_cfg)))
+		return;
+
+	tx = dmaengine_prep_slave_single(dma, req->req.dma + req->req.actual,
+					 length, DMA_MEM_TO_DEV, 0);
+	if (WARN_ON(!tx))
+		return;
+
+	cookie = dmaengine_submit(tx);
+	if (WARN_ON(dma_submit_error(cookie)))
+		return;
+
+	ep->dma_cookie = cookie;
+	dma_async_issue_pending(dma);
+
 	w = omap_readw(UDC_DMA_IRQ_EN);
 	w |= UDC_TX_DONE_IE(ep->dma_channel);
 	omap_writew(w, UDC_DMA_IRQ_EN);
@@ -549,11 +532,14 @@  static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
 
 static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status)
 {
+	struct dma_tx_state state;
 	u16 w;
 
-	if (status == 0) {
-		req->req.actual += req->dma_bytes;
+	dmaengine_tx_status(ep->dma, ep->dma_cookie, &state);
+
+	req->req.actual += req->dma_bytes - state.residue;
 
+	if (status == 0) {
 		/* return if this request needs to send data or zlp */
 		if (req->req.actual < req->req.length)
 			return;
@@ -561,36 +547,47 @@  static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status)
 				&& req->dma_bytes != 0
 				&& (req->req.actual % ep->maxpacket) == 0)
 			return;
-	} else
-		req->req.actual += dma_src_len(ep, req->req.dma
-							+ req->req.actual);
+	}
 
 	/* tx completion */
-	omap_stop_dma(ep->lch);
+	dmaengine_terminate_async(ep->dma);
+
 	w = omap_readw(UDC_DMA_IRQ_EN);
 	w &= ~UDC_TX_DONE_IE(ep->dma_channel);
 	omap_writew(w, UDC_DMA_IRQ_EN);
 	done(ep, req, status);
 }
 
+static struct dma_slave_config omap_udc_out_cfg = {
+	.direction = DMA_DEV_TO_MEM,
+	.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+	/*
+	 * DMAengine uses frame sync mode, setting maxburst=1
+	 * is equivalent to element sync mode.
+	 */
+	.src_maxburst = 1,
+	.src_addr = UDC_DATA_DMA,
+};
+
 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;
+	unsigned packets, length;
 	u16 w;
 
-	/* set up this DMA transfer, enable the fifo, start */
-	packets /= ep->ep.maxpacket;
-	packets = min(packets, (unsigned)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);
+	length = req->req.length - req->req.actual;
+	packets = min_t(unsigned, length / ep->ep.maxpacket, UDC_RXN_TC + 1);
+	length = packets * ep->ep.maxpacket;
+
+	if (WARN_ON(dmaengine_slave_config(dma, &omap_udc_out_cfg)))
+		return;
+
+	tx = dmaengine_prep_slave_single(dma, req->req.dma + req->req.actual,
+					 length, DMA_DEV_TO_MEM, 0);
+	if (WARN_ON(!tx))
+		return;
 
 	omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel));
 	w = omap_readw(UDC_DMA_IRQ_EN);
@@ -599,29 +596,42 @@  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);
+	cookie = dmaengine_submit(tx);
+	if (WARN_ON(dma_submit_error(cookie)))
+		return;
+
+	ep->dma_cookie = cookie;
+	dma_async_issue_pending(dma);
+	req->dma_bytes = length;
 }
 
 static void
 finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one)
 {
+	struct dma_tx_state state;
 	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);
+	dmaengine_tx_status(ep->dma, ep->dma_cookie, &state);
+
+	count = req->dma_bytes - state.residue;
 	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) {
+		dmaengine_terminate_async(ep->dma);
 	/* 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);
@@ -683,32 +693,25 @@  static void dma_irq(struct omap_udc *udc, u16 irq_src)
 	}
 }
 
-static void dma_error(int lch, u16 ch_status, void *data)
-{
-	struct omap_ep	*ep = data;
-
-	/* if ch_status & OMAP_DMA_DROP_IRQ ... */
-	/* if ch_status & OMAP1_DMA_TOUT_IRQ ... */
-	ERR("%s dma error, lch %d status %02x\n", ep->ep.name, lch, ch_status);
-
-	/* complete current transfer ... */
-}
-
 static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
 {
+	dma_cap_mask_t mask;
+	struct dma_chan *dma;
+	u32 dma_cfg;
 	u16	reg;
 	int	status, restart, is_in;
 	int	dma_channel;
 
 	is_in = ep->bEndpointAddress & USB_DIR_IN;
 	if (is_in)
-		reg = omap_readw(UDC_TXDMA_CFG);
+		dma_cfg = UDC_TXDMA_CFG;
 	else
-		reg = omap_readw(UDC_RXDMA_CFG);
+		dma_cfg = UDC_RXDMA_CFG;
+	reg = omap_readw(dma_cfg);
 	reg |= UDC_DMA_REQ;		/* "pulse" activated */
 
 	ep->dma_channel = 0;
-	ep->lch = -1;
+	ep->dma = NULL;
 	if (channel == 0 || channel > 3) {
 		if ((reg & 0x0f00) == 0)
 			channel = 3;
@@ -722,65 +725,38 @@  static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
 		}
 	}
 	reg |= (0x0f & ep->bEndpointAddress) << (4 * (channel - 1));
-	ep->dma_channel = channel;
 
-	if (is_in) {
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	if (is_in)
 		dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
-		status = omap_request_dma(dma_channel,
-			ep->ep.name, dma_error, ep, &ep->lch);
-		if (status == 0) {
-			omap_writew(reg, UDC_TXDMA_CFG);
-			/* EMIFF or SDRC */
-			omap_set_dma_src_burst_mode(ep->lch,
-						OMAP_DMA_DATA_BURST_4);
-			omap_set_dma_src_data_pack(ep->lch, 1);
-			/* TIPB */
-			omap_set_dma_dest_params(ep->lch,
-				OMAP_DMA_PORT_TIPB,
-				OMAP_DMA_AMODE_CONSTANT,
-				UDC_DATA_DMA,
-				0, 0);
-		}
-	} else {
+	else
 		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) {
-			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);
-		}
-	}
-	if (status)
-		ep->dma_channel = 0;
-	else {
-		ep->has_dma = 1;
-		omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
 
-		/* channel type P: hw synch (fifo) */
-		if (!cpu_is_omap15xx())
-			omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P);
+	dma = __dma_request_channel(&mask, omap_dma_filter_fn,
+				    (void *)dma_channel);
+	if (dma) {
+		omap_writew(reg, dma_cfg);
+		ep->dma_channel = channel;
+		ep->dma = dma;
+		ep->has_dma = 1;
+		status = 0;
+	} else {
+		ep->dma_channel = 0;
+		status = -EINVAL;
 	}
 
 just_restart:
 	/* restart any queue, even if the claim failed  */
 	restart = !ep->stopped && !list_empty(&ep->queue);
 
-	if (status)
-		DBG("%s no dma channel: %d%s\n", ep->ep.name, status,
-			restart ? " (restart)" : "");
+	if (ep->dma)
+		DBG("%s claimed %cxdma%d dmaengine %s%s\n", ep->ep.name,
+			is_in ? 't' : 'r', ep->dma_channel - 1,
+			dma_chan_name(ep->dma), restart ? " (restart)" : "");
 	else
-		DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name,
-			is_in ? 't' : 'r',
-			ep->dma_channel - 1, ep->lch,
+		DBG("%s no dma channel: %d%s\n", ep->ep.name, status,
 			restart ? " (restart)" : "");
 
 	if (restart) {
@@ -814,7 +790,8 @@  static void dma_channel_release(struct omap_ep *ep)
 	else
 		req = NULL;
 
-	active = omap_get_dma_active_status(ep->lch);
+	active = dma_async_is_tx_complete(ep->dma, ep->dma_cookie, NULL, NULL)
+			== DMA_IN_PROGRESS;
 
 	DBG("%s release %s %cxdma%d %p\n", ep->ep.name,
 			active ? "active" : "idle",
@@ -850,9 +827,9 @@  static void dma_channel_release(struct omap_ep *ep)
 		if (req)
 			finish_out_dma(ep, req, -ECONNRESET, 0);
 	}
-	omap_free_dma(ep->lch);
+	dma_release_channel(ep->dma);
 	ep->dma_channel = 0;
-	ep->lch = -1;
+	ep->dma = NULL;
 	/* has_dma still set, till endpoint is fully quiesced */
 }
 
@@ -2146,9 +2123,9 @@  static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
 	use_ep(ep, 0);
 
 	if (use_dma && ep->has_dma)
-		snprintf(buf, sizeof buf, "(%cxdma%d lch%d) ",
+		snprintf(buf, sizeof buf, "(%cxdma%d dma %s) ",
 			(ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r',
-			ep->dma_channel - 1, ep->lch);
+			ep->dma_channel - 1, dma_chan_name(ep->dma));
 	else
 		buf[0] = 0;
 
@@ -2194,9 +2171,11 @@  static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
 			unsigned	length = req->req.actual;
 
 			if (use_dma && buf[0]) {
-				length += ((ep->bEndpointAddress & USB_DIR_IN)
-						? dma_src_len : dma_dest_len)
-					(ep, req->req.dma + length);
+				struct dma_tx_state state;
+
+				dmaengine_tx_status(ep->dma, ep->dma_cookie,
+						    &state);
+				length += req->dma_bytes - state.residue;
 				buf[0] = 0;
 			}
 			seq_printf(s, "\treq %p len %d/%d buf %p\n",
diff --git a/drivers/usb/gadget/udc/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h
index 00f9e608e755..e04c48f669ed 100644
--- a/drivers/usb/gadget/udc/omap_udc.h
+++ b/drivers/usb/gadget/udc/omap_udc.h
@@ -152,7 +152,8 @@  struct omap_ep {
 	u8				ackwait;
 	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;
 };