diff options
author | Haibo Chen <haibo.chen@freescale.com> | 2014-12-09 04:04:05 -0500 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2015-01-19 03:56:25 -0500 |
commit | 348487cb28e66b032bae1b38424d81bf5b444408 (patch) | |
tree | b484e039977c15b9894ead7500346cc1bb1e9392 | |
parent | 2070d805bc8cf14fcc9447273928af7e531697fe (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.c | 99 | ||||
-rw-r--r-- | include/linux/mmc/sdhci.h | 6 |
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 *); | |||
53 | static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); | 53 | static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); |
54 | static void sdhci_tuning_timer(unsigned long data); | 54 | static void sdhci_tuning_timer(unsigned long data); |
55 | static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); | 55 | static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); |
56 | static 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 |
58 | static int sdhci_runtime_pm_get(struct sdhci_host *host); | 61 | static 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 | ||
652 | static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) | 655 | static 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 | ||
2129 | static 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 | |||
2144 | static 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 | |||
2183 | static 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 | |||
2128 | static void sdhci_card_event(struct mmc_host *mmc) | 2200 | static 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 | ||
2159 | static const struct mmc_host_ops sdhci_ops = { | 2231 | static 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 | ||
20 | struct sdhci_host_next { | ||
21 | unsigned int sg_count; | ||
22 | s32 cookie; | ||
23 | }; | ||
24 | |||
20 | struct sdhci_host { | 25 | struct 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 */ |