diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mmc/exynos_dw_mmc.c | 57 | ||||
| -rw-r--r-- | drivers/mmc/mmc.c | 63 | ||||
| -rw-r--r-- | drivers/mmc/mxsmmc.c | 30 | ||||
| -rw-r--r-- | drivers/mmc/sdhci.c | 3 | ||||
| -rw-r--r-- | drivers/mmc/tegra_mmc.c | 64 | 
6 files changed, 153 insertions, 65 deletions
| diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index a1dd7302b..65791aa21 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -47,6 +47,7 @@ COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o  COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o  COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o  COBJS-$(CONFIG_DWMMC) += dw_mmc.o +COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c new file mode 100644 index 000000000..72a31b73f --- /dev/null +++ b/drivers/mmc/exynos_dw_mmc.c @@ -0,0 +1,57 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Jaehoon Chung <jh80.chung@samsung.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,  MA 02111-1307 USA + * + */ + +#include <common.h> +#include <malloc.h> +#include <dwmmc.h> +#include <asm/arch/dwmmc.h> +#include <asm/arch/clk.h> + +static char *EXYNOS_NAME = "EXYNOS DWMMC"; + +static void exynos_dwmci_clksel(struct dwmci_host *host) +{ +	u32 val; +	val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | +		DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(0); + +	dwmci_writel(host, DWMCI_CLKSEL, val); +} + +int exynos_dwmci_init(u32 regbase, int bus_width, int index) +{ +	struct dwmci_host *host = NULL; +	host = malloc(sizeof(struct dwmci_host)); +	if (!host) { +		printf("dwmci_host malloc fail!\n"); +		return 1; +	} + +	host->name = EXYNOS_NAME; +	host->ioaddr = (void *)regbase; +	host->buswidth = bus_width; +	host->clksel = exynos_dwmci_clksel; +	host->dev_index = index; + +	add_dwmci(host, 52000000, 400000); + +	return 0; +} + diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5ffd8c59e..72e8ce6da 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -868,7 +868,7 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width)  static int mmc_startup(struct mmc *mmc)  { -	int err, width; +	int err;  	uint mult, freq;  	u64 cmult, csize, capacity;  	struct mmc_cmd cmd; @@ -1087,21 +1087,44 @@ static int mmc_startup(struct mmc *mmc)  		else  			mmc->tran_speed = 25000000;  	} else { -		width = ((mmc->host_caps & MMC_MODE_MASK_WIDTH_BITS) >> -			 MMC_MODE_WIDTH_BITS_SHIFT); -		for (; width >= 0; width--) { -			/* Set the card to use 4 bit*/ +		int idx; + +		/* An array of possible bus widths in order of preference */ +		static unsigned ext_csd_bits[] = { +			EXT_CSD_BUS_WIDTH_8, +			EXT_CSD_BUS_WIDTH_4, +			EXT_CSD_BUS_WIDTH_1, +		}; + +		/* An array to map CSD bus widths to host cap bits */ +		static unsigned ext_to_hostcaps[] = { +			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, +			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, +		}; + +		/* An array to map chosen bus width to an integer */ +		static unsigned widths[] = { +			8, 4, 1, +		}; + +		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { +			unsigned int extw = ext_csd_bits[idx]; + +			/* +			 * Check to make sure the controller supports +			 * this bus width, if it's more than 1 +			 */ +			if (extw != EXT_CSD_BUS_WIDTH_1 && +					!(mmc->host_caps & ext_to_hostcaps[extw])) +				continue; +  			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, -					EXT_CSD_BUS_WIDTH, width); +					EXT_CSD_BUS_WIDTH, extw);  			if (err)  				continue; -			if (!width) { -				mmc_set_bus_width(mmc, 1); -				break; -			} else -				mmc_set_bus_width(mmc, 4 * width); +			mmc_set_bus_width(mmc, widths[idx]);  			err = mmc_send_ext_csd(mmc, test_csd);  			if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ @@ -1115,7 +1138,7 @@ static int mmc_startup(struct mmc *mmc)  				 && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \  					&test_csd[EXT_CSD_SEC_CNT], 4) == 0) { -				mmc->card_caps |= width; +				mmc->card_caps |= ext_to_hostcaps[extw];  				break;  			}  		} @@ -1135,13 +1158,15 @@ static int mmc_startup(struct mmc *mmc)  	mmc->block_dev.type = 0;  	mmc->block_dev.blksz = mmc->read_bl_len;  	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); -	sprintf(mmc->block_dev.vendor, "Man %06x Snr %08x", mmc->cid[0] >> 8, -			(mmc->cid[2] << 8) | (mmc->cid[3] >> 24)); -	sprintf(mmc->block_dev.product, "%c%c%c%c%c", mmc->cid[0] & 0xff, -			(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, -			(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); -	sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28, -			(mmc->cid[2] >> 24) & 0xf); +	sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", +		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), +		(mmc->cid[3] >> 16) & 0xffff); +	sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff, +		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, +		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, +		(mmc->cid[2] >> 24) & 0xff); +	sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, +		(mmc->cid[2] >> 16) & 0xf);  #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)  	init_part(&mmc->block_dev);  #endif diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 109acbf62..024df592f 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -96,11 +96,11 @@ static int mxsmmc_send_cmd_pio(struct mxsmmc_priv *priv, struct mmc_data *data)  static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data)  {  	uint32_t data_count = data->blocksize * data->blocks; -	uint32_t cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN);  	int dmach;  	struct mxs_dma_desc *desc = priv->desc; -	void *addr, *backup; -	uint8_t flags; +	void *addr; +	unsigned int flags; +	struct bounce_buffer bbstate;  	memset(desc, 0, sizeof(struct mxs_dma_desc));  	desc->address = (dma_addr_t)desc; @@ -115,19 +115,9 @@ static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data)  		flags = GEN_BB_READ;  	} -	bounce_buffer_start(&addr, data_count, &backup, flags); +	bounce_buffer_start(&bbstate, addr, data_count, flags); -	priv->desc->cmd.address = (dma_addr_t)addr; - -	if (data->flags & MMC_DATA_WRITE) { -		/* Flush data to DRAM so DMA can pick them up */ -		flush_dcache_range((uint32_t)addr, -			(uint32_t)(addr) + cache_data_count); -	} - -	/* Invalidate the area, so no writeback into the RAM races with DMA */ -	invalidate_dcache_range((uint32_t)priv->desc->cmd.address, -			(uint32_t)(priv->desc->cmd.address + cache_data_count)); +	priv->desc->cmd.address = (dma_addr_t)bbstate.bounce_buffer;  	priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |  				(data_count << MXS_DMA_DESC_BYTES_OFFSET); @@ -135,17 +125,11 @@ static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data)  	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id;  	mxs_dma_desc_append(dmach, priv->desc);  	if (mxs_dma_go(dmach)) { -		bounce_buffer_stop(&addr, data_count, &backup, flags); +		bounce_buffer_stop(&bbstate);  		return COMM_ERR;  	} -	/* The data arrived into DRAM, invalidate cache over them */ -	if (data->flags & MMC_DATA_READ) { -		invalidate_dcache_range((uint32_t)addr, -			(uint32_t)(addr) + cache_data_count); -	} - -	bounce_buffer_stop(&addr, data_count, &backup, flags); +	bounce_buffer_stop(&bbstate);  	return 0;  } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 7845f873a..b9cbe34f1 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -340,6 +340,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)  		return;  	} +	if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) +		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); +  	pwr |= SDHCI_POWER_ON;  	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 1fd5592f2..d749ab095 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -19,6 +19,7 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   */ +#include <bouncebuf.h>  #include <common.h>  #include <asm/gpio.h>  #include <asm/io.h> @@ -66,14 +67,17 @@ static void tegra_get_setup(struct mmc_host *host, int dev_index)  	host->reg = (struct tegra_mmc *)host->base;  } -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) +static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, +				struct bounce_buffer *bbstate)  {  	unsigned char ctrl; -	debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n", -	(u32)data->dest, data->blocks, data->blocksize); -	writel((u32)data->dest, &host->reg->sysad); +	debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", +		bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, +		data->blocksize); + +	writel((u32)bbstate->bounce_buffer, &host->reg->sysad);  	/*  	 * DMASEL[4:3]  	 * 00 = Selects SDMA @@ -114,14 +118,6 @@ static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)  	if (data->flags & MMC_DATA_READ)  		mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; -	if (data->flags & MMC_DATA_WRITE) { -		if ((uintptr_t)data->src & (ARCH_DMA_MINALIGN - 1)) -			printf("Warning: unaligned write to %p may fail\n", -			       data->src); -		flush_dcache_range((ulong)data->src, (ulong)data->src + -			data->blocks * data->blocksize); -	} -  	writew(mode, &host->reg->trnmod);  } @@ -156,8 +152,8 @@ static int mmc_wait_inhibit(struct mmc_host *host,  	return 0;  } -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, -			struct mmc_data *data) +static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, +			struct mmc_data *data, struct bounce_buffer *bbstate)  {  	struct mmc_host *host = (struct mmc_host *)mmc->priv;  	int flags, i; @@ -172,7 +168,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  		return result;  	if (data) -		mmc_prepare_data(host, data); +		mmc_prepare_data(host, data, bbstate);  	debug("cmd->arg: %08x\n", cmd->cmdarg);  	writel(cmd->cmdarg, &host->reg->argument); @@ -322,20 +318,42 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  			}  		}  		writel(mask, &host->reg->norintsts); -		if (data->flags & MMC_DATA_READ) { -			if ((uintptr_t)data->dest & (ARCH_DMA_MINALIGN - 1)) -				printf("Warning: unaligned read from %p " -					"may fail\n", data->dest); -			invalidate_dcache_range((ulong)data->dest, -				(ulong)data->dest + -					data->blocks * data->blocksize); -		}  	}  	udelay(1000);  	return 0;  } +static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, +			struct mmc_data *data) +{ +	void *buf; +	unsigned int bbflags; +	size_t len; +	struct bounce_buffer bbstate; +	int ret; + +	if (data) { +		if (data->flags & MMC_DATA_READ) { +			buf = data->dest; +			bbflags = GEN_BB_WRITE; +		} else { +			buf = (void *)data->src; +			bbflags = GEN_BB_READ; +		} +		len = data->blocks * data->blocksize; + +		bounce_buffer_start(&bbstate, buf, len, bbflags); +	} + +	ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); + +	if (data) +		bounce_buffer_stop(&bbstate); + +	return ret; +} +  static void mmc_change_clock(struct mmc_host *host, uint clock)  {  	int div; |