diff options
Diffstat (limited to 'drivers/mmc/host/mxcmmc.c')
-rw-r--r-- | drivers/mmc/host/mxcmmc.c | 181 |
1 files changed, 104 insertions, 77 deletions
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 4428594261c5..cc20e0259325 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c | |||
@@ -32,16 +32,14 @@ | |||
32 | #include <linux/io.h> | 32 | #include <linux/io.h> |
33 | #include <linux/gpio.h> | 33 | #include <linux/gpio.h> |
34 | #include <linux/regulator/consumer.h> | 34 | #include <linux/regulator/consumer.h> |
35 | #include <linux/dmaengine.h> | ||
35 | 36 | ||
36 | #include <asm/dma.h> | 37 | #include <asm/dma.h> |
37 | #include <asm/irq.h> | 38 | #include <asm/irq.h> |
38 | #include <asm/sizes.h> | 39 | #include <asm/sizes.h> |
39 | #include <mach/mmc.h> | 40 | #include <mach/mmc.h> |
40 | 41 | ||
41 | #ifdef CONFIG_ARCH_MX2 | 42 | #include <mach/dma.h> |
42 | #include <mach/dma-mx1-mx2.h> | ||
43 | #define HAS_DMA | ||
44 | #endif | ||
45 | 43 | ||
46 | #define DRIVER_NAME "mxc-mmc" | 44 | #define DRIVER_NAME "mxc-mmc" |
47 | 45 | ||
@@ -118,7 +116,8 @@ struct mxcmci_host { | |||
118 | void __iomem *base; | 116 | void __iomem *base; |
119 | int irq; | 117 | int irq; |
120 | int detect_irq; | 118 | int detect_irq; |
121 | int dma; | 119 | struct dma_chan *dma; |
120 | struct dma_async_tx_descriptor *desc; | ||
122 | int do_dma; | 121 | int do_dma; |
123 | int default_irq_mask; | 122 | int default_irq_mask; |
124 | int use_sdio; | 123 | int use_sdio; |
@@ -129,7 +128,6 @@ struct mxcmci_host { | |||
129 | struct mmc_command *cmd; | 128 | struct mmc_command *cmd; |
130 | struct mmc_data *data; | 129 | struct mmc_data *data; |
131 | 130 | ||
132 | unsigned int dma_nents; | ||
133 | unsigned int datasize; | 131 | unsigned int datasize; |
134 | unsigned int dma_dir; | 132 | unsigned int dma_dir; |
135 | 133 | ||
@@ -144,6 +142,11 @@ struct mxcmci_host { | |||
144 | spinlock_t lock; | 142 | spinlock_t lock; |
145 | 143 | ||
146 | struct regulator *vcc; | 144 | struct regulator *vcc; |
145 | |||
146 | int burstlen; | ||
147 | int dmareq; | ||
148 | struct dma_slave_config dma_slave_config; | ||
149 | struct imx_dma_data dma_data; | ||
147 | }; | 150 | }; |
148 | 151 | ||
149 | static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); | 152 | static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); |
@@ -206,17 +209,16 @@ static void mxcmci_softreset(struct mxcmci_host *host) | |||
206 | 209 | ||
207 | writew(0xff, host->base + MMC_REG_RES_TO); | 210 | writew(0xff, host->base + MMC_REG_RES_TO); |
208 | } | 211 | } |
212 | static int mxcmci_setup_dma(struct mmc_host *mmc); | ||
209 | 213 | ||
210 | static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) | 214 | static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) |
211 | { | 215 | { |
212 | unsigned int nob = data->blocks; | 216 | unsigned int nob = data->blocks; |
213 | unsigned int blksz = data->blksz; | 217 | unsigned int blksz = data->blksz; |
214 | unsigned int datasize = nob * blksz; | 218 | unsigned int datasize = nob * blksz; |
215 | #ifdef HAS_DMA | ||
216 | struct scatterlist *sg; | 219 | struct scatterlist *sg; |
217 | int i; | 220 | int i, nents; |
218 | int ret; | 221 | |
219 | #endif | ||
220 | if (data->flags & MMC_DATA_STREAM) | 222 | if (data->flags & MMC_DATA_STREAM) |
221 | nob = 0xffff; | 223 | nob = 0xffff; |
222 | 224 | ||
@@ -227,7 +229,9 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) | |||
227 | writew(blksz, host->base + MMC_REG_BLK_LEN); | 229 | writew(blksz, host->base + MMC_REG_BLK_LEN); |
228 | host->datasize = datasize; | 230 | host->datasize = datasize; |
229 | 231 | ||
230 | #ifdef HAS_DMA | 232 | if (!mxcmci_use_dma(host)) |
233 | return 0; | ||
234 | |||
231 | for_each_sg(data->sg, sg, data->sg_len, i) { | 235 | for_each_sg(data->sg, sg, data->sg_len, i) { |
232 | if (sg->offset & 3 || sg->length & 3) { | 236 | if (sg->offset & 3 || sg->length & 3) { |
233 | host->do_dma = 0; | 237 | host->do_dma = 0; |
@@ -235,34 +239,30 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) | |||
235 | } | 239 | } |
236 | } | 240 | } |
237 | 241 | ||
238 | if (data->flags & MMC_DATA_READ) { | 242 | if (data->flags & MMC_DATA_READ) |
239 | host->dma_dir = DMA_FROM_DEVICE; | 243 | host->dma_dir = DMA_FROM_DEVICE; |
240 | host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, | 244 | else |
241 | data->sg_len, host->dma_dir); | ||
242 | |||
243 | ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, | ||
244 | datasize, | ||
245 | host->res->start + MMC_REG_BUFFER_ACCESS, | ||
246 | DMA_MODE_READ); | ||
247 | } else { | ||
248 | host->dma_dir = DMA_TO_DEVICE; | 245 | host->dma_dir = DMA_TO_DEVICE; |
249 | host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, | ||
250 | data->sg_len, host->dma_dir); | ||
251 | 246 | ||
252 | ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, | 247 | nents = dma_map_sg(host->dma->device->dev, data->sg, |
253 | datasize, | 248 | data->sg_len, host->dma_dir); |
254 | host->res->start + MMC_REG_BUFFER_ACCESS, | 249 | if (nents != data->sg_len) |
255 | DMA_MODE_WRITE); | 250 | return -EINVAL; |
256 | } | 251 | |
252 | host->desc = host->dma->device->device_prep_slave_sg(host->dma, | ||
253 | data->sg, data->sg_len, host->dma_dir, | ||
254 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
257 | 255 | ||
258 | if (ret) { | 256 | if (!host->desc) { |
259 | dev_err(mmc_dev(host->mmc), "failed to setup DMA : %d\n", ret); | 257 | dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len, |
260 | return ret; | 258 | host->dma_dir); |
259 | host->do_dma = 0; | ||
260 | return 0; /* Fall back to PIO */ | ||
261 | } | 261 | } |
262 | wmb(); | 262 | wmb(); |
263 | 263 | ||
264 | imx_dma_enable(host->dma); | 264 | dmaengine_submit(host->desc); |
265 | #endif /* HAS_DMA */ | 265 | |
266 | return 0; | 266 | return 0; |
267 | } | 267 | } |
268 | 268 | ||
@@ -337,13 +337,11 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat) | |||
337 | struct mmc_data *data = host->data; | 337 | struct mmc_data *data = host->data; |
338 | int data_error; | 338 | int data_error; |
339 | 339 | ||
340 | #ifdef HAS_DMA | ||
341 | if (mxcmci_use_dma(host)) { | 340 | if (mxcmci_use_dma(host)) { |
342 | imx_dma_disable(host->dma); | 341 | dmaengine_terminate_all(host->dma); |
343 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents, | 342 | dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len, |
344 | host->dma_dir); | 343 | host->dma_dir); |
345 | } | 344 | } |
346 | #endif | ||
347 | 345 | ||
348 | if (stat & STATUS_ERR_MASK) { | 346 | if (stat & STATUS_ERR_MASK) { |
349 | dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", | 347 | dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", |
@@ -545,7 +543,6 @@ static void mxcmci_datawork(struct work_struct *work) | |||
545 | } | 543 | } |
546 | } | 544 | } |
547 | 545 | ||
548 | #ifdef HAS_DMA | ||
549 | static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) | 546 | static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) |
550 | { | 547 | { |
551 | struct mmc_data *data = host->data; | 548 | struct mmc_data *data = host->data; |
@@ -568,7 +565,6 @@ static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) | |||
568 | mxcmci_finish_request(host, host->req); | 565 | mxcmci_finish_request(host, host->req); |
569 | } | 566 | } |
570 | } | 567 | } |
571 | #endif /* HAS_DMA */ | ||
572 | 568 | ||
573 | static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) | 569 | static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) |
574 | { | 570 | { |
@@ -606,12 +602,10 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) | |||
606 | sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio; | 602 | sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio; |
607 | spin_unlock_irqrestore(&host->lock, flags); | 603 | spin_unlock_irqrestore(&host->lock, flags); |
608 | 604 | ||
609 | #ifdef HAS_DMA | ||
610 | if (mxcmci_use_dma(host) && | 605 | if (mxcmci_use_dma(host) && |
611 | (stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE))) | 606 | (stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE))) |
612 | writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE, | 607 | writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE, |
613 | host->base + MMC_REG_STATUS); | 608 | host->base + MMC_REG_STATUS); |
614 | #endif | ||
615 | 609 | ||
616 | if (sdio_irq) { | 610 | if (sdio_irq) { |
617 | writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_REG_STATUS); | 611 | writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_REG_STATUS); |
@@ -621,14 +615,14 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) | |||
621 | if (stat & STATUS_END_CMD_RESP) | 615 | if (stat & STATUS_END_CMD_RESP) |
622 | mxcmci_cmd_done(host, stat); | 616 | mxcmci_cmd_done(host, stat); |
623 | 617 | ||
624 | #ifdef HAS_DMA | ||
625 | if (mxcmci_use_dma(host) && | 618 | if (mxcmci_use_dma(host) && |
626 | (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) | 619 | (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) |
627 | mxcmci_data_done(host, stat); | 620 | mxcmci_data_done(host, stat); |
628 | #endif | 621 | |
629 | if (host->default_irq_mask && | 622 | if (host->default_irq_mask && |
630 | (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL))) | 623 | (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL))) |
631 | mmc_detect_change(host->mmc, msecs_to_jiffies(200)); | 624 | mmc_detect_change(host->mmc, msecs_to_jiffies(200)); |
625 | |||
632 | return IRQ_HANDLED; | 626 | return IRQ_HANDLED; |
633 | } | 627 | } |
634 | 628 | ||
@@ -642,9 +636,10 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req) | |||
642 | 636 | ||
643 | host->req = req; | 637 | host->req = req; |
644 | host->cmdat &= ~CMD_DAT_CONT_INIT; | 638 | host->cmdat &= ~CMD_DAT_CONT_INIT; |
645 | #ifdef HAS_DMA | 639 | |
646 | host->do_dma = 1; | 640 | if (host->dma) |
647 | #endif | 641 | host->do_dma = 1; |
642 | |||
648 | if (req->data) { | 643 | if (req->data) { |
649 | error = mxcmci_setup_data(host, req->data); | 644 | error = mxcmci_setup_data(host, req->data); |
650 | if (error) { | 645 | if (error) { |
@@ -660,6 +655,7 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req) | |||
660 | } | 655 | } |
661 | 656 | ||
662 | error = mxcmci_start_cmd(host, req->cmd, cmdat); | 657 | error = mxcmci_start_cmd(host, req->cmd, cmdat); |
658 | |||
663 | out: | 659 | out: |
664 | if (error) | 660 | if (error) |
665 | mxcmci_finish_request(host, req); | 661 | mxcmci_finish_request(host, req); |
@@ -698,22 +694,46 @@ static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios) | |||
698 | prescaler, divider, clk_in, clk_ios); | 694 | prescaler, divider, clk_in, clk_ios); |
699 | } | 695 | } |
700 | 696 | ||
697 | static int mxcmci_setup_dma(struct mmc_host *mmc) | ||
698 | { | ||
699 | struct mxcmci_host *host = mmc_priv(mmc); | ||
700 | struct dma_slave_config *config = &host->dma_slave_config; | ||
701 | |||
702 | config->dst_addr = host->res->start + MMC_REG_BUFFER_ACCESS; | ||
703 | config->src_addr = host->res->start + MMC_REG_BUFFER_ACCESS; | ||
704 | config->dst_addr_width = 4; | ||
705 | config->src_addr_width = 4; | ||
706 | config->dst_maxburst = host->burstlen; | ||
707 | config->src_maxburst = host->burstlen; | ||
708 | |||
709 | return dmaengine_slave_config(host->dma, config); | ||
710 | } | ||
711 | |||
701 | static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 712 | static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
702 | { | 713 | { |
703 | struct mxcmci_host *host = mmc_priv(mmc); | 714 | struct mxcmci_host *host = mmc_priv(mmc); |
704 | #ifdef HAS_DMA | 715 | int burstlen, ret; |
705 | unsigned int blen; | 716 | |
706 | /* | 717 | /* |
707 | * use burstlen of 64 in 4 bit mode (--> reg value 0) | 718 | * use burstlen of 64 in 4 bit mode (--> reg value 0) |
708 | * use burstlen of 16 in 1 bit mode (--> reg value 16) | 719 | * use burstlen of 16 in 1 bit mode (--> reg value 16) |
709 | */ | 720 | */ |
710 | if (ios->bus_width == MMC_BUS_WIDTH_4) | 721 | if (ios->bus_width == MMC_BUS_WIDTH_4) |
711 | blen = 0; | 722 | burstlen = 64; |
712 | else | 723 | else |
713 | blen = 16; | 724 | burstlen = 16; |
725 | |||
726 | if (mxcmci_use_dma(host) && burstlen != host->burstlen) { | ||
727 | host->burstlen = burstlen; | ||
728 | ret = mxcmci_setup_dma(mmc); | ||
729 | if (ret) { | ||
730 | dev_err(mmc_dev(host->mmc), | ||
731 | "failed to config DMA channel. Falling back to PIO\n"); | ||
732 | dma_release_channel(host->dma); | ||
733 | host->do_dma = 0; | ||
734 | } | ||
735 | } | ||
714 | 736 | ||
715 | imx_dma_config_burstlen(host->dma, blen); | ||
716 | #endif | ||
717 | if (ios->bus_width == MMC_BUS_WIDTH_4) | 737 | if (ios->bus_width == MMC_BUS_WIDTH_4) |
718 | host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; | 738 | host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; |
719 | else | 739 | else |
@@ -794,6 +814,18 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card) | |||
794 | host->caps |= MMC_CAP_4_BIT_DATA; | 814 | host->caps |= MMC_CAP_4_BIT_DATA; |
795 | } | 815 | } |
796 | 816 | ||
817 | static bool filter(struct dma_chan *chan, void *param) | ||
818 | { | ||
819 | struct mxcmci_host *host = param; | ||
820 | |||
821 | if (!imx_dma_is_general_purpose(chan)) | ||
822 | return false; | ||
823 | |||
824 | chan->private = &host->dma_data; | ||
825 | |||
826 | return true; | ||
827 | } | ||
828 | |||
797 | static const struct mmc_host_ops mxcmci_ops = { | 829 | static const struct mmc_host_ops mxcmci_ops = { |
798 | .request = mxcmci_request, | 830 | .request = mxcmci_request, |
799 | .set_ios = mxcmci_set_ios, | 831 | .set_ios = mxcmci_set_ios, |
@@ -808,6 +840,7 @@ static int mxcmci_probe(struct platform_device *pdev) | |||
808 | struct mxcmci_host *host = NULL; | 840 | struct mxcmci_host *host = NULL; |
809 | struct resource *iores, *r; | 841 | struct resource *iores, *r; |
810 | int ret = 0, irq; | 842 | int ret = 0, irq; |
843 | dma_cap_mask_t mask; | ||
811 | 844 | ||
812 | printk(KERN_INFO "i.MX SDHC driver\n"); | 845 | printk(KERN_INFO "i.MX SDHC driver\n"); |
813 | 846 | ||
@@ -883,29 +916,23 @@ static int mxcmci_probe(struct platform_device *pdev) | |||
883 | 916 | ||
884 | writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR); | 917 | writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR); |
885 | 918 | ||
886 | #ifdef HAS_DMA | ||
887 | host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW); | ||
888 | if (host->dma < 0) { | ||
889 | dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n"); | ||
890 | ret = -EBUSY; | ||
891 | goto out_clk_put; | ||
892 | } | ||
893 | |||
894 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); | 919 | r = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
895 | if (!r) { | 920 | if (r) { |
896 | ret = -EINVAL; | 921 | host->dmareq = r->start; |
897 | goto out_free_dma; | 922 | host->dma_data.peripheral_type = IMX_DMATYPE_SDHC; |
923 | host->dma_data.priority = DMA_PRIO_LOW; | ||
924 | host->dma_data.dma_request = host->dmareq; | ||
925 | dma_cap_zero(mask); | ||
926 | dma_cap_set(DMA_SLAVE, mask); | ||
927 | host->dma = dma_request_channel(mask, filter, host); | ||
928 | if (host->dma) | ||
929 | mmc->max_seg_size = dma_get_max_seg_size( | ||
930 | host->dma->device->dev); | ||
898 | } | 931 | } |
899 | 932 | ||
900 | ret = imx_dma_config_channel(host->dma, | 933 | if (!host->dma) |
901 | IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO, | 934 | dev_info(mmc_dev(host->mmc), "dma not available. Using PIO\n"); |
902 | IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | 935 | |
903 | r->start, 0); | ||
904 | if (ret) { | ||
905 | dev_err(mmc_dev(host->mmc), "failed to config DMA channel\n"); | ||
906 | goto out_free_dma; | ||
907 | } | ||
908 | #endif | ||
909 | INIT_WORK(&host->datawork, mxcmci_datawork); | 936 | INIT_WORK(&host->datawork, mxcmci_datawork); |
910 | 937 | ||
911 | ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host); | 938 | ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host); |
@@ -928,9 +955,8 @@ static int mxcmci_probe(struct platform_device *pdev) | |||
928 | out_free_irq: | 955 | out_free_irq: |
929 | free_irq(host->irq, host); | 956 | free_irq(host->irq, host); |
930 | out_free_dma: | 957 | out_free_dma: |
931 | #ifdef HAS_DMA | 958 | if (host->dma) |
932 | imx_dma_free(host->dma); | 959 | dma_release_channel(host->dma); |
933 | #endif | ||
934 | out_clk_put: | 960 | out_clk_put: |
935 | clk_disable(host->clk); | 961 | clk_disable(host->clk); |
936 | clk_put(host->clk); | 962 | clk_put(host->clk); |
@@ -960,9 +986,10 @@ static int mxcmci_remove(struct platform_device *pdev) | |||
960 | 986 | ||
961 | free_irq(host->irq, host); | 987 | free_irq(host->irq, host); |
962 | iounmap(host->base); | 988 | iounmap(host->base); |
963 | #ifdef HAS_DMA | 989 | |
964 | imx_dma_free(host->dma); | 990 | if (host->dma) |
965 | #endif | 991 | dma_release_channel(host->dma); |
992 | |||
966 | clk_disable(host->clk); | 993 | clk_disable(host->clk); |
967 | clk_put(host->clk); | 994 | clk_put(host->clk); |
968 | 995 | ||