diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/omap.c | 48 |
1 files changed, 42 insertions, 6 deletions
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 3d59c5d81b3d..ab0974d261e5 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c | |||
@@ -138,6 +138,11 @@ struct mmc_omap_host { | |||
138 | unsigned abort:1; | 138 | unsigned abort:1; |
139 | struct timer_list cmd_abort_timer; | 139 | struct timer_list cmd_abort_timer; |
140 | 140 | ||
141 | struct work_struct slot_release_work; | ||
142 | struct mmc_omap_slot *next_slot; | ||
143 | struct work_struct send_stop_work; | ||
144 | struct mmc_data *stop_data; | ||
145 | |||
141 | unsigned int sg_len; | 146 | unsigned int sg_len; |
142 | int sg_idx; | 147 | int sg_idx; |
143 | u16 * buffer; | 148 | u16 * buffer; |
@@ -236,6 +241,21 @@ no_claim: | |||
236 | static void mmc_omap_start_request(struct mmc_omap_host *host, | 241 | static void mmc_omap_start_request(struct mmc_omap_host *host, |
237 | struct mmc_request *req); | 242 | struct mmc_request *req); |
238 | 243 | ||
244 | static void mmc_omap_slot_release_work(struct work_struct *work) | ||
245 | { | ||
246 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | ||
247 | slot_release_work); | ||
248 | struct mmc_omap_slot *next_slot = host->next_slot; | ||
249 | struct mmc_request *rq; | ||
250 | |||
251 | host->next_slot = NULL; | ||
252 | mmc_omap_select_slot(next_slot, 1); | ||
253 | |||
254 | rq = next_slot->mrq; | ||
255 | next_slot->mrq = NULL; | ||
256 | mmc_omap_start_request(host, rq); | ||
257 | } | ||
258 | |||
239 | static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) | 259 | static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) |
240 | { | 260 | { |
241 | struct mmc_omap_host *host = slot->host; | 261 | struct mmc_omap_host *host = slot->host; |
@@ -257,21 +277,19 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) | |||
257 | /* Check for any pending requests */ | 277 | /* Check for any pending requests */ |
258 | for (i = 0; i < host->nr_slots; i++) { | 278 | for (i = 0; i < host->nr_slots; i++) { |
259 | struct mmc_omap_slot *new_slot; | 279 | struct mmc_omap_slot *new_slot; |
260 | struct mmc_request *rq; | ||
261 | 280 | ||
262 | if (host->slots[i] == NULL || host->slots[i]->mrq == NULL) | 281 | if (host->slots[i] == NULL || host->slots[i]->mrq == NULL) |
263 | continue; | 282 | continue; |
264 | 283 | ||
284 | BUG_ON(host->next_slot != NULL); | ||
265 | new_slot = host->slots[i]; | 285 | new_slot = host->slots[i]; |
266 | /* The current slot should not have a request in queue */ | 286 | /* The current slot should not have a request in queue */ |
267 | BUG_ON(new_slot == host->current_slot); | 287 | BUG_ON(new_slot == host->current_slot); |
268 | 288 | ||
289 | host->next_slot = new_slot; | ||
269 | host->mmc = new_slot->mmc; | 290 | host->mmc = new_slot->mmc; |
270 | spin_unlock_irqrestore(&host->slot_lock, flags); | 291 | spin_unlock_irqrestore(&host->slot_lock, flags); |
271 | mmc_omap_select_slot(new_slot, 1); | 292 | schedule_work(&host->slot_release_work); |
272 | rq = new_slot->mrq; | ||
273 | new_slot->mrq = NULL; | ||
274 | mmc_omap_start_request(host, rq); | ||
275 | return; | 293 | return; |
276 | } | 294 | } |
277 | 295 | ||
@@ -400,6 +418,20 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data, | |||
400 | dma_data_dir); | 418 | dma_data_dir); |
401 | } | 419 | } |
402 | 420 | ||
421 | static void mmc_omap_send_stop_work(struct work_struct *work) | ||
422 | { | ||
423 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | ||
424 | send_stop_work); | ||
425 | struct mmc_omap_slot *slot = host->current_slot; | ||
426 | struct mmc_data *data = host->stop_data; | ||
427 | unsigned long tick_ns; | ||
428 | |||
429 | tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq; | ||
430 | ndelay(8*tick_ns); | ||
431 | |||
432 | mmc_omap_start_command(host, data->stop); | ||
433 | } | ||
434 | |||
403 | static void | 435 | static void |
404 | mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | 436 | mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) |
405 | { | 437 | { |
@@ -424,7 +456,8 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | |||
424 | return; | 456 | return; |
425 | } | 457 | } |
426 | 458 | ||
427 | mmc_omap_start_command(host, data->stop); | 459 | host->stop_data = data; |
460 | schedule_work(&host->send_stop_work); | ||
428 | } | 461 | } |
429 | 462 | ||
430 | static void | 463 | static void |
@@ -1389,6 +1422,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
1389 | goto err_free_mem_region; | 1422 | goto err_free_mem_region; |
1390 | } | 1423 | } |
1391 | 1424 | ||
1425 | INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work); | ||
1426 | INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work); | ||
1427 | |||
1392 | INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command); | 1428 | INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command); |
1393 | setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer, | 1429 | setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer, |
1394 | (unsigned long) host); | 1430 | (unsigned long) host); |