From: supriya karanth <supriya.karanth@xxxxxxxxxxxxxx> 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 <supriya.karanth@xxxxxxxxxxxxxx> Signed-off-by: Praveena NADAHALLY <praveen.nadahally@xxxxxxxxxxxxxx> Acked-by: Linus Walleij <linus.walleij@xxxxxxxxxx> --- drivers/usb/musb/musb_dma.h | 12 +++ drivers/usb/musb/musb_gadget.c | 125 +++++++++++++++++++++++++-- drivers/usb/musb/musb_gadget.h | 3 + drivers/usb/musb/ux500_dma.c | 184 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 312 insertions(+), 12 deletions(-) diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 24d3921..bcad195 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 b6b84da..b351f52 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -806,14 +806,10 @@ static void rxstate(struct musb *musb, struct musb_request *req) /* 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); + channel->max_len); csr &= ~MUSB_RXCSR_DMAMODE; csr |= (MUSB_RXCSR_DMAENAB | @@ -821,13 +817,23 @@ static void rxstate(struct musb *musb, struct musb_request *req) musb_writew(epio, MUSB_RXCSR, csr); - if (transfer_size <= musb_ep->packet_sz) { + 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); + + /* 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; } if (c->channel_program(channel, @@ -835,9 +841,16 @@ static void rxstate(struct musb *musb, struct musb_request *req) channel->desired_mode, request->dma + request->actual, - transfer_size)) + transfer_size)){ + + dev_dbg(musb->controller, + "%s OUT/RX DMA Size %d/%d maxpacket %d\n", + musb_ep->end_point.name, + len, transfer_size, + musb_ep->packet_sz); return; + } } #endif /* Mentor's DMA */ @@ -915,6 +928,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; @@ -924,8 +941,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; @@ -960,7 +981,57 @@ void musb_g_rx(struct musb *musb, u8 epnum) /* "should not happen"; likely RXPKTRDY pending for DMA */ dev_dbg(musb->controller, "%s busy, csr %04x\n", musb_ep->end_point.name, csr); +#ifndef CONFIG_USB_UX500_DMA return; +#else + /* 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; +#endif } if (dma && (csr & MUSB_RXCSR_DMAENAB)) { @@ -988,6 +1059,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 @@ -1361,8 +1452,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) diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index f1059e7..6eddf14 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -106,7 +106,8 @@ static bool ux500_configure_channel(struct dma_channel *channel, direction = ux500_channel->is_tx ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; addr_width = (len & 0x3) ? DMA_SLAVE_BUSWIDTH_1_BYTE : - DMA_SLAVE_BUSWIDTH_4_BYTES; + ((dma_addr & 0x2) ? DMA_SLAVE_BUSWIDTH_2_BYTES : + DMA_SLAVE_BUSWIDTH_4_BYTES); slave_conf.direction = direction; slave_conf.src_addr = usb_fifo_addr; @@ -134,6 +135,15 @@ static bool ux500_configure_channel(struct dma_channel *channel, return true; } +/** + * ux500_dma_controller_allocate() - allocates the DMA channels + * @c: pointer to DMA controller + * @hw_ep: pointer to endpoint + * @is_tx: transmit or receive direction + * + * This function allocates the DMA channel and initializes + * the channel +*/ static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c, struct musb_hw_ep *hw_ep, u8 is_tx) { @@ -173,6 +183,13 @@ static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c, return &(ux500_channel->channel); } +/** + * ux500_dma_channel_release() - releases the DMA channel + * @channel: channel to be released + * + * This function releases the DMA channel + * +*/ static void ux500_dma_channel_release(struct dma_channel *channel) { struct ux500_dma_channel *ux500_channel = channel->private_data; @@ -192,13 +209,22 @@ static int ux500_dma_is_compatible(struct dma_channel *channel, { if ((maxpacket & 0x3) || ((int)buf & 0x3) || - (length < 512) || - (length & 0x3)) + (length < 512)) return false; else return true; } +/** + * ux500_dma_channel_program() - Configures the channel and initiates transfer + * @channel: pointer to DMA channel + * @packet_sz: packet size + * @mode: mode + * @dma_addr: physical address of memory + * @len: length + * + * This function configures the channel and initiates the DMA transfer +*/ static int ux500_dma_channel_program(struct dma_channel *channel, u16 packet_sz, u8 mode, dma_addr_t dma_addr, u32 len) @@ -220,6 +246,12 @@ static int ux500_dma_channel_program(struct dma_channel *channel, return ret; } +/** + * ux500_dma_channel_abort() - aborts the DMA transfer + * @channel: pointer to DMA channel. + * + * This function aborts the DMA transfer. +*/ static int ux500_dma_channel_abort(struct dma_channel *channel) { struct ux500_dma_channel *ux500_channel = channel->private_data; @@ -254,6 +286,124 @@ static int ux500_dma_channel_abort(struct dma_channel *channel) return 0; } +/** + * ux500_dma_channel_pause() - pauses the DMA transfer + * @channel: pointer to DMA channel. + * + * This function pauses the DMA transfer. This is needed to get + * the correct residue of an ongoing DMA transfer +*/ +static int ux500_dma_channel_pause(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct ux500_dma_controller *controller = ux500_channel->controller; + struct musb *musb = controller->private_data; + int status; + + status = ux500_channel->dma_chan->device-> + device_control(ux500_channel->dma_chan, + DMA_PAUSE, 0); + + dev_dbg(musb->controller, + "%s channel=%d, is_tx=%d, status=%d\n", + __func__, ux500_channel->ch_num, ux500_channel->is_tx + , status); + return status; +} + +/** + * ux500_dma_channel_resume() - resumes the DMA transfer + * @channel: pointer to DMA channel. + * + * This function resumes a paused DMA transfer. +*/ +static int ux500_dma_channel_resume(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct ux500_dma_controller *controller = ux500_channel->controller; + struct musb *musb = controller->private_data; + int status; + + status = ux500_channel->dma_chan->device-> + device_control(ux500_channel->dma_chan, + DMA_RESUME, 0); + dev_dbg(musb->controller, + "%s channel=%d, is_tx=%d, status=%d\n", + __func__, ux500_channel->ch_num, ux500_channel->is_tx + , status); + return status; +} + +/** + * ux500_dma_tx_status() - Gets the residue of an ongoing DMA transfer + * @channel: pointer to DMA channel + * + * This function will get the number of bytes left to be transferred + * over the DMA + */ +static int ux500_dma_tx_status(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct ux500_dma_controller *controller = ux500_channel->controller; + struct musb *musb = controller->private_data; + struct dma_tx_state txstate; + + ux500_channel->dma_chan->device-> + device_tx_status(ux500_channel->dma_chan, + ux500_channel->cookie, &txstate); + dev_dbg(musb->controller, + "%s channel=%d, is_tx=%d, residue=%d\n", + __func__, ux500_channel->ch_num, ux500_channel->is_tx + , txstate.residue); + return txstate.residue; +} + +/** + * ux500_dma_check_residue() - Checks if the DMA transfer residue is valid + * @channel: pointer to DMA channel + */ +static int ux500_dma_check_residue(struct dma_channel *channel, u32 residue) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct ux500_dma_controller *controller = ux500_channel->controller; + struct musb *musb = controller->private_data; + int status; + + dev_dbg(musb->controller, + "%s channel=%d, is_tx=%d, residue=%d\n", + __func__, ux500_channel->ch_num, ux500_channel->is_tx + , residue); + + /* In cases where we know the transfer length and were expecting + * a DMA completion we could get into the DMA busy condition + * here if the next packet is short and the EP interrupt occurs + * before we recieve dma_completion interrupt for current transfer + * Wait for dma_completion. MUSB will interrupt us again for this + * short packet when we clear the DMA bits + */ + if (!residue) { + dev_dbg(musb->controller, + "%s: Wait for DMA completion\n", + __func__); + status = -EINPROGRESS; + } else if (residue == ux500_channel->channel.prog_len) { + /* Nothing transferred over DMA? */ + WARN_ON(1); + status = -EINVAL; + } else { + /* residue looks OK */ + status = 0; + } + + return status; +} + +/** + * ux500_dma_controller_stop() - releases all the channels and frees the DMA pipes + * @c: pointer to DMA controller + * + * This function frees all of the logical channels and frees the DMA pipes +*/ static int ux500_dma_controller_stop(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, @@ -285,6 +435,15 @@ static int ux500_dma_controller_stop(struct dma_controller *c) return 0; } + +/** + * ux500_dma_controller_start() - creates the logical channels pool and registers callbacks + * @c: pointer to DMA Controller + * + * This function requests the logical channels from the DMA driver and creates + * logical channels based on event lines and also registers the callbacks which + * are invoked after data transfer in the transmit or receive direction. +*/ static int ux500_dma_controller_start(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, @@ -356,6 +515,12 @@ static int ux500_dma_controller_start(struct dma_controller *c) return 0; } +/** + * dma_controller_destroy() - deallocates the DMA controller + * @c: pointer to dma controller. + * + * This function deallocates the DMA controller. +*/ void dma_controller_destroy(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, @@ -364,6 +529,15 @@ void dma_controller_destroy(struct dma_controller *c) kfree(controller); } +/** + * dma_controller_create() - creates the dma controller and initializes callbacks + * + * @musb: pointer to mentor core driver data instance| + * @base: base address of musb registers. + * + * This function creates the DMA controller and initializes the callbacks + * that are invoked from the Mentor IP core. +*/ struct dma_controller *__devinit dma_controller_create(struct musb *musb, void __iomem *base) { @@ -387,6 +561,10 @@ dma_controller_create(struct musb *musb, void __iomem *base) controller->controller.channel_release = ux500_dma_channel_release; controller->controller.channel_program = ux500_dma_channel_program; controller->controller.channel_abort = ux500_dma_channel_abort; + controller->controller.channel_pause = ux500_dma_channel_pause; + controller->controller.channel_resume = ux500_dma_channel_resume; + controller->controller.tx_status = ux500_dma_tx_status; + controller->controller.check_residue = ux500_dma_check_residue; controller->controller.is_compatible = ux500_dma_is_compatible; return &controller->controller; -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html