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 | ||||
| 
 | ||||
| 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 | ||||
| 	bool "ARM AMBA Multimedia Card Interface and compatible support" | ||||
| 	depends on DM_MMC && OF_CONTROL | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
|  * generic mmc spi driver | ||||
|  * | ||||
|  * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> | ||||
|  * Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com> | ||||
|  * | ||||
|  * Licensed under the GPL-2 or later. | ||||
|  */ | ||||
| #include <common.h> | ||||
|  | @ -9,21 +11,23 @@ | |||
| #include <malloc.h> | ||||
| #include <part.h> | ||||
| #include <mmc.h> | ||||
| #include <spi.h> | ||||
| #include <stdlib.h> | ||||
| #include <u-boot/crc.h> | ||||
| #include <linux/crc7.h> | ||||
| #include <asm/byteorder.h> | ||||
| #include <dm.h> | ||||
| #include <spi.h> | ||||
| 
 | ||||
| /* MMC/SD in SPI mode reports R1 status always */ | ||||
| #define R1_SPI_IDLE		(1 << 0) | ||||
| #define R1_SPI_ERASE_RESET	(1 << 1) | ||||
| #define R1_SPI_ILLEGAL_COMMAND	(1 << 2) | ||||
| #define R1_SPI_COM_CRC		(1 << 3) | ||||
| #define R1_SPI_ERASE_SEQ	(1 << 4) | ||||
| #define R1_SPI_ADDRESS		(1 << 5) | ||||
| #define R1_SPI_PARAMETER	(1 << 6) | ||||
| #define R1_SPI_IDLE			BIT(0) | ||||
| #define R1_SPI_ERASE_RESET		BIT(1) | ||||
| #define R1_SPI_ILLEGAL_COMMAND		BIT(2) | ||||
| #define R1_SPI_COM_CRC			BIT(3) | ||||
| #define R1_SPI_ERASE_SEQ		BIT(4) | ||||
| #define R1_SPI_ADDRESS			BIT(5) | ||||
| #define R1_SPI_PARAMETER		BIT(6) | ||||
| /* 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: */ | ||||
| #define SPI_MMC_RESPONSE_CODE(x)	((x) & 0x1f) | ||||
|  | @ -34,28 +38,45 @@ | |||
| /* Read and write blocks start with these tokens and end with crc;
 | ||||
|  * on error, read tokens act like a subset of R2_SPI_* values. | ||||
|  */ | ||||
| #define SPI_TOKEN_SINGLE	0xfe	/* single block r/w, multiblock read */ | ||||
| #define SPI_TOKEN_MULTI_WRITE	0xfc	/* multiblock write */ | ||||
| #define SPI_TOKEN_STOP_TRAN	0xfd	/* terminate multiblock write */ | ||||
| /* single block write multiblock read */ | ||||
| #define SPI_TOKEN_SINGLE		0xfe | ||||
| /* 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" */ | ||||
| #define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) | ||||
| #define MMC_SPI_CMD(x) (0x40 | (x)) | ||||
| 
 | ||||
| /* bus capability */ | ||||
| #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_VOLTAGE			(MMC_VDD_32_33 | MMC_VDD_33_34) | ||||
| #define MMC_SPI_MIN_CLOCK		400000	/* 400KHz to meet MMC spec */ | ||||
| #define MMC_SPI_MAX_CLOCK		25000000 /* SD/MMC legacy speed */ | ||||
| 
 | ||||
| /* timeout value */ | ||||
| #define CTOUT 8 | ||||
| #define RTOUT 3000000 /* 1 sec */ | ||||
| #define WTOUT 3000000 /* 1 sec */ | ||||
| #define CMD_TIMEOUT			8 | ||||
| #define READ_TIMEOUT			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; | ||||
| 	u8 cmdo[7]; | ||||
| 	u8 r1; | ||||
| 	int i; | ||||
| 	int i, rpos = 0, ret = 0; | ||||
| 	u8 cmdo[7], r; | ||||
| 
 | ||||
