diff options
Diffstat (limited to 'drivers/usb/musb/musb_cppi41.c')
-rw-r--r-- | drivers/usb/musb/musb_cppi41.c | 69 |
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 | ||
116 | static 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 | |||
115 | static void cppi41_dma_callback(void *private_data); | 128 | static void cppi41_dma_callback(void *private_data); |
116 | 129 | ||
117 | static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) | 130 | static 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 | ||
182 | static 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 | |||
168 | static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) | 208 | static 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; |