summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-30 15:41:06 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-30 15:41:06 -0600
commitad374ec9ecf01e605caa8bc762f9cfe657040373 (patch)
treed96a55509fd402f2e850f6274e9c3efae920968d
parentf7882286445bda04c0258df09a4444bf724d08ce (diff)
downloadnuttx-ad374ec9ecf01e605caa8bc762f9cfe657040373.tar.gz
nuttx-ad374ec9ecf01e605caa8bc762f9cfe657040373.tar.bz2
nuttx-ad374ec9ecf01e605caa8bc762f9cfe657040373.zip
SAMA5 UDPHS: Write DMA logic added. Still incomplete
-rw-r--r--nuttx/arch/arm/src/sama5/sam_udphs.c189
1 files changed, 158 insertions, 31 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_udphs.c b/nuttx/arch/arm/src/sama5/sam_udphs.c
index f1a21473b..ac5ffae4e 100644
--- a/nuttx/arch/arm/src/sama5/sam_udphs.c
+++ b/nuttx/arch/arm/src/sama5/sam_udphs.c
@@ -391,7 +391,7 @@ static inline void sam_putreg(uint32_t regval, uintptr_t regaddr);
static void sam_suspend(struct sam_usbdev_s *priv);
static void sam_resume(struct sam_usbdev_s *priv);
-/* DMA **********************************************************************/
+/* DMA/Request Helpers ******************************************************/
#ifdef CONFIG_SAMA5_UDPHS_SCATTERGATHER
static struct sam_dtd_s *sam_dtd_alloc(struct sam_usbdev_s *priv);
@@ -399,6 +399,8 @@ static void sam_dtd_free(struct sam_usbdev_s *priv, struct sam_dtd_s *dtd);
#endif
static void sam_dma_single(uint8_t epno, struct sam_req_s *privreq,
uint32_t dmacontrol);
+static int sam_req_wrdma(struct sam_usbdev_s *priv,
+ struct sam_ep_s *privep, struct sam_req_s *privreq)
/* Request Helpers **********************************************************/
@@ -412,6 +414,8 @@ static inline void
static void sam_req_complete(struct sam_ep_s *privep, int16_t result);
static void sam_fifo_write(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, const uint8_t *data, size_t nbytes);
+static int sam_req_wrnodma(struct sam_usbdev_s *priv,
+ struct sam_ep_s *privep, struct sam_req_s *privreq)
static int sam_req_write(struct sam_usbdev_s *priv,
struct sam_ep_s *privep);
static int sam_req_read(struct sam_usbdev_s *priv,
@@ -808,7 +812,9 @@ static void sam_dma_single(uint8_t epno, struct sam_req_s *privreq,
/* Clear any pending interrupts then enable the DMA interrupt */
(void)sam_getreg(SAM_UDPHS_DMASTATUS(epno));
- sam_putreg(UDPHS_INT_DMA(epno), SAM_UDPHS_IEN);
+ regval = sam_getreg(SAM_UDPHS_IEN);
+ regval |= UDPHS_INT_DMA(epno)
+ sam_putreg(regval, SAM_UDPHS_IEN);
/* Setup and enable the DMA */
@@ -819,6 +825,84 @@ static void sam_dma_single(uint8_t epno, struct sam_req_s *privreq,
}
/****************************************************************************
+ * Name: sam_req_wrdma
+ *
+ * Description:
+ * Process the next queued write request for an endpoint that supports DMA.
+ *
+ ****************************************************************************/
+
+static int sam_req_wrdma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
+ struct sam_req_s *privreq)
+{
+ uint32_t regval;
+ int epno;
+
+ /* The endpoint must be IDLE and ready to begin the next transfer */
+
+ if (privep->epstate != UDPHS_EPSTATE_IDLE)
+ {
+ usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPINBUSY), privep->epstate);
+ return -EBUSY;
+ }
+
+ /* Switch to the sending state */
+
+ privep->epstate = UDPHS_EPSTATE_SENDING;
+ privep->txnullpkt = 0;
+ privreq->inflight = 0;
+ privreq->req.xfrd = 0;
+
+ /* Get the endpoint number */
+
+ epno = USB_EPNO(privep->ep.eplog);
+
+ /* Either (1) we are committed to sending the null packet (because
+ * txnullpkt == 1 && nbytes == 0), or (2) we havenot yet sent the last
+ * packet (nbytes > 0). In either case, it is appropriate to clear
+ * txnullpkt now.
+ */
+
+ privep->txnullpkt = 0;
+
+ /* How many bytes remain to be transferred in the request? */
+
+ remaining = privreq->req.len - privreq->req.xfrd - privreq->inflight;
+
+ /* If there are no bytes to send, then send a null packet */
+
+ if (remaining > 0)
+ {
+ /* Clip the transfer to the size of the DMA FIFO */
+
+ if (remaining > DMA_MAX_FIFO_SIZE)
+ {
+ privreq->inflight = DMA_MAX_FIFO_SIZE;
+ }
+ else
+ {
+ privreq->inflight = remaining;
+ }
+
+ /* Single transfer */
+
+ sam_dma_single(epno, privreq, UDPHS_DMACONTROL_ENDBEN
+ | UDPHS_DMACONTROL_ENDBUFFIT
+ | UDPHS_DMACONTROL_CHANNENB);
+ return OK;
+ }
+
+ /* Enable the endpoint interrupt */
+
+ regval = sam_getreg(SAM_UDPHS_IEN);
+ regval |= UDPHS_INT_EPT(epno);
+ sam_putreg(regval, SAM_UDPHS_IEN);
+
+ sam_putreg(UDPHS_EPTCTL_TXRDY, SAM_UDPHS_EPTCTLENB);
+ return OK;
+}
+
+/****************************************************************************
* Request Helpers
****************************************************************************/
/****************************************************************************
@@ -929,12 +1013,17 @@ static void sam_req_complete(struct sam_ep_s *privep, int16_t result)
}
/****************************************************************************
- * Name: sam_req_write
+ * Name: sam_req_wrnodma
+ *
+ * Description:
+ * Process the next queued write request for an endpoint that does not
+ * support DMA.
+ *
****************************************************************************/
-static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
+static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
+ struct sam_req_s *privreq)
{
- struct sam_req_s *privreq;
uint8_t *buf;
uint8_t *fifo;
uint8_t epno;
@@ -942,28 +1031,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
int nbytes;
int bytesleft;
- /* We get here when an IN endpoint interrupt occurs. So now we know that
- * there is no TX transfer in progress.
- */
-
- privep->txbusy = false;
-
- /* Check the request from the head of the endpoint request queue */
-
- privreq = sam_rqpeek(privep);
- if (!privreq)
- {
- /* There is no TX transfer in progress and no new pending TX
- * requests to send.
- */
-
- usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), 0);
- return -ENOENT;
- }
-
epno = USB_EPNO(privep->ep.eplog);
- ullvdbg("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n",
- epno, privreq, privreq->req.len, privreq->req.xfrd, privep->txnullpkt);
/* Get the number of bytes to send. The totals bytes remaining to be sent
* is the the total size of the buffer, minus the number of bytes
@@ -984,8 +1052,8 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
}
/* Either (1) we are committed to sending the null packet (because txnullpkt == 1
- * && nbytes == 0), or (2) we have not yet send the last packet (nbytes > 0).
- * In either case, it is appropriate to clearn txnullpkt now.
+ * && nbytes == 0), or (2) we havenot yet sent the last packet (nbytes > 0).
+ * In either case, it is appropriate to clear txnullpkt now.
*/
privep->txnullpkt = 0;
@@ -1046,11 +1114,68 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
privep->txbusy = true;
}
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_req_write
+ *
+ * Description:
+ * Process the next queued write request
+ *
+ ****************************************************************************/
+
+static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
+{
+ struct sam_req_s *privreq;
+ uint8_t epno;
+
+ /* We get here when an IN endpoint interrupt occurs. So now we know that
+ * there is no TX transfer in progress.
+ */
+
+ privep->txbusy = false;
+
+ /* Check the request from the head of the endpoint request queue */
+
+ privreq = sam_rqpeek(privep);
+ if (!privreq)
+ {
+ /* There is no TX transfer in progress and no new pending TX
+ * requests to send.
+ */
+
+ usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), 0);
+ return -ENOENT;
+ }
+
+ epno = USB_EPNO(privep->ep.eplog);
+ ullvdbg("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n",
+ epno, privreq, privreq->req.len, privreq->req.xfrd, privep->txnullpkt);
+
+ /* The way that we handle the transfer is going to depend on whether
+ * or not this endpoint supports DMA.
+ */
+
+ if ((SAM_EPSET_DMA & SAM_EP_BIT(epno)) != 0)
+ {
+ ret = sam_req_wrdma(priv, privep, privreq);
+ }
+ else
+ {
+ ret = sam_req_wrnodma(priv, privep, privreq);
+ }
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
/* If all of the bytes were sent (including any final null packet)
* then we are finished with the request buffer).
*/
- if (privreq->req.len - privreq->req.xfrd && !privep->txnullpkt)
+ if (privreq->req.len == privreq->req.xfrd && !privep->txnullpkt)
{
/* Return the write request to the class driver */
@@ -2053,7 +2178,7 @@ static void sam_dma_interrupt(struct sam_usbdev_s *priv, int epno)
regval = UDPHS_DMACONTROL_ENDTREN | UDPHS_DMACONTROL_ENDTRIT |
UDPHS_DMACONTROL_ENDBEN | UDPHS_DMACONTROL_ENDBUFFIT |
- UDPHS_DMACONTROL_CHANNENB
+ UDPHS_DMACONTROL_CHANNENB;
sam_dma_single(epno, privreq, regval);
}
}
@@ -2383,7 +2508,9 @@ void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset)
/* Disable endpoint interrupt */
- sam_putreg(~UDPHS_INT_EPT(epno), SAM_UDPHS_IEN);
+ regval = sam_getreg(SAM_UDPHS_IEN);
+ regval &= ~UDPHS_INT_EPT(epno);
+ sam_putreg(regval, SAM_UDPHS_IEN);
/* Cancel any queued requests. Since they are canceled
* with status -ESHUTDOWN, then will not be requeued