diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-04-16 15:30:54 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-04-16 15:30:54 -0300 |
commit | bdcfd44fb5b5fb8fd660e7f93f1095c507481024 (patch) | |
tree | e423b07154d422b711ddfadedb87c43317d3c4f6 /drivers/mmc/host/sdhci.c | |
parent | 4a327fcef90ba27150a3e8741441b68c605ae248 (diff) |
Linux-libre 4.5.1-gnupck-4.5.1-gnu
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 142 |
1 files changed, 84 insertions, 58 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index add9fdfd1..8059d7248 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -465,8 +465,6 @@ static void sdhci_adma_mark_end(void *desc) static int sdhci_adma_table_pre(struct sdhci_host *host, struct mmc_data *data) { - int direction; - void *desc; void *align; dma_addr_t addr; @@ -483,20 +481,9 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, * We currently guess that it is LE. */ - if (data->flags & MMC_DATA_READ) - direction = DMA_FROM_DEVICE; - else - direction = DMA_TO_DEVICE; - - host->align_addr = dma_map_single(mmc_dev(host->mmc), - host->align_buffer, host->align_buffer_sz, direction); - if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) - goto fail; - BUG_ON(host->align_addr & SDHCI_ADMA2_MASK); - host->sg_count = sdhci_pre_dma_transfer(host, data); if (host->sg_count < 0) - goto unmap_align; + return -EINVAL; desc = host->adma_table; align = host->align_buffer; @@ -570,22 +557,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, /* nop, end, valid */ sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID); } - - /* - * Resync align buffer as we might have changed it. - */ - if (data->flags & MMC_DATA_WRITE) { - dma_sync_single_for_device(mmc_dev(host->mmc), - host->align_addr, host->align_buffer_sz, direction); - } - return 0; - -unmap_align: - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, - host->align_buffer_sz, direction); -fail: - return -EINVAL; } static void sdhci_adma_table_post(struct sdhci_host *host, @@ -605,9 +577,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host, else direction = DMA_TO_DEVICE; - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, - host->align_buffer_sz, direction); - /* Do a quick scan of the SG list for any unaligned mappings */ has_unaligned = false; for_each_sg(data->sg, sg, host->sg_count, i) @@ -666,9 +635,20 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) if (!data) target_timeout = cmd->busy_timeout * 1000; else { - target_timeout = data->timeout_ns / 1000; - if (host->clock) - target_timeout += data->timeout_clks / host->clock; + target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); + if (host->clock && data->timeout_clks) { + unsigned long long val; + + /* + * data->timeout_clks is in units of clock cycles. + * host->clock is in Hz. target_timeout is in us. + * Hence, us = 1000000 * cycles / Hz. Round up. + */ + val = 1000000 * data->timeout_clks; + if (do_div(val, host->clock)) + target_timeout++; + target_timeout += val; + } } /* @@ -1003,6 +983,9 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) WARN_ON(host->cmd); + /* Initially, a command has no error */ + cmd->error = 0; + /* Wait max 10 ms */ timeout = 10; @@ -1097,8 +1080,6 @@ static void sdhci_finish_command(struct sdhci_host *host) } } - host->cmd->error = 0; - /* Finished CMD23, now send actual command. */ if (host->cmd == host->mrq->sbc) { host->cmd = NULL; @@ -2114,14 +2095,13 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, struct sdhci_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (host->flags & SDHCI_REQ_USE_DMA) { - if (data->host_cookie == COOKIE_GIVEN || - data->host_cookie == COOKIE_MAPPED) - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - data->flags & MMC_DATA_WRITE ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - data->host_cookie = COOKIE_UNMAPPED; - } + if (data->host_cookie == COOKIE_GIVEN || + data->host_cookie == COOKIE_MAPPED) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + data->flags & MMC_DATA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + data->host_cookie = COOKIE_UNMAPPED; } static int sdhci_pre_dma_transfer(struct sdhci_host *host, @@ -2238,6 +2218,22 @@ static void sdhci_tasklet_finish(unsigned long param) mrq = host->mrq; /* + * Always unmap the data buffers if they were mapped by + * sdhci_prepare_data() whenever we finish with a request. + * This avoids leaking DMA mappings on error. + */ + if (host->flags & SDHCI_REQ_USE_DMA) { + struct mmc_data *data = mrq->data; + + if (data && data->host_cookie == COOKIE_MAPPED) { + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + data->host_cookie = COOKIE_UNMAPPED; + } + } + + /* * The controller needs a reset of internal state machines * upon error conditions. */ @@ -2322,13 +2318,30 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) return; } - if (intmask & SDHCI_INT_TIMEOUT) - host->cmd->error = -ETIMEDOUT; - else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | - SDHCI_INT_INDEX)) - host->cmd->error = -EILSEQ; + if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | + SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) { + if (intmask & SDHCI_INT_TIMEOUT) + host->cmd->error = -ETIMEDOUT; + else + host->cmd->error = -EILSEQ; + + /* + * If this command initiates a data phase and a response + * CRC error is signalled, the card can start transferring + * data - the card may have received the command without + * error. We must not terminate the mmc_request early. + * + * If the card did not receive the command or returned an + * error which prevented it sending data, the data phase + * will time out. + */ + if (host->cmd->data && + (intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) == + SDHCI_INT_CRC) { + host->cmd = NULL; + return; + } - if (host->cmd->error) { tasklet_schedule(&host->finish_tasklet); return; } @@ -2967,14 +2980,21 @@ int sdhci_add_host(struct sdhci_host *host) &host->adma_addr, GFP_KERNEL); host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN; - host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL); + 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); - kfree(host->align_buffer); + if (host->align_buffer) + dma_free_coherent(mmc_dev(mmc), + host->align_buffer_sz, + host->align_buffer, + host->align_addr); pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; @@ -2986,10 +3006,14 @@ int sdhci_add_host(struct sdhci_host *host) host->flags &= ~SDHCI_USE_ADMA; dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, host->adma_table, host->adma_addr); - kfree(host->align_buffer); + 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_alloc_coherent returns page aligned and sized buffers */ + BUG_ON(host->align_addr & SDHCI_ADMA2_MASK); } /* @@ -3072,14 +3096,14 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; + if (override_timeout_clk) + host->timeout_clk = override_timeout_clk; + mmc->max_busy_timeout = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; mmc->max_busy_timeout /= host->timeout_clk; } - if (override_timeout_clk) - host->timeout_clk = override_timeout_clk; - mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; @@ -3452,7 +3476,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) if (host->adma_table) dma_free_coherent(mmc_dev(mmc), host->adma_table_sz, host->adma_table, host->adma_addr); - kfree(host->align_buffer); + if (host->align_buffer) + dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz, + host->align_buffer, host->align_addr); host->adma_table = NULL; host->align_buffer = NULL; |