Skip to content

Commit 8454144

Browse files
committed
Merge tag 'mmc-v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC host fixes from Ulf Hansson: - renesas_sdhi: Fix build error in case NO_DMA=y - sdhci: Implement a bounce buffer to address throughput regressions * tag 'mmc-v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: mmc: MMC_SDHI_{SYS,INTERNAL}_DMAC should depend on HAS_DMA mmc: sdhci: Implement an SDHCI-specific bounce buffer
2 parents 9dd6a64 + c70fad5 commit 8454144

File tree

3 files changed

+161
-10
lines changed

3 files changed

+161
-10
lines changed

drivers/mmc/host/Kconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ config MMC_SDHI
605605

606606
config MMC_SDHI_SYS_DMAC
607607
tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC"
608-
depends on MMC_SDHI
608+
depends on MMC_SDHI && HAS_DMA
609609
default MMC_SDHI if (SUPERH || ARM)
610610
help
611611
This provides DMA support for SDHI SD/SDIO controllers
@@ -615,7 +615,7 @@ config MMC_SDHI_SYS_DMAC
615615
config MMC_SDHI_INTERNAL_DMAC
616616
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
617617
depends on ARM64 || COMPILE_TEST
618-
depends on MMC_SDHI
618+
depends on MMC_SDHI && HAS_DMA
619619
default MMC_SDHI if ARM64
620620
help
621621
This provides DMA support for SDHI SD/SDIO controllers

drivers/mmc/host/sdhci.c

Lines changed: 156 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/dma-mapping.h>
2222
#include <linux/slab.h>
2323
#include <linux/scatterlist.h>
24+
#include <linux/sizes.h>
2425
#include <linux/swiotlb.h>
2526
#include <linux/regulator/consumer.h>
2627
#include <linux/pm_runtime.h>
@@ -502,8 +503,35 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
502503
if (data->host_cookie == COOKIE_PRE_MAPPED)
503504
return data->sg_count;
504505

505-
sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
506-
mmc_get_dma_dir(data));
506+
/* Bounce write requests to the bounce buffer */
507+
if (host->bounce_buffer) {
508+
unsigned int length = data->blksz * data->blocks;
509+
510+
if (length > host->bounce_buffer_size) {
511+
pr_err("%s: asked for transfer of %u bytes exceeds bounce buffer %u bytes\n",
512+
mmc_hostname(host->mmc), length,
513+
host->bounce_buffer_size);
514+
return -EIO;
515+
}
516+
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
517+
/* Copy the data to the bounce buffer */
518+
sg_copy_to_buffer(data->sg, data->sg_len,
519+
host->bounce_buffer,
520+
length);
521+
}
522+
/* Switch ownership to the DMA */
523+
dma_sync_single_for_device(host->mmc->parent,
524+
host->bounce_addr,
525+
host->bounce_buffer_size,
526+
mmc_get_dma_dir(data));
527+
/* Just a dummy value */
528+
sg_count = 1;
529+
} else {
530+
/* Just access the data directly from memory */
531+
sg_count = dma_map_sg(mmc_dev(host->mmc),
532+
data->sg, data->sg_len,
533+
mmc_get_dma_dir(data));
534+
}
507535

508536
if (sg_count == 0)
509537
return -ENOSPC;
@@ -673,6 +701,14 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
673701
}
674702
}
675703

704+
static u32 sdhci_sdma_address(struct sdhci_host *host)
705+
{
706+
if (host->bounce_buffer)
707+
return host->bounce_addr;
708+
else
709+
return sg_dma_address(host->data->sg);
710+
}
711+
676712
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
677713
{
678714
u8 count;
@@ -858,8 +894,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
858894
SDHCI_ADMA_ADDRESS_HI);
859895
} else {
860896
WARN_ON(sg_cnt != 1);
861-
sdhci_writel(host, sg_dma_address(data->sg),
862-
SDHCI_DMA_ADDRESS);
897+
sdhci_writel(host, sdhci_sdma_address(host),
898+
SDHCI_DMA_ADDRESS);
863899
}
864900
}
865901

@@ -2255,7 +2291,12 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
22552291

22562292
mrq->data->host_cookie = COOKIE_UNMAPPED;
22572293

2258-
if (host->flags & SDHCI_REQ_USE_DMA)
2294+
/*
2295+
* No pre-mapping in the pre hook if we're using the bounce buffer,
2296+
* for that we would need two bounce buffers since one buffer is
2297+
* in flight when this is getting called.
2298+
*/
2299+
if (host->flags & SDHCI_REQ_USE_DMA && !host->bounce_buffer)
22592300
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
22602301
}
22612302

@@ -2359,8 +2400,45 @@ static bool sdhci_request_done(struct sdhci_host *host)
23592400
struct mmc_data *data = mrq->data;
23602401

23612402
if (data && data->host_cookie == COOKIE_MAPPED) {
2362-
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
2363-
mmc_get_dma_dir(data));
2403+
if (host->bounce_buffer) {
2404+
/*
2405+
* On reads, copy the bounced data into the
2406+
* sglist
2407+
*/
2408+
if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
2409+
unsigned int length = data->bytes_xfered;
2410+
2411+
if (length > host->bounce_buffer_size) {
2412+
pr_err("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
2413+
mmc_hostname(host->mmc),
2414+
host->bounce_buffer_size,
2415+
data->bytes_xfered);
2416+
/* Cap it down and continue */
2417+
length = host->bounce_buffer_size;
2418+
}
2419+
dma_sync_single_for_cpu(
2420+
host->mmc->parent,
2421+
host->bounce_addr,
2422+
host->bounce_buffer_size,
2423+
DMA_FROM_DEVICE);
2424+
sg_copy_from_buffer(data->sg,
2425+
data->sg_len,
2426+
host->bounce_buffer,
2427+
length);
2428+
} else {
2429+
/* No copying, just switch ownership */
2430+
dma_sync_single_for_cpu(
2431+
host->mmc->parent,
2432+
host->bounce_addr,
2433+
host->bounce_buffer_size,
2434+
mmc_get_dma_dir(data));
2435+
}
2436+
} else {
2437+
/* Unmap the raw data */
2438+
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
2439+
data->sg_len,
2440+
mmc_get_dma_dir(data));
2441+
}
23642442
data->host_cookie = COOKIE_UNMAPPED;
23652443
}
23662444
}
@@ -2643,7 +2721,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
26432721
*/
26442722
if (intmask & SDHCI_INT_DMA_END) {
26452723
u32 dmastart, dmanow;
2646-
dmastart = sg_dma_address(host->data->sg);
2724+
2725+
dmastart = sdhci_sdma_address(host);
26472726
dmanow = dmastart + host->data->bytes_xfered;
26482727
/*
26492728
* Force update to the next DMA block boundary.
@@ -3234,6 +3313,68 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
32343313
}
32353314
EXPORT_SYMBOL_GPL(__sdhci_read_caps);
32363315

3316+
static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
3317+
{
3318+
struct mmc_host *mmc = host->mmc;
3319+
unsigned int max_blocks;
3320+
unsigned int bounce_size;
3321+
int ret;
3322+
3323+
/*
3324+
* Cap the bounce buffer at 64KB. Using a bigger bounce buffer
3325+
* has diminishing returns, this is probably because SD/MMC
3326+
* cards are usually optimized to handle this size of requests.
3327+
*/
3328+
bounce_size = SZ_64K;
3329+
/*
3330+
* Adjust downwards to maximum request size if this is less
3331+
* than our segment size, else hammer down the maximum
3332+
* request size to the maximum buffer size.
3333+
*/
3334+
if (mmc->max_req_size < bounce_size)
3335+
bounce_size = mmc->max_req_size;
3336+
max_blocks = bounce_size / 512;
3337+
3338+
/*
3339+
* When we just support one segment, we can get significant
3340+
* speedups by the help of a bounce buffer to group scattered
3341+
* reads/writes together.
3342+
*/
3343+
host->bounce_buffer = devm_kmalloc(mmc->parent,
3344+
bounce_size,
3345+
GFP_KERNEL);
3346+
if (!host->bounce_buffer) {
3347+
pr_err("%s: failed to allocate %u bytes for bounce buffer, falling back to single segments\n",
3348+
mmc_hostname(mmc),
3349+
bounce_size);
3350+
/*
3351+
* Exiting with zero here makes sure we proceed with
3352+
* mmc->max_segs == 1.
3353+
*/
3354+
return 0;
3355+
}
3356+
3357+
host->bounce_addr = dma_map_single(mmc->parent,
3358+
host->bounce_buffer,
3359+
bounce_size,
3360+
DMA_BIDIRECTIONAL);
3361+
ret = dma_mapping_error(mmc->parent, host->bounce_addr);
3362+
if (ret)
3363+
/* Again fall back to max_segs == 1 */
3364+
return 0;
3365+
host->bounce_buffer_size = bounce_size;
3366+
3367+
/* Lie about this since we're bouncing */
3368+
mmc->max_segs = max_blocks;
3369+
mmc->max_seg_size = bounce_size;
3370+
mmc->max_req_size = bounce_size;
3371+
3372+
pr_info("%s bounce up to %u segments into one, max segment size %u bytes\n",
3373+
mmc_hostname(mmc), max_blocks, bounce_size);
3374+
3375+
return 0;
3376+
}
3377+
32373378
int sdhci_setup_host(struct sdhci_host *host)
32383379
{
32393380
struct mmc_host *mmc;
@@ -3730,6 +3871,13 @@ int sdhci_setup_host(struct sdhci_host *host)
37303871
*/
37313872
mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
37323873

3874+
if (mmc->max_segs == 1) {
3875+
/* This may alter mmc->*_blk_* parameters */
3876+
ret = sdhci_allocate_bounce_buffer(host);
3877+
if (ret)
3878+
return ret;
3879+
}
3880+
37333881
return 0;
37343882

37353883
unreg:

drivers/mmc/host/sdhci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ struct sdhci_host {
440440

441441
int irq; /* Device IRQ */
442442
void __iomem *ioaddr; /* Mapped address */
443+
char *bounce_buffer; /* For packing SDMA reads/writes */
444+
dma_addr_t bounce_addr;
445+
unsigned int bounce_buffer_size;
443446

444447
const struct sdhci_ops *ops; /* Low level hw interface */
445448

0 commit comments

Comments
 (0)