mmc: mmc_spi: Re-write driver using DM framework
This patch rewrites MMC SPI driver using U-Boot DM framework and get it's working on SiFive Unleashed board. Signed-off-by: Bhargav Shah <bhargavshah1988@gmail.com> Signed-off-by: Anup Patel <anup.patel@wdc.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Tested-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
		
							parent
							
								
									f49ff79935
								
							
						
					
					
						commit
						05e35d4297
					
				|  | @ -46,6 +46,24 @@ config SPL_DM_MMC | ||||||
| 
 | 
 | ||||||
| if MMC | if MMC | ||||||
| 
 | 
 | ||||||
|  | config MMC_SPI | ||||||
|  | 	bool "Support for SPI-based MMC controller" | ||||||
|  | 	depends on DM_MMC && DM_SPI | ||||||
|  | 	help | ||||||
|  | 	  This selects SPI-based MMC controllers. | ||||||
|  | 	  If you have an MMC controller on a SPI bus, say Y here. | ||||||
|  | 
 | ||||||
|  | 	  If unsure, say N. | ||||||
|  | 
 | ||||||
|  | config MMC_SPI_CRC_ON | ||||||
|  | 	bool "Support CRC for SPI-based MMC controller" | ||||||
|  | 	depends on MMC_SPI | ||||||
|  | 	default y | ||||||
|  | 	help | ||||||
|  | 	  This enables CRC for SPI-based MMC controllers. | ||||||
|  | 
 | ||||||
|  | 	  If unsure, say N. | ||||||
|  | 
 | ||||||
| config ARM_PL180_MMCI | config ARM_PL180_MMCI | ||||||
| 	bool "ARM AMBA Multimedia Card Interface and compatible support" | 	bool "ARM AMBA Multimedia Card Interface and compatible support" | ||||||
| 	depends on DM_MMC && OF_CONTROL | 	depends on DM_MMC && OF_CONTROL | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ | ||||||
|  * generic mmc spi driver |  * generic mmc spi driver | ||||||
|  * |  * | ||||||
|  * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> |  * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> | ||||||
|  |  * Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com> | ||||||
|  |  * | ||||||
|  * Licensed under the GPL-2 or later. |  * Licensed under the GPL-2 or later. | ||||||
|  */ |  */ | ||||||
| #include <common.h> | #include <common.h> | ||||||
|  | @ -9,21 +11,23 @@ | ||||||
| #include <malloc.h> | #include <malloc.h> | ||||||
| #include <part.h> | #include <part.h> | ||||||
| #include <mmc.h> | #include <mmc.h> | ||||||
| #include <spi.h> | #include <stdlib.h> | ||||||
| #include <u-boot/crc.h> | #include <u-boot/crc.h> | ||||||
| #include <linux/crc7.h> | #include <linux/crc7.h> | ||||||
| #include <asm/byteorder.h> | #include <asm/byteorder.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <spi.h> | ||||||
| 
 | 
 | ||||||
| /* MMC/SD in SPI mode reports R1 status always */ | /* MMC/SD in SPI mode reports R1 status always */ | ||||||
| #define R1_SPI_IDLE		(1 << 0) | #define R1_SPI_IDLE			BIT(0) | ||||||
| #define R1_SPI_ERASE_RESET	(1 << 1) | #define R1_SPI_ERASE_RESET		BIT(1) | ||||||
| #define R1_SPI_ILLEGAL_COMMAND	(1 << 2) | #define R1_SPI_ILLEGAL_COMMAND		BIT(2) | ||||||
| #define R1_SPI_COM_CRC		(1 << 3) | #define R1_SPI_COM_CRC			BIT(3) | ||||||
| #define R1_SPI_ERASE_SEQ	(1 << 4) | #define R1_SPI_ERASE_SEQ		BIT(4) | ||||||
| #define R1_SPI_ADDRESS		(1 << 5) | #define R1_SPI_ADDRESS			BIT(5) | ||||||
| #define R1_SPI_PARAMETER	(1 << 6) | #define R1_SPI_PARAMETER		BIT(6) | ||||||
| /* R1 bit 7 is always zero, reuse this bit for error */ | /* R1 bit 7 is always zero, reuse this bit for error */ | ||||||
| #define R1_SPI_ERROR		(1 << 7) | #define R1_SPI_ERROR			BIT(7) | ||||||
| 
 | 
 | ||||||
