/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Secure Digital (SD) Host Controller interface DMA support code */ #include #include #include #include #include #include #include #include "sdhci.h" #include "sd_mmc.h" #include "storage.h" static void sdhci_alloc_adma_descs(struct sdhci_ctrlr *sdhci_ctrlr, u32 need_descriptors) { if (sdhci_ctrlr->adma_descs) { if (sdhci_ctrlr->adma_desc_count < need_descriptors) { /* Previously allocated array is too small */ free(sdhci_ctrlr->adma_descs); sdhci_ctrlr->adma_desc_count = 0; sdhci_ctrlr->adma_descs = NULL; } } /* use dma_malloc() to make sure we get the coherent/uncached memory */ if (!sdhci_ctrlr->adma_descs) { sdhci_ctrlr->adma_descs = malloc(need_descriptors * sizeof(*sdhci_ctrlr->adma_descs)); if (sdhci_ctrlr->adma_descs == NULL) die("fail to malloc adma_descs\n"); sdhci_ctrlr->adma_desc_count = need_descriptors; } memset(sdhci_ctrlr->adma_descs, 0, sizeof(*sdhci_ctrlr->adma_descs) * need_descriptors); } static void sdhci_alloc_adma64_descs(struct sdhci_ctrlr *sdhci_ctrlr, u32 need_descriptors) { if (sdhci_ctrlr->adma64_descs) { if (sdhci_ctrlr->adma_desc_count < need_descriptors) { /* Previously allocated array is too small */ free(sdhci_ctrlr->adma64_descs); sdhci_ctrlr->adma_desc_count = 0; sdhci_ctrlr->adma64_descs = NULL; } } /* use dma_malloc() to make sure we get the coherent/uncached memory */ if (!sdhci_ctrlr->adma64_descs) { sdhci_ctrlr->adma64_descs = malloc(need_descriptors * sizeof(*sdhci_ctrlr->adma64_descs)); if (sdhci_ctrlr->adma64_descs == NULL) die("fail to malloc adma64_descs\n"); sdhci_ctrlr->adma_desc_count = need_descriptors; } memset(sdhci_ctrlr->adma64_descs, 0, sizeof(*sdhci_ctrlr->adma64_descs) * need_descriptors); } int sdhci_setup_adma(struct sdhci_ctrlr *sdhci_ctrlr, struct mmc_data *data) { int i, togo, need_descriptors; int dma64; char *buffer_data; u16 attributes; togo = data->blocks * data->blocksize; if (!togo) { sdhc_error("%s: MmcData corrupted: %d blocks of %d bytes\n", __func__, data->blocks, data->blocksize); return -1; } need_descriptors = 1 + togo / SDHCI_MAX_PER_DESCRIPTOR; dma64 = sdhci_ctrlr->sd_mmc_ctrlr.caps & DRVR_CAP_DMA_64BIT; if (dma64) sdhci_alloc_adma64_descs(sdhci_ctrlr, need_descriptors); else sdhci_alloc_adma_descs(sdhci_ctrlr, need_descriptors); buffer_data = data->dest; /* Now set up the descriptor chain. */ for (i = 0; togo; i++) { unsigned int desc_length; if (togo < SDHCI_MAX_PER_DESCRIPTOR) desc_length = togo; else desc_length = SDHCI_MAX_PER_DESCRIPTOR; togo -= desc_length; attributes = SDHCI_ADMA_VALID | SDHCI_ACT_TRAN; if (togo == 0) attributes |= SDHCI_ADMA_END; if (dma64) { sdhci_ctrlr->adma64_descs[i].addr = (uintptr_t)buffer_data; sdhci_ctrlr->adma64_descs[i].addr_hi = 0; sdhci_ctrlr->adma64_descs[i].length = desc_length; sdhci_ctrlr->adma64_descs[i].attributes = attributes; } else { sdhci_ctrlr->adma_descs[i].addr = (uintptr_t)buffer_data; sdhci_ctrlr->adma_descs[i].length = desc_length; sdhci_ctrlr->adma_descs[i].attributes = attributes; } buffer_data += desc_length; } if (dma64) sdhci_writel(sdhci_ctrlr, (uintptr_t)sdhci_ctrlr->adma64_descs, SDHCI_ADMA_ADDRESS); else sdhci_writel(sdhci_ctrlr, (uintptr_t)sdhci_ctrlr->adma_descs, SDHCI_ADMA_ADDRESS); return 0; } int sdhci_complete_adma(struct sdhci_ctrlr *sdhci_ctrlr, struct mmc_command *cmd) { int retry; u32 stat = 0, mask; mask = SDHCI_INT_RESPONSE | SDHCI_INT_ERROR; retry = 10000; /* Command should be done in way less than 10 ms. */ while (--retry) { stat = sdhci_readl(sdhci_ctrlr, SDHCI_INT_STATUS); if (stat & mask) break; udelay(1); } sdhci_writel(sdhci_ctrlr, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); if (retry && !(stat & SDHCI_INT_ERROR)) { /* Command OK, let's wait for data transfer completion. */ mask = SDHCI_INT_DATA_END | SDHCI_INT_ERROR | SDHCI_INT_ADMA_ERROR; /* Transfer should take 10 seconds tops. */ retry = 10 * 1000 * 1000; while (--retry) { stat = sdhci_readl(sdhci_ctrlr, SDHCI_INT_STATUS); if (stat & mask) break; udelay(1); } sdhci_writel(sdhci_ctrlr, stat, SDHCI_INT_STATUS); if (retry && !(stat & SDHCI_INT_ERROR)) { sdhci_cmd_done(sdhci_ctrlr, cmd); return 0; } } sdhc_error("%s: transfer error, stat %#x, adma error %#x, retry %d\n", __func__, stat, sdhci_readl(sdhci_ctrlr, SDHCI_ADMA_ERROR), retry); sdhci_reset(sdhci_ctrlr, SDHCI_RESET_CMD); sdhci_reset(sdhci_ctrlr, SDHCI_RESET_DATA); if (stat & SDHCI_INT_TIMEOUT) return CARD_TIMEOUT; return CARD_COMM_ERR; }