aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaibo Chen <haibo.chen@freescale.com>2014-12-09 04:04:05 -0500
committerUlf Hansson <ulf.hansson@linaro.org>2015-01-19 03:56:25 -0500
commit348487cb28e66b032bae1b38424d81bf5b444408 (patch)
treeb484e039977c15b9894ead7500346cc1bb1e9392
parent2070d805bc8cf14fcc9447273928af7e531697fe (diff)
mmc: sdhci: use pipeline mmc requests to improve performance
This patch is based on the patches by Per Forlin, Tony Lin and Ryan QIAN. This patch complete the API 'post_req' and 'pre_req' in sdhci host side, Test Env: 1. i.MX6Q-SABREAUTO board, CPU @ 996MHz, use ADMA in uSDHC controller. 2. Test command: $ echo 1 > /proc/sys/vm/drop_caches write to sd card: $ dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=2000 conv=fsync read the sd card: $ dd if=/dev/mmcblk0 of=/dev/null bs=1M count=2000 3. TOSHIBA 16GB SD3.0 card, running at 4 bit, SDR104 @ 198MHZ Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~76.7 MB/s | ~23.3 MB/s | |------------------------------------------------ |without this patch | ~60.5 MB/s | ~22.5 MB/s | ------------------------------------------------- 4. SanDisk 8GB SD3.0 card, running at 4 bit, DDR50 @ 50MHZ Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~40.5 MB/s | ~15.6 MB/s | |------------------------------------------------ |without this patch | ~36.1 MB/s | ~14.1 MB/s | ------------------------------------------------- 5. Kingston 8GB SD2.0 card, running at 4 bit, High-speed @ 50MHZ Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~22.7 MB/s | ~8.2 MB/s | |------------------------------------------------ |without this patch | ~21.3 MB/s | ~8.0 MB/s | ------------------------------------------------- 6. About eMMC, Sandisk 8GB eMMC on i.MX6DL-sabresd board, CPU @ 792MHZ, eMMC running at 8 bit, DDR52 @ 52MHZ. Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~37.3 MB/s | ~10.5 MB/s | |------------------------------------------------ |without this patch | ~33.4 MB/s | ~10.5 MB/s | ------------------------------------------------- Signed-off-by: Haibo Chen <haibo.chen@freescale.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/mmc/host/sdhci.c99
-rw-r--r--include/linux/mmc/sdhci.h6
2 files changed, 93 insertions, 12 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2558d705c92e..1aab8a120d02 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -53,6 +53,9 @@ static void sdhci_finish_command(struct sdhci_host *);
53static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); 53static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
54static void sdhci_tuning_timer(unsigned long data); 54static void sdhci_tuning_timer(unsigned long data);
55static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); 55static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
56static int sdhci_pre_dma_transfer(struct sdhci_host *host,
57 struct mmc_data *data,
58 struct sdhci_host_next *next);
56 59
57#ifdef CONFIG_PM 60#ifdef CONFIG_PM
58static int sdhci_runtime_pm_get(struct sdhci_host *host); 61static int sdhci_runtime_pm_get(struct sdhci_host *host);
@@ -505,9 +508,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
505 goto fail; 508 goto fail;
506 BUG_ON(host->align_addr & host->align_mask); 509 BUG_ON(host->align_addr & host->align_mask);
507 510
508 host->sg_count = dma_map_sg(mmc_dev(host->mmc), 511 host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
509 data->sg, data->sg_len, direction); 512 if (host->sg_count < 0)
510 if (host->sg_count == 0)
511 goto unmap_align; 513 goto unmap_align;
512 514
513 desc = host->adma_table; 515 desc = host->adma_table;
@@ -645,8 +647,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
645 } 647 }
646 } 648 }
647 649
648 dma_unmap_sg(mmc_dev(host->mmc), data->sg, 650 if (!data->host_cookie)
649 data->sg_len, direction); 651 dma_unmap_sg(mmc_dev(host->mmc), data->sg,
652 data->sg_len, direction);
650} 653}
651 654
652static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) 655static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
@@ -842,11 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
842 } else { 845 } else {
843 int sg_cnt; 846 int sg_cnt;
844 847
845 sg_cnt = dma_map_sg(mmc_dev(host->mmc), 848 sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
846 data->sg, data->sg_len,
847 (data->flags & MMC_DATA_READ) ?
848 DMA_FROM_DEVICE :
849 DMA_TO_DEVICE);
850 if (sg_cnt == 0) { 849 if (sg_cnt == 0) {
851 /* 850 /*
852 * This only happens when someone fed 851 * This only happens when someone fed
@@ -959,8 +958,10 @@ static void sdhci_finish_data(struct sdhci_host *host)
959 if (host->flags & SDHCI_USE_ADMA) 958 if (host->flags & SDHCI_USE_ADMA)
960 sdhci_adma_table_post(host, data); 959 sdhci_adma_table_post(host, data);
961 else { 960 else {
962 dma_unmap_sg(mmc_dev(host->mmc), data->sg, 961 if (!data->host_cookie)
963 data->sg_len, (data->flags & MMC_DATA_READ) ? 962 dma_unmap_sg(mmc_dev(host->mmc),
963 data->sg, data->sg_len,
964 (data->flags & MMC_DATA_READ) ?
964 DMA_FROM_DEVICE : DMA_TO_DEVICE); 965 DMA_FROM_DEVICE : DMA_TO_DEVICE);
965 } 966 }
966 } 967 }
@@ -2125,6 +2126,77 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
2125 } 2126 }
2126} 2127}
2127 2128
2129static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
2130 int err)
2131{
2132 struct sdhci_host *host = mmc_priv(mmc);
2133 struct mmc_data *data = mrq->data;
2134
2135 if (host->flags & SDHCI_REQ_USE_DMA) {
2136 if (data->host_cookie)
2137 dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
2138 data->flags & MMC_DATA_WRITE ?
2139 DMA_TO_DEVICE : DMA_FROM_DEVICE);
2140 mrq->data->host_cookie = 0;
2141 }
2142}
2143
2144static int sdhci_pre_dma_transfer(struct sdhci_host *host,
2145 struct mmc_data *data,
2146 struct sdhci_host_next *next)
2147{
2148 int sg_count;
2149
2150 if (!next && data->host_cookie &&
2151 data->host_cookie != host->next_data.cookie) {
2152 pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
2153 __func__, data->host_cookie, host->next_data.cookie);
2154 data->host_cookie = 0;
2155 }
2156
2157 /* Check if next job is already prepared */
2158 if (next ||
2159 (!next && data->host_cookie != host->next_data.cookie)) {
2160 sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
2161 data->sg_len,
2162 data->flags & MMC_DATA_WRITE ?
2163 DMA_TO_DEVICE : DMA_FROM_DEVICE);
2164
2165 } else {
2166 sg_count = host->next_data.sg_count;
2167 host->next_data.sg_count = 0;
2168 }
2169
2170
2171 if (sg_count == 0)
2172 return -EINVAL;
2173
2174 if (next) {
2175 next->sg_count = sg_count;
2176 data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
2177 } else
2178 host->sg_count = sg_count;
2179
2180 return sg_count;
2181}
2182
2183static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
2184 bool is_first_req)
2185{
2186 struct sdhci_host *host = mmc_priv(mmc);
2187
2188 if (mrq->data->host_cookie) {
2189 mrq->data->host_cookie = 0;
2190 return;
2191 }
2192
2193 if (host->flags & SDHCI_REQ_USE_DMA)
2194 if (sdhci_pre_dma_transfer(host,
2195 mrq->data,
2196 &host->next_data) < 0)
2197 mrq->data->host_cookie = 0;
2198}
2199
2128static void sdhci_card_event(struct mmc_host *mmc) 2200static void sdhci_card_event(struct mmc_host *mmc)
2129{ 2201{
2130 struct sdhci_host *host = mmc_priv(mmc); 2202 struct sdhci_host *host = mmc_priv(mmc);
@@ -2158,6 +2230,8 @@ static void sdhci_card_event(struct mmc_host *mmc)
2158 2230
2159static const struct mmc_host_ops sdhci_ops = { 2231static const struct mmc_host_ops sdhci_ops = {
2160 .request = sdhci_request, 2232 .request = sdhci_request,
2233 .post_req = sdhci_post_req,
2234 .pre_req = sdhci_pre_req,
2161 .set_ios = sdhci_set_ios, 2235 .set_ios = sdhci_set_ios,
2162 .get_cd = sdhci_get_cd, 2236 .get_cd = sdhci_get_cd,
2163 .get_ro = sdhci_get_ro, 2237 .get_ro = sdhci_get_ro,
@@ -3015,6 +3089,7 @@ int sdhci_add_host(struct sdhci_host *host)
3015 host->max_clk = host->ops->get_max_clock(host); 3089 host->max_clk = host->ops->get_max_clock(host);
3016 } 3090 }
3017 3091
3092 host->next_data.cookie = 1;
3018 /* 3093 /*
3019 * In case of Host Controller v3.00, find out whether clock 3094 * In case of Host Controller v3.00, find out whether clock
3020 * multiplier is supported. 3095 * multiplier is supported.
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index f767a0de611f..cb8b94ff6a26 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -17,6 +17,11 @@
17#include <linux/io.h> 17#include <linux/io.h>
18#include <linux/mmc/host.h> 18#include <linux/mmc/host.h>
19 19
20struct sdhci_host_next {
21 unsigned int sg_count;
22 s32 cookie;
23};
24
20struct sdhci_host { 25struct sdhci_host {
21 /* Data set by hardware interface driver */ 26 /* Data set by hardware interface driver */
22 const char *hw_name; /* Hardware bus name */ 27 const char *hw_name; /* Hardware bus name */
@@ -203,6 +208,7 @@ struct sdhci_host {
203#define SDHCI_TUNING_MODE_1 0 208#define SDHCI_TUNING_MODE_1 0
204 struct timer_list tuning_timer; /* Timer for tuning */ 209 struct timer_list tuning_timer; /* Timer for tuning */
205 210
211 struct sdhci_host_next next_data;
206 unsigned long private[0] ____cacheline_aligned; 212 unsigned long private[0] ____cacheline_aligned;
207}; 213};
208#endif /* LINUX_MMC_SDHCI_H */ 214#endif /* LINUX_MMC_SDHCI_H */