mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-10-01 00:50:43 +09:00
mmc: sdhci: Wait for SDHCI_INT_DATA_END when transferring.
sdhci_transfer_data() function transfers the blocks passed up to the number of blocks defined in mmc_data, but returns immediately once all the blocks are transferred, even if the loop exit condition is not met (bit SDHCI_INT_DATA_END set in the STATUS word). When doing multiple writes to mmc, returning right after the last block is transferred can cause the write to fail when sending the MMC_CMD_STOP_TRANSMISSION command right after the MMC_CMD_WRITE_MULTIPLE_BLOCK command, leaving the mmc driver in an unconsistent state until reboot. This error was observed in the rpi3 board. This patch waits for the SDHCI_INT_DATA_END bit to be set even after sending all the blocks. Test: Reliably wrote 2GiB of data to mmc in a rpi3. Signed-off-by: Alex Deymo <deymo@google.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
4db2b61fcf
commit
7dde50d707
@ -72,6 +72,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data,
|
|||||||
unsigned int start_addr)
|
unsigned int start_addr)
|
||||||
{
|
{
|
||||||
unsigned int stat, rdy, mask, timeout, block = 0;
|
unsigned int stat, rdy, mask, timeout, block = 0;
|
||||||
|
bool transfer_done = false;
|
||||||
#ifdef CONFIG_MMC_SDHCI_SDMA
|
#ifdef CONFIG_MMC_SDHCI_SDMA
|
||||||
unsigned char ctrl;
|
unsigned char ctrl;
|
||||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||||
@ -89,17 +90,23 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data,
|
|||||||
__func__, stat);
|
__func__, stat);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (stat & rdy) {
|
if (!transfer_done && (stat & rdy)) {
|
||||||
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
|
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
|
||||||
continue;
|
continue;
|
||||||
sdhci_writel(host, rdy, SDHCI_INT_STATUS);
|
sdhci_writel(host, rdy, SDHCI_INT_STATUS);
|
||||||
sdhci_transfer_pio(host, data);
|
sdhci_transfer_pio(host, data);
|
||||||
data->dest += data->blocksize;
|
data->dest += data->blocksize;
|
||||||
if (++block >= data->blocks)
|
if (++block >= data->blocks) {
|
||||||
break;
|
/* Keep looping until the SDHCI_INT_DATA_END is
|
||||||
|
* cleared, even if we finished sending all the
|
||||||
|
* blocks.
|
||||||
|
*/
|
||||||
|
transfer_done = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_MMC_SDHCI_SDMA
|
#ifdef CONFIG_MMC_SDHCI_SDMA
|
||||||
if (stat & SDHCI_INT_DMA_END) {
|
if (!transfer_done && (stat & SDHCI_INT_DMA_END)) {
|
||||||
sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS);
|
sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS);
|
||||||
start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1);
|
start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1);
|
||||||
start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE;
|
start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE;
|
||||||
|
Loading…
Reference in New Issue
Block a user