| /* Response tokens used to ack each block written: */ | /* Response tokens used to ack each block written: */ | ||||||
| #define SPI_MMC_RESPONSE_CODE(x)	((x) & 0x1f) | #define SPI_MMC_RESPONSE_CODE(x)	((x) & 0x1f) | ||||||
|  | @ -34,28 +38,45 @@ | ||||||
| /* Read and write blocks start with these tokens and end with crc;
 | /* Read and write blocks start with these tokens and end with crc;
 | ||||||
|  * on error, read tokens act like a subset of R2_SPI_* values. |  * on error, read tokens act like a subset of R2_SPI_* values. | ||||||
|  */ |  */ | ||||||
| #define SPI_TOKEN_SINGLE	0xfe	/* single block r/w, multiblock read */ | /* single block write multiblock read */ | ||||||
| #define SPI_TOKEN_MULTI_WRITE	0xfc	/* multiblock write */ | #define SPI_TOKEN_SINGLE		0xfe | ||||||
| #define SPI_TOKEN_STOP_TRAN	0xfd	/* terminate multiblock write */ | /* multiblock write */ | ||||||
|  | #define SPI_TOKEN_MULTI_WRITE		0xfc | ||||||
|  | /* terminate multiblock write */ | ||||||
|  | #define SPI_TOKEN_STOP_TRAN		0xfd | ||||||
| 
 | 
 | ||||||
| /* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ | /* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ | ||||||
| #define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) | #define MMC_SPI_CMD(x) (0x40 | (x)) | ||||||
| 
 | 
 | ||||||
| /* bus capability */ | /* bus capability */ | ||||||
| #define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34) | #define MMC_SPI_VOLTAGE			(MMC_VDD_32_33 | MMC_VDD_33_34) | ||||||
| #define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */ | #define MMC_SPI_MIN_CLOCK		400000	/* 400KHz to meet MMC spec */ | ||||||
|  | #define MMC_SPI_MAX_CLOCK		25000000 /* SD/MMC legacy speed */ | ||||||
| 
 | 
 | ||||||
| /* timeout value */ | /* timeout value */ | ||||||
| #define CTOUT 8 | #define CMD_TIMEOUT			8 | ||||||
| #define RTOUT 3000000 /* 1 sec */ | #define READ_TIMEOUT			3000000 /* 1 sec */ | ||||||
| #define WTOUT 3000000 /* 1 sec */ | #define WRITE_TIMEOUT			3000000 /* 1 sec */ | ||||||
| 
 | 
 | ||||||
