diff options
Diffstat (limited to 'drivers/spi/mxs_spi.c')
| -rw-r--r-- | drivers/spi/mxs_spi.c | 58 | 
1 files changed, 33 insertions, 25 deletions
| diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 168dbe497..42e4c9952 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -224,8 +224,10 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,  	struct mxs_dma_desc *dp;  	uint32_t ctrl0;  	uint32_t cache_data_count; +	const uint32_t dstart = (uint32_t)data;  	int dmach;  	int tl; +	int ret = 0;  	ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count); @@ -239,17 +241,17 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,  	if (!write)  		ctrl0 |= SSP_CTRL0_READ; -	writel(length, &ssp_regs->hw_ssp_xfer_size); -  	if (length % ARCH_DMA_MINALIGN)  		cache_data_count = roundup(length, ARCH_DMA_MINALIGN);  	else  		cache_data_count = length; +	/* Flush data to DRAM so DMA can pick them up */  	if (write) -		/* Flush data to DRAM so DMA can pick them up */ -		flush_dcache_range((uint32_t)data, -			(uint32_t)(data + cache_data_count)); +		flush_dcache_range(dstart, dstart + cache_data_count); + +	/* Invalidate the area, so no writeback into the RAM races with DMA */ +	invalidate_dcache_range(dstart, dstart + cache_data_count);  	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus; @@ -281,41 +283,47 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,  			tl = min(length, xfer_max_sz);  		dp->cmd.data |= -			(tl << MXS_DMA_DESC_BYTES_OFFSET) | -			(1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | +			((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | +			(4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |  			MXS_DMA_DESC_HALT_ON_TERMINATE |  			MXS_DMA_DESC_TERMINATE_FLUSH; -		dp->cmd.pio_words[0] = ctrl0;  		data += tl;  		length -= tl; +		if (!length) { +			dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; + +			if (flags & SPI_XFER_END) { +				ctrl0 &= ~SSP_CTRL0_LOCK_CS; +				ctrl0 |= SSP_CTRL0_IGNORE_CRC; +			} +		} + +		/* +		 * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is +		 * essential that the XFER_SIZE register is written on +		 * a per-descriptor basis with the same size as is the +		 * descriptor! +		 */ +		dp->cmd.pio_words[0] = ctrl0; +		dp->cmd.pio_words[1] = 0; +		dp->cmd.pio_words[2] = 0; +		dp->cmd.pio_words[3] = tl; +  		mxs_dma_desc_append(dmach, dp);  		dp++;  	} -	dp->address = (dma_addr_t)dp; -	dp->cmd.address = (dma_addr_t)0; -	dp->cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | -			(1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | -			MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; -	if (flags & SPI_XFER_END) { -		ctrl0 &= ~SSP_CTRL0_LOCK_CS; -		dp->cmd.pio_words[0] = ctrl0 | SSP_CTRL0_IGNORE_CRC; -	} -	mxs_dma_desc_append(dmach, dp); -  	if (mxs_dma_go(dmach)) -		return -EINVAL; +		ret = -EINVAL;  	/* The data arrived into DRAM, invalidate cache over them */ -	if (!write) { -		invalidate_dcache_range((uint32_t)data, -			(uint32_t)(data + cache_data_count)); -	} +	if (!write) +		invalidate_dcache_range(dstart, dstart + cache_data_count); -	return 0; +	return ret;  }  int spi_xfer(struct spi_slave *slave, unsigned int bitlen, |