diff options
author | George Cherian <george.cherian@ti.com> | 2014-01-27 15:07:26 +0530 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-02-18 09:25:56 -0600 |
commit | 1af54b7a40ca9bbd549e626be01870caa3f0299d (patch) | |
tree | 9191beaa4ca1ad13fa05794d9dbcde5a69739b4e /drivers/usb/musb/musb_cppi41.c | |
parent | f82503f549c70c15e283511270e6713a912fef37 (diff) | |
download | linux-stable-1af54b7a40ca9bbd549e626be01870caa3f0299d.tar.gz linux-stable-1af54b7a40ca9bbd549e626be01870caa3f0299d.tar.bz2 linux-stable-1af54b7a40ca9bbd549e626be01870caa3f0299d.zip |
usb: musb: musb_cppi41: Handle ISOCH differently and not use the hrtimer.
In case of ISOCH transfers the hrtimer workaround for the hardware issue
is not very reliable. Instead of checking musb_is_tx_fifo_empty() in hrtimer
routine, schedule a completion work and check the same in completion work.
Signed-off-by: George Cherian <george.cherian@ti.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/musb/musb_cppi41.c')
-rw-r--r-- | drivers/usb/musb/musb_cppi41.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 39ee516c8cbf..5b0f2a58f8ce 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -39,6 +39,7 @@ struct cppi41_dma_channel { u32 transferred; u32 packet_sz; struct list_head tx_check; + struct work_struct dma_completion; }; #define MUSB_DMA_NUM_CHANNELS 15 @@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep) return true; } +static bool is_isoc(struct musb_hw_ep *hw_ep, bool in) +{ + if (in && hw_ep->in_qh) { + if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC) + return true; + } else if (hw_ep->out_qh) { + if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC) + return true; + } + return false; +} + static void cppi41_dma_callback(void *private_data); static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) @@ -165,6 +178,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) } } +static void cppi_trans_done_work(struct work_struct *work) +{ + unsigned long flags; + struct cppi41_dma_channel *cppi41_channel = + container_of(work, struct cppi41_dma_channel, dma_completion); + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + bool empty; + + if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) { + spin_lock_irqsave(&musb->lock, flags); + cppi41_trans_done(cppi41_channel); + spin_unlock_irqrestore(&musb->lock, flags); + } else { + empty = musb_is_tx_fifo_empty(hw_ep); + if (empty) { + spin_lock_irqsave(&musb->lock, flags); + cppi41_trans_done(cppi41_channel); + spin_unlock_irqrestore(&musb->lock, flags); + } else { + schedule_work(&cppi41_channel->dma_completion); + } + } +} + static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) { struct cppi41_dma_controller *controller; @@ -228,6 +267,14 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; + if (!cppi41_channel->is_tx) { + if (is_isoc(hw_ep, 1)) + schedule_work(&cppi41_channel->dma_completion); + else + cppi41_trans_done(cppi41_channel); + goto out; + } + empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); @@ -264,6 +311,10 @@ static void cppi41_dma_callback(void *private_data) goto out; } } + if (is_isoc(hw_ep, 0)) { + schedule_work(&cppi41_channel->dma_completion); + goto out; + } list_add_tail(&cppi41_channel->tx_check, &controller->early_tx_list); if (!hrtimer_active(&controller->early_tx)) { @@ -620,6 +671,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) cppi41_channel->port_num = port; cppi41_channel->is_tx = is_tx; INIT_LIST_HEAD(&cppi41_channel->tx_check); + INIT_WORK(&cppi41_channel->dma_completion, + cppi_trans_done_work); musb_dma = &cppi41_channel->channel; musb_dma->private_data = cppi41_channel; |