diff options
-rw-r--r-- | arch/avr32/include/asm/atmel-mci.h | 4 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap700x.c | 16 | ||||
-rw-r--r-- | drivers/mmc/host/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mmc/host/atmel-mci.c | 274 |
4 files changed, 289 insertions, 16 deletions
diff --git a/arch/avr32/include/asm/atmel-mci.h b/arch/avr32/include/asm/atmel-mci.h index 5d5ae1295cfd..59f3fadd0b68 100644 --- a/arch/avr32/include/asm/atmel-mci.h +++ b/arch/avr32/include/asm/atmel-mci.h | |||
@@ -3,6 +3,8 @@ | |||
3 | 3 | ||
4 | #define ATMEL_MCI_MAX_NR_SLOTS 2 | 4 | #define ATMEL_MCI_MAX_NR_SLOTS 2 |
5 | 5 | ||
6 | struct dma_slave; | ||
7 | |||
6 | /** | 8 | /** |
7 | * struct mci_slot_pdata - board-specific per-slot configuration | 9 | * struct mci_slot_pdata - board-specific per-slot configuration |
8 | * @bus_width: Number of data lines wired up the slot | 10 | * @bus_width: Number of data lines wired up the slot |
@@ -26,9 +28,11 @@ struct mci_slot_pdata { | |||
26 | 28 | ||
27 | /** | 29 | /** |
28 | * struct mci_platform_data - board-specific MMC/SDcard configuration | 30 | * struct mci_platform_data - board-specific MMC/SDcard configuration |
31 | * @dma_slave: DMA slave interface to use in data transfers, or NULL. | ||
29 | * @slot: Per-slot configuration data. | 32 | * @slot: Per-slot configuration data. |
30 | */ | 33 | */ |
31 | struct mci_platform_data { | 34 | struct mci_platform_data { |
35 | struct dma_slave *dma_slave; | ||
32 | struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS]; | 36 | struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS]; |
33 | }; | 37 | }; |
34 | 38 | ||
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 9967d5a3b6eb..f1b9a3ac2733 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c | |||
@@ -1273,6 +1273,7 @@ struct platform_device *__init | |||
1273 | at32_add_device_mci(unsigned int id, struct mci_platform_data *data) | 1273 | at32_add_device_mci(unsigned int id, struct mci_platform_data *data) |
1274 | { | 1274 | { |
1275 | struct platform_device *pdev; | 1275 | struct platform_device *pdev; |
1276 | struct dw_dma_slave *dws; | ||
1276 | 1277 | ||
1277 | if (id != 0 || !data) | 1278 | if (id != 0 || !data) |
1278 | return NULL; | 1279 | return NULL; |
@@ -1289,6 +1290,21 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) | |||
1289 | ARRAY_SIZE(atmel_mci0_resource))) | 1290 | ARRAY_SIZE(atmel_mci0_resource))) |
1290 | goto fail; | 1291 | goto fail; |
1291 | 1292 | ||
1293 | if (data->dma_slave) | ||
1294 | dws = kmemdup(to_dw_dma_slave(data->dma_slave), | ||
1295 | sizeof(struct dw_dma_slave), GFP_KERNEL); | ||
1296 | else | ||
1297 | dws = kzalloc(sizeof(struct dw_dma_slave), GFP_KERNEL); | ||
1298 | |||
1299 | dws->slave.dev = &pdev->dev; | ||
1300 | dws->slave.dma_dev = &dw_dmac0_device.dev; | ||
1301 | dws->slave.reg_width = DMA_SLAVE_WIDTH_32BIT; | ||
1302 | dws->cfg_hi = (DWC_CFGH_SRC_PER(0) | ||
1303 | | DWC_CFGH_DST_PER(1)); | ||
1304 | dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | ||
1305 | | DWC_CFGL_HS_SRC_POL); | ||
1306 | |||
1307 | data->dma_slave = &dws->slave; | ||
1292 | 1308 | ||
1293 | if (platform_device_add_data(pdev, data, | 1309 | if (platform_device_add_data(pdev, data, |
1294 | sizeof(struct mci_platform_data))) | 1310 | sizeof(struct mci_platform_data))) |
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ea8d7a3490d9..1ce21d4c8608 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig | |||
@@ -114,6 +114,17 @@ config MMC_ATMELMCI | |||
114 | 114 | ||
115 | If unsure, say N. | 115 | If unsure, say N. |
116 | 116 | ||
117 | config MMC_ATMELMCI_DMA | ||
118 | bool "Atmel MCI DMA support (EXPERIMENTAL)" | ||
119 | depends on MMC_ATMELMCI && DMA_ENGINE && EXPERIMENTAL | ||
120 | help | ||
121 | Say Y here to have the Atmel MCI driver use a DMA engine to | ||
122 | do data transfers and thus increase the throughput and | ||
123 | reduce the CPU utilization. Note that this is highly | ||
124 | experimental and may cause the driver to lock up. | ||
125 | |||
126 | If unsure, say N. | ||
127 | |||
117 | config MMC_IMX | 128 | config MMC_IMX |
118 | tristate "Motorola i.MX Multimedia Card Interface support" | 129 | tristate "Motorola i.MX Multimedia Card Interface support" |
119 | depends on ARCH_IMX | 130 | depends on ARCH_IMX |
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index d8ab35175a53..d45dfa259386 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c | |||
@@ -11,6 +11,8 @@ | |||
11 | #include <linux/clk.h> | 11 | #include <linux/clk.h> |
12 | #include <linux/debugfs.h> | 12 | #include <linux/debugfs.h> |
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/dmaengine.h> | ||
15 | #include <linux/dma-mapping.h> | ||
14 | #include <linux/err.h> | 16 | #include <linux/err.h> |
15 | #include <linux/gpio.h> | 17 | #include <linux/gpio.h> |
16 | #include <linux/init.h> | 18 | #include <linux/init.h> |
@@ -33,6 +35,7 @@ | |||
33 | #include "atmel-mci-regs.h" | 35 | #include "atmel-mci-regs.h" |
34 | 36 | ||
35 | #define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE) | 37 | #define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE) |
38 | #define ATMCI_DMA_THRESHOLD 16 | ||
36 | 39 | ||
37 | enum { | 40 | enum { |
38 | EVENT_CMD_COMPLETE = 0, | 41 | EVENT_CMD_COMPLETE = 0, |
@@ -50,6 +53,14 @@ enum atmel_mci_state { | |||
50 | STATE_DATA_ERROR, | 53 | STATE_DATA_ERROR, |
51 | }; | 54 | }; |
52 | 55 | ||
56 | struct atmel_mci_dma { | ||
57 | #ifdef CONFIG_MMC_ATMELMCI_DMA | ||
58 | struct dma_client client; | ||
59 | struct dma_chan *chan; | ||
60 | struct dma_async_tx_descriptor *data_desc; | ||
61 | #endif | ||
62 | }; | ||
63 | |||
53 | /** | 64 | /** |
54 | * struct atmel_mci - MMC controller state shared between all slots | 65 | * struct atmel_mci - MMC controller state shared between all slots |
55 | * @lock: Spinlock protecting the queue and associated data. | 66 | * @lock: Spinlock protecting the queue and associated data. |
@@ -62,6 +73,8 @@ enum atmel_mci_state { | |||
62 | * @cmd: The command currently being sent to the card, or NULL. | 73 | * @cmd: The command currently being sent to the card, or NULL. |
63 | * @data: The data currently being transferred, or NULL if no data | 74 | * @data: The data currently being transferred, or NULL if no data |
64 | * transfer is in progress. | 75 | * transfer is in progress. |
76 | * @dma: DMA client state. | ||
77 | * @data_chan: DMA channel being used for the current data transfer. | ||
65 | * @cmd_status: Snapshot of SR taken upon completion of the current | 78 | * @cmd_status: Snapshot of SR taken upon completion of the current |
66 | * command. Only valid when EVENT_CMD_COMPLETE is pending. | 79 | * command. Only valid when EVENT_CMD_COMPLETE is pending. |
67 | * @data_status: Snapshot of SR taken upon completion of the current | 80 | * @data_status: Snapshot of SR taken upon completion of the current |
@@ -126,6 +139,9 @@ struct atmel_mci { | |||
126 | struct mmc_command *cmd; | 139 | struct mmc_command *cmd; |
127 | struct mmc_data *data; | 140 | struct mmc_data *data; |
128 | 141 | ||
142 | struct atmel_mci_dma dma; | ||
143 | struct dma_chan *data_chan; | ||
144 | |||
129 | u32 cmd_status; | 145 | u32 cmd_status; |
130 | u32 data_status; | 146 | u32 data_status; |
131 | u32 stop_cmdr; | 147 | u32 stop_cmdr; |
@@ -485,6 +501,144 @@ static void send_stop_cmd(struct atmel_mci *host, struct mmc_data *data) | |||
485 | mci_writel(host, IER, MCI_CMDRDY); | 501 | mci_writel(host, IER, MCI_CMDRDY); |
486 | } | 502 | } |
487 | 503 | ||
504 | #ifdef CONFIG_MMC_ATMELMCI_DMA | ||
505 | static void atmci_dma_cleanup(struct atmel_mci *host) | ||
506 | { | ||
507 | struct mmc_data *data = host->data; | ||
508 | |||
509 | dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, | ||
510 | ((data->flags & MMC_DATA_WRITE) | ||
511 | ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); | ||
512 | } | ||
513 | |||
514 | static void atmci_stop_dma(struct atmel_mci *host) | ||
515 | { | ||
516 | struct dma_chan *chan = host->data_chan; | ||
517 | |||
518 | if (chan) { | ||
519 | chan->device->device_terminate_all(chan); | ||
520 | atmci_dma_cleanup(host); | ||
521 | } else { | ||
522 | /* Data transfer was stopped by the interrupt handler */ | ||
523 | atmci_set_pending(host, EVENT_XFER_COMPLETE); | ||
524 | mci_writel(host, IER, MCI_NOTBUSY); | ||
525 | } | ||
526 | } | ||
527 | |||
528 | /* This function is called by the DMA driver from tasklet context. */ | ||
529 | static void atmci_dma_complete(void *arg) | ||
530 | { | ||
531 | struct atmel_mci *host = arg; | ||
532 | struct mmc_data *data = host->data; | ||
533 | |||
534 | dev_vdbg(&host->pdev->dev, "DMA complete\n"); | ||
535 | |||
536 | atmci_dma_cleanup(host); | ||
537 | |||
538 | /* | ||
539 | * If the card was removed, data will be NULL. No point trying | ||
540 | * to send the stop command or waiting for NBUSY in this case. | ||
541 | */ | ||
542 | if (data) { | ||
543 | atmci_set_pending(host, EVENT_XFER_COMPLETE); | ||
544 | tasklet_schedule(&host->tasklet); | ||
545 | |||
546 | /* | ||
547 | * Regardless of what the documentation says, we have | ||
548 | * to wait for NOTBUSY even after block read | ||
549 | * operations. | ||
550 | * | ||
551 | * When the DMA transfer is complete, the controller | ||
552 | * may still be reading the CRC from the card, i.e. | ||
553 | * the data transfer is still in progress and we | ||
554 | * haven't seen all the potential error bits yet. | ||
555 | * | ||
556 | * The interrupt handler will schedule a different | ||
557 | * tasklet to finish things up when the data transfer | ||
558 | * is completely done. | ||
559 | * | ||
560 | * We may not complete the mmc request here anyway | ||
561 | * because the mmc layer may call back and cause us to | ||
562 | * violate the "don't submit new operations from the | ||
563 | * completion callback" rule of the dma engine | ||
564 | * framework. | ||
565 | */ | ||
566 | mci_writel(host, IER, MCI_NOTBUSY); | ||
567 | } | ||
568 | } | ||
569 | |||
570 | static int | ||
571 | atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) | ||
572 | { | ||
573 | struct dma_chan *chan; | ||
574 | struct dma_async_tx_descriptor *desc; | ||
575 | struct scatterlist *sg; | ||
576 | unsigned int i; | ||
577 | enum dma_data_direction direction; | ||
578 | |||
579 | /* | ||
580 | * We don't do DMA on "complex" transfers, i.e. with | ||
581 | * non-word-aligned buffers or lengths. Also, we don't bother | ||
582 | * with all the DMA setup overhead for short transfers. | ||
583 | */ | ||
584 | if (data->blocks * data->blksz < ATMCI_DMA_THRESHOLD) | ||
585 | return -EINVAL; | ||
586 | if (data->blksz & 3) | ||
587 | return -EINVAL; | ||
588 | |||
589 | for_each_sg(data->sg, sg, data->sg_len, i) { | ||
590 | if (sg->offset & 3 || sg->length & 3) | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | |||
594 | /* If we don't have a channel, we can't do DMA */ | ||
595 | chan = host->dma.chan; | ||
596 | if (chan) { | ||
597 | dma_chan_get(chan); | ||
598 | host->data_chan = chan; | ||
599 | } | ||
600 | |||
601 | if (!chan) | ||
602 | return -ENODEV; | ||
603 | |||
604 | if (data->flags & MMC_DATA_READ) | ||
605 | direction = DMA_FROM_DEVICE; | ||
606 | else | ||
607 | direction = DMA_TO_DEVICE; | ||
608 | |||
609 | desc = chan->device->device_prep_slave_sg(chan, | ||
610 | data->sg, data->sg_len, direction, | ||
611 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
612 | if (!desc) | ||
613 | return -ENOMEM; | ||
614 | |||
615 | host->dma.data_desc = desc; | ||
616 | desc->callback = atmci_dma_complete; | ||
617 | desc->callback_param = host; | ||
618 | desc->tx_submit(desc); | ||
619 | |||
620 | /* Go! */ | ||
621 | chan->device->device_issue_pending(chan); | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | #else /* CONFIG_MMC_ATMELMCI_DMA */ | ||
627 | |||
628 | static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) | ||
629 | { | ||
630 | return -ENOSYS; | ||
631 | } | ||
632 | |||
633 | static void atmci_stop_dma(struct atmel_mci *host) | ||
634 | { | ||
635 | /* Data transfer was stopped by the interrupt handler */ | ||
636 | atmci_set_pending(host, EVENT_XFER_COMPLETE); | ||
637 | mci_writel(host, IER, MCI_NOTBUSY); | ||
638 | } | ||
639 | |||
640 | #endif /* CONFIG_MMC_ATMELMCI_DMA */ | ||
641 | |||
488 | /* | 642 | /* |
489 | * Returns a mask of interrupt flags to be enabled after the whole | 643 | * Returns a mask of interrupt flags to be enabled after the whole |
490 | * request has been prepared. | 644 | * request has been prepared. |
@@ -500,24 +654,27 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data) | |||
500 | host->data = data; | 654 | host->data = data; |
501 | 655 | ||
502 | iflags = ATMCI_DATA_ERROR_FLAGS; | 656 | iflags = ATMCI_DATA_ERROR_FLAGS; |
657 | if (atmci_submit_data_dma(host, data)) { | ||
658 | host->data_chan = NULL; | ||
503 | 659 | ||
504 | /* | 660 | /* |
505 | * Errata: MMC data write operation with less than 12 | 661 | * Errata: MMC data write operation with less than 12 |
506 | * bytes is impossible. | 662 | * bytes is impossible. |
507 | * | 663 | * |
508 | * Errata: MCI Transmit Data Register (TDR) FIFO | 664 | * Errata: MCI Transmit Data Register (TDR) FIFO |
509 | * corruption when length is not multiple of 4. | 665 | * corruption when length is not multiple of 4. |
510 | */ | 666 | */ |
511 | if (data->blocks * data->blksz < 12 | 667 | if (data->blocks * data->blksz < 12 |
512 | || (data->blocks * data->blksz) & 3) | 668 | || (data->blocks * data->blksz) & 3) |
513 | host->need_reset = true; | 669 | host->need_reset = true; |
514 | 670 | ||
515 | host->sg = data->sg; | 671 | host->sg = data->sg; |
516 | host->pio_offset = 0; | 672 | host->pio_offset = 0; |
517 | if (data->flags & MMC_DATA_READ) | 673 | if (data->flags & MMC_DATA_READ) |
518 | iflags |= MCI_RXRDY; | 674 | iflags |= MCI_RXRDY; |
519 | else | 675 | else |
520 | iflags |= MCI_TXRDY; | 676 | iflags |= MCI_TXRDY; |
677 | } | ||
521 | 678 | ||
522 | return iflags; | 679 | return iflags; |
523 | } | 680 | } |
@@ -848,6 +1005,7 @@ static void atmci_command_complete(struct atmel_mci *host, | |||
848 | 1005 | ||
849 | if (cmd->data) { | 1006 | if (cmd->data) { |
850 | host->data = NULL; | 1007 | host->data = NULL; |
1008 | atmci_stop_dma(host); | ||
851 | mci_writel(host, IDR, MCI_NOTBUSY | 1009 | mci_writel(host, IDR, MCI_NOTBUSY |
852 | | MCI_TXRDY | MCI_RXRDY | 1010 | | MCI_TXRDY | MCI_RXRDY |
853 | | ATMCI_DATA_ERROR_FLAGS); | 1011 | | ATMCI_DATA_ERROR_FLAGS); |
@@ -917,6 +1075,7 @@ static void atmci_detect_change(unsigned long data) | |||
917 | /* fall through */ | 1075 | /* fall through */ |
918 | case STATE_SENDING_DATA: | 1076 | case STATE_SENDING_DATA: |
919 | mrq->data->error = -ENOMEDIUM; | 1077 | mrq->data->error = -ENOMEDIUM; |
1078 | atmci_stop_dma(host); | ||
920 | break; | 1079 | break; |
921 | case STATE_DATA_BUSY: | 1080 | case STATE_DATA_BUSY: |
922 | case STATE_DATA_ERROR: | 1081 | case STATE_DATA_ERROR: |
@@ -995,6 +1154,7 @@ static void atmci_tasklet_func(unsigned long priv) | |||
995 | case STATE_SENDING_DATA: | 1154 | case STATE_SENDING_DATA: |
996 | if (atmci_test_and_clear_pending(host, | 1155 | if (atmci_test_and_clear_pending(host, |
997 | EVENT_DATA_ERROR)) { | 1156 | EVENT_DATA_ERROR)) { |
1157 | atmci_stop_dma(host); | ||
998 | if (data->stop) | 1158 | if (data->stop) |
999 | send_stop_cmd(host, data); | 1159 | send_stop_cmd(host, data); |
1000 | state = STATE_DATA_ERROR; | 1160 | state = STATE_DATA_ERROR; |
@@ -1280,6 +1440,60 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) | |||
1280 | return IRQ_HANDLED; | 1440 | return IRQ_HANDLED; |
1281 | } | 1441 | } |
1282 | 1442 | ||
1443 | #ifdef CONFIG_MMC_ATMELMCI_DMA | ||
1444 | |||
1445 | static inline struct atmel_mci * | ||
1446 | dma_client_to_atmel_mci(struct dma_client *client) | ||
1447 | { | ||
1448 | return container_of(client, struct atmel_mci, dma.client); | ||
1449 | } | ||
1450 | |||
1451 | static enum dma_state_client atmci_dma_event(struct dma_client *client, | ||
1452 | struct dma_chan *chan, enum dma_state state) | ||
1453 | { | ||
1454 | struct atmel_mci *host; | ||
1455 | enum dma_state_client ret = DMA_NAK; | ||
1456 | |||
1457 | host = dma_client_to_atmel_mci(client); | ||
1458 | |||
1459 | switch (state) { | ||
1460 | case DMA_RESOURCE_AVAILABLE: | ||
1461 | spin_lock_bh(&host->lock); | ||
1462 | if (!host->dma.chan) { | ||
1463 | host->dma.chan = chan; | ||
1464 | ret = DMA_ACK; | ||
1465 | } | ||
1466 | spin_unlock_bh(&host->lock); | ||
1467 | |||
1468 | if (ret == DMA_ACK) | ||
1469 | dev_info(&host->pdev->dev, | ||
1470 | "Using %s for DMA transfers\n", | ||
1471 | chan->dev.bus_id); | ||
1472 | break; | ||
1473 | |||
1474 | case DMA_RESOURCE_REMOVED: | ||
1475 | spin_lock_bh(&host->lock); | ||
1476 | if (host->dma.chan == chan) { | ||
1477 | host->dma.chan = NULL; | ||
1478 | ret = DMA_ACK; | ||
1479 | } | ||
1480 | spin_unlock_bh(&host->lock); | ||
1481 | |||
1482 | if (ret == DMA_ACK) | ||
1483 | dev_info(&host->pdev->dev, | ||
1484 | "Lost %s, falling back to PIO\n", | ||
1485 | chan->dev.bus_id); | ||
1486 | break; | ||
1487 | |||
1488 | default: | ||
1489 | break; | ||
1490 | } | ||
1491 | |||
1492 | |||
1493 | return ret; | ||
1494 | } | ||
1495 | #endif /* CONFIG_MMC_ATMELMCI_DMA */ | ||
1496 | |||
1283 | static int __init atmci_init_slot(struct atmel_mci *host, | 1497 | static int __init atmci_init_slot(struct atmel_mci *host, |
1284 | struct mci_slot_pdata *slot_data, unsigned int id, | 1498 | struct mci_slot_pdata *slot_data, unsigned int id, |
1285 | u32 sdc_reg) | 1499 | u32 sdc_reg) |
@@ -1434,6 +1648,25 @@ static int __init atmci_probe(struct platform_device *pdev) | |||
1434 | if (ret) | 1648 | if (ret) |
1435 | goto err_request_irq; | 1649 | goto err_request_irq; |
1436 | 1650 | ||
1651 | #ifdef CONFIG_MMC_ATMELMCI_DMA | ||
1652 | if (pdata->dma_slave) { | ||
1653 | struct dma_slave *slave = pdata->dma_slave; | ||
1654 | |||
1655 | slave->tx_reg = regs->start + MCI_TDR; | ||
1656 | slave->rx_reg = regs->start + MCI_RDR; | ||
1657 | |||
1658 | /* Try to grab a DMA channel */ | ||
1659 | host->dma.client.event_callback = atmci_dma_event; | ||
1660 | dma_cap_set(DMA_SLAVE, host->dma.client.cap_mask); | ||
1661 | host->dma.client.slave = slave; | ||
1662 | |||
1663 | dma_async_client_register(&host->dma.client); | ||
1664 | dma_async_client_chan_request(&host->dma.client); | ||
1665 | } else { | ||
1666 | dev_notice(&pdev->dev, "DMA not available, using PIO\n"); | ||
1667 | } | ||
1668 | #endif /* CONFIG_MMC_ATMELMCI_DMA */ | ||
1669 | |||
1437 | platform_set_drvdata(pdev, host); | 1670 | platform_set_drvdata(pdev, host); |
1438 | 1671 | ||
1439 | /* We need at least one slot to succeed */ | 1672 | /* We need at least one slot to succeed */ |
@@ -1462,6 +1695,10 @@ static int __init atmci_probe(struct platform_device *pdev) | |||
1462 | return 0; | 1695 | return 0; |
1463 | 1696 | ||
1464 | err_init_slot: | 1697 | err_init_slot: |
1698 | #ifdef CONFIG_MMC_ATMELMCI_DMA | ||
1699 | if (pdata->dma_slave) | ||
1700 | dma_async_client_unregister(&host->dma.client); | ||
1701 | #endif | ||
1465 | free_irq(irq, host); | 1702 | free_irq(irq, host); |
1466 | err_request_irq: | 1703 | err_request_irq: |
1467 | iounmap(host->regs); | 1704 | iounmap(host->regs); |
@@ -1490,6 +1727,11 @@ static int __exit atmci_remove(struct platform_device *pdev) | |||
1490 | mci_readl(host, SR); | 1727 | mci_readl(host, SR); |
1491 | clk_disable(host->mck); | 1728 | clk_disable(host->mck); |
1492 | 1729 | ||
1730 | #ifdef CONFIG_MMC_ATMELMCI_DMA | ||
1731 | if (host->dma.client.slave) | ||
1732 | dma_async_client_unregister(&host->dma.client); | ||
1733 | #endif | ||
1734 | |||
1493 | free_irq(platform_get_irq(pdev, 0), host); | 1735 | free_irq(platform_get_irq(pdev, 0), host); |
1494 | iounmap(host->regs); | 1736 | iounmap(host->regs); |
1495 | 1737 | ||