aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/musb/musb_cppi41.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/musb/musb_cppi41.c')
-rw-r--r--drivers/usb/musb/musb_cppi41.c69
1 files changed, 68 insertions, 1 deletions
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index f88929609bac..7b8bbf53127e 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -39,6 +39,7 @@ struct cppi41_dma_channel {
39 u32 transferred; 39 u32 transferred;
40 u32 packet_sz; 40 u32 packet_sz;
41 struct list_head tx_check; 41 struct list_head tx_check;
42 struct work_struct dma_completion;
42}; 43};
43 44
44#define MUSB_DMA_NUM_CHANNELS 15 45#define MUSB_DMA_NUM_CHANNELS 15
@@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
112 return true; 113 return true;
113} 114}
114 115
116static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
117{
118 if (in && hw_ep->in_qh) {
119 if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
120 return true;
121 } else if (hw_ep->out_qh) {
122 if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
123 return true;
124 }
125 return false;
126}
127
115static void cppi41_dma_callback(void *private_data); 128static void cppi41_dma_callback(void *private_data);
116 129
117static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) 130static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
@@ -119,7 +132,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
119 struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; 132 struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
120 struct musb *musb = hw_ep->musb; 133 struct musb *musb = hw_ep->musb;
121 134
122 if (!cppi41_channel->prog_len) { 135 if (!cppi41_channel->prog_len ||
136 (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) {
123 137
124 /* done, complete */ 138 /* done, complete */
125 cppi41_channel->channel.actual_len = 139 cppi41_channel->channel.actual_len =
@@ -165,6 +179,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
165 } 179 }
166} 180}
167 181
182static void cppi_trans_done_work(struct work_struct *work)
183{
184 unsigned long flags;
185 struct cppi41_dma_channel *cppi41_channel =
186 container_of(work, struct cppi41_dma_channel, dma_completion);
187 struct cppi41_dma_controller *controller = cppi41_channel->controller;
188 struct musb *musb = controller->musb;
189 struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
190 bool empty;
191
192 if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
193 spin_lock_irqsave(&musb->lock, flags);
194 cppi41_trans_done(cppi41_channel);
195 spin_unlock_irqrestore(&musb->lock, flags);
196 } else {
197 empty = musb_is_tx_fifo_empty(hw_ep);
198 if (empty) {
199 spin_lock_irqsave(&musb->lock, flags);
200 cppi41_trans_done(cppi41_channel);
201 spin_unlock_irqrestore(&musb->lock, flags);
202 } else {
203 schedule_work(&cppi41_channel->dma_completion);
204 }
205 }
206}
207
168static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) 208static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
169{ 209{
170 struct cppi41_dma_controller *controller; 210 struct cppi41_dma_controller *controller;
@@ -228,6 +268,14 @@ static void cppi41_dma_callback(void *private_data)
228 transferred < cppi41_channel->packet_sz) 268 transferred < cppi41_channel->packet_sz)
229 cppi41_channel->prog_len = 0; 269 cppi41_channel->prog_len = 0;
230 270
271 if (!cppi41_channel->is_tx) {
272 if (is_isoc(hw_ep, 1))
273 schedule_work(&cppi41_channel->dma_completion);
274 else
275 cppi41_trans_done(cppi41_channel);
276 goto out;
277 }
278
231 empty = musb_is_tx_fifo_empty(hw_ep); 279 empty = musb_is_tx_fifo_empty(hw_ep);
232 if (empty) { 280 if (empty) {
233 cppi41_trans_done(cppi41_channel); 281 cppi41_trans_done(cppi41_channel);
@@ -264,6 +312,10 @@ static void cppi41_dma_callback(void *private_data)
264 goto out; 312 goto out;
265 } 313 }
266 } 314 }
315 if (is_isoc(hw_ep, 0)) {
316 schedule_work(&cppi41_channel->dma_completion);
317 goto out;
318 }
267 list_add_tail(&cppi41_channel->tx_check, 319 list_add_tail(&cppi41_channel->tx_check,
268 &controller->early_tx_list); 320 &controller->early_tx_list);
269 if (!hrtimer_active(&controller->early_tx)) { 321 if (!hrtimer_active(&controller->early_tx)) {
@@ -448,12 +500,25 @@ static int cppi41_dma_channel_program(struct dma_channel *channel,
448 dma_addr_t dma_addr, u32 len) 500 dma_addr_t dma_addr, u32 len)
449{ 501{
450 int ret; 502 int ret;
503 struct cppi41_dma_channel *cppi41_channel = channel->private_data;
504 int hb_mult = 0;
451 505
452 BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || 506 BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
453 channel->status == MUSB_DMA_STATUS_BUSY); 507 channel->status == MUSB_DMA_STATUS_BUSY);
454 508
509 if (is_host_active(cppi41_channel->controller->musb)) {
510 if (cppi41_channel->is_tx)
511 hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult;
512 else
513 hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult;
514 }
515
455 channel->status = MUSB_DMA_STATUS_BUSY; 516 channel->status = MUSB_DMA_STATUS_BUSY;
456 channel->actual_len = 0; 517 channel->actual_len = 0;
518
519 if (hb_mult)
520 packet_sz = hb_mult * (packet_sz & 0x7FF);
521
457 ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len); 522 ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len);
458 if (!ret) 523 if (!ret)
459 channel->status = MUSB_DMA_STATUS_FREE; 524 channel->status = MUSB_DMA_STATUS_FREE;
@@ -607,6 +672,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
607 cppi41_channel->port_num = port; 672 cppi41_channel->port_num = port;
608 cppi41_channel->is_tx = is_tx; 673 cppi41_channel->is_tx = is_tx;
609 INIT_LIST_HEAD(&cppi41_channel->tx_check); 674 INIT_LIST_HEAD(&cppi41_channel->tx_check);
675 INIT_WORK(&cppi41_channel->dma_completion,
676 cppi_trans_done_work);
610 677
611 musb_dma = &cppi41_channel->channel; 678 musb_dma = &cppi41_channel->channel;
612 musb_dma->private_data = cppi41_channel; 679 musb_dma->private_data = cppi41_channel;