diff options
author | Nicolas Ferre <nicolas.ferre@atmel.com> | 2009-12-14 21:01:31 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-15 11:53:35 -0500 |
commit | 74791a2dc8dc2af3feeaf3242d642ff58d33bd30 (patch) | |
tree | 2062b23a6bd0063f4999e73bb0fb9bd22839ffb7 /drivers/mmc | |
parent | 2635d1ba711560d521f6218c585a3e0401f566e1 (diff) |
mmc: atmel-mci: new MCI2 module support in atmel-mci driver
This new revision of the IP adds some improvements to the MCI already
present in several Atmel SOC.
Some new registers are added and a particular way of handling DMA
interaction lead to a new sequence in function call which is backward
compatible: On MCI2, we must set the DMAEN bit to enable the DMA
handshaking interface. This must happen before the data transfer command
is sent.
A new function is able to differentiate MCI2 code and is based on
knowledge of processor id (cpu_is_xxx()).
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/atmel-mci.c | 85 |
1 files changed, 75 insertions, 10 deletions
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index ba8b219d44c1..8072128e933b 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c | |||
@@ -94,6 +94,7 @@ struct atmel_mci_dma { | |||
94 | * @need_clock_update: Update the clock rate before the next request. | 94 | * @need_clock_update: Update the clock rate before the next request. |
95 | * @need_reset: Reset controller before next request. | 95 | * @need_reset: Reset controller before next request. |
96 | * @mode_reg: Value of the MR register. | 96 | * @mode_reg: Value of the MR register. |
97 | * @cfg_reg: Value of the CFG register. | ||
97 | * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus | 98 | * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus |
98 | * rate and timeout calculations. | 99 | * rate and timeout calculations. |
99 | * @mapbase: Physical address of the MMIO registers. | 100 | * @mapbase: Physical address of the MMIO registers. |
@@ -157,6 +158,7 @@ struct atmel_mci { | |||
157 | bool need_clock_update; | 158 | bool need_clock_update; |
158 | bool need_reset; | 159 | bool need_reset; |
159 | u32 mode_reg; | 160 | u32 mode_reg; |
161 | u32 cfg_reg; | ||
160 | unsigned long bus_hz; | 162 | unsigned long bus_hz; |
161 | unsigned long mapbase; | 163 | unsigned long mapbase; |
162 | struct clk *mck; | 164 | struct clk *mck; |
@@ -225,6 +227,19 @@ static bool mci_has_rwproof(void) | |||
225 | } | 227 | } |
226 | 228 | ||
227 | /* | 229 | /* |
230 | * The new MCI2 module isn't 100% compatible with the old MCI module, | ||
231 | * and it has a few nice features which we want to use... | ||
232 | */ | ||
233 | static inline bool atmci_is_mci2(void) | ||
234 | { | ||
235 | if (cpu_is_at91sam9g45()) | ||
236 | return true; | ||
237 | |||
238 | return false; | ||
239 | } | ||
240 | |||
241 | |||
242 | /* | ||
228 | * The debugfs stuff below is mostly optimized away when | 243 | * The debugfs stuff below is mostly optimized away when |
229 | * CONFIG_DEBUG_FS is not set. | 244 | * CONFIG_DEBUG_FS is not set. |
230 | */ | 245 | */ |
@@ -359,12 +374,33 @@ static int atmci_regs_show(struct seq_file *s, void *v) | |||
359 | buf[MCI_BLKR / 4], | 374 | buf[MCI_BLKR / 4], |
360 | buf[MCI_BLKR / 4] & 0xffff, | 375 | buf[MCI_BLKR / 4] & 0xffff, |
361 | (buf[MCI_BLKR / 4] >> 16) & 0xffff); | 376 | (buf[MCI_BLKR / 4] >> 16) & 0xffff); |
377 | if (atmci_is_mci2()) | ||
378 | seq_printf(s, "CSTOR:\t0x%08x\n", buf[MCI_CSTOR / 4]); | ||
362 | 379 | ||
363 | /* Don't read RSPR and RDR; it will consume the data there */ | 380 | /* Don't read RSPR and RDR; it will consume the data there */ |
364 | 381 | ||
365 | atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]); | 382 | atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]); |
366 | atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]); | 383 | atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]); |
367 | 384 | ||
385 | if (atmci_is_mci2()) { | ||
386 | u32 val; | ||
387 | |||
388 | val = buf[MCI_DMA / 4]; | ||
389 | seq_printf(s, "DMA:\t0x%08x OFFSET=%u CHKSIZE=%u%s\n", | ||
390 | val, val & 3, | ||
391 | ((val >> 4) & 3) ? | ||
392 | 1 << (((val >> 4) & 3) + 1) : 1, | ||
393 | val & MCI_DMAEN ? " DMAEN" : ""); | ||
394 | |||
395 | val = buf[MCI_CFG / 4]; | ||
396 | seq_printf(s, "CFG:\t0x%08x%s%s%s%s\n", | ||
397 | val, | ||
398 | val & MCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "", | ||
399 | val & MCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "", | ||
400 | val & MCI_CFG_HSMODE ? " HSMODE" : "", | ||
401 | val & MCI_CFG_LSYNC ? " LSYNC" : ""); | ||
402 | } | ||
403 | |||
368 | kfree(buf); | 404 | kfree(buf); |
369 | 405 | ||
370 | return 0; | 406 | return 0; |
@@ -559,6 +595,10 @@ static void atmci_dma_complete(void *arg) | |||
559 | 595 | ||
560 | dev_vdbg(&host->pdev->dev, "DMA complete\n"); | 596 | dev_vdbg(&host->pdev->dev, "DMA complete\n"); |
561 | 597 | ||
598 | if (atmci_is_mci2()) | ||
599 | /* Disable DMA hardware handshaking on MCI */ | ||
600 | mci_writel(host, DMA, mci_readl(host, DMA) & ~MCI_DMAEN); | ||
601 | |||
562 | atmci_dma_cleanup(host); | 602 | atmci_dma_cleanup(host); |
563 | 603 | ||
564 | /* | 604 | /* |
@@ -594,7 +634,7 @@ static void atmci_dma_complete(void *arg) | |||
594 | } | 634 | } |
595 | 635 | ||
596 | static int | 636 | static int |
597 | atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) | 637 | atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) |
598 | { | 638 | { |
599 | struct dma_chan *chan; | 639 | struct dma_chan *chan; |
600 | struct dma_async_tx_descriptor *desc; | 640 | struct dma_async_tx_descriptor *desc; |
@@ -626,6 +666,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) | |||
626 | if (!chan) | 666 | if (!chan) |
627 | return -ENODEV; | 667 | return -ENODEV; |
628 | 668 | ||
669 | if (atmci_is_mci2()) | ||
670 | mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN); | ||
671 | |||
629 | if (data->flags & MMC_DATA_READ) | 672 | if (data->flags & MMC_DATA_READ) |
630 | direction = DMA_FROM_DEVICE; | 673 | direction = DMA_FROM_DEVICE; |
631 | else | 674 | else |
@@ -643,10 +686,6 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) | |||
643 | host->dma.data_desc = desc; | 686 | host->dma.data_desc = desc; |
644 | desc->callback = atmci_dma_complete; | 687 | desc->callback = atmci_dma_complete; |
645 | desc->callback_param = host; | 688 | desc->callback_param = host; |
646 | desc->tx_submit(desc); | ||
647 | |||
648 | /* Go! */ | ||
649 | chan->device->device_issue_pending(chan); | ||
650 | 689 | ||
651 | return 0; | 690 | return 0; |
652 | unmap_exit: | 691 | unmap_exit: |
@@ -654,13 +693,26 @@ unmap_exit: | |||
654 | return -ENOMEM; | 693 | return -ENOMEM; |
655 | } | 694 | } |
656 | 695 | ||
696 | static void atmci_submit_data(struct atmel_mci *host) | ||
697 | { | ||
698 | struct dma_chan *chan = host->data_chan; | ||
699 | struct dma_async_tx_descriptor *desc = host->dma.data_desc; | ||
700 | |||
701 | if (chan) { | ||
702 | desc->tx_submit(desc); | ||
703 | chan->device->device_issue_pending(chan); | ||
704 | } | ||
705 | } | ||
706 | |||
657 | #else /* CONFIG_MMC_ATMELMCI_DMA */ | 707 | #else /* CONFIG_MMC_ATMELMCI_DMA */ |
658 | 708 | ||
659 | static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) | 709 | static int atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) |
660 | { | 710 | { |
661 | return -ENOSYS; | 711 | return -ENOSYS; |
662 | } | 712 | } |
663 | 713 | ||
714 | static void atmci_submit_data(struct atmel_mci *host) {} | ||
715 | |||
664 | static void atmci_stop_dma(struct atmel_mci *host) | 716 | static void atmci_stop_dma(struct atmel_mci *host) |
665 | { | 717 | { |
666 | /* Data transfer was stopped by the interrupt handler */ | 718 | /* Data transfer was stopped by the interrupt handler */ |
@@ -674,7 +726,7 @@ static void atmci_stop_dma(struct atmel_mci *host) | |||
674 | * Returns a mask of interrupt flags to be enabled after the whole | 726 | * Returns a mask of interrupt flags to be enabled after the whole |
675 | * request has been prepared. | 727 | * request has been prepared. |
676 | */ | 728 | */ |
677 | static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data) | 729 | static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data) |
678 | { | 730 | { |
679 | u32 iflags; | 731 | u32 iflags; |
680 | 732 | ||
@@ -685,7 +737,7 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data) | |||
685 | host->data = data; | 737 | host->data = data; |
686 | 738 | ||
687 | iflags = ATMCI_DATA_ERROR_FLAGS; | 739 | iflags = ATMCI_DATA_ERROR_FLAGS; |
688 | if (atmci_submit_data_dma(host, data)) { | 740 | if (atmci_prepare_data_dma(host, data)) { |
689 | host->data_chan = NULL; | 741 | host->data_chan = NULL; |
690 | 742 | ||
691 | /* | 743 | /* |
@@ -731,6 +783,8 @@ static void atmci_start_request(struct atmel_mci *host, | |||
731 | mci_writel(host, CR, MCI_CR_SWRST); | 783 | mci_writel(host, CR, MCI_CR_SWRST); |
732 | mci_writel(host, CR, MCI_CR_MCIEN); | 784 | mci_writel(host, CR, MCI_CR_MCIEN); |
733 | mci_writel(host, MR, host->mode_reg); | 785 | mci_writel(host, MR, host->mode_reg); |
786 | if (atmci_is_mci2()) | ||
787 | mci_writel(host, CFG, host->cfg_reg); | ||
734 | host->need_reset = false; | 788 | host->need_reset = false; |
735 | } | 789 | } |
736 | mci_writel(host, SDCR, slot->sdc_reg); | 790 | mci_writel(host, SDCR, slot->sdc_reg); |
@@ -746,6 +800,7 @@ static void atmci_start_request(struct atmel_mci *host, | |||
746 | while (!(mci_readl(host, SR) & MCI_CMDRDY)) | 800 | while (!(mci_readl(host, SR) & MCI_CMDRDY)) |
747 | cpu_relax(); | 801 | cpu_relax(); |
748 | } | 802 | } |
803 | iflags = 0; | ||
749 | data = mrq->data; | 804 | data = mrq->data; |
750 | if (data) { | 805 | if (data) { |
751 | atmci_set_timeout(host, slot, data); | 806 | atmci_set_timeout(host, slot, data); |
@@ -755,15 +810,17 @@ static void atmci_start_request(struct atmel_mci *host, | |||
755 | | MCI_BLKLEN(data->blksz)); | 810 | | MCI_BLKLEN(data->blksz)); |
756 | dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n", | 811 | dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n", |
757 | MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); | 812 | MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); |
813 | |||
814 | iflags |= atmci_prepare_data(host, data); | ||
758 | } | 815 | } |
759 | 816 | ||
760 | iflags = MCI_CMDRDY; | 817 | iflags |= MCI_CMDRDY; |
761 | cmd = mrq->cmd; | 818 | cmd = mrq->cmd; |
762 | cmdflags = atmci_prepare_command(slot->mmc, cmd); | 819 | cmdflags = atmci_prepare_command(slot->mmc, cmd); |
763 | atmci_start_command(host, cmd, cmdflags); | 820 | atmci_start_command(host, cmd, cmdflags); |
764 | 821 | ||
765 | if (data) | 822 | if (data) |
766 | iflags |= atmci_submit_data(host, data); | 823 | atmci_submit_data(host); |
767 | 824 | ||
768 | if (mrq->stop) { | 825 | if (mrq->stop) { |
769 | host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop); | 826 | host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop); |
@@ -859,6 +916,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
859 | clk_enable(host->mck); | 916 | clk_enable(host->mck); |
860 | mci_writel(host, CR, MCI_CR_SWRST); | 917 | mci_writel(host, CR, MCI_CR_SWRST); |
861 | mci_writel(host, CR, MCI_CR_MCIEN); | 918 | mci_writel(host, CR, MCI_CR_MCIEN); |
919 | if (atmci_is_mci2()) | ||
920 | mci_writel(host, CFG, host->cfg_reg); | ||
862 | } | 921 | } |
863 | 922 | ||
864 | /* | 923 | /* |
@@ -1097,6 +1156,8 @@ static void atmci_detect_change(unsigned long data) | |||
1097 | mci_writel(host, CR, MCI_CR_SWRST); | 1156 | mci_writel(host, CR, MCI_CR_SWRST); |
1098 | mci_writel(host, CR, MCI_CR_MCIEN); | 1157 | mci_writel(host, CR, MCI_CR_MCIEN); |
1099 | mci_writel(host, MR, host->mode_reg); | 1158 | mci_writel(host, MR, host->mode_reg); |
1159 | if (atmci_is_mci2()) | ||
1160 | mci_writel(host, CFG, host->cfg_reg); | ||
1100 | 1161 | ||
1101 | host->data = NULL; | 1162 | host->data = NULL; |
1102 | host->cmd = NULL; | 1163 | host->cmd = NULL; |
@@ -1620,6 +1681,10 @@ static void atmci_configure_dma(struct atmel_mci *host) | |||
1620 | } | 1681 | } |
1621 | if (!host->dma.chan) | 1682 | if (!host->dma.chan) |
1622 | dev_notice(&host->pdev->dev, "DMA not available, using PIO\n"); | 1683 | dev_notice(&host->pdev->dev, "DMA not available, using PIO\n"); |
1684 | else | ||
1685 | dev_info(&host->pdev->dev, | ||
1686 | "Using %s for DMA transfers\n", | ||
1687 | dma_chan_name(host->dma.chan)); | ||
1623 | } | 1688 | } |
1624 | #else | 1689 | #else |
1625 | static void atmci_configure_dma(struct atmel_mci *host) {} | 1690 | static void atmci_configure_dma(struct atmel_mci *host) {} |