diff options
author | Vasily Khoruzhick <anarsoul@gmail.com> | 2014-06-01 13:22:09 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2014-07-09 05:26:13 -0400 |
commit | b45e4b5093298e5d42f4a80f95f08e511dc06767 (patch) | |
tree | ec73d222490a07b750f1b84a2e92369de6183b21 /drivers/mmc/host/s3cmci.c | |
parent | 1b3f626e64b50e4ac3bca046c71cfbaec815670d (diff) |
mmc: s3cmci: port DMA code to dmaengine API
Utilise new s3c24xx-dma dmaengine driver for DMA ops.
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc/host/s3cmci.c')
-rw-r--r-- | drivers/mmc/host/s3cmci.c | 180 |
1 files changed, 50 insertions, 130 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 501ec210780c..e5516a226362 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c | |||
@@ -12,6 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/dmaengine.h> | ||
15 | #include <linux/dma-mapping.h> | 16 | #include <linux/dma-mapping.h> |
16 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
17 | #include <linux/mmc/host.h> | 18 | #include <linux/mmc/host.h> |
@@ -27,6 +28,7 @@ | |||
27 | #include <mach/dma.h> | 28 | #include <mach/dma.h> |
28 | #include <mach/gpio-samsung.h> | 29 | #include <mach/gpio-samsung.h> |
29 | 30 | ||
31 | #include <linux/platform_data/dma-s3c24xx.h> | ||
30 | #include <linux/platform_data/mmc-s3cmci.h> | 32 | #include <linux/platform_data/mmc-s3cmci.h> |
31 | 33 | ||
32 | #include "s3cmci.h" | 34 | #include "s3cmci.h" |
@@ -140,10 +142,6 @@ static const int dbgmap_debug = dbg_err | dbg_debug; | |||
140 | dev_dbg(&host->pdev->dev, args); \ | 142 | dev_dbg(&host->pdev->dev, args); \ |
141 | } while (0) | 143 | } while (0) |
142 | 144 | ||
143 | static struct s3c2410_dma_client s3cmci_dma_client = { | ||
144 | .name = "s3c-mci", | ||
145 | }; | ||
146 | |||
147 | static void finalize_request(struct s3cmci_host *host); | 145 | static void finalize_request(struct s3cmci_host *host); |
148 | static void s3cmci_send_request(struct mmc_host *mmc); | 146 | static void s3cmci_send_request(struct mmc_host *mmc); |
149 | static void s3cmci_reset(struct s3cmci_host *host); | 147 | static void s3cmci_reset(struct s3cmci_host *host); |
@@ -256,25 +254,8 @@ static inline bool s3cmci_host_usedma(struct s3cmci_host *host) | |||
256 | { | 254 | { |
257 | #ifdef CONFIG_MMC_S3C_PIO | 255 | #ifdef CONFIG_MMC_S3C_PIO |
258 | return false; | 256 | return false; |
259 | #elif defined(CONFIG_MMC_S3C_DMA) | 257 | #else /* CONFIG_MMC_S3C_DMA */ |
260 | return true; | 258 | return true; |
261 | #else | ||
262 | return host->dodma; | ||
263 | #endif | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * s3cmci_host_canpio - return true if host has pio code available | ||
268 | * | ||
269 | * Return true if the driver has been compiled with the PIO support code | ||
270 | * available. | ||
271 | */ | ||
272 | static inline bool s3cmci_host_canpio(void) | ||
273 | { | ||
274 | #ifdef CONFIG_MMC_S3C_PIO | ||
275 | return true; | ||
276 | #else | ||
277 | return false; | ||
278 | #endif | 259 | #endif |
279 | } | 260 | } |
280 | 261 | ||
@@ -841,60 +822,24 @@ static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) | |||
841 | return IRQ_HANDLED; | 822 | return IRQ_HANDLED; |
842 | } | 823 | } |
843 | 824 | ||
844 | static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, | 825 | static void s3cmci_dma_done_callback(void *arg) |
845 | void *buf_id, int size, | ||
846 | enum s3c2410_dma_buffresult result) | ||
847 | { | 826 | { |
848 | struct s3cmci_host *host = buf_id; | 827 | struct s3cmci_host *host = arg; |
849 | unsigned long iflags; | 828 | unsigned long iflags; |
850 | u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt; | ||
851 | |||
852 | mci_csta = readl(host->base + S3C2410_SDICMDSTAT); | ||
853 | mci_dsta = readl(host->base + S3C2410_SDIDSTA); | ||
854 | mci_fsta = readl(host->base + S3C2410_SDIFSTA); | ||
855 | mci_dcnt = readl(host->base + S3C2410_SDIDCNT); | ||
856 | 829 | ||
857 | BUG_ON(!host->mrq); | 830 | BUG_ON(!host->mrq); |
858 | BUG_ON(!host->mrq->data); | 831 | BUG_ON(!host->mrq->data); |
859 | BUG_ON(!host->dmatogo); | ||
860 | 832 | ||
861 | spin_lock_irqsave(&host->complete_lock, iflags); | 833 | spin_lock_irqsave(&host->complete_lock, iflags); |
862 | 834 | ||
863 | if (result != S3C2410_RES_OK) { | 835 | dbg(host, dbg_dma, "DMA FINISHED\n"); |
864 | dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x " | ||
865 | "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n", | ||
866 | mci_csta, mci_dsta, mci_fsta, | ||
867 | mci_dcnt, result, host->dmatogo); | ||
868 | |||
869 | goto fail_request; | ||
870 | } | ||
871 | |||
872 | host->dmatogo--; | ||
873 | if (host->dmatogo) { | ||
874 | dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] " | ||
875 | "DCNT:[%08x] toGo:%u\n", | ||
876 | size, mci_dsta, mci_dcnt, host->dmatogo); | ||
877 | |||
878 | goto out; | ||
879 | } | ||
880 | |||
881 | dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n", | ||
882 | size, mci_dsta, mci_dcnt); | ||
883 | 836 | ||
884 | host->dma_complete = 1; | 837 | host->dma_complete = 1; |
885 | host->complete_what = COMPLETION_FINALIZE; | 838 | host->complete_what = COMPLETION_FINALIZE; |
886 | 839 | ||
887 | out: | ||
888 | tasklet_schedule(&host->pio_tasklet); | 840 | tasklet_schedule(&host->pio_tasklet); |
889 | spin_unlock_irqrestore(&host->complete_lock, iflags); | 841 | spin_unlock_irqrestore(&host->complete_lock, iflags); |
890 | return; | ||
891 | 842 | ||
892 | fail_request: | ||
893 | host->mrq->data->error = -EINVAL; | ||
894 | host->complete_what = COMPLETION_FINALIZE; | ||
895 | clear_imask(host); | ||
896 | |||
897 | goto out; | ||
898 | } | 843 | } |
899 | 844 | ||
900 | static void finalize_request(struct s3cmci_host *host) | 845 | static void finalize_request(struct s3cmci_host *host) |
@@ -966,7 +911,7 @@ static void finalize_request(struct s3cmci_host *host) | |||
966 | * DMA channel and the fifo to clear out any garbage. */ | 911 | * DMA channel and the fifo to clear out any garbage. */ |
967 | if (mrq->data->error != 0) { | 912 | if (mrq->data->error != 0) { |
968 | if (s3cmci_host_usedma(host)) | 913 | if (s3cmci_host_usedma(host)) |
969 | s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); | 914 | dmaengine_terminate_all(host->dma); |
970 | 915 | ||
971 | if (host->is2440) { | 916 | if (host->is2440) { |
972 | /* Clear failure register and reset fifo. */ | 917 | /* Clear failure register and reset fifo. */ |
@@ -992,29 +937,6 @@ request_done: | |||
992 | mmc_request_done(host->mmc, mrq); | 937 | mmc_request_done(host->mmc, mrq); |
993 | } | 938 | } |
994 | 939 | ||
995 | static void s3cmci_dma_setup(struct s3cmci_host *host, | ||
996 | enum dma_data_direction source) | ||
997 | { | ||
998 | static enum dma_data_direction last_source = -1; | ||
999 | static int setup_ok; | ||
1000 | |||
1001 | if (last_source == source) | ||
1002 | return; | ||
1003 | |||
1004 | last_source = source; | ||
1005 | |||
1006 | s3c2410_dma_devconfig(host->dma, source, | ||
1007 | host->mem->start + host->sdidata); | ||
1008 | |||
1009 | if (!setup_ok) { | ||
1010 | s3c2410_dma_config(host->dma, 4); | ||
1011 | s3c2410_dma_set_buffdone_fn(host->dma, | ||
1012 | s3cmci_dma_done_callback); | ||
1013 | s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART); | ||
1014 | setup_ok = 1; | ||
1015 | } | ||
1016 | } | ||
1017 | |||
1018 | static void s3cmci_send_command(struct s3cmci_host *host, | 940 | static void s3cmci_send_command(struct s3cmci_host *host, |
1019 | struct mmc_command *cmd) | 941 | struct mmc_command *cmd) |
1020 | { | 942 | { |
@@ -1162,43 +1084,45 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) | |||
1162 | 1084 | ||
1163 | static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) | 1085 | static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) |
1164 | { | 1086 | { |
1165 | int dma_len, i; | ||
1166 | int rw = data->flags & MMC_DATA_WRITE; | 1087 | int rw = data->flags & MMC_DATA_WRITE; |
1088 | struct dma_async_tx_descriptor *desc; | ||
1089 | struct dma_slave_config conf = { | ||
1090 | .src_addr = host->mem->start + host->sdidata, | ||
1091 | .dst_addr = host->mem->start + host->sdidata, | ||
1092 | .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, | ||
1093 | .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, | ||
1094 | }; | ||
1167 | 1095 | ||
1168 | BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); | 1096 | BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); |
1169 | 1097 | ||
1170 | s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); | 1098 | /* Restore prescaler value */ |
1171 | s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); | 1099 | writel(host->prescaler, host->base + S3C2410_SDIPRE); |
1172 | |||
1173 | dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, | ||
1174 | rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); | ||
1175 | |||
1176 | if (dma_len == 0) | ||
1177 | return -ENOMEM; | ||
1178 | |||
1179 | host->dma_complete = 0; | ||
1180 | host->dmatogo = dma_len; | ||
1181 | |||
1182 | for (i = 0; i < dma_len; i++) { | ||
1183 | int res; | ||
1184 | |||
1185 | dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i, | ||
1186 | sg_dma_address(&data->sg[i]), | ||
1187 | sg_dma_len(&data->sg[i])); | ||
1188 | 1100 | ||
1189 | res = s3c2410_dma_enqueue(host->dma, host, | 1101 | if (!rw) |
1190 | sg_dma_address(&data->sg[i]), | 1102 | conf.direction = DMA_DEV_TO_MEM; |
1191 | sg_dma_len(&data->sg[i])); | 1103 | else |
1104 | conf.direction = DMA_MEM_TO_DEV; | ||
1192 | 1105 | ||
1193 | if (res) { | 1106 | dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, |
1194 | s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); | 1107 | rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); |
1195 | return -EBUSY; | ||
1196 | } | ||
1197 | } | ||
1198 | 1108 | ||
1199 | s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START); | 1109 | dmaengine_slave_config(host->dma, &conf); |
1110 | desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len, | ||
1111 | conf.direction, | ||
1112 | DMA_CTRL_ACK | DMA_PREP_INTERRUPT); | ||
1113 | if (!desc) | ||
1114 | goto unmap_exit; | ||
1115 | desc->callback = s3cmci_dma_done_callback; | ||
1116 | desc->callback_param = host; | ||
1117 | dmaengine_submit(desc); | ||
1118 | dma_async_issue_pending(host->dma); | ||
1200 | 1119 | ||
1201 | return 0; | 1120 | return 0; |
1121 | |||
1122 | unmap_exit: | ||
1123 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, | ||
1124 | rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); | ||
1125 | return -ENOMEM; | ||
1202 | } | 1126 | } |
1203 | 1127 | ||
1204 | static void s3cmci_send_request(struct mmc_host *mmc) | 1128 | static void s3cmci_send_request(struct mmc_host *mmc) |
@@ -1676,10 +1600,6 @@ static int s3cmci_probe(struct platform_device *pdev) | |||
1676 | host->complete_what = COMPLETION_NONE; | 1600 | host->complete_what = COMPLETION_NONE; |
1677 | host->pio_active = XFER_NONE; | 1601 | host->pio_active = XFER_NONE; |
1678 | 1602 | ||
1679 | #ifdef CONFIG_MMC_S3C_PIODMA | ||
1680 | host->dodma = host->pdata->use_dma; | ||
1681 | #endif | ||
1682 | |||
1683 | host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1603 | host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1684 | if (!host->mem) { | 1604 | if (!host->mem) { |
1685 | dev_err(&pdev->dev, | 1605 | dev_err(&pdev->dev, |
@@ -1765,17 +1685,17 @@ static int s3cmci_probe(struct platform_device *pdev) | |||
1765 | /* depending on the dma state, get a dma channel to use. */ | 1685 | /* depending on the dma state, get a dma channel to use. */ |
1766 | 1686 | ||
1767 | if (s3cmci_host_usedma(host)) { | 1687 | if (s3cmci_host_usedma(host)) { |
1768 | host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client, | 1688 | dma_cap_mask_t mask; |
1769 | host); | 1689 | |
1770 | if (host->dma < 0) { | 1690 | dma_cap_zero(mask); |
1691 | dma_cap_set(DMA_SLAVE, mask); | ||
1692 | |||
1693 | host->dma = dma_request_slave_channel_compat(mask, | ||
1694 | s3c24xx_dma_filter, (void *)DMACH_SDI, &pdev->dev, "rx-tx"); | ||
1695 | if (!host->dma) { | ||
1771 | dev_err(&pdev->dev, "cannot get DMA channel.\n"); | 1696 | dev_err(&pdev->dev, "cannot get DMA channel.\n"); |
1772 | if (!s3cmci_host_canpio()) { | 1697 | ret = -EBUSY; |
1773 | ret = -EBUSY; | 1698 | goto probe_free_gpio_wp; |
1774 | goto probe_free_gpio_wp; | ||
1775 | } else { | ||
1776 | dev_warn(&pdev->dev, "falling back to PIO.\n"); | ||
1777 | host->dodma = 0; | ||
1778 | } | ||
1779 | } | 1699 | } |
1780 | } | 1700 | } |
1781 | 1701 | ||
@@ -1816,7 +1736,7 @@ static int s3cmci_probe(struct platform_device *pdev) | |||
1816 | mmc->max_segs = 128; | 1736 | mmc->max_segs = 128; |
1817 | 1737 | ||
1818 | dbg(host, dbg_debug, | 1738 | dbg(host, dbg_debug, |
1819 | "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n", | 1739 | "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%p.\n", |
1820 | (host->is2440?"2440":""), | 1740 | (host->is2440?"2440":""), |
1821 | host->base, host->irq, host->irq_cd, host->dma); | 1741 | host->base, host->irq, host->irq_cd, host->dma); |
1822 | 1742 | ||
@@ -1852,7 +1772,7 @@ static int s3cmci_probe(struct platform_device *pdev) | |||
1852 | 1772 | ||
1853 | probe_free_dma: | 1773 | probe_free_dma: |
1854 | if (s3cmci_host_usedma(host)) | 1774 | if (s3cmci_host_usedma(host)) |
1855 | s3c2410_dma_free(host->dma, &s3cmci_dma_client); | 1775 | dma_release_channel(host->dma); |
1856 | 1776 | ||
1857 | probe_free_gpio_wp: | 1777 | probe_free_gpio_wp: |
1858 | if (!host->pdata->no_wprotect) | 1778 | if (!host->pdata->no_wprotect) |
@@ -1914,7 +1834,7 @@ static int s3cmci_remove(struct platform_device *pdev) | |||
1914 | tasklet_disable(&host->pio_tasklet); | 1834 | tasklet_disable(&host->pio_tasklet); |
1915 | 1835 | ||
1916 | if (s3cmci_host_usedma(host)) | 1836 | if (s3cmci_host_usedma(host)) |
1917 | s3c2410_dma_free(host->dma, &s3cmci_dma_client); | 1837 | dma_release_channel(host->dma); |
1918 | 1838 | ||
1919 | free_irq(host->irq, host); | 1839 | free_irq(host->irq, host); |
1920 | 1840 | ||