Move drivers/storage into commonlib/storage to enable access by libpayload and indirectly by payloads. * Remove SD/MMC specific include files from include/device * Remove files from drivers/storage * Add SD/MMC specific include files to commonlib/include * Add files to commonlib/storage * Fix header file references * Add subdir entry in commonlib/Makefile.inc to build the SD/MMC driver * Add Kconfig source for commonlib/storage * Rename *DEVICE* to *COMMONLIB* * Rename *DRIVERS_STORAGE* to *COMMONLIB_STORAGE* TEST=Build and run on Galileo Gen2 Change-Id: I4339e4378491db9a0da1f2dc34e1906a5ba31ad6 Signed-off-by: Lee Leahy <Leroy.P.Leahy@intel.com> Reviewed-on: https://review.coreboot.org/19672 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Georgi <pgeorgi@google.com>
195 lines
5.3 KiB
C
195 lines
5.3 KiB
C
/*
|
|
* Copyright 2011, Marvell Semiconductor Inc.
|
|
* Lei Wen <leiwen@marvell.com>
|
|
*
|
|
* Copyright 2017 Intel Corporation
|
|
*
|
|
* Secure Digital (SD) Host Controller interface DMA support code
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <commonlib/sdhci.h>
|
|
#include <commonlib/storage.h>
|
|
#include <delay.h>
|
|
#include <endian.h>
|
|
#include "sdhci.h"
|
|
#include "sd_mmc.h"
|
|
#include "storage.h"
|
|
#include <string.h>
|
|
#include <timer.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;
|
|
}
|