diff options
author | George Cherian <george.cherian@ti.com> | 2014-01-27 04:37:26 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-02-18 10:25:56 -0500 |
commit | 1af54b7a40ca9bbd549e626be01870caa3f0299d (patch) | |
tree | 9191beaa4ca1ad13fa05794d9dbcde5a69739b4e /drivers/usb/musb | |
parent | f82503f549c70c15e283511270e6713a912fef37 (diff) |
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')
-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 { | |||
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) |
@@ -165,6 +178,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) | |||
165 | } | 178 | } |
166 | } | 179 | } |
167 | 180 | ||
181 | static void cppi_trans_done_work(struct work_struct *work) | ||
182 | { | ||
183 | unsigned long flags; | ||
184 | struct cppi41_dma_channel *cppi41_channel = | ||
185 | container_of(work, struct cppi41_dma_channel, dma_completion); | ||
186 | struct cppi41_dma_controller *controller = cppi41_channel->controller; | ||
187 | struct musb *musb = controller->musb; | ||
188 | struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; | ||
189 | bool empty; | ||
190 | |||
191 | if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) { | ||
192 | spin_lock_irqsave(&musb->lock, flags); | ||
193 | cppi41_trans_done(cppi41_channel); | ||
194 | spin_unlock_irqrestore(&musb->lock, flags); | ||
195 | } else { | ||
196 | empty = musb_is_tx_fifo_empty(hw_ep); | ||
197 | if (empty) { | ||
198 | spin_lock_irqsave(&musb->lock, flags); | ||
199 | cppi41_trans_done(cppi41_channel); | ||
200 | spin_unlock_irqrestore(&musb->lock, flags); | ||
201 | } else { | ||
202 | schedule_work(&cppi41_channel->dma_completion); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
168 | static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) | 207 | static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) |
169 | { | 208 | { |
170 | struct cppi41_dma_controller *controller; | 209 | struct cppi41_dma_controller *controller; |
@@ -228,6 +267,14 @@ static void cppi41_dma_callback(void *private_data) | |||
228 | transferred < cppi41_channel->packet_sz) | 267 | transferred < cppi41_channel->packet_sz) |
229 | cppi41_channel->prog_len = 0; | 268 | cppi41_channel->prog_len = 0; |
230 | 269 | ||
270 | if (!cppi41_channel->is_tx) { | ||
271 | if (is_isoc(hw_ep, 1)) | ||
272 | schedule_work(&cppi41_channel->dma_completion); | ||
273 | else | ||
274 | cppi41_trans_done(cppi41_channel); | ||
275 | goto out; | ||
276 | } | ||
277 | |||
231 | empty = musb_is_tx_fifo_empty(hw_ep); | 278 | empty = musb_is_tx_fifo_empty(hw_ep); |
232 | if (empty) { | 279 | if (empty) { |
233 | cppi41_trans_done(cppi41_channel); | 280 | cppi41_trans_done(cppi41_channel); |
@@ -264,6 +311,10 @@ static void cppi41_dma_callback(void *private_data) | |||
264 | goto out; | 311 | goto out; |
265 | } | 312 | } |
266 | } | 313 | } |
314 | if (is_isoc(hw_ep, 0)) { | ||
315 | schedule_work(&cppi41_channel->dma_completion); | ||
316 | goto out; | ||
317 | } | ||
267 | list_add_tail(&cppi41_channel->tx_check, | 318 | list_add_tail(&cppi41_channel->tx_check, |
268 | &controller->early_tx_list); | 319 | &controller->early_tx_list); |
269 | if (!hrtimer_active(&controller->early_tx)) { | 320 | if (!hrtimer_active(&controller->early_tx)) { |
@@ -620,6 +671,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) | |||
620 | cppi41_channel->port_num = port; | 671 | cppi41_channel->port_num = port; |
621 | cppi41_channel->is_tx = is_tx; | 672 | cppi41_channel->is_tx = is_tx; |
622 | INIT_LIST_HEAD(&cppi41_channel->tx_check); | 673 | INIT_LIST_HEAD(&cppi41_channel->tx_check); |
674 | INIT_WORK(&cppi41_channel->dma_completion, | ||
675 | cppi_trans_done_work); | ||
623 | 676 | ||
624 | musb_dma = &cppi41_channel->channel; | 677 | musb_dma = &cppi41_channel->channel; |
625 | musb_dma->private_data = cppi41_channel; | 678 | musb_dma->private_data = cppi41_channel; |