diff options
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
| -rw-r--r-- | drivers/mmc/host/sdhci.c | 163 | 
1 files changed, 73 insertions, 90 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 17701c3da73..c3a5db72ddd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -173,119 +173,95 @@ static void sdhci_led_control(struct led_classdev *led,   *                                                                           *  \*****************************************************************************/ -static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) -{ -	return sg_virt(host->cur_sg); -} - -static inline int sdhci_next_sg(struct sdhci_host* host) -{ -	/* -	 * Skip to next SG entry. -	 */ -	host->cur_sg++; -	host->num_sg--; - -	/* -	 * Any entries left? -	 */ -	if (host->num_sg > 0) { -		host->offset = 0; -		host->remain = host->cur_sg->length; -	} - -	return host->num_sg; -} -  static void sdhci_read_block_pio(struct sdhci_host *host)  { -	int blksize, chunk_remain; -	u32 data; -	char *buffer; -	int size; +	unsigned long flags; +	size_t blksize, len, chunk; +	u32 scratch; +	u8 *buf;  	DBG("PIO reading\n");  	blksize = host->data->blksz; -	chunk_remain = 0; -	data = 0; +	chunk = 0; -	buffer = sdhci_sg_to_buffer(host) + host->offset; +	local_irq_save(flags);  	while (blksize) { -		if (chunk_remain == 0) { -			data = readl(host->ioaddr + SDHCI_BUFFER); -			chunk_remain = min(blksize, 4); -		} +		if (!sg_miter_next(&host->sg_miter)) +			BUG(); -		size = min(host->remain, chunk_remain); +		len = min(host->sg_miter.length, blksize); -		chunk_remain -= size; -		blksize -= size; -		host->offset += size; -		host->remain -= size; +		blksize -= len; +		host->sg_miter.consumed = len; -		while (size) { -			*buffer = data & 0xFF; -			buffer++; -			data >>= 8; -			size--; -		} +		buf = host->sg_miter.addr; -		if (host->remain == 0) { -			if (sdhci_next_sg(host) == 0) { -				BUG_ON(blksize != 0); -				return; +		while (len) { +			if (chunk == 0) { +				scratch = readl(host->ioaddr + SDHCI_BUFFER); +				chunk = 4;  			} -			buffer = sdhci_sg_to_buffer(host); + +			*buf = scratch & 0xFF; + +			buf++; +			scratch >>= 8; +			chunk--; +			len--;  		}  	} + +	sg_miter_stop(&host->sg_miter); + +	local_irq_restore(flags);  }  static void sdhci_write_block_pio(struct sdhci_host *host)  { -	int blksize, chunk_remain; -	u32 data; -	char *buffer; -	int bytes, size; +	unsigned long flags; +	size_t blksize, len, chunk; +	u32 scratch; +	u8 *buf;  	DBG("PIO writing\n");  	blksize = host->data->blksz; -	chunk_remain = 4; -	data = 0; +	chunk = 0; +	scratch = 0; -	bytes = 0; -	buffer = sdhci_sg_to_buffer(host) + host->offset; +	local_irq_save(flags);  	while (blksize) { -		size = min(host->remain, chunk_remain); +		if (!sg_miter_next(&host->sg_miter)) +			BUG(); -		chunk_remain -= size; -		blksize -= size; -		host->offset += size; -		host->remain -= size; +		len = min(host->sg_miter.length, blksize); -		while (size) { -			data >>= 8; -			data |= (u32)*buffer << 24; -			buffer++; -			size--; -		} +		blksize -= len; +		host->sg_miter.consumed = len; -		if (chunk_remain == 0) { -			writel(data, host->ioaddr + SDHCI_BUFFER); -			chunk_remain = min(blksize, 4); -		} +		buf = host->sg_miter.addr; -		if (host->remain == 0) { -			if (sdhci_next_sg(host) == 0) { -				BUG_ON(blksize != 0); -				return; +		while (len) { +			scratch |= (u32)*buf << (chunk * 8); + +			buf++; +			chunk++; +			len--; + +			if ((chunk == 4) || ((len == 0) && (blksize == 0))) { +				writel(scratch, host->ioaddr + SDHCI_BUFFER); +				chunk = 0; +				scratch = 0;  			} -			buffer = sdhci_sg_to_buffer(host);  		}  	} + +	sg_miter_stop(&host->sg_miter); + +	local_irq_restore(flags);  }  static void sdhci_transfer_pio(struct sdhci_host *host) @@ -294,7 +270,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)  	BUG_ON(!host->data); -	if (host->num_sg == 0) +	if (host->blocks == 0)  		return;  	if (host->data->flags & MMC_DATA_READ) @@ -308,7 +284,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)  		else  			sdhci_write_block_pio(host); -		if (host->num_sg == 0) +		host->blocks--; +		if (host->blocks == 0)  			break;  	} @@ -389,6 +366,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,  		if (offset) {  			if (data->flags & MMC_DATA_WRITE) {  				buffer = sdhci_kmap_atomic(sg, &flags); +				WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));  				memcpy(align, buffer, offset);  				sdhci_kunmap_atomic(buffer, &flags);  			} @@ -510,6 +488,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host,  				size = 4 - (sg_dma_address(sg) & 0x3);  				buffer = sdhci_kmap_atomic(sg, &flags); +				WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));  				memcpy(buffer, align, size);  				sdhci_kunmap_atomic(buffer, &flags); @@ -687,7 +666,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)  				WARN_ON(1);  				host->flags &= ~SDHCI_USE_DMA;  			} else { -				WARN_ON(count != 1); +				WARN_ON(sg_cnt != 1);  				writel(sg_dma_address(data->sg),  					host->ioaddr + SDHCI_DMA_ADDRESS);  			} @@ -711,11 +690,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)  	}  	if (!(host->flags & SDHCI_REQ_USE_DMA)) { -		host->cur_sg = data->sg; -		host->num_sg = data->sg_len; - -		host->offset = 0; -		host->remain = host->cur_sg->length; +		sg_miter_start(&host->sg_miter, +			data->sg, data->sg_len, SG_MITER_ATOMIC); +		host->blocks = data->blocks;  	}  	/* We do not handle DMA boundaries, so set it to max (512 KiB) */ @@ -1581,9 +1558,15 @@ int sdhci_add_host(struct sdhci_host *host)  		}  	} -	/* XXX: Hack to get MMC layer to avoid highmem */ -	if (!(host->flags & SDHCI_USE_DMA)) -		mmc_dev(host->mmc)->dma_mask = NULL; +	/* +	 * If we use DMA, then it's up to the caller to set the DMA +	 * mask, but PIO does not need the hw shim so we set a new +	 * mask here in that case. +	 */ +	if (!(host->flags & SDHCI_USE_DMA)) { +		host->dma_mask = DMA_BIT_MASK(64); +		mmc_dev(host->mmc)->dma_mask = &host->dma_mask; +	}  	host->max_clk =  		(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;  |