| static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) | struct mmc_spi_priv { | ||||||
|  | 	struct spi_slave *spi; | ||||||
|  | 	struct mmc_config cfg; | ||||||
|  | 	struct mmc mmc; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int mmc_spi_sendcmd(struct udevice *dev, | ||||||
|  | 			   ushort cmdidx, u32 cmdarg, u32 resp_type, | ||||||
|  | 			   u8 *resp, u32 resp_size, | ||||||
|  | 			   bool resp_match, u8 resp_match_value) | ||||||
| { | { | ||||||
| 	struct spi_slave *spi = mmc->priv; | 	int i, rpos = 0, ret = 0; | ||||||
| 	u8 cmdo[7]; | 	u8 cmdo[7], r; | ||||||
| 	u8 r1; | 
 | ||||||
| 	int i; | 	debug("%s: cmd%d cmdarg=0x%x resp_type=0x%x " | ||||||
|  | 	      "resp_size=%d resp_match=%d resp_match_value=0x%x\n", | ||||||
|  | 	      __func__, cmdidx, cmdarg, resp_type, | ||||||
|  | 	      resp_size, resp_match, resp_match_value); | ||||||
|  | 
 | ||||||
| 	cmdo[0] = 0xff; | 	cmdo[0] = 0xff; | ||||||
| 	cmdo[1] = MMC_SPI_CMD(cmdidx); | 	cmdo[1] = MMC_SPI_CMD(cmdidx); | ||||||
| 	cmdo[2] = cmdarg >> 24; | 	cmdo[2] = cmdarg >> 24; | ||||||
|  | @ -63,37 +84,79 @@ static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) | ||||||
| 	cmdo[4] = cmdarg >> 8; | 	cmdo[4] = cmdarg >> 8; | ||||||
| 	cmdo[5] = cmdarg; | 	cmdo[5] = cmdarg; | ||||||
| 	cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; | 	cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; | ||||||
| 	spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); | 	ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, 0); | ||||||
| 	for (i = 0; i < CTOUT; i++) { | 	if (ret) | ||||||
| 		spi_xfer(spi, 1 * 8, NULL, &r1, 0); | 		return ret; | ||||||
| 		if (i && (r1 & 0x80) == 0) /* r1 response */ |  | ||||||
| 			break; |  | ||||||
| 	} |  | ||||||
| 	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); |  | ||||||
| 	return r1; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, | 	ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0); | ||||||
| 				u32 bcnt, u32 bsize) | 	if (ret) | ||||||
| { | 		return ret; | ||||||
| 	struct spi_slave *spi = mmc->priv; | 
 | ||||||
| 	u8 *buf = xbuf; | 	if (!resp || !resp_size) | ||||||
| 	u8 r1; | 		return 0; | ||||||
| 	u16 crc; | 
 | ||||||
| 	int i; | 	debug("%s: cmd%d", __func__, cmdidx); | ||||||
| 	while (bcnt--) { | 
 | ||||||
| 		for (i = 0; i < RTOUT; i++) { | 	if (resp_match) { | ||||||
| 			spi_xfer(spi, 1 * 8, NULL, &r1, 0); | 		r = ~resp_match_value; | ||||||
| 			if (r1 != 0xff) /* data token */ | 		i = CMD_TIMEOUT; | ||||||
|  | 		while (i--) { | ||||||
|  | 			ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0); | ||||||
|  | 			if (ret) | ||||||
|  | 				return ret; | ||||||
|  | 			debug(" resp%d=0x%x", rpos, r); | ||||||
|  | 			rpos++; | ||||||
|  | 			if (r == resp_match_value) | ||||||
| 				break; | 				break; | ||||||
| 		} | 		} | ||||||
| 		debug("%s:tok%d %x\n", __func__, i, r1); | 		if (!i && (r != resp_match_value)) | ||||||
|  | 			return -ETIMEDOUT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < resp_size; i++) { | ||||||
|  | 		if (i == 0 && resp_match) { | ||||||
|  | 			resp[i] = resp_match_value; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 		debug(" resp%d=0x%x", rpos, r); | ||||||
|  | 		rpos++; | ||||||
|  | 		resp[i] = r; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	debug("\n"); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mmc_spi_readdata(struct udevice *dev, | ||||||
|  | 			    void *xbuf, u32 bcnt, u32 bsize) | ||||||
|  | { | ||||||
|  | 	u16 crc; | ||||||
|  | 	u8 *buf = xbuf, r1; | ||||||
|  | 	int i, ret = 0; | ||||||
|  | 
 | ||||||
|  | 	while (bcnt--) { | ||||||
|  | 		for (i = 0; i < READ_TIMEOUT; i++) { | ||||||
|  | 			ret = dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||||
|  | 			if (ret) | ||||||
|  | 				return ret; | ||||||
|  | 			if (r1 == SPI_TOKEN_SINGLE) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 		debug("%s: data tok%d 0x%x\n", __func__, i, r1); | ||||||
| 		if (r1 == SPI_TOKEN_SINGLE) { | 		if (r1 == SPI_TOKEN_SINGLE) { | ||||||
| 			spi_xfer(spi, bsize * 8, NULL, buf, 0); | 			ret = dm_spi_xfer(dev, bsize * 8, NULL, buf, 0); | ||||||
| 			spi_xfer(spi, 2 * 8, NULL, &crc, 0); | 			if (ret) | ||||||
|  | 				return ret; | ||||||
|  | 			ret = dm_spi_xfer(dev, 2 * 8, NULL, &crc, 0); | ||||||
|  | 			if (ret) | ||||||
|  | 				return ret; | ||||||
| #ifdef CONFIG_MMC_SPI_CRC_ON | #ifdef CONFIG_MMC_SPI_CRC_ON | ||||||
| 			if (be_to_cpu16(crc16_ccitt(0, buf, bsize)) != crc) { | 			if (be16_to_cpu(crc16_ccitt(0, buf, bsize)) != crc) { | ||||||
| 				debug("%s: CRC error\n", mmc->cfg->name); | 				debug("%s: data crc error\n", __func__); | ||||||
| 				r1 = R1_SPI_COM_CRC; | 				r1 = R1_SPI_COM_CRC; | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
|  | @ -105,48 +168,56 @@ static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, | ||||||
| 		} | 		} | ||||||
| 		buf += bsize; | 		buf += bsize; | ||||||
| 	} | 	} | ||||||
| 	return r1; | 
 | ||||||
|  | 	if (r1 & R1_SPI_COM_CRC) | ||||||
|  | 		ret = -ECOMM; | ||||||
|  | 	else if (r1) /* other errors */ | ||||||
|  | 		ret = -ETIMEDOUT; | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, | static int mmc_spi_writedata(struct udevice *dev, const void *xbuf, | ||||||
| 			      u32 bcnt, u32 bsize, int multi) | 			     u32 bcnt, u32 bsize, int multi) | ||||||
| { | { | ||||||
| 	struct spi_slave *spi = mmc->priv; |  | ||||||
| 	const u8 *buf = xbuf; | 	const u8 *buf = xbuf; | ||||||
| 	u8 r1; | 	u8 r1, tok[2]; | ||||||
| 	u16 crc; | 	u16 crc; | ||||||
| 	u8 tok[2]; | 	int i, ret = 0; | ||||||
| 	int i; | 
 | ||||||
| 	tok[0] = 0xff; | 	tok[0] = 0xff; | ||||||
| 	tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; | 	tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; | ||||||
|  | 
 | ||||||
| 	while (bcnt--) { | 	while (bcnt--) { | ||||||
| #ifdef CONFIG_MMC_SPI_CRC_ON | #ifdef CONFIG_MMC_SPI_CRC_ON | ||||||
| 		crc = cpu_to_be16(crc16_ccitt(0, (u8 *)buf, bsize)); | 		crc = cpu_to_be16(crc16_ccitt(0, (u8 *)buf, bsize)); | ||||||
| #endif | #endif | ||||||
| 		spi_xfer(spi, 2 * 8, tok, NULL, 0); | 		dm_spi_xfer(dev, 2 * 8, tok, NULL, 0); | ||||||
| 		spi_xfer(spi, bsize * 8, buf, NULL, 0); | 		dm_spi_xfer(dev, bsize * 8, buf, NULL, 0); | ||||||
| 		spi_xfer(spi, 2 * 8, &crc, NULL, 0); | 		dm_spi_xfer(dev, 2 * 8, &crc, NULL, 0); | ||||||
| 		for (i = 0; i < CTOUT; i++) { | 		for (i = 0; i < CMD_TIMEOUT; i++) { | ||||||
| 			spi_xfer(spi, 1 * 8, NULL, &r1, 0); | 			dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||||
| 			if ((r1 & 0x10) == 0) /* response token */ | 			if ((r1 & 0x10) == 0) /* response token */ | ||||||
| 				break; | 				break; | ||||||
| 		} | 		} | ||||||
| 		debug("%s:tok%d %x\n", __func__, i, r1); | 		debug("%s: data tok%d 0x%x\n", __func__, i, r1); | ||||||
| 		if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { | 		if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { | ||||||
| 			for (i = 0; i < WTOUT; i++) { /* wait busy */ | 			debug("%s: data accepted\n", __func__); | ||||||
| 				spi_xfer(spi, 1 * 8, NULL, &r1, 0); | 			for (i = 0; i < WRITE_TIMEOUT; i++) { /* wait busy */ | ||||||
|  | 				dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||||
| 				if (i && r1 == 0xff) { | 				if (i && r1 == 0xff) { | ||||||
| 					r1 = 0; | 					r1 = 0; | ||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			if (i == WTOUT) { | 			if (i == WRITE_TIMEOUT) { | ||||||
| 				debug("%s:wtout %x\n", __func__, r1); | 				debug("%s: data write timeout 0x%x\n", | ||||||
|  | 				      __func__, r1); | ||||||
| 				r1 = R1_SPI_ERROR; | 				r1 = R1_SPI_ERROR; | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			debug("%s: err %x\n", __func__, r1); | 			debug("%s: data error 0x%x\n", __func__, r1); | ||||||
| 			r1 = R1_SPI_COM_CRC; | 			r1 = R1_SPI_COM_CRC; | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  | @ -154,140 +225,204 @@ static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, | ||||||
| 	} | 	} | ||||||
| 	if (multi && bcnt == -1) { /* stop multi write */ | 	if (multi && bcnt == -1) { /* stop multi write */ | ||||||
| 		tok[1] = SPI_TOKEN_STOP_TRAN; | 		tok[1] = SPI_TOKEN_STOP_TRAN; | ||||||
| 		spi_xfer(spi, 2 * 8, tok, NULL, 0); | 		dm_spi_xfer(dev, 2 * 8, tok, NULL, 0); | ||||||
| 		for (i = 0; i < WTOUT; i++) { /* wait busy */ | 		for (i = 0; i < WRITE_TIMEOUT; i++) { /* wait busy */ | ||||||
| 			spi_xfer(spi, 1 * 8, NULL, &r1, 0); | 			dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||||
| 			if (i && r1 == 0xff) { | 			if (i && r1 == 0xff) { | ||||||
| 				r1 = 0; | 				r1 = 0; | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if (i == WTOUT) { | 		if (i == WRITE_TIMEOUT) { | ||||||
| 			debug("%s:wstop %x\n", __func__, r1); | 			debug("%s: data write timeout 0x%x\n", __func__, r1); | ||||||
| 			r1 = R1_SPI_ERROR; | 			r1 = R1_SPI_ERROR; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return r1; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, | 	if (r1 & R1_SPI_COM_CRC) | ||||||
| 		struct mmc_data *data) |  | ||||||
| { |  | ||||||
| 	struct spi_slave *spi = mmc->priv; |  | ||||||
| 	u8 r1; |  | ||||||
| 	int i; |  | ||||||
| 	int ret = 0; |  | ||||||
| 	debug("%s:cmd%d %x %x\n", __func__, |  | ||||||
| 	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg); |  | ||||||
| 	spi_claim_bus(spi); |  | ||||||
| 	spi_cs_activate(spi); |  | ||||||
| 	r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); |  | ||||||
| 	if (r1 == 0xff) { /* no response */ |  | ||||||
| 		ret = -ENOMEDIUM; |  | ||||||
| 		goto done; |  | ||||||
| 	} else if (r1 & R1_SPI_COM_CRC) { |  | ||||||
| 		ret = -ECOMM; | 		ret = -ECOMM; | ||||||
| 		goto done; | 	else if (r1) /* other errors */ | ||||||
| 	} else if (r1 & ~R1_SPI_IDLE) { /* other errors */ |  | ||||||
| 		ret = -ETIMEDOUT; | 		ret = -ETIMEDOUT; | ||||||
| 		goto done; | 
 | ||||||
| 	} else if (cmd->resp_type == MMC_RSP_R2) { |  | ||||||
| 		r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16); |  | ||||||
| 		for (i = 0; i < 4; i++) |  | ||||||
| 			cmd->response[i] = be32_to_cpu(cmd->response[i]); |  | ||||||
| 		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1], |  | ||||||
| 		      cmd->response[2], cmd->response[3]); |  | ||||||
| 	} else if (!data) { |  | ||||||
| 		switch (cmd->cmdidx) { |  | ||||||
| 		case SD_CMD_APP_SEND_OP_COND: |  | ||||||
| 		case MMC_CMD_SEND_OP_COND: |  | ||||||
| 			cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY; |  | ||||||
| 			break; |  | ||||||
| 		case SD_CMD_SEND_IF_COND: |  | ||||||
| 		case MMC_CMD_SPI_READ_OCR: |  | ||||||
| 			spi_xfer(spi, 4 * 8, NULL, cmd->response, 0); |  | ||||||
| 			cmd->response[0] = be32_to_cpu(cmd->response[0]); |  | ||||||
| 			debug("r32 %x\n", cmd->response[0]); |  | ||||||
| 			break; |  | ||||||
| 		case MMC_CMD_SEND_STATUS: |  | ||||||
| 			spi_xfer(spi, 1 * 8, NULL, cmd->response, 0); |  | ||||||
| 			cmd->response[0] = (cmd->response[0] & 0xff) ? |  | ||||||
| 				MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA; |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		debug("%s:data %x %x %x\n", __func__, |  | ||||||
| 		      data->flags, data->blocks, data->blocksize); |  | ||||||
| 		if (data->flags == MMC_DATA_READ) |  | ||||||
| 			r1 = mmc_spi_readdata(mmc, data->dest, |  | ||||||
| 				data->blocks, data->blocksize); |  | ||||||
| 		else if  (data->flags == MMC_DATA_WRITE) |  | ||||||
| 			r1 = mmc_spi_writedata(mmc, data->src, |  | ||||||
| 				data->blocks, data->blocksize, |  | ||||||
| 				(cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)); |  | ||||||
| 		if (r1 & R1_SPI_COM_CRC) |  | ||||||
| 			ret = -ECOMM; |  | ||||||
| 		else if (r1) /* other errors */ |  | ||||||
| 			ret = -ETIMEDOUT; |  | ||||||
| 	} |  | ||||||
| done: |  | ||||||
| 	spi_cs_deactivate(spi); |  | ||||||
| 	spi_release_bus(spi); |  | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int mmc_spi_set_ios(struct mmc *mmc) | static int dm_mmc_spi_set_ios(struct udevice *dev) | ||||||
| { | { | ||||||
| 	struct spi_slave *spi = mmc->priv; |  | ||||||
| 
 |  | ||||||
| 	debug("%s: clock %u\n", __func__, mmc->clock); |  | ||||||
| 	if (mmc->clock) |  | ||||||
| 		spi_set_speed(spi, mmc->clock); |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int mmc_spi_init_p(struct mmc *mmc) | static int dm_mmc_spi_request(struct udevice *dev, struct mmc_cmd *cmd, | ||||||
|  | 			      struct mmc_data *data) | ||||||
| { | { | ||||||
| 	struct spi_slave *spi = mmc->priv; | 	int i, multi, ret = 0; | ||||||
| 	spi_set_speed(spi, MMC_SPI_MIN_CLOCK); | 	u8 *resp = NULL; | ||||||
| 	spi_claim_bus(spi); | 	u32 resp_size = 0; | ||||||
| 	/* cs deactivated for 100+ clock */ | 	bool resp_match = false; | ||||||
| 	spi_xfer(spi, 18 * 8, NULL, NULL, 0); | 	u8 resp8 = 0, resp40[5] = { 0 }, resp_match_value = 0; | ||||||
| 	spi_release_bus(spi); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static const struct mmc_ops mmc_spi_ops = { | 	dm_spi_claim_bus(dev); | ||||||
| 	.send_cmd	= mmc_spi_request, |  | ||||||
| 	.set_ios	= mmc_spi_set_ios, |  | ||||||
| 	.init		= mmc_spi_init_p, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| static struct mmc_config mmc_spi_cfg = { | 	for (i = 0; i < 4; i++) | ||||||
| 	.name		= "MMC_SPI", | 		cmd->response[i] = 0; | ||||||
| 	.ops		= &mmc_spi_ops, |  | ||||||
| 	.host_caps	= MMC_MODE_SPI, |  | ||||||
| 	.voltages	= MMC_SPI_VOLTAGE, |  | ||||||
| 	.f_min		= MMC_SPI_MIN_CLOCK, |  | ||||||
| 	.part_type	= PART_TYPE_DOS, |  | ||||||
| 	.b_max		= CONFIG_SYS_MMC_MAX_BLK_COUNT, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) | 	switch (cmd->cmdidx) { | ||||||
| { | 	case SD_CMD_APP_SEND_OP_COND: | ||||||
| 	struct mmc *mmc; | 	case MMC_CMD_SEND_OP_COND: | ||||||
| 	struct spi_slave *spi; | 		resp = &resp8; | ||||||
|  | 		resp_size = sizeof(resp8); | ||||||
|  | 		cmd->cmdarg = 0x40000000; | ||||||
|  | 		break; | ||||||
|  | 	case SD_CMD_SEND_IF_COND: | ||||||
|  | 		resp = (u8 *)&resp40[0]; | ||||||
|  | 		resp_size = sizeof(resp40); | ||||||
|  | 		resp_match = true; | ||||||
|  | 		resp_match_value = R1_SPI_IDLE; | ||||||
|  | 		break; | ||||||
|  | 	case MMC_CMD_SPI_READ_OCR: | ||||||
|  | 		resp = (u8 *)&resp40[0]; | ||||||
|  | 		resp_size = sizeof(resp40); | ||||||
|  | 		break; | ||||||
|  | 	case MMC_CMD_SEND_STATUS: | ||||||
|  | 	case MMC_CMD_SET_BLOCKLEN: | ||||||
|  | 	case MMC_CMD_SPI_CRC_ON_OFF: | ||||||
|  | 	case MMC_CMD_STOP_TRANSMISSION: | ||||||
|  | 		resp = &resp8; | ||||||
|  | 		resp_size = sizeof(resp8); | ||||||
|  | 		resp_match = true; | ||||||
|  | 		resp_match_value = 0x0; | ||||||
|  | 		break; | ||||||
|  | 	case MMC_CMD_SEND_CSD: | ||||||
|  | 	case MMC_CMD_SEND_CID: | ||||||
|  | 	case MMC_CMD_READ_SINGLE_BLOCK: | ||||||
|  | 	case MMC_CMD_READ_MULTIPLE_BLOCK: | ||||||
|  | 	case MMC_CMD_WRITE_SINGLE_BLOCK: | ||||||
|  | 	case MMC_CMD_WRITE_MULTIPLE_BLOCK: | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		resp = &resp8; | ||||||
|  | 		resp_size = sizeof(resp8); | ||||||
|  | 		resp_match = true; | ||||||
|  | 		resp_match_value = R1_SPI_IDLE; | ||||||
|  | 		break; | ||||||
|  | 	}; | ||||||
| 
 | 
 | ||||||
| 	spi = spi_setup_slave(bus, cs, speed, mode); | 	ret = mmc_spi_sendcmd(dev, cmd->cmdidx, cmd->cmdarg, cmd->resp_type, | ||||||
| 	if (spi == NULL) | 			      resp, resp_size, resp_match, resp_match_value); | ||||||
| 		return NULL; | 	if (ret) | ||||||
|  | 		goto done; | ||||||
| 
 | 
 | ||||||
| 	mmc_spi_cfg.f_max = speed; | 	switch (cmd->cmdidx) { | ||||||
| 
 | 	case SD_CMD_APP_SEND_OP_COND: | ||||||
| 	mmc = mmc_create(&mmc_spi_cfg, spi); | 	case MMC_CMD_SEND_OP_COND: | ||||||
| 	if (mmc == NULL) { | 		cmd->response[0] = (resp8 & R1_SPI_IDLE) ? 0 : OCR_BUSY; | ||||||
| 		spi_free_slave(spi); | 		break; | ||||||
| 		return NULL; | 	case SD_CMD_SEND_IF_COND: | ||||||
|  | 	case MMC_CMD_SPI_READ_OCR: | ||||||
|  | 		cmd->response[0] = resp40[4]; | ||||||
|  | 		cmd->response[0] |= (uint)resp40[3] << 8; | ||||||
|  | 		cmd->response[0] |= (uint)resp40[2] << 16; | ||||||
|  | 		cmd->response[0] |= (uint)resp40[1] << 24; | ||||||
|  | 		break; | ||||||
|  | 	case MMC_CMD_SEND_STATUS: | ||||||
|  | 		cmd->response[0] = (resp8 & 0xff) ? | ||||||
|  | 			MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA; | ||||||
|  | 		break; | ||||||
|  | 	case MMC_CMD_SEND_CID: | ||||||
|  | 	case MMC_CMD_SEND_CSD: | ||||||
|  | 		ret = mmc_spi_readdata(dev, cmd->response, 1, 16); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 		for (i = 0; i < 4; i++) | ||||||
|  | 			cmd->response[i] = | ||||||
|  | 				cpu_to_be32(cmd->response[i]); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		cmd->response[0] = resp8; | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
| 	return mmc; | 
 | ||||||
|  | 	debug("%s: cmd%d resp0=0x%x resp1=0x%x resp2=0x%x resp3=0x%x\n", | ||||||
|  | 	      __func__, cmd->cmdidx, cmd->response[0], cmd->response[1], | ||||||
|  | 	      cmd->response[2], cmd->response[3]); | ||||||
|  | 
 | ||||||
|  | 	if (data) { | ||||||
|  | 		debug("%s: data flags=0x%x blocks=%d block_size=%d\n", | ||||||
|  | 		      __func__, data->flags, data->blocks, data->blocksize); | ||||||
|  | 		multi = (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK); | ||||||
|  | 		if (data->flags == MMC_DATA_READ) | ||||||
|  | 			ret = mmc_spi_readdata(dev, data->dest, | ||||||
|  | 					       data->blocks, data->blocksize); | ||||||
|  | 		else if  (data->flags == MMC_DATA_WRITE) | ||||||
|  | 			ret = mmc_spi_writedata(dev, data->src, | ||||||
|  | 						data->blocks, data->blocksize, | ||||||
|  | 						multi); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | done: | ||||||
|  | 	dm_spi_release_bus(dev); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static int mmc_spi_probe(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	struct mmc_spi_priv *priv = dev_get_priv(dev); | ||||||
|  | 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); | ||||||
|  | 	char *name; | ||||||
|  | 
 | ||||||
|  | 	priv->spi = dev_get_parent_priv(dev); | ||||||
|  | 	if (!priv->spi->max_hz) | ||||||
|  | 		priv->spi->max_hz = MMC_SPI_MAX_CLOCK; | ||||||
|  | 	priv->spi->speed = 0; | ||||||
|  | 	priv->spi->mode = SPI_MODE_0; | ||||||
|  | 	priv->spi->wordlen = 8; | ||||||
|  | 
 | ||||||
|  | 	name = malloc(strlen(dev->parent->name) + strlen(dev->name) + 4); | ||||||
|  | 	if (!name) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	sprintf(name, "%s:%s", dev->parent->name, dev->name); | ||||||
|  | 
 | ||||||
|  | 	priv->cfg.name = name; | ||||||
|  | 	priv->cfg.host_caps = MMC_MODE_SPI; | ||||||
|  | 	priv->cfg.voltages = MMC_SPI_VOLTAGE; | ||||||
|  | 	priv->cfg.f_min = MMC_SPI_MIN_CLOCK; | ||||||
|  | 	priv->cfg.f_max = priv->spi->max_hz; | ||||||
|  | 	priv->cfg.part_type = PART_TYPE_DOS; | ||||||
|  | 	priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | ||||||
|  | 
 | ||||||
|  | 	priv->mmc.cfg = &priv->cfg; | ||||||
|  | 	priv->mmc.priv = priv; | ||||||
|  | 	priv->mmc.dev = dev; | ||||||
|  | 
 | ||||||
|  | 	upriv->mmc = &priv->mmc; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mmc_spi_bind(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	struct mmc_spi_priv *priv = dev_get_priv(dev); | ||||||
|  | 
 | ||||||
|  | 	return mmc_bind(dev, &priv->mmc, &priv->cfg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct dm_mmc_ops mmc_spi_ops = { | ||||||
|  | 	.send_cmd	= dm_mmc_spi_request, | ||||||
|  | 	.set_ios	= dm_mmc_spi_set_ios, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct udevice_id dm_mmc_spi_match[] = { | ||||||
|  | 	{ .compatible = "mmc-spi-slot" }, | ||||||
|  | 	{ /* sentinel */ } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(mmc_spi) = { | ||||||
|  | 	.name = "mmc_spi", | ||||||
|  | 	.id = UCLASS_MMC, | ||||||
|  | 	.of_match = dm_mmc_spi_match, | ||||||
|  | 	.ops = &mmc_spi_ops, | ||||||
|  | 	.probe = mmc_spi_probe, | ||||||
|  | 	.bind = mmc_spi_bind, | ||||||
|  | 	.priv_auto_alloc_size = sizeof(struct mmc_spi_priv), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -1165,12 +1165,6 @@ CONFIG_MMCBOOTCOMMAND | ||||||
| CONFIG_MMCROOT | CONFIG_MMCROOT | ||||||
| CONFIG_MMC_DEFAULT_DEV | CONFIG_MMC_DEFAULT_DEV | ||||||
| CONFIG_MMC_RPMB_TRACE | CONFIG_MMC_RPMB_TRACE | ||||||
| CONFIG_MMC_SPI |  | ||||||
| CONFIG_MMC_SPI_BUS |  | ||||||
| CONFIG_MMC_SPI_CRC_ON |  | ||||||
| CONFIG_MMC_SPI_CS |  | ||||||
| CONFIG_MMC_SPI_MODE |  | ||||||
| CONFIG_MMC_SPI_SPEED |  | ||||||
| CONFIG_MMC_SUNXI_SLOT | CONFIG_MMC_SUNXI_SLOT | ||||||
| CONFIG_MMU | CONFIG_MMU | ||||||
| CONFIG_MONITOR_IS_IN_RAM | CONFIG_MONITOR_IS_IN_RAM | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue