summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2016-01-26 13:39:55 +0000
committerUlf Hansson <ulf.hansson@linaro.org>2016-02-29 11:03:18 +0100
commite66e61cba1fffc53151b5303688f9c077f87104c (patch)
tree4ef79503ced5d00d49917c439490e1dbc1f0a3c8 /drivers/mmc
parent7f05538af71c7d30b5fc821cbe9f318edc645961 (diff)
mmc: sdhci: allocate alignment and DMA descriptor buffer together
Allocate both the alignment and DMA descriptor buffers together. The size of the alignment buffer will always be aligned to the hosts required alignment, which gives appropriate alignment to the DMA descriptors. We have a maximum of 128 segments, and a maximum alignment of 64 bits. This gives a maximum alignment buffer size of 1024 bytes. The DMA descriptors are a maximum of 12 bytes, and we allocate 128 * 2 + 1 of these, which gives a maximum DMA descriptor buffer size of 3084 bytes. This means the allocation for a 4K page sized system will be an order-1 allocation, since the resulting overall size is 4108. This is more prone to failure than page-sized allocations, but since this allocation commonly occurs at startup, the chances of failure are small. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> [ Changed to check ADMA table alignment ] Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Tested-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci.c56
1 files changed, 20 insertions, 36 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 5a7517f44187..59ffe3538c7b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2960,6 +2960,9 @@ int sdhci_add_host(struct sdhci_host *host)
host->flags &= ~SDHCI_USE_SDMA;
if (host->flags & SDHCI_USE_ADMA) {
+ dma_addr_t dma;
+ void *buf;
+
/*
* The DMA descriptor table size is calculated as the maximum
* number of segments times 2, to allow for an alignment
@@ -2975,45 +2978,28 @@ int sdhci_add_host(struct sdhci_host *host)
SDHCI_ADMA2_32_DESC_SZ;
host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
}
- host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
- host->adma_table_sz,
- &host->adma_addr,
- GFP_KERNEL);
+
host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
- host->align_buffer = dma_alloc_coherent(mmc_dev(mmc),
- host->align_buffer_sz,
- &host->align_addr,
- GFP_KERNEL);
- if (!host->adma_table || !host->align_buffer) {
- if (host->adma_table)
- dma_free_coherent(mmc_dev(mmc),
- host->adma_table_sz,
- host->adma_table,
- host->adma_addr);
- if (host->align_buffer)
- dma_free_coherent(mmc_dev(mmc),
- host->align_buffer_sz,
- host->align_buffer,
- host->align_addr);
+ buf = dma_alloc_coherent(mmc_dev(mmc), host->align_buffer_sz +
+ host->adma_table_sz, &dma, GFP_KERNEL);
+ if (!buf) {
pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
- host->adma_table = NULL;
- host->align_buffer = NULL;
- } else if (host->adma_addr & (SDHCI_ADMA2_DESC_ALIGN - 1)) {
+ } else if ((dma + host->align_buffer_sz) &
+ (SDHCI_ADMA2_DESC_ALIGN - 1)) {
pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
- dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
- host->adma_table, host->adma_addr);
- dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz,
- host->align_buffer, host->align_addr);
- host->adma_table = NULL;
- host->align_buffer = NULL;
- }
+ dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
+ host->adma_table_sz, buf, dma);
+ } else {
+ host->align_buffer = buf;
+ host->align_addr = dma;
- /* dma_alloc_coherent returns page aligned and sized buffers */
- BUG_ON(host->align_addr & SDHCI_ADMA2_MASK);
+ host->adma_table = buf + host->align_buffer_sz;
+ host->adma_addr = dma + host->align_buffer_sz;
+ }
}
/*
@@ -3473,12 +3459,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
if (!IS_ERR(mmc->supply.vqmmc))
regulator_disable(mmc->supply.vqmmc);
- if (host->adma_table)
- dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
- host->adma_table, host->adma_addr);
if (host->align_buffer)
- dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz,
- host->align_buffer, host->align_addr);
+ dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
+ host->adma_table_sz, host->align_buffer,
+ host->align_addr);
host->adma_table = NULL;
host->align_buffer = NULL;