From: supriya karanth <[email protected]>
Generic changes for enabling DMA Mode1.
Cleanup of rxtsate function making programming
same for all platforms
Handles:
1) Known transfer length
a) Non multiple of packet size
b) Multiple of packet size
2) Unknown transfer lengths
- Short packet indicates end of transfer
---------------> OUT Endpoint interrupt recieved
| |
| ____________________|_________________
| | |
| -> No ongoing transfer ->DMA Transfer ongoing and RXPKTRDY set?
| | ->Short Packet recieved
| request queued? ->PAUSE DMA transfer, Read Residue
| | ________________|____________
| | | |
| | residue!=0 residue=0
| | -> abort DMA ->Resume DMA
| | ->Update request->actual_len ->Wait for DMA
| | -> Clear DMA bits in CSR completion
| | -> Read Short Packet |
| |__________________________| |______
| | |
| call rxstate |
| ->RXPKTRDY set? Read RXCOUNT |
| _____________________|____________________ |
| | | |
| ->RXCOUNT == EP MAX packet Size ->RXCOUNT < EP MAX packet Size|
| ->Program DMA in Mode1 for length -> Program in PIO / Mode0 |
| which is multiple of packet -> Read Short packet from FIFO|
| size -> Update request->actual_len |
| | -> call musb_giveback |
| |________________________________________| |
|____________________________| |
| | |
| |____________________________________|
| |
| DMA completion interrupt recieved
| _______________________|_________________
| | |
| ->req->len = req->actual ->req->len < req->actual
| ->Clear DMA bits -> Short packet expected
| ->Call musb_giveback ->Clear DMA bits
| | ->wait for next OUT
| |_________________________________________|
|_______________________________|
Signed-off-by: Supriya Karanth <[email protected]>
Signed-off-by: Praveena NADAHALLY <[email protected]>
Acked-by: Linus Walleij <[email protected]>
---
drivers/usb/musb/musb_dma.h | 12 +++
drivers/usb/musb/musb_gadget.c | 215 +++++++++++++++++++++++-----------------
drivers/usb/musb/musb_gadget.h | 3 +
3 files changed, 138 insertions(+), 92 deletions(-)
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 1b6b827..7754515 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -116,6 +116,7 @@ struct dma_controller;
* @max_len: the maximum number of bytes the channel can move in one
* transaction (typically representing many USB maximum-sized packets)
* @actual_len: how many bytes have been transferred
+ * @prog_len: how many bytes have been programmed for transfer
* @status: current channel status (updated e.g. on interrupt)
* @desired_mode: true if mode 1 is desired; false if mode 0 is desired
*
@@ -127,6 +128,7 @@ struct dma_channel {
/* FIXME not void* private_data, but a dma_controller * */
size_t max_len;
size_t actual_len;
+ size_t prog_len;
enum dma_channel_status status;
bool desired_mode;
};
@@ -155,6 +157,11 @@ dma_channel_status(struct dma_channel *c)
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
+ * @channel_pause: This function pauses the ongoing DMA transfer
+ * @channel_resume: This function resumes the ongoing DMA transfer
+ * @tx_status: Gets the residue of an ongoing DMA transfer
+ * @check_resiudue: checks if the residue of an ongoing DMA
+ * transfer is valid
*
* Controllers manage dma channels.
*/
@@ -169,6 +176,11 @@ struct dma_controller {
dma_addr_t dma_addr,
u32 length);
int (*channel_abort)(struct dma_channel *);
+ int (*channel_pause)(struct dma_channel *);
+ int (*channel_resume)(struct dma_channel *);
+ int (*tx_status)(struct dma_channel *);
+ int (*check_residue)(struct dma_channel *,
+ u32 residue);
int (*is_compatible)(struct dma_channel *channel,
u16 maxpacket,
void *buf, u32 length);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 9db664a..9d47276 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -666,13 +666,17 @@ static void rxstate(struct musb *musb, struct
musb_request *req)
* file_storage and f_mass_storage drivers
*/
- if (request->short_not_ok && fifo_count == musb_ep->packet_sz)
+ if (fifo_count == musb_ep->packet_sz)
use_mode_1 = 1;
else
use_mode_1 = 0;
+#ifdef CONFIG_USB_TUSB_OMAP_DMA
+ if (tusb_dma_omap())
+ use_mode_1 = 0;
+#endif
+
if (request->actual < request->length) {
-#ifdef CONFIG_USB_INVENTRA_DMA
struct dma_controller *c;
struct dma_channel *channel;
int use_dma = 0;
@@ -681,45 +685,35 @@ static void rxstate(struct musb *musb, struct
musb_request *req)
c = musb->dma_controller;
channel = musb_ep->dma;
- /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in
- * mode 0 only. So we do not get endpoint interrupts due to DMA
- * completion. We only get interrupts from DMA controller.
- *
- * We could operate in DMA mode 1 if we knew the size of the tranfer
- * in advance. For mass storage class, request->length = what the host
- * sends, so that'd work. But for pretty much everything else,
- * request->length is routinely more than what the host sends. For
- * most these gadgets, end of is signified either by a short packet,
- * or filling the last byte of the buffer. (Sending extra data in
- * that last pckate should trigger an overflow fault.) But in mode 1,
- * we don't get DMA completion interrupt for short packets.
- *
- * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1),
- * to get endpoint interrupt on every DMA req, but that didn't seem
- * to work reliably.
- *
- * REVISIT an updated g_file_storage can set req->short_not_ok, which
- * then becomes usable as a runtime "use mode 1" hint...
- */
-
- /* Experimental: Mode1 works with mass storage use
cases */
if (use_mode_1) {
csr |= MUSB_RXCSR_AUTOCLEAR;
musb_writew(epio, MUSB_RXCSR, csr);
csr |= MUSB_RXCSR_DMAENAB;
musb_writew(epio, MUSB_RXCSR, csr);
+ musb_writew(epio, MUSB_RXCSR,
+ csr | MUSB_RXCSR_DMAMODE);
+
+#ifdef CONFIG_USB_INVENTRA_DMA
/*
* this special sequence (enabling and then
* disabling MUSB_RXCSR_DMAMODE) is required
* to get DMAReq to activate
*/
- musb_writew(epio, MUSB_RXCSR,
- csr | MUSB_RXCSR_DMAMODE);
musb_writew(epio, MUSB_RXCSR, csr);
-
+#endif
transfer_size = min(request->length -
request->actual,
channel->max_len);
+
+ /* Program the transfer length to be
+ * a multiple of packet size because
+ * short packets cant be transferred
+ * over mode1
+ */
+ transfer_size = transfer_size -
+ (transfer_size
+ % musb_ep->packet_sz);
+ musb_ep->dma->prog_len = transfer_size;
musb_ep->dma->desired_mode = 1;
} else {
@@ -742,54 +736,16 @@ static void rxstate(struct musb *musb, struct
musb_request *req)
+ request->actual,
transfer_size);
- if (use_dma)
+ if (use_dma) {
+ dev_dbg(musb->controller,
+ "%s OUT/RX DMA Size %d/%d maxpacket
%d\n",
+ musb_ep->end_point.name,
+ fifo_count, transfer_size,
+ musb_ep->packet_sz);
return;
-#elif defined(CONFIG_USB_UX500_DMA)
- if (request->actual < request->length) {
- struct dma_controller *c;
- struct dma_channel *channel;
- int transfer_size = 0;
-
- c = musb->dma_controller;
- channel = musb_ep->dma;
-
- /* In case first packet is short */
- if (fifo_count < musb_ep->packet_sz)
- transfer_size = fifo_count;
- else if (request->short_not_ok)
- transfer_size = min(request->length -
- request->actual,
- channel->max_len);
- else
- transfer_size = min(request->length -
- request->actual,
- (unsigned)fifo_count);
-
- csr &= ~MUSB_RXCSR_DMAMODE;
- csr |= (MUSB_RXCSR_DMAENAB |
- MUSB_RXCSR_AUTOCLEAR);
-
- musb_writew(epio, MUSB_RXCSR, csr);
-
- if (transfer_size <= musb_ep->packet_sz) {
- musb_ep->dma->desired_mode = 0;
- } else {
- musb_ep->dma->desired_mode = 1;
- /* Mode must be set after DMAENAB */
- csr |= MUSB_RXCSR_DMAMODE;
- musb_writew(epio, MUSB_RXCSR, csr);
- }
-
- if (c->channel_program(channel,
- musb_ep->packet_sz,
- channel->desired_mode,
- request->dma
- + request->actual,
- transfer_size))
-
- return;
}
-#endif /* Mentor's DMA */
+
+ /* DMA programming failed. Use PIO mode */
len = request->length - request->actual;
dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d,
maxpacket %d\n",
@@ -799,22 +755,6 @@ static void rxstate(struct musb *musb, struct musb_request
*req)
fifo_count = min_t(unsigned, len, fifo_count);
-#ifdef CONFIG_USB_TUSB_OMAP_DMA
- if (tusb_dma_omap()) {
- struct dma_controller *c = musb->dma_controller;
- struct dma_channel *channel = musb_ep->dma;
- u32 dma_addr = request->dma + request->actual;
- int ret;
-
- ret = c->channel_program(channel,
- musb_ep->packet_sz,
- channel->desired_mode,
- dma_addr,
- fifo_count);
- if (ret)
- return;
- }
-#endif
/*
* Unmap the dma buffer back to cpu if dma channel
* programming fails. This buffer is mapped if the
@@ -863,6 +803,10 @@ void musb_g_rx(struct musb *musb, u8 epnum)
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
+ u16 len;
+ u32 residue;
+ struct dma_controller *c = musb->dma_controller;
+ int status;
if (hw_ep->is_shared_fifo)
musb_ep = &hw_ep->ep_in;
@@ -872,8 +816,12 @@ void musb_g_rx(struct musb *musb, u8 epnum)
musb_ep_select(mbase, epnum);
req = next_request(musb_ep);
- if (!req)
+ if (!req) {
+ musb_ep->rx_pending = 1;
+ dev_dbg(musb->controller, "Packet recieved on %s but no request
queued\n",
+ musb_ep->end_point.name);
return;
+ }
request = &req->request;
@@ -905,9 +853,56 @@ void musb_g_rx(struct musb *musb, u8 epnum)
}
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
- /* "should not happen"; likely RXPKTRDY pending for DMA */
+
dev_dbg(musb->controller, "%s busy, csr %04x\n",
musb_ep->end_point.name, csr);
+
+ /* For short_not_ok type transfers and mode0 transfers */
+ if (dma->desired_mode == 0 || request->short_not_ok)
+ return;
+
+ if (!(csr & MUSB_RXCSR_RXPKTRDY)) {
+ dev_dbg(musb->controller,
+ "%s: %s, DMA busy and Packet not ready\n",
+ __func__, musb_ep->end_point.name);
+ return;
+ }
+
+ /* For Mode1 we get here for the last short packet */
+ len = musb_readw(epio, MUSB_RXCOUNT);
+
+ /* We should get here only for a short packet.*/
+ if (len == musb_ep->packet_sz) {
+ dev_dbg(musb->controller,
+ "%s: %s, Packet not short RXCOUNT=%d\n",
+ __func__, musb_ep->end_point.name, len);
+ return;
+ }
+ /* Pause the channel to get the correct transfer residue.*/
+ status = c->channel_pause(musb_ep->dma);
+ residue = c->tx_status(musb_ep->dma);
+ status = c->check_residue(musb_ep->dma, residue);
+
+ if (status) {
+ /* Something's wrong */
+ status = c->channel_resume(musb_ep->dma);
+ return;
+ }
+ /* In cases when we don't know the transfer length the short
+ * packet indicates end of current transfer.
+ */
+ status = c->channel_abort(musb_ep->dma);
+ /* Update with the actual number of bytes transferred */
+ request->actual = musb_ep->dma->prog_len - residue;
+ /* Clear DMA bits in the CSR */
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB
+ | MUSB_RXCSR_DMAMODE);
+ musb_writew(epio, MUSB_RXCSR, csr);
+ /* Proceed to read the short packet */
+ rxstate(musb, req);
+ /* Don't program next transfer, it will tamper with the DMA
+ * busy condition. Wait for next OUT
+ */
return;
}
@@ -936,6 +931,26 @@ void musb_g_rx(struct musb *musb, u8 epnum)
musb_writew(epio, MUSB_RXCSR, csr);
}
+ /* We get here after DMA completion */
+ if ((dma->desired_mode == 1) && (!request->short_not_ok)) {
+ /* Incomplete? wait for next OUT packet */
+ if (request->actual < request->length) {
+ dev_dbg(musb->controller,
+ "%s: %s, Wait for next OUT\n",
+ __func__, musb_ep->end_point.name);
+ } else if (request->actual == request->length) {
+ dev_dbg(musb->controller,
+ "%s: %s, Transfer over mode1 done\n",
+ __func__, musb_ep->end_point.name);
+ musb_g_giveback(musb_ep, request, 0);
+ } else {
+ dev_dbg(musb->controller,
+ "%s: %s, Transfer length exceeded!!\n",
+ __func__, musb_ep->end_point.name);
+ }
+ return;
+ }
+
/* incomplete, and not short? wait for next IN packet */
if ((request->actual < request->length)
&& (musb_ep->dma->actual_len
@@ -1305,8 +1320,24 @@ static int musb_gadget_queue(struct usb_ep *ep, struct
usb_request *req,
list_add_tail(&request->list, &musb_ep->req_list);
/* it this is the head of the queue, start i/o ... */
- if (!musb_ep->busy && &request->list == musb_ep->req_list.next)
+ if (!musb_ep->busy && &request->list == musb_ep->req_list.next) {
+
+ /* In case of RX, if there is no packet pending to be read
+ * from fifo then wait for next interrupt
+ */
+ if (!request->tx) {
+ if (!musb_ep->rx_pending) {
+ dev_dbg(musb->controller, "No packet pending
for %s\n",
+ ep->name);
+ goto cleanup;
+ } else {
+ musb_ep->rx_pending = 0;
+ dev_dbg(musb->controller, "Read packet from
fifo %s\n",
+ ep->name);
+ }
+ }
musb_ep_restart(musb, request);
+ }
cleanup:
spin_unlock_irqrestore(&musb->lock, lockflags);
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index 66b7c5e..55eb686 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -90,6 +90,9 @@ struct musb_ep {
u8 busy;
u8 hb_mult;
+
+ /* true if packet is received in fifo and req_list is empty */
+ u8 rx_pending;
};
static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html