diff options
Diffstat (limited to 'drivers/mmc/mmc.c')
| -rw-r--r-- | drivers/mmc/mmc.c | 102 | 
1 files changed, 99 insertions, 3 deletions
| diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 49c3349f5..74e5fea66 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -47,10 +47,105 @@ int __board_mmc_getcd(struct mmc *mmc) {  int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,  	alias("__board_mmc_getcd"))); +#ifdef CONFIG_MMC_BOUNCE_BUFFER +static int mmc_bounce_need_bounce(struct mmc_data *orig) +{ +	ulong addr, len; + +	if (orig->flags & MMC_DATA_READ) +		addr = (ulong)orig->dest; +	else +		addr = (ulong)orig->src; + +	if (addr % ARCH_DMA_MINALIGN) { +		debug("MMC: Unaligned data destination address %08lx!\n", addr); +		return 1; +	} + +	len = (ulong)(orig->blocksize * orig->blocks); +	if (len % ARCH_DMA_MINALIGN) { +		debug("MMC: Unaligned data destination length %08lx!\n", len); +		return 1; +	} + +	return 0; +} + +static int mmc_bounce_buffer_start(struct mmc_data *backup, +					struct mmc_data *orig) +{ +	ulong origlen, len; +	void *buffer; + +	if (!orig) +		return 0; + +	if (!mmc_bounce_need_bounce(orig)) +		return 0; + +	memcpy(backup, orig, sizeof(struct mmc_data)); + +	origlen = orig->blocksize * orig->blocks; +	len = roundup(origlen, ARCH_DMA_MINALIGN); +	buffer = memalign(ARCH_DMA_MINALIGN, len); +	if (!buffer) { +		puts("MMC: Error allocating MMC bounce buffer!\n"); +		return 1; +	} + +	if (orig->flags & MMC_DATA_READ) { +		orig->dest = buffer; +	} else { +		memcpy(buffer, orig->src, origlen); +		orig->src = buffer; +	} + +	return 0; +} + +static void mmc_bounce_buffer_stop(struct mmc_data *backup, +					struct mmc_data *orig) +{ +	ulong len; + +	if (!orig) +		return; + +	if (!mmc_bounce_need_bounce(backup)) +		return; + +	if (backup->flags & MMC_DATA_READ) { +		len = backup->blocksize * backup->blocks; +		memcpy(backup->dest, orig->dest, len); +		free(orig->dest); +		orig->dest = backup->dest; +	} else { +		free((void *)orig->src); +		orig->src = backup->src; +	} + +	return; + +} +#else +static inline int mmc_bounce_buffer_start(struct mmc_data *backup, +					struct mmc_data *orig) { } +static inline void mmc_bounce_buffer_stop(struct mmc_data *backup, +					struct mmc_data *orig) { } +#endif +  int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  { -#ifdef CONFIG_MMC_TRACE +	struct mmc_data backup;  	int ret; + +	memset(&backup, 0, sizeof(backup)); + +	ret = mmc_bounce_buffer_start(&backup, data); +	if (ret) +		return ret; + +#ifdef CONFIG_MMC_TRACE  	int i;  	u8 *ptr; @@ -99,10 +194,11 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  			printf("\t\tERROR MMC rsp not supported\n");  			break;  	} -	return ret;  #else -	return mmc->send_cmd(mmc, cmd, data); +	ret = mmc->send_cmd(mmc, cmd, data);  #endif +	mmc_bounce_buffer_stop(&backup, data); +	return ret;  }  int mmc_send_status(struct mmc *mmc, int timeout) |