| 	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[1] = MMC_SPI_CMD(cmdidx); | ||||
| 	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[5] = cmdarg; | ||||
| 	cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; | ||||
| 	spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); | ||||
| 	for (i = 0; i < CTOUT; i++) { | ||||
| 		spi_xfer(spi, 1 * 8, NULL, &r1, 0); | ||||
| 		if (i && (r1 & 0x80) == 0) /* r1 response */ | ||||
| 			break; | ||||
| 	} | ||||
| 	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); | ||||
| 	return r1; | ||||
| } | ||||
| 	ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, 0); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, | ||||
| 				u32 bcnt, u32 bsize) | ||||
| { | ||||
| 	struct spi_slave *spi = mmc->priv; | ||||
| 	u8 *buf = xbuf; | ||||
| 	u8 r1; | ||||
| 	u16 crc; | ||||
| 	int i; | ||||
| 	while (bcnt--) { | ||||
| 		for (i = 0; i < RTOUT; i++) { | ||||
| 			spi_xfer(spi, 1 * 8, NULL, &r1, 0); | ||||
| 			if (r1 != 0xff) /* data token */ | ||||
| 	ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (!resp || !resp_size) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	debug("%s: cmd%d", __func__, cmdidx); | ||||
| 
 | ||||
| 	if (resp_match) { | ||||
| 		r = ~resp_match_value; | ||||
| 		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; | ||||
| 		} | ||||
| 		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) { | ||||
| 			spi_xfer(spi, bsize * 8, NULL, buf, 0); | ||||
| 			spi_xfer(spi, 2 * 8, NULL, &crc, 0); | ||||
| 			ret = dm_spi_xfer(dev, bsize * 8, NULL, buf, 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 | ||||
| 			if (be_to_cpu16(crc16_ccitt(0, buf, bsize)) != crc) { | ||||
| 				debug("%s: CRC error\n", mmc->cfg->name); | ||||
| 			if (be16_to_cpu(crc16_ccitt(0, buf, bsize)) != crc) { | ||||
| 				debug("%s: data crc error\n", __func__); | ||||
| 				r1 = R1_SPI_COM_CRC; | ||||
| 				break; | ||||
| 			} | ||||
|  | @ -105,48 +168,56 @@ static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, | |||
| 		} | ||||
| 		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, | ||||
| 			      u32 bcnt, u32 bsize, int multi) | ||||
| static int mmc_spi_writedata(struct udevice *dev, const void *xbuf, | ||||
| 			     u32 bcnt, u32 bsize, int multi) | ||||
| { | ||||
| 	struct spi_slave *spi = mmc->priv; | ||||
| 	const u8 *buf = xbuf; | ||||
| 	u8 r1; | ||||
| 	u8 r1, tok[2]; | ||||
| 	u16 crc; | ||||
| 	u8 tok[2]; | ||||
| 	int i; | ||||
| 	int i, ret = 0; | ||||
| 
 | ||||
| 	tok[0] = 0xff; | ||||
| 	tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; | ||||
| 
 | ||||
| 	while (bcnt--) { | ||||
| #ifdef CONFIG_MMC_SPI_CRC_ON | ||||
| 		crc = cpu_to_be16(crc16_ccitt(0, (u8 *)buf, bsize)); | ||||
| #endif | ||||
| 		spi_xfer(spi, 2 * 8, tok, NULL, 0); | ||||
| 		spi_xfer(spi, bsize * 8, buf, NULL, 0); | ||||
| 		spi_xfer(spi, 2 * 8, &crc, NULL, 0); | ||||
| 		for (i = 0; i < CTOUT; i++) { | ||||
| 			spi_xfer(spi, 1 * 8, NULL, &r1, 0); | ||||
| 		dm_spi_xfer(dev, 2 * 8, tok, NULL, 0); | ||||
| 		dm_spi_xfer(dev, bsize * 8, buf, NULL, 0); | ||||
| 		dm_spi_xfer(dev, 2 * 8, &crc, NULL, 0); | ||||
| 		for (i = 0; i < CMD_TIMEOUT; i++) { | ||||
| 			dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||
| 			if ((r1 & 0x10) == 0) /* response token */ | ||||
| 				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) { | ||||
| 			for (i = 0; i < WTOUT; i++) { /* wait busy */ | ||||
| 				spi_xfer(spi, 1 * 8, NULL, &r1, 0); | ||||
| 			debug("%s: data accepted\n", __func__); | ||||
| 			for (i = 0; i < WRITE_TIMEOUT; i++) { /* wait busy */ | ||||
| 				dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||
| 				if (i && r1 == 0xff) { | ||||
| 					r1 = 0; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (i == WTOUT) { | ||||
| 				debug("%s:wtout %x\n", __func__, r1); | ||||
| 			if (i == WRITE_TIMEOUT) { | ||||
| 				debug("%s: data write timeout 0x%x\n", | ||||
| 				      __func__, r1); | ||||
| 				r1 = R1_SPI_ERROR; | ||||
| 				break; | ||||
| 			} | ||||
| 		} else { | ||||
| 			debug("%s: err %x\n", __func__, r1); | ||||
| 			debug("%s: data error 0x%x\n", __func__, r1); | ||||
| 			r1 = R1_SPI_COM_CRC; | ||||
| 			break; | ||||
| 		} | ||||
|  | @ -154,140 +225,204 @@ static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, | |||
| 	} | ||||
| 	if (multi && bcnt == -1) { /* stop multi write */ | ||||
| 		tok[1] = SPI_TOKEN_STOP_TRAN; | ||||
| 		spi_xfer(spi, 2 * 8, tok, NULL, 0); | ||||
| 		for (i = 0; i < WTOUT; i++) { /* wait busy */ | ||||
| 			spi_xfer(spi, 1 * 8, NULL, &r1, 0); | ||||
| 		dm_spi_xfer(dev, 2 * 8, tok, NULL, 0); | ||||
| 		for (i = 0; i < WRITE_TIMEOUT; i++) { /* wait busy */ | ||||
| 			dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0); | ||||
| 			if (i && r1 == 0xff) { | ||||
| 				r1 = 0; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (i == WTOUT) { | ||||
| 			debug("%s:wstop %x\n", __func__, r1); | ||||
| 		if (i == WRITE_TIMEOUT) { | ||||
| 			debug("%s: data write timeout 0x%x\n", __func__, r1); | ||||
| 			r1 = R1_SPI_ERROR; | ||||
| 		} | ||||
| 	} | ||||
| 	return r1; | ||||
| } | ||||
| 
 | ||||
| static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, | ||||
| 		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) { | ||||
| 	if (r1 & R1_SPI_COM_CRC) | ||||
| 		ret = -ECOMM; | ||||
| 		goto done; | ||||
| 	} else if (r1 & ~R1_SPI_IDLE) { /* other errors */ | ||||
| 	else if (r1) /* other errors */ | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 	spi_set_speed(spi, MMC_SPI_MIN_CLOCK); | ||||
| 	spi_claim_bus(spi); | ||||
| 	/* cs deactivated for 100+ clock */ | ||||
| 	spi_xfer(spi, 18 * 8, NULL, NULL, 0); | ||||
| 	spi_release_bus(spi); | ||||
| 	return 0; | ||||
| } | ||||
| 	int i, multi, ret = 0; | ||||
| 	u8 *resp = NULL; | ||||
| 	u32 resp_size = 0; | ||||
| 	bool resp_match = false; | ||||
| 	u8 resp8 = 0, resp40[5] = { 0 }, resp_match_value = 0; | ||||
| 
 | ||||
| static const struct mmc_ops mmc_spi_ops = { | ||||
| 	.send_cmd	= mmc_spi_request, | ||||
| 	.set_ios	= mmc_spi_set_ios, | ||||
| 	.init		= mmc_spi_init_p, | ||||
| }; | ||||
| 	dm_spi_claim_bus(dev); | ||||
| 
 | ||||
| static struct mmc_config mmc_spi_cfg = { | ||||
| 	.name		= "MMC_SPI", | ||||
| 	.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, | ||||
| }; | ||||
| 	for (i = 0; i < 4; i++) | ||||
| 		cmd->response[i] = 0; | ||||
| 
 | ||||
| struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) | ||||
| { | ||||
| 	struct mmc *mmc; | ||||
| 	struct spi_slave *spi; | ||||
| 	switch (cmd->cmdidx) { | ||||
| 	case SD_CMD_APP_SEND_OP_COND: | ||||
| 	case MMC_CMD_SEND_OP_COND: | ||||
| 		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); | ||||
| 	if (spi == NULL) | ||||
| 		return NULL; | ||||
| 	ret = mmc_spi_sendcmd(dev, cmd->cmdidx, cmd->cmdarg, cmd->resp_type, | ||||
| 			      resp, resp_size, resp_match, resp_match_value); | ||||
| 	if (ret) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	mmc_spi_cfg.f_max = speed; | ||||
| 
 | ||||
| 	mmc = mmc_create(&mmc_spi_cfg, spi); | ||||
| 	if (mmc == NULL) { | ||||
| 		spi_free_slave(spi); | ||||
| 		return NULL; | ||||
| 	switch (cmd->cmdidx) { | ||||
| 	case SD_CMD_APP_SEND_OP_COND: | ||||
| 	case MMC_CMD_SEND_OP_COND: | ||||
| 		cmd->response[0] = (resp8 & R1_SPI_IDLE) ? 0 : OCR_BUSY; | ||||
| 		break; | ||||
| 	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_MMC_DEFAULT_DEV | ||||
| 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_MMU | ||||
| CONFIG_MONITOR_IS_IN_RAM | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue