diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mmc/fsl_esdhc.c | 39 | ||||
| -rw-r--r-- | drivers/mmc/mmc.c | 4 | ||||
| -rw-r--r-- | drivers/mmc/mxsmmc.c | 5 | ||||
| -rw-r--r-- | drivers/mmc/omap_hsmmc.c | 114 | ||||
| -rw-r--r-- | drivers/mmc/s5p_mmc.c | 490 | ||||
| -rw-r--r-- | drivers/mmc/s5p_sdhci.c | 98 | ||||
| -rw-r--r-- | drivers/mmc/sdhci.c | 24 | 
8 files changed, 268 insertions, 508 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index c245352ac..a8e681c2f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -39,8 +39,8 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o  COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o  COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o  COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o -COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o  COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o  COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o  COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index a2f35e3e9..07370b572 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -307,19 +307,56 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  #else  	esdhc_write32(®s->xfertyp, xfertyp);  #endif + +	/* Mask all irqs */ +	esdhc_write32(®s->irqsigen, 0); +  	/* Wait for the command to complete */ -	while (!(esdhc_read32(®s->irqstat) & IRQSTAT_CC)) +	while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE)))  		;  	irqstat = esdhc_read32(®s->irqstat);  	esdhc_write32(®s->irqstat, irqstat); +	/* Reset CMD and DATA portions on error */ +	if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { +		esdhc_write32(®s->sysctl, esdhc_read32(®s->sysctl) | +			      SYSCTL_RSTC); +		while (esdhc_read32(®s->sysctl) & SYSCTL_RSTC) +			; + +		if (data) { +			esdhc_write32(®s->sysctl, +				      esdhc_read32(®s->sysctl) | +				      SYSCTL_RSTD); +			while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTD)) +				; +		} +	} +  	if (irqstat & CMD_ERR)  		return COMM_ERR;  	if (irqstat & IRQSTAT_CTOE)  		return TIMEOUT; +	/* Workaround for ESDHC errata ENGcm03648 */ +	if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { +		int timeout = 2500; + +		/* Poll on DATA0 line for cmd with busy signal for 250 ms */ +		while (timeout > 0 && !(esdhc_read32(®s->prsstat) & +					PRSSTAT_DAT0)) { +			udelay(100); +			timeout--; +		} + +		if (timeout <= 0) { +			printf("Timeout waiting for DAT0 to go high!\n"); +			return TIMEOUT; +		} +	} +  	/* Copy the response to the response buffer */  	if (cmd->resp_type & MMC_RSP_136) {  		u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 596732e80..aebe578ff 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1199,7 +1199,9 @@ int mmc_startup(struct mmc *mmc)  		else  			mmc_set_clock(mmc, 25000000);  	} else { -		for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) { +		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*/  			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,  					EXT_CSD_BUS_WIDTH, width); diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 35c6bdabb..4187a9412 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -133,7 +133,8 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  		/* READ or WRITE */  		if (data->flags & MMC_DATA_READ) {  			ctrl0 |= SSP_CTRL0_READ; -		} else if (priv->mmc_is_wp(mmc->block_dev.dev)) { +		} else if (priv->mmc_is_wp && +			priv->mmc_is_wp(mmc->block_dev.dev)) {  			printf("MMC%d: Can not write a locked card!\n",  				mmc->block_dev.dev);  			return UNUSABLE_ERR; @@ -406,7 +407,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))  	 */  	mmc->f_min = 400000;  	mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2; -	mmc->b_max = 0x40; +	mmc->b_max = 0x20;  	mmc_register(mmc);  	return 0; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2400db2f3..afd9b30b5 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -29,10 +29,15 @@  #include <i2c.h>  #include <twl4030.h>  #include <twl6030.h> +#include <twl6035.h>  #include <asm/io.h>  #include <asm/arch/mmc_host_def.h>  #include <asm/arch/sys_proto.h> +/* common definitions for all OMAPs */ +#define SYSCTL_SRC	(1 << 25) +#define SYSCTL_SRD	(1 << 26) +  /* If we fail after 1 second wait, something is really bad */  #define MAX_RETRY_MS	1000 @@ -45,8 +50,8 @@ static struct mmc hsmmc_dev[2];  static void omap4_vmmc_pbias_config(struct mmc *mmc)  {  	u32 value = 0; -	struct omap4_sys_ctrl_regs *const ctrl = -		(struct omap4_sys_ctrl_regs *)SYSCTRL_GENERAL_CORE_BASE; +	struct omap_sys_ctrl_regs *const ctrl = +		(struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE;  	value = readl(&ctrl->control_pbiaslite); @@ -60,17 +65,51 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc)  }  #endif -unsigned char mmc_board_init(struct mmc *mmc) +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +static void omap5_pbias_config(struct mmc *mmc)  { -#if defined(CONFIG_TWL4030_POWER) -	twl4030_power_mmc_init(); +	u32 value = 0; +	struct omap_sys_ctrl_regs *const ctrl = +		(struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE; + +	value = readl(&ctrl->control_pbias); +	value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); +	value |= SDCARD_BIAS_HIZ_MODE; +	writel(value, &ctrl->control_pbias); + +	twl6035_mmc1_poweron_ldo(); + +	value = readl(&ctrl->control_pbias); +	value &= ~SDCARD_BIAS_HIZ_MODE; +	value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ; +	writel(value, &ctrl->control_pbias); + +	value = readl(&ctrl->control_pbias); +	if (value & (1 << 23)) { +		value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); +		value |= SDCARD_BIAS_HIZ_MODE; +		writel(value, &ctrl->control_pbias); +	} +}  #endif +unsigned char mmc_board_init(struct mmc *mmc) +{  #if defined(CONFIG_OMAP34XX)  	t2_t *t2_base = (t2_t *)T2_BASE;  	struct prcm *prcm_base = (struct prcm *)PRCM_BASE; +	u32 pbias_lite; -	writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 | +	pbias_lite = readl(&t2_base->pbias_lite); +	pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0); +	writel(pbias_lite, &t2_base->pbias_lite); +#endif +#if defined(CONFIG_TWL4030_POWER) +	twl4030_power_mmc_init(); +	mdelay(100);	/* ramp-up delay from Linux code */ +#endif +#if defined(CONFIG_OMAP34XX) +	writel(pbias_lite | PBIASLITEPWRDNZ1 |  		PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0,  		&t2_base->pbias_lite); @@ -80,6 +119,11 @@ unsigned char mmc_board_init(struct mmc *mmc)  	writel(readl(&t2_base->devconf1) | MMCSDIO2ADPCLKISEL,  		&t2_base->devconf1); +	/* Change from default of 52MHz to 26MHz if necessary */ +	if (!(mmc->host_caps & MMC_MODE_HS_52MHz)) +		writel(readl(&t2_base->ctl_prog_io1) & ~CTLPROGIO1SPEEDCTRL, +			&t2_base->ctl_prog_io1); +  	writel(readl(&prcm_base->fclken1_core) |  		EN_MMC1 | EN_MMC2 | EN_MMC3,  		&prcm_base->fclken1_core); @@ -94,6 +138,10 @@ unsigned char mmc_board_init(struct mmc *mmc)  	if (mmc->block_dev.dev == 0)  		omap4_vmmc_pbias_config(mmc);  #endif +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +	if (mmc->block_dev.dev == 0) +		omap5_pbias_config(mmc); +#endif  	return 0;  } @@ -189,6 +237,27 @@ static int mmc_init_setup(struct mmc *mmc)  	return 0;  } +/* + * MMC controller internal finite state machine reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + */ +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) +{ +	ulong start; + +	mmc_reg_out(&mmc_base->sysctl, bit, bit); + +	start = get_timer(0); +	while ((readl(&mmc_base->sysctl) & bit) != 0) { +		if (get_timer(0) - start > MAX_RETRY_MS) { +			printf("%s: timedout waiting for sysctl %x to clear\n", +				__func__, bit); +			return; +		} +	} +}  static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  			struct mmc_data *data) @@ -209,7 +278,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  	start = get_timer(0);  	while (readl(&mmc_base->stat)) {  		if (get_timer(0) - start > MAX_RETRY_MS) { -			printf("%s: timedout waiting for stat!\n", __func__); +			printf("%s: timedout waiting for STAT (%x) to clear\n", +				__func__, readl(&mmc_base->stat));  			return TIMEOUT;  		}  	} @@ -277,9 +347,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  		}  	} while (!mmc_stat); -	if ((mmc_stat & IE_CTO) != 0) +	if ((mmc_stat & IE_CTO) != 0) { +		mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);  		return TIMEOUT; -	else if ((mmc_stat & ERRI_MASK) != 0) +	} else if ((mmc_stat & ERRI_MASK) != 0)  		return -1;  	if (mmc_stat & CC_MASK) { @@ -330,6 +401,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size)  			}  		} while (mmc_stat == 0); +		if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) +			mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); +  		if ((mmc_stat & ERRI_MASK) != 0)  			return 1; @@ -382,6 +456,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,  			}  		} while (mmc_stat == 0); +		if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) +			mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); +  		if ((mmc_stat & ERRI_MASK) != 0)  			return 1; @@ -463,7 +540,7 @@ static void mmc_set_ios(struct mmc *mmc)  	writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);  } -int omap_mmc_init(int dev_index) +int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max)  {  	struct mmc *mmc; @@ -494,11 +571,22 @@ int omap_mmc_init(int dev_index)  		return 1;  	}  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; -	mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | -				MMC_MODE_HC; +	mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | +				MMC_MODE_HC) & ~host_caps_mask;  	mmc->f_min = 400000; -	mmc->f_max = 52000000; + +	if (f_max != 0) +		mmc->f_max = f_max; +	else { +		if (mmc->host_caps & MMC_MODE_HS) { +			if (mmc->host_caps & MMC_MODE_HS_52MHz) +				mmc->f_max = 52000000; +			else +				mmc->f_max = 26000000; +		} else +			mmc->f_max = 20000000; +	}  	mmc->b_max = 0; diff --git a/drivers/mmc/s5p_mmc.c b/drivers/mmc/s5p_mmc.c deleted file mode 100644 index 4ae3aaf77..000000000 --- a/drivers/mmc/s5p_mmc.c +++ /dev/null @@ -1,490 +0,0 @@ -/* - * (C) Copyright 2009 SAMSUNG Electronics - * Minkyu Kang <mk7.kang@samsung.com> - * 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 <mmc.h> -#include <asm/io.h> -#include <asm/arch/mmc.h> -#include <asm/arch/clk.h> - -/* support 4 mmc hosts */ -struct mmc mmc_dev[4]; -struct mmc_host mmc_host[4]; - -static inline struct s5p_mmc *s5p_get_base_mmc(int dev_index) -{ -	unsigned long offset = dev_index * sizeof(struct s5p_mmc); -	return (struct s5p_mmc *)(samsung_get_base_mmc() + offset); -} - -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) -{ -	unsigned char ctrl; - -	debug("data->dest: %08x\n", (u32)data->dest); -	writel((u32)data->dest, &host->reg->sysad); -	/* -	 * DMASEL[4:3] -	 * 00 = Selects SDMA -	 * 01 = Reserved -	 * 10 = Selects 32-bit Address ADMA2 -	 * 11 = Selects 64-bit Address ADMA2 -	 */ -	ctrl = readb(&host->reg->hostctl); -	ctrl &= ~(3 << 3); -	writeb(ctrl, &host->reg->hostctl); - -	/* We do not handle DMA boundaries, so set it to max (512 KiB) */ -	writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); -	writew(data->blocks, &host->reg->blkcnt); -} - -static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) -{ -	unsigned short mode; - -	/* -	 * TRNMOD -	 * MUL1SIN0[5]	: Multi/Single Block Select -	 * RD1WT0[4]	: Data Transfer Direction Select -	 *	1 = read -	 *	0 = write -	 * ENACMD12[2]	: Auto CMD12 Enable -	 * ENBLKCNT[1]	: Block Count Enable -	 * ENDMA[0]	: DMA Enable -	 */ -	mode = (1 << 1) | (1 << 0); -	if (data->blocks > 1) -		mode |= (1 << 5); -	if (data->flags & MMC_DATA_READ) -		mode |= (1 << 4); - -	writew(mode, &host->reg->trnmod); -} - -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, -			struct mmc_data *data) -{ -	struct mmc_host *host = (struct mmc_host *)mmc->priv; -	int flags, i; -	unsigned int timeout; -	unsigned int mask; -	unsigned int retry = 0x100000; - -	/* Wait max 10 ms */ -	timeout = 10; - -	/* -	 * PRNSTS -	 * CMDINHDAT[1]	: Command Inhibit (DAT) -	 * CMDINHCMD[0]	: Command Inhibit (CMD) -	 */ -	mask = (1 << 0); -	if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY)) -		mask |= (1 << 1); - -	/* -	 * We shouldn't wait for data inihibit for stop commands, even -	 * though they might use busy signaling -	 */ -	if (data) -		mask &= ~(1 << 1); - -	while (readl(&host->reg->prnsts) & mask) { -		if (timeout == 0) { -			printf("%s: timeout error\n", __func__); -			return -1; -		} -		timeout--; -		udelay(1000); -	} - -	if (data) -		mmc_prepare_data(host, data); - -	debug("cmd->arg: %08x\n", cmd->cmdarg); -	writel(cmd->cmdarg, &host->reg->argument); - -	if (data) -		mmc_set_transfer_mode(host, data); - -	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) -		return -1; - -	/* -	 * CMDREG -	 * CMDIDX[13:8]	: Command index -	 * DATAPRNT[5]	: Data Present Select -	 * ENCMDIDX[4]	: Command Index Check Enable -	 * ENCMDCRC[3]	: Command CRC Check Enable -	 * RSPTYP[1:0] -	 *	00 = No Response -	 *	01 = Length 136 -	 *	10 = Length 48 -	 *	11 = Length 48 Check busy after response -	 */ -	if (!(cmd->resp_type & MMC_RSP_PRESENT)) -		flags = 0; -	else if (cmd->resp_type & MMC_RSP_136) -		flags = (1 << 0); -	else if (cmd->resp_type & MMC_RSP_BUSY) -		flags = (3 << 0); -	else -		flags = (2 << 0); - -	if (cmd->resp_type & MMC_RSP_CRC) -		flags |= (1 << 3); -	if (cmd->resp_type & MMC_RSP_OPCODE) -		flags |= (1 << 4); -	if (data) -		flags |= (1 << 5); - -	debug("cmd: %d\n", cmd->cmdidx); - -	writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); - -	for (i = 0; i < retry; i++) { -		mask = readl(&host->reg->norintsts); -		/* Command Complete */ -		if (mask & (1 << 0)) { -			if (!data) -				writel(mask, &host->reg->norintsts); -			break; -		} -	} - -	if (i == retry) { -		printf("%s: waiting for status update\n", __func__); -		return TIMEOUT; -	} - -	if (mask & (1 << 16)) { -		/* Timeout Error */ -		debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); -		return TIMEOUT; -	} else if (mask & (1 << 15)) { -		/* Error Interrupt */ -		debug("error: %08x cmd %d\n", mask, cmd->cmdidx); -		return -1; -	} - -	if (cmd->resp_type & MMC_RSP_PRESENT) { -		if (cmd->resp_type & MMC_RSP_136) { -			/* CRC is stripped so we need to do some shifting. */ -			for (i = 0; i < 4; i++) { -				unsigned int offset = -					(unsigned int)(&host->reg->rspreg3 - i); -				cmd->response[i] = readl(offset) << 8; - -				if (i != 3) { -					cmd->response[i] |= -						readb(offset - 1); -				} -				debug("cmd->resp[%d]: %08x\n", -						i, cmd->response[i]); -			} -		} else if (cmd->resp_type & MMC_RSP_BUSY) { -			for (i = 0; i < retry; i++) { -				/* PRNTDATA[23:20] : DAT[3:0] Line Signal */ -				if (readl(&host->reg->prnsts) -					& (1 << 20))	/* DAT[0] */ -					break; -			} - -			if (i == retry) { -				printf("%s: card is still busy\n", __func__); -				return TIMEOUT; -			} - -			cmd->response[0] = readl(&host->reg->rspreg0); -			debug("cmd->resp[0]: %08x\n", cmd->response[0]); -		} else { -			cmd->response[0] = readl(&host->reg->rspreg0); -			debug("cmd->resp[0]: %08x\n", cmd->response[0]); -		} -	} - -	if (data) { -		while (1) { -			mask = readl(&host->reg->norintsts); - -			if (mask & (1 << 15)) { -				/* Error Interrupt */ -				writel(mask, &host->reg->norintsts); -				printf("%s: error during transfer: 0x%08x\n", -						__func__, mask); -				return -1; -			} else if (mask & (1 << 3)) { -				/* -				 * DMA Interrupt, restart the transfer where -				 * it was interrupted. -				 */ -				unsigned int address = readl(&host->reg->sysad); - -				debug("DMA end\n"); -				writel((1 << 3), &host->reg->norintsts); -				writel(address, &host->reg->sysad); -			} else if (mask & (1 << 1)) { -				/* Transfer Complete */ -				debug("r/w is done\n"); -				break; -			} -		} -		writel(mask, &host->reg->norintsts); -	} - -	udelay(1000); -	return 0; -} - -static void mmc_change_clock(struct mmc_host *host, uint clock) -{ -	int div; -	unsigned short clk; -	unsigned long timeout; -	unsigned long ctrl2; - -	/* -	 * SELBASECLK[5:4] -	 * 00/01 = HCLK -	 * 10 = EPLL -	 * 11 = XTI or XEXTCLK -	 */ -	ctrl2 = readl(&host->reg->control2); -	ctrl2 &= ~(3 << 4); -	ctrl2 |= (2 << 4); -	writel(ctrl2, &host->reg->control2); - -	writew(0, &host->reg->clkcon); - -	/* XXX: we assume that clock is between 40MHz and 50MHz */ -	if (clock == 0) -		goto out; -	else if (clock <= 400000) -		div = 0x100; -	else if (clock <= 20000000) -		div = 4; -	else if (clock <= 26000000) -		div = 2; -	else -		div = 1; -	debug("div: %d\n", div); - -	div >>= 1; -	/* -	 * CLKCON -	 * SELFREQ[15:8]	: base clock divied by value -	 * ENSDCLK[2]		: SD Clock Enable -	 * STBLINTCLK[1]	: Internal Clock Stable -	 * ENINTCLK[0]		: Internal Clock Enable -	 */ -	clk = (div << 8) | (1 << 0); -	writew(clk, &host->reg->clkcon); - -	set_mmc_clk(host->dev_index, div); - -	/* Wait max 10 ms */ -	timeout = 10; -	while (!(readw(&host->reg->clkcon) & (1 << 1))) { -		if (timeout == 0) { -			printf("%s: timeout error\n", __func__); -			return; -		} -		timeout--; -		udelay(1000); -	} - -	clk |= (1 << 2); -	writew(clk, &host->reg->clkcon); - -out: -	host->clock = clock; -} - -static void mmc_set_ios(struct mmc *mmc) -{ -	struct mmc_host *host = mmc->priv; -	unsigned char ctrl; -	unsigned long val; - -	debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); - -	/* -	 * SELCLKPADDS[17:16] -	 * 00 = 2mA -	 * 01 = 4mA -	 * 10 = 7mA -	 * 11 = 9mA -	 */ -	writel(0x3 << 16, &host->reg->control4); - -	val = readl(&host->reg->control2); -	val &= (0x3 << 4); - -	val |=	(1 << 31) |	/* write status clear async mode enable */ -		(1 << 30) |	/* command conflict mask enable */ -		(1 << 14) |	/* Feedback Clock Enable for Rx Clock */ -		(1 << 8);	/* SDCLK hold enable */ - -	writel(val, &host->reg->control2); - -	/* -	 * FCSEL1[15] FCSEL0[7] -	 * FCSel[1:0] : Rx Feedback Clock Delay Control -	 *	Inverter delay means10ns delay if SDCLK 50MHz setting -	 *	01 = Delay1 (basic delay) -	 *	11 = Delay2 (basic delay + 2ns) -	 *	00 = Delay3 (inverter delay) -	 *	10 = Delay4 (inverter delay + 2ns) -	 */ -	writel(0x8080, &host->reg->control3); - -	mmc_change_clock(host, mmc->clock); - -	ctrl = readb(&host->reg->hostctl); - -	/* -	 * WIDE8[5] -	 * 0 = Depend on WIDE4 -	 * 1 = 8-bit mode -	 * WIDE4[1] -	 * 1 = 4-bit mode -	 * 0 = 1-bit mode -	 */ -	if (mmc->bus_width == 8) -		ctrl |= (1 << 5); -	else if (mmc->bus_width == 4) -		ctrl |= (1 << 1); -	else -		ctrl &= ~(1 << 1); - -	/* -	 * OUTEDGEINV[2] -	 * 1 = Riging edge output -	 * 0 = Falling edge output -	 */ -	ctrl &= ~(1 << 2); - -	writeb(ctrl, &host->reg->hostctl); -} - -static void mmc_reset(struct mmc_host *host) -{ -	unsigned int timeout; - -	/* -	 * RSTALL[0] : Software reset for all -	 * 1 = reset -	 * 0 = work -	 */ -	writeb((1 << 0), &host->reg->swrst); - -	host->clock = 0; - -	/* Wait max 100 ms */ -	timeout = 100; - -	/* hw clears the bit when it's done */ -	while (readb(&host->reg->swrst) & (1 << 0)) { -		if (timeout == 0) { -			printf("%s: timeout error\n", __func__); -			return; -		} -		timeout--; -		udelay(1000); -	} -} - -static int mmc_core_init(struct mmc *mmc) -{ -	struct mmc_host *host = (struct mmc_host *)mmc->priv; -	unsigned int mask; - -	mmc_reset(host); - -	host->version = readw(&host->reg->hcver); - -	/* mask all */ -	writel(0xffffffff, &host->reg->norintstsen); -	writel(0xffffffff, &host->reg->norintsigen); - -	writeb(0xe, &host->reg->timeoutcon);	/* TMCLK * 2^27 */ - -	/* -	 * NORMAL Interrupt Status Enable Register init -	 * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable -	 * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable -	 * [3] ENSTADMAINT : DMA Interrupt Status Enable -	 * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable -	 * [0] ENSTACMDCMPLT : Command Complete Status Enable -	 */ -	mask = readl(&host->reg->norintstsen); -	mask &= ~(0xffff); -	mask |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1) | (1 << 0); -	writel(mask, &host->reg->norintstsen); - -	/* -	 * NORMAL Interrupt Signal Enable Register init -	 * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable -	 */ -	mask = readl(&host->reg->norintsigen); -	mask &= ~(0xffff); -	mask |= (1 << 1); -	writel(mask, &host->reg->norintsigen); - -	return 0; -} - -static int s5p_mmc_initialize(int dev_index, int bus_width) -{ -	struct mmc *mmc; - -	mmc = &mmc_dev[dev_index]; - -	sprintf(mmc->name, "SAMSUNG SD/MMC"); -	mmc->priv = &mmc_host[dev_index]; -	mmc->send_cmd = mmc_send_cmd; -	mmc->set_ios = mmc_set_ios; -	mmc->init = mmc_core_init; -	mmc->getcd = NULL; - -	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; -	if (bus_width == 8) -		mmc->host_caps = MMC_MODE_8BIT; -	else -		mmc->host_caps = MMC_MODE_4BIT; -	mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; - -	mmc->f_min = 400000; -	mmc->f_max = 52000000; - -	mmc_host[dev_index].dev_index = dev_index; -	mmc_host[dev_index].clock = 0; -	mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index); -	mmc->b_max = 0; -	mmc_register(mmc); - -	return 0; -} - -int s5p_mmc_init(int dev_index, int bus_width) -{ -	return s5p_mmc_initialize(dev_index, bus_width); -} diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c new file mode 100644 index 000000000..1d4481b97 --- /dev/null +++ b/drivers/mmc/s5p_sdhci.c @@ -0,0 +1,98 @@ +/* + * (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 <sdhci.h> +#include <asm/arch/mmc.h> + +static char *S5P_NAME = "SAMSUNG SDHCI"; +static void s5p_sdhci_set_control_reg(struct sdhci_host *host) +{ +	unsigned long val, ctrl; +	/* +	 * SELCLKPADDS[17:16] +	 * 00 = 2mA +	 * 01 = 4mA +	 * 10 = 7mA +	 * 11 = 9mA +	 */ +	sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); + +	val = sdhci_readl(host, SDHCI_CONTROL2); +	val &= SDHCI_CTRL2_SELBASECLK_SHIFT; + +	val |=	SDHCI_CTRL2_ENSTAASYNCCLR | +		SDHCI_CTRL2_ENCMDCNFMSK | +		SDHCI_CTRL2_ENFBCLKRX | +		SDHCI_CTRL2_ENCLKOUTHOLD; + +	sdhci_writel(host, val, SDHCI_CONTROL2); + +	/* +	 * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] +	 * FCSel[1:0] : Rx Feedback Clock Delay Control +	 *	Inverter delay means10ns delay if SDCLK 50MHz setting +	 *	01 = Delay1 (basic delay) +	 *	11 = Delay2 (basic delay + 2ns) +	 *	00 = Delay3 (inverter delay) +	 *	10 = Delay4 (inverter delay + 2ns) +	 */ +	val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1; +	sdhci_writel(host, val, SDHCI_CONTROL3); + +	/* +	 * SELBASECLK[5:4] +	 * 00/01 = HCLK +	 * 10 = EPLL +	 * 11 = XTI or XEXTCLK +	 */ +	ctrl = sdhci_readl(host, SDHCI_CONTROL2); +	ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); +	ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); +	sdhci_writel(host, ctrl, SDHCI_CONTROL2); +} + +int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ +	struct sdhci_host *host = NULL; +	host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); +	if (!host) { +		printf("sdhci__host malloc fail!\n"); +		return 1; +	} + +	host->name = S5P_NAME; +	host->ioaddr = (void *)regbase; +	host->quirks = quirks; + +	host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE; +	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; +	if (quirks & SDHCI_QUIRK_REG32_RW) +		host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; +	else +		host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + +	host->set_control_reg = &s5p_sdhci_set_control_reg; + +	host->host_caps = MMC_MODE_HC; + +	add_sdhci(host, max_clk, min_clk); +	return 0; +} diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index fc904b530..1709643da 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -128,6 +128,7 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,  	int trans_bytes = 0, is_aligned = 1;  	u32 mask, flags, mode;  	unsigned int timeout, start_addr = 0; +	unsigned int retry = 10000;  	/* Wait max 10 ms */  	timeout = 10; @@ -210,8 +211,19 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,  		stat = sdhci_readl(host, SDHCI_INT_STATUS);  		if (stat & SDHCI_INT_ERROR)  			break; +		if (--retry == 0) +			break;  	} while ((stat & mask) != mask); +	if (retry == 0) { +		if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) +			return 0; +		else { +			printf("Timeout for status update!\n"); +			return TIMEOUT; +		} +	} +  	if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {  		sdhci_cmd_done(host, cmd);  		sdhci_writel(host, mask, SDHCI_INT_STATUS); @@ -325,6 +337,9 @@ void sdhci_set_ios(struct mmc *mmc)  	u32 ctrl;  	struct sdhci_host *host = (struct sdhci_host *)mmc->priv; +	if (host->set_control_reg) +		host->set_control_reg(host); +  	if (mmc->clock != host->clock)  		sdhci_set_clock(mmc, mmc->clock); @@ -348,6 +363,9 @@ void sdhci_set_ios(struct mmc *mmc)  	else  		ctrl &= ~SDHCI_CTRL_HISPD; +	if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT) +		ctrl &= ~SDHCI_CTRL_HISPD; +  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);  } @@ -431,9 +449,15 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  		mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;  	if (caps & SDHCI_CAN_VDD_180)  		mmc->voltages |= MMC_VDD_165_195; + +	if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) +		mmc->voltages |= host->voltages; +  	mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;  	if (caps & SDHCI_CAN_DO_8BIT)  		mmc->host_caps |= MMC_MODE_8BIT; +	if (host->host_caps) +		mmc->host_caps |= host->host_caps;  	sdhci_reset(host, SDHCI_RESET_ALL);  	mmc_register(mmc);  |