diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mmc/omap3_mmc.c | 155 | ||||
| -rw-r--r-- | drivers/mmc/pxa_mmc.c | 646 | ||||
| -rw-r--r-- | drivers/mmc/pxa_mmc.h | 138 | 
4 files changed, 857 insertions, 83 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 6aa24f5d8..1b0af12a7 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o  COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o  COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o  COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o +COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/mmc/omap3_mmc.c b/drivers/mmc/omap3_mmc.c index 01487022f..e90db7ee3 100644 --- a/drivers/mmc/omap3_mmc.c +++ b/drivers/mmc/omap3_mmc.c @@ -28,6 +28,8 @@  #include <mmc.h>  #include <part.h>  #include <i2c.h> +#include <asm/io.h> +#include <asm/arch/mmc.h>  const unsigned short mmc_transspeed_val[15][4] = {  	{CLKD(10, 1), CLKD(10, 10), CLKD(10, 100), CLKD(10, 1000)}, @@ -49,6 +51,7 @@ const unsigned short mmc_transspeed_val[15][4] = {  mmc_card_data cur_card_data;  static block_dev_desc_t mmc_blk_dev; +static hsmmc_t *mmc_base = (hsmmc_t *)OMAP_HSMMC_BASE;  block_dev_desc_t *mmc_get_dev(int dev)  { @@ -59,55 +62,49 @@ void twl4030_mmc_config(void)  {  	unsigned char data; -	data = 0x20; -	i2c_write(0x4B, 0x82, 1, &data, 1); -	data = 0x2; -	i2c_write(0x4B, 0x85, 1, &data, 1); +	data = DEV_GRP_P1; +	i2c_write(PWRMGT_ADDR_ID4, VMMC1_DEV_GRP, 1, &data, 1); +	data = VMMC1_VSEL_30; +	i2c_write(PWRMGT_ADDR_ID4, VMMC1_DEDICATED, 1, &data, 1);  }  unsigned char mmc_board_init(void)  { -	unsigned int value = 0; +	t2_t *t2_base = (t2_t *)T2_BASE;  	twl4030_mmc_config(); -	value = CONTROL_PBIAS_LITE; -	CONTROL_PBIAS_LITE = value | (1 << 2) | (1 << 1) | (1 << 9); +	writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 | +		PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, +		&t2_base->pbias_lite); -	value = CONTROL_DEV_CONF0; -	CONTROL_DEV_CONF0 = value | (1 << 24); +	writel(readl(&t2_base->devconf0) | MMCSDIO1ADPCLKISEL, +		&t2_base->devconf0);  	return 1;  }  void mmc_init_stream(void)  { -	volatile unsigned int mmc_stat; +	writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con); -	OMAP_HSMMC_CON |= INIT_INITSTREAM; +	writel(MMC_CMD0, &mmc_base->cmd); +	while (!(readl(&mmc_base->stat) & CC_MASK)); -	OMAP_HSMMC_CMD = MMC_CMD0; -	do { -		mmc_stat = OMAP_HSMMC_STAT; -	} while (!(mmc_stat & CC_MASK)); - -	OMAP_HSMMC_STAT = CC_MASK; +	writel(CC_MASK, &mmc_base->stat); -	OMAP_HSMMC_CMD = MMC_CMD0; -	do { -		mmc_stat = OMAP_HSMMC_STAT; -	} while (!(mmc_stat & CC_MASK)); +	writel(MMC_CMD0, &mmc_base->cmd); +	while (!(readl(&mmc_base->stat) & CC_MASK)); -	OMAP_HSMMC_STAT = OMAP_HSMMC_STAT; -	OMAP_HSMMC_CON &= ~INIT_INITSTREAM; +	writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);  }  unsigned char mmc_clock_config(unsigned int iclk, unsigned short clk_div)  {  	unsigned int val; -	mmc_reg_out(OMAP_HSMMC_SYSCTL, (ICE_MASK | DTO_MASK | CEN_MASK), -		    (ICE_STOP | DTO_15THDTO | CEN_DISABLE)); +	mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), +			(ICE_STOP | DTO_15THDTO | CEN_DISABLE));  	switch (iclk) {  	case CLK_INITSEQ: @@ -122,12 +119,12 @@ unsigned char mmc_clock_config(unsigned int iclk, unsigned short clk_div)  	default:  		return 0;  	} -	mmc_reg_out(OMAP_HSMMC_SYSCTL, -		    ICE_MASK | CLKD_MASK, (val << CLKD_OFFSET) | ICE_OSCILLATE); +	mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, +			(val << CLKD_OFFSET) | ICE_OSCILLATE); -	while ((OMAP_HSMMC_SYSCTL & ICS_MASK) == ICS_NOTREADY) ; +	while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY); -	OMAP_HSMMC_SYSCTL |= CEN_ENABLE; +	writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);  	return 1;  } @@ -137,59 +134,63 @@ unsigned char mmc_init_setup(void)  	mmc_board_init(); -	OMAP_HSMMC_SYSCONFIG |= MMC_SOFTRESET; -	while ((OMAP_HSMMC_SYSSTATUS & RESETDONE) == 0) ; +	writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET, +		&mmc_base->sysconfig); +	while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0); -	OMAP_HSMMC_SYSCTL |= SOFTRESETALL; -	while ((OMAP_HSMMC_SYSCTL & SOFTRESETALL) != 0x0) ; +	writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl); +	while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0); -	OMAP_HSMMC_HCTL = DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0; -	OMAP_HSMMC_CAPA |= VS30_3V0SUP | VS18_1V8SUP; +	writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); +	writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, +		&mmc_base->capa); -	reg_val = OMAP_HSMMC_CON & RESERVED_MASK; +	reg_val = readl(&mmc_base->con) & RESERVED_MASK; -	OMAP_HSMMC_CON = CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | -		CDP_ACTIVEHIGH | MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | -		STR_BLOCK | HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN; +	writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH | +		MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK | +		HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con);  	mmc_clock_config(CLK_INITSEQ, 0); -	OMAP_HSMMC_HCTL |= SDBP_PWRON; +	writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); -	OMAP_HSMMC_IE = 0x307f0033; +	writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | +		IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC, +		&mmc_base->ie);  	mmc_init_stream();  	return 1;  }  unsigned char mmc_send_cmd(unsigned int cmd, unsigned int arg, -			   unsigned int *response) +				unsigned int *response)  { -	volatile unsigned int mmc_stat; +	unsigned int mmc_stat; -	while ((OMAP_HSMMC_PSTATE & DATI_MASK) == DATI_CMDDIS) ; +	while ((readl(&mmc_base->pstate) & DATI_MASK) == DATI_CMDDIS); -	OMAP_HSMMC_BLK = BLEN_512BYTESLEN | NBLK_STPCNT; -	OMAP_HSMMC_STAT = 0xFFFFFFFF; -	OMAP_HSMMC_ARG = arg; -	OMAP_HSMMC_CMD = cmd | CMD_TYPE_NORMAL | CICE_NOCHECK | -	    CCCE_NOCHECK | MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | -	    DE_DISABLE; +	writel(BLEN_512BYTESLEN | NBLK_STPCNT, &mmc_base->blk); +	writel(0xFFFFFFFF, &mmc_base->stat); +	writel(arg, &mmc_base->arg); +	writel(cmd | CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK | +		MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE, +		&mmc_base->cmd);  	while (1) {  		do { -			mmc_stat = OMAP_HSMMC_STAT; +			mmc_stat = readl(&mmc_base->stat);  		} while (mmc_stat == 0);  		if ((mmc_stat & ERRI_MASK) != 0)  			return (unsigned char) mmc_stat;  		if (mmc_stat & CC_MASK) { -			OMAP_HSMMC_STAT = CC_MASK; -			response[0] = OMAP_HSMMC_RSP10; +			writel(CC_MASK, &mmc_base->stat); +			response[0] = readl(&mmc_base->rsp10);  			if ((cmd & RSP_TYPE_MASK) == RSP_TYPE_LGHT136) { -				response[1] = OMAP_HSMMC_RSP32; -				response[2] = OMAP_HSMMC_RSP54; -				response[3] = OMAP_HSMMC_RSP76; +				response[1] = readl(&mmc_base->rsp32); +				response[2] = readl(&mmc_base->rsp54); +				response[3] = readl(&mmc_base->rsp76);  			}  			break;  		} @@ -199,7 +200,7 @@ unsigned char mmc_send_cmd(unsigned int cmd, unsigned int arg,  unsigned char mmc_read_data(unsigned int *output_buf)  { -	volatile unsigned int mmc_stat; +	unsigned int mmc_stat;  	unsigned int read_count = 0;  	/* @@ -207,7 +208,7 @@ unsigned char mmc_read_data(unsigned int *output_buf)  	 */  	while (1) {  		do { -			mmc_stat = OMAP_HSMMC_STAT; +			mmc_stat = readl(&mmc_base->stat);  		} while (mmc_stat == 0);  		if ((mmc_stat & ERRI_MASK) != 0) @@ -216,19 +217,22 @@ unsigned char mmc_read_data(unsigned int *output_buf)  		if (mmc_stat & BRR_MASK) {  			unsigned int k; -			OMAP_HSMMC_STAT |= BRR_MASK; +			writel(readl(&mmc_base->stat) | BRR_MASK, +				&mmc_base->stat);  			for (k = 0; k < MMCSD_SECTOR_SIZE / 4; k++) { -				*output_buf = OMAP_HSMMC_DATA; +				*output_buf = readl(&mmc_base->data);  				output_buf++;  				read_count += 4;  			}  		}  		if (mmc_stat & BWR_MASK) -			OMAP_HSMMC_STAT |= BWR_MASK; +			writel(readl(&mmc_base->stat) | BWR_MASK, +				&mmc_base->stat);  		if (mmc_stat & TC_MASK) { -			OMAP_HSMMC_STAT |= TC_MASK; +			writel(readl(&mmc_base->stat) | TC_MASK, +				&mmc_base->stat);  			break;  		}  	} @@ -272,8 +276,8 @@ unsigned char mmc_detect_card(mmc_card_data *mmc_card_cur)  		mmc_card_cur->card_type = MMC_CARD;  		ocr_value |= MMC_OCR_REG_ACCESS_MODE_SECTOR;  		ret_cmd41 = MMC_CMD1; -		OMAP_HSMMC_CON &= ~OD; -		OMAP_HSMMC_CON |= OPENDRAIN; +		writel(readl(&mmc_base->con) & ~OD, &mmc_base->con); +		writel(readl(&mmc_base->con) | OPENDRAIN, &mmc_base->con);  	}  	argument = ocr_value; @@ -341,8 +345,8 @@ unsigned char mmc_detect_card(mmc_card_data *mmc_card_cur)  		mmc_card_cur->RCA = ((mmc_resp_r6 *) resp)->newpublishedrca;  	} -	OMAP_HSMMC_CON &= ~OD; -	OMAP_HSMMC_CON |= NOOPENDRAIN; +	writel(readl(&mmc_base->con) & ~OD, &mmc_base->con); +	writel(readl(&mmc_base->con) | NOOPENDRAIN, &mmc_base->con);  	return 1;  } @@ -517,11 +521,11 @@ unsigned long mmc_bread(int dev_num, unsigned long blknr, lbaint_t blkcnt,  			void *dst)  {  	omap_mmc_read_sect(blknr, (blkcnt * MMCSD_SECTOR_SIZE), &cur_card_data, -			   (unsigned long *) dst); +				(unsigned long *) dst);  	return 1;  } -int mmc_init(int verbose) +int mmc_legacy_init(int verbose)  {  	if (configure_mmc(&cur_card_data) != 1)  		return 1; @@ -541,18 +545,3 @@ int mmc_init(int verbose)  	fat_register_device(&mmc_blk_dev, 1);  	return 0;  } - -int mmc_read(ulong src, uchar *dst, int size) -{ -	return 0; -} - -int mmc_write(uchar *src, ulong dst, int size) -{ -	return 0; -} - -int mmc2info(ulong addr) -{ -	return 0; -} diff --git a/drivers/mmc/pxa_mmc.c b/drivers/mmc/pxa_mmc.c new file mode 100644 index 000000000..8225235bf --- /dev/null +++ b/drivers/mmc/pxa_mmc.c @@ -0,0 +1,646 @@ +/* + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <config.h> +#include <common.h> +#include <mmc.h> +#include <asm/errno.h> +#include <asm/arch/hardware.h> +#include <part.h> + +#include "pxa_mmc.h" + +extern int fat_register_device(block_dev_desc_t * dev_desc, int part_no); + +static block_dev_desc_t mmc_dev; + +block_dev_desc_t *mmc_get_dev(int dev) +{ +	return ((block_dev_desc_t *) & mmc_dev); +} + +/* + * FIXME needs to read cid and csd info to determine block size + * and other parameters + */ +static uchar mmc_buf[MMC_BLOCK_SIZE]; +static uchar spec_ver; +static int mmc_ready = 0; +static int wide = 0; + +static uint32_t * +/****************************************************/ +mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat) +/****************************************************/ +{ +	static uint32_t resp[4], a, b, c; +	ulong status; +	int i; + +	debug("mmc_cmd %u 0x%04x 0x%04x 0x%04x\n", cmd, argh, argl, +	      cmdat | wide); +	MMC_STRPCL = MMC_STRPCL_STOP_CLK; +	MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF; +	while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF)) ; +	MMC_CMD = cmd; +	MMC_ARGH = argh; +	MMC_ARGL = argl; +	MMC_CMDAT = cmdat | wide; +	MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES; +	MMC_STRPCL = MMC_STRPCL_START_CLK; +	while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES)) ; + +	status = MMC_STAT; +	debug("MMC status 0x%08x\n", status); +	if (status & MMC_STAT_TIME_OUT_RESPONSE) { +		return 0; +	} + +	/* Linux says: +	 * Did I mention this is Sick.  We always need to +	 * discard the upper 8 bits of the first 16-bit word. +	 */ +	a = (MMC_RES & 0xffff); +	for (i = 0; i < 4; i++) { +		b = (MMC_RES & 0xffff); +		c = (MMC_RES & 0xffff); +		resp[i] = (a << 24) | (b << 8) | (c >> 8); +		a = c; +		debug("MMC resp[%d] = %#08x\n", i, resp[i]); +	} + +	return resp; +} + +int +/****************************************************/ +mmc_block_read(uchar * dst, ulong src, ulong len) +/****************************************************/ +{ +	ushort argh, argl; +	ulong status; + +	if (len == 0) { +		return 0; +	} + +	debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong) dst, src, len); + +	argh = len >> 16; +	argl = len & 0xffff; + +	/* set block len */ +	mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); + +	/* send read command */ +	argh = src >> 16; +	argl = src & 0xffff; +	MMC_STRPCL = MMC_STRPCL_STOP_CLK; +	MMC_RDTO = 0xffff; +	MMC_NOB = 1; +	MMC_BLKLEN = len; +	mmc_cmd(MMC_CMD_READ_SINGLE_BLOCK, argh, argl, +		MMC_CMDAT_R1 | MMC_CMDAT_READ | MMC_CMDAT_BLOCK | +		MMC_CMDAT_DATA_EN); + +	MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ; +	while (len) { +		if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) { +#ifdef CONFIG_PXA27X +			int i; +			for (i = min(len, 32); i; i--) { +				*dst++ = *((volatile uchar *)&MMC_RXFIFO); +				len--; +			} +#else +			*dst++ = MMC_RXFIFO; +			len--; +#endif +		} +		status = MMC_STAT; +		if (status & MMC_STAT_ERRORS) { +			printf("MMC_STAT error %lx\n", status); +			return -1; +		} +	} +	MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; +	while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ; +	status = MMC_STAT; +	if (status & MMC_STAT_ERRORS) { +		printf("MMC_STAT error %lx\n", status); +		return -1; +	} +	return 0; +} + +int +/****************************************************/ +mmc_block_write(ulong dst, uchar * src, int len) +/****************************************************/ +{ +	ushort argh, argl; +	ulong status; + +	if (len == 0) { +		return 0; +	} + +	debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong) src, len); + +	argh = len >> 16; +	argl = len & 0xffff; + +	/* set block len */ +	mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); + +	/* send write command */ +	argh = dst >> 16; +	argl = dst & 0xffff; +	MMC_STRPCL = MMC_STRPCL_STOP_CLK; +	MMC_NOB = 1; +	MMC_BLKLEN = len; +	mmc_cmd(MMC_CMD_WRITE_SINGLE_BLOCK, argh, argl, +		MMC_CMDAT_R1 | MMC_CMDAT_WRITE | MMC_CMDAT_BLOCK | +		MMC_CMDAT_DATA_EN); + +	MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ; +	while (len) { +		if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) { +			int i, bytes = min(32, len); + +			for (i = 0; i < bytes; i++) { +				MMC_TXFIFO = *src++; +			} +			if (bytes < 32) { +				MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL; +			} +			len -= bytes; +		} +		status = MMC_STAT; +		if (status & MMC_STAT_ERRORS) { +			printf("MMC_STAT error %lx\n", status); +			return -1; +		} +	} +	MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; +	while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ; +	MMC_I_MASK = ~MMC_I_MASK_PRG_DONE; +	while (!(MMC_I_REG & MMC_I_REG_PRG_DONE)) ; +	status = MMC_STAT; +	if (status & MMC_STAT_ERRORS) { +		printf("MMC_STAT error %lx\n", status); +		return -1; +	} +	return 0; +} + +int +/****************************************************/ +pxa_mmc_read(long src, uchar * dst, int size) +/****************************************************/ +{ +	ulong end, part_start, part_end, part_len, aligned_start, aligned_end; +	ulong mmc_block_size, mmc_block_address; + +	if (size == 0) { +		return 0; +	} + +	if (!mmc_ready) { +		printf("Please initial the MMC first\n"); +		return -1; +	} + +	mmc_block_size = MMC_BLOCK_SIZE; +	mmc_block_address = ~(mmc_block_size - 1); + +	src -= CONFIG_SYS_MMC_BASE; +	end = src + size; +	part_start = ~mmc_block_address & src; +	part_end = ~mmc_block_address & end; +	aligned_start = mmc_block_address & src; +	aligned_end = mmc_block_address & end; + +	/* all block aligned accesses */ +	debug +	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +	     src, (ulong) dst, end, part_start, part_end, aligned_start, +	     aligned_end); +	if (part_start) { +		part_len = mmc_block_size - part_start; +		debug +		    ("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +		     src, (ulong) dst, end, part_start, part_end, aligned_start, +		     aligned_end); +		if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < +		    0) { +			return -1; +		} +		memcpy(dst, mmc_buf + part_start, part_len); +		dst += part_len; +		src += part_len; +	} +	debug +	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +	     src, (ulong) dst, end, part_start, part_end, aligned_start, +	     aligned_end); +	for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size) { +		debug +		    ("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +		     src, (ulong) dst, end, part_start, part_end, aligned_start, +		     aligned_end); +		if ((mmc_block_read((uchar *) (dst), src, mmc_block_size)) < 0) { +			return -1; +		} +	} +	debug +	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +	     src, (ulong) dst, end, part_start, part_end, aligned_start, +	     aligned_end); +	if (part_end && src < end) { +		debug +		    ("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +		     src, (ulong) dst, end, part_start, part_end, aligned_start, +		     aligned_end); +		if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { +			return -1; +		} +		memcpy(dst, mmc_buf, part_end); +	} +	return 0; +} + +int +/****************************************************/ +pxa_mmc_write(uchar * src, ulong dst, int size) +/****************************************************/ +{ +	ulong end, part_start, part_end, part_len, aligned_start, aligned_end; +	ulong mmc_block_size, mmc_block_address; + +	if (size == 0) { +		return 0; +	} + +	if (!mmc_ready) { +		printf("Please initial the MMC first\n"); +		return -1; +	} + +	mmc_block_size = MMC_BLOCK_SIZE; +	mmc_block_address = ~(mmc_block_size - 1); + +	dst -= CONFIG_SYS_MMC_BASE; +	end = dst + size; +	part_start = ~mmc_block_address & dst; +	part_end = ~mmc_block_address & end; +	aligned_start = mmc_block_address & dst; +	aligned_end = mmc_block_address & end; + +	/* all block aligned accesses */ +	debug +	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +	     src, (ulong) dst, end, part_start, part_end, aligned_start, +	     aligned_end); +	if (part_start) { +		part_len = mmc_block_size - part_start; +		debug +		    ("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +		     (ulong) src, dst, end, part_start, part_end, aligned_start, +		     aligned_end); +		if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < +		    0) { +			return -1; +		} +		memcpy(mmc_buf + part_start, src, part_len); +		if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < +		    0) { +			return -1; +		} +		dst += part_len; +		src += part_len; +	} +	debug +	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +	     src, (ulong) dst, end, part_start, part_end, aligned_start, +	     aligned_end); +	for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) { +		debug +		    ("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +		     src, (ulong) dst, end, part_start, part_end, aligned_start, +		     aligned_end); +		if ((mmc_block_write(dst, (uchar *) src, mmc_block_size)) < 0) { +			return -1; +		} +	} +	debug +	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +	     src, (ulong) dst, end, part_start, part_end, aligned_start, +	     aligned_end); +	if (part_end && dst < end) { +		debug +		    ("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", +		     src, (ulong) dst, end, part_start, part_end, aligned_start, +		     aligned_end); +		if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { +			return -1; +		} +		memcpy(mmc_buf, src, part_end); +		if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0) { +			return -1; +		} +	} +	return 0; +} + +static ulong +/****************************************************/ +mmc_bread(int dev_num, ulong blknr, lbaint_t blkcnt, void *dst) +/****************************************************/ +{ +	int mmc_block_size = MMC_BLOCK_SIZE; +	ulong src = blknr * mmc_block_size + CONFIG_SYS_MMC_BASE; + +	pxa_mmc_read(src, (uchar *) dst, blkcnt * mmc_block_size); +	return blkcnt; +} + +#ifdef __GNUC__ +#define likely(x)       __builtin_expect(!!(x), 1) +#define unlikely(x)     __builtin_expect(!!(x), 0) +#else +#define likely(x)	(x) +#define unlikely(x)	(x) +#endif + +#define UNSTUFF_BITS(resp,start,size)					\ +	({								\ +		const int __size = size;				\ +		const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1;	\ +		const int32_t __off = 3 - ((start) / 32);			\ +		const int32_t __shft = (start) & 31;			\ +		uint32_t __res;						\ +									\ +		__res = resp[__off] >> __shft;				\ +		if (__size + __shft > 32)				\ +			__res |= resp[__off-1] << ((32 - __shft) % 32);	\ +		__res & __mask;						\ +	}) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(uint32_t * resp) +{ +	if (IF_TYPE_SD == mmc_dev.if_type) { +		/* +		 * SD doesn't currently have a version field so we will +		 * have to assume we can parse this. +		 */ +		sprintf((char *)mmc_dev.vendor, +			"Man %02x OEM %c%c \"%c%c%c%c%c\" Date %02u/%04u", +			UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, 112, 8), +			UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, 96, 8), +			UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, 80, 8), +			UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, 64, 8), +			UNSTUFF_BITS(resp, 8, 4), UNSTUFF_BITS(resp, 12, +							       8) + 2000); +		sprintf((char *)mmc_dev.revision, "%d.%d", +			UNSTUFF_BITS(resp, 60, 4), UNSTUFF_BITS(resp, 56, 4)); +		sprintf((char *)mmc_dev.product, "%u", +			UNSTUFF_BITS(resp, 24, 32)); +	} else { +		/* +		 * The selection of the format here is based upon published +		 * specs from sandisk and from what people have reported. +		 */ +		switch (spec_ver) { +		case 0:	/* MMC v1.0 - v1.2 */ +		case 1:	/* MMC v1.4 */ +			sprintf((char *)mmc_dev.vendor, +				"Man %02x%02x%02x \"%c%c%c%c%c%c%c\" Date %02u/%04u", +				UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, +									 112, +									 8), +				UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, +									 96, 8), +				UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, +									80, 8), +				UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, +									64, 8), +				UNSTUFF_BITS(resp, 56, 8), UNSTUFF_BITS(resp, +									48, 8), +				UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8, +									4) + +				1997); +			sprintf((char *)mmc_dev.revision, "%d.%d", +				UNSTUFF_BITS(resp, 44, 4), UNSTUFF_BITS(resp, +									40, 4)); +			sprintf((char *)mmc_dev.product, "%u", +				UNSTUFF_BITS(resp, 16, 24)); +			break; + +		case 2:	/* MMC v2.0 - v2.2 */ +		case 3:	/* MMC v3.1 - v3.3 */ +		case 4:	/* MMC v4 */ +			sprintf((char *)mmc_dev.vendor, +				"Man %02x OEM %04x \"%c%c%c%c%c%c\" Date %02u/%04u", +				UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, +									 104, +									 16), +				UNSTUFF_BITS(resp, 96, 8), UNSTUFF_BITS(resp, +									88, 8), +				UNSTUFF_BITS(resp, 80, 8), UNSTUFF_BITS(resp, +									72, 8), +				UNSTUFF_BITS(resp, 64, 8), UNSTUFF_BITS(resp, +									56, 8), +				UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8, +									4) + +				1997); +			sprintf((char *)mmc_dev.product, "%u", +				UNSTUFF_BITS(resp, 16, 32)); +			sprintf((char *)mmc_dev.revision, "N/A"); +			break; + +		default: +			printf("MMC card has unknown MMCA version %d\n", +			       spec_ver); +			break; +		} +	} +	printf("%s card.\nVendor: %s\nProduct: %s\nRevision: %s\n", +	       (IF_TYPE_SD == mmc_dev.if_type) ? "SD" : "MMC", mmc_dev.vendor, +	       mmc_dev.product, mmc_dev.revision); +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(uint32_t * resp) +{ +	unsigned int mult, csd_struct; + +	if (IF_TYPE_SD == mmc_dev.if_type) { +		csd_struct = UNSTUFF_BITS(resp, 126, 2); +		if (csd_struct != 0) { +			printf("SD: unrecognised CSD structure version %d\n", +			       csd_struct); +			return; +		} +	} else { +		/* +		 * We only understand CSD structure v1.1 and v1.2. +		 * v1.2 has extra information in bits 15, 11 and 10. +		 */ +		csd_struct = UNSTUFF_BITS(resp, 126, 2); +		if (csd_struct != 1 && csd_struct != 2) { +			printf("MMC: unrecognised CSD structure version %d\n", +			       csd_struct); +			return; +		} + +		spec_ver = UNSTUFF_BITS(resp, 122, 4); +		mmc_dev.if_type = IF_TYPE_MMC; +	} + +	mult = 1 << (UNSTUFF_BITS(resp, 47, 3) + 2); +	mmc_dev.lba = (1 + UNSTUFF_BITS(resp, 62, 12)) * mult; +	mmc_dev.blksz = 1 << UNSTUFF_BITS(resp, 80, 4); + +	/* FIXME: The following just makes assumes that's the partition type -- should really read it */ +	mmc_dev.part_type = PART_TYPE_DOS; +	mmc_dev.dev = 0; +	mmc_dev.lun = 0; +	mmc_dev.type = DEV_TYPE_HARDDISK; +	mmc_dev.removable = 0; +	mmc_dev.block_read = mmc_bread; + +	printf("Detected: %lu blocks of %lu bytes (%luMB) ", +		mmc_dev.lba, +		mmc_dev.blksz, +		mmc_dev.lba * mmc_dev.blksz / (1024 * 1024)); +} + +int +/****************************************************/ +mmc_legacy_init(int verbose) +/****************************************************/ +{ +	int retries, rc = -ENODEV; +	uint32_t cid_resp[4]; +	uint32_t *resp; +	uint16_t rca = 0; + +	/* Reset device interface type */ +	mmc_dev.if_type = IF_TYPE_UNKNOWN; + +#if defined (CONFIG_LUBBOCK) || (defined (CONFIG_GUMSTIX) && !defined(CONFIG_PXA27X)) +	set_GPIO_mode(GPIO6_MMCCLK_MD); +	set_GPIO_mode(GPIO8_MMCCS0_MD); +#endif +	CKEN |= CKEN12_MMC;	/* enable MMC unit clock */ + +	MMC_CLKRT = MMC_CLKRT_0_3125MHZ; +	MMC_RESTO = MMC_RES_TO_MAX; +	MMC_SPI = MMC_SPI_DISABLE; + +	/* reset */ +	mmc_cmd(MMC_CMD_GO_IDLE_STATE, 0, 0, MMC_CMDAT_INIT | MMC_CMDAT_R0); +	udelay(200000); +	retries = 3; +	while (retries--) { +		resp = mmc_cmd(MMC_CMD_APP_CMD, 0, 0, MMC_CMDAT_R1); +		if (!(resp[0] & 0x00000020)) {	/* Card does not support APP_CMD */ +			debug("Card does not support APP_CMD\n"); +			break; +		} + +		/* Select 3.2-3.3V and 3.3-3.4V */ +		resp = mmc_cmd(SD_CMD_APP_SEND_OP_COND, 0x0030, 0x0000, +				MMC_CMDAT_R3 | (retries < 2 ? 0 +					: MMC_CMDAT_INIT)); +		if (resp[0] & 0x80000000) { +			mmc_dev.if_type = IF_TYPE_SD; +			debug("Detected SD card\n"); +			break; +		} +#ifdef CONFIG_PXA27X +		udelay(10000); +#else +		udelay(200000); +#endif +	} + +	if (retries <= 0 || !(IF_TYPE_SD == mmc_dev.if_type)) { +		debug("Failed to detect SD Card, trying MMC\n"); +		resp = +		    mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, MMC_CMDAT_R3); + +		retries = 10; +		while (retries-- && resp && !(resp[0] & 0x80000000)) { +#ifdef CONFIG_PXA27X +			udelay(10000); +#else +			udelay(200000); +#endif +			resp = +			    mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, +				    MMC_CMDAT_R3); +		} +	} + +	/* try to get card id */ +	resp = +	    mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, 0, MMC_CMDAT_R2 | MMC_CMDAT_BUSY); +	if (resp) { +		memcpy(cid_resp, resp, sizeof(cid_resp)); + +		/* MMC exists, get CSD too */ +		resp = mmc_cmd(MMC_CMD_SET_RELATIVE_ADDR, 0, 0, MMC_CMDAT_R1); +		if (IF_TYPE_SD == mmc_dev.if_type) +			rca = ((resp[0] & 0xffff0000) >> 16); +		resp = mmc_cmd(MMC_CMD_SEND_CSD, rca, 0, MMC_CMDAT_R2); +		if (resp) { +			mmc_decode_csd(resp); +			rc = 0; +			mmc_ready = 1; +		} + +		mmc_decode_cid(cid_resp); +	} + +	MMC_CLKRT = 0;		/* 20 MHz */ +	resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca, 0, MMC_CMDAT_R1); + +#ifdef CONFIG_PXA27X +	if (IF_TYPE_SD == mmc_dev.if_type) { +		resp = mmc_cmd(MMC_CMD_APP_CMD, rca, 0, MMC_CMDAT_R1); +		resp = mmc_cmd(SD_CMD_APP_SET_BUS_WIDTH, 0, 2, MMC_CMDAT_R1); +		wide = MMC_CMDAT_SD_4DAT; +	} +#endif + +	fat_register_device(&mmc_dev, 1);	/* partitions start counting with 1 */ + +	return rc; +} diff --git a/drivers/mmc/pxa_mmc.h b/drivers/mmc/pxa_mmc.h new file mode 100644 index 000000000..6fa42686e --- /dev/null +++ b/drivers/mmc/pxa_mmc.h @@ -0,0 +1,138 @@ +/* + *  linux/drivers/mmc/mmc_pxa.h + * + *  Author: Vladimir Shebordaev, Igor Oblakov + *  Copyright:  MontaVista Software Inc. + * + *  $Id: mmc_pxa.h,v 0.3.1.6 2002/09/25 19:25:48 ted Exp ted $ + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation. + */ +#ifndef __MMC_PXA_P_H__ +#define __MMC_PXA_P_H__ + +/* PXA-250 MMC controller registers */ + +/* MMC_STRPCL */ +#define MMC_STRPCL_STOP_CLK		(0x0001UL) +#define MMC_STRPCL_START_CLK		(0x0002UL) + +/* MMC_STAT */ +#define MMC_STAT_END_CMD_RES		(0x0001UL << 13) +#define MMC_STAT_PRG_DONE		(0x0001UL << 12) +#define MMC_STAT_DATA_TRAN_DONE		(0x0001UL << 11) +#define MMC_STAT_CLK_EN			(0x0001UL << 8) +#define MMC_STAT_RECV_FIFO_FULL		(0x0001UL << 7) +#define MMC_STAT_XMIT_FIFO_EMPTY	(0x0001UL << 6) +#define MMC_STAT_RES_CRC_ERROR		(0x0001UL << 5) +#define MMC_STAT_SPI_READ_ERROR_TOKEN   (0x0001UL << 4) +#define MMC_STAT_CRC_READ_ERROR		(0x0001UL << 3) +#define MMC_STAT_CRC_WRITE_ERROR	(0x0001UL << 2) +#define MMC_STAT_TIME_OUT_RESPONSE	(0x0001UL << 1) +#define MMC_STAT_READ_TIME_OUT		(0x0001UL) + +#define MMC_STAT_ERRORS (MMC_STAT_RES_CRC_ERROR|MMC_STAT_SPI_READ_ERROR_TOKEN\ +	|MMC_STAT_CRC_READ_ERROR|MMC_STAT_TIME_OUT_RESPONSE\ +	|MMC_STAT_READ_TIME_OUT|MMC_STAT_CRC_WRITE_ERROR) + +/* MMC_CLKRT */ +#define MMC_CLKRT_20MHZ			(0x0000UL) +#define MMC_CLKRT_10MHZ			(0x0001UL) +#define MMC_CLKRT_5MHZ			(0x0002UL) +#define MMC_CLKRT_2_5MHZ		(0x0003UL) +#define MMC_CLKRT_1_25MHZ		(0x0004UL) +#define MMC_CLKRT_0_625MHZ		(0x0005UL) +#define MMC_CLKRT_0_3125MHZ		(0x0006UL) + +/* MMC_SPI */ +#define MMC_SPI_DISABLE			(0x00UL) +#define MMC_SPI_EN			(0x01UL) +#define MMC_SPI_CS_EN			(0x01UL << 2) +#define MMC_SPI_CS_ADDRESS		(0x01UL << 3) +#define MMC_SPI_CRC_ON			(0x01UL << 1) + +/* MMC_CMDAT */ +#define MMC_CMDAT_SD_4DAT		(0x0001UL << 8) +#define MMC_CMDAT_MMC_DMA_EN		(0x0001UL << 7) +#define MMC_CMDAT_INIT			(0x0001UL << 6) +#define MMC_CMDAT_BUSY			(0x0001UL << 5) +#define MMC_CMDAT_BCR			(0x0003UL << 5) +#define MMC_CMDAT_STREAM		(0x0001UL << 4) +#define MMC_CMDAT_BLOCK			(0x0000UL << 4) +#define MMC_CMDAT_WRITE			(0x0001UL << 3) +#define MMC_CMDAT_READ			(0x0000UL << 3) +#define MMC_CMDAT_DATA_EN		(0x0001UL << 2) +#define MMC_CMDAT_R0			(0) +#define MMC_CMDAT_R1			(0x0001UL) +#define MMC_CMDAT_R2			(0x0002UL) +#define MMC_CMDAT_R3			(0x0003UL) + +/* MMC_RESTO */ +#define MMC_RES_TO_MAX			(0x007fUL) /* [6:0] */ + +/* MMC_RDTO */ +#define MMC_READ_TO_MAX			(0x0ffffUL) /* [15:0] */ + +/* MMC_BLKLEN */ +#define MMC_BLK_LEN_MAX			(0x03ffUL) /* [9:0] */ + +/* MMC_PRTBUF */ +#define MMC_PRTBUF_BUF_PART_FULL	(0x01UL) +#define MMC_PRTBUF_BUF_FULL		(0x00UL    ) + +/* MMC_I_MASK */ +#define MMC_I_MASK_TXFIFO_WR_REQ	(0x01UL << 6) +#define MMC_I_MASK_RXFIFO_RD_REQ	(0x01UL << 5) +#define MMC_I_MASK_CLK_IS_OFF		(0x01UL << 4) +#define MMC_I_MASK_STOP_CMD		(0x01UL << 3) +#define MMC_I_MASK_END_CMD_RES		(0x01UL << 2) +#define MMC_I_MASK_PRG_DONE		(0x01UL << 1) +#define MMC_I_MASK_DATA_TRAN_DONE       (0x01UL) +#define MMC_I_MASK_ALL			(0x07fUL) + + +/* MMC_I_REG */ +#define MMC_I_REG_TXFIFO_WR_REQ		(0x01UL << 6) +#define MMC_I_REG_RXFIFO_RD_REQ		(0x01UL << 5) +#define MMC_I_REG_CLK_IS_OFF		(0x01UL << 4) +#define MMC_I_REG_STOP_CMD		(0x01UL << 3) +#define MMC_I_REG_END_CMD_RES		(0x01UL << 2) +#define MMC_I_REG_PRG_DONE		(0x01UL << 1) +#define MMC_I_REG_DATA_TRAN_DONE	(0x01UL) +#define MMC_I_REG_ALL			(0x007fUL) + +/* MMC_CMD */ +#define MMC_CMD_INDEX_MAX		(0x006fUL)  /* [5:0] */ +#define CMD(x)  (x) + +#define MMC_DEFAULT_RCA			1 + +#define MMC_BLOCK_SIZE			512 +#define MMC_MAX_BLOCK_SIZE		512 + +#define MMC_R1_IDLE_STATE		0x01 +#define MMC_R1_ERASE_STATE		0x02 +#define MMC_R1_ILLEGAL_CMD		0x04 +#define MMC_R1_COM_CRC_ERR		0x08 +#define MMC_R1_ERASE_SEQ_ERR		0x01 +#define MMC_R1_ADDR_ERR			0x02 +#define MMC_R1_PARAM_ERR		0x04 + +#define MMC_R1B_WP_ERASE_SKIP		0x0002 +#define MMC_R1B_ERR			0x0004 +#define MMC_R1B_CC_ERR			0x0008 +#define MMC_R1B_CARD_ECC_ERR		0x0010 +#define MMC_R1B_WP_VIOLATION		0x0020 +#define MMC_R1B_ERASE_PARAM		0x0040 +#define MMC_R1B_OOR			0x0080 +#define MMC_R1B_IDLE_STATE		0x0100 +#define MMC_R1B_ERASE_RESET		0x0200 +#define MMC_R1B_ILLEGAL_CMD		0x0400 +#define MMC_R1B_COM_CRC_ERR		0x0800 +#define MMC_R1B_ERASE_SEQ_ERR		0x1000 +#define MMC_R1B_ADDR_ERR		0x2000 +#define MMC_R1B_PARAM_ERR		0x4000 + +#endif /* __MMC_PXA_P_H__ */  |