diff options
Diffstat (limited to 'drivers/spi/exynos_spi.c')
| -rw-r--r-- | drivers/spi/exynos_spi.c | 107 | 
1 files changed, 88 insertions, 19 deletions
| diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index efc8b1e3a..699c57eb6 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -26,6 +26,7 @@ struct spi_bus {  	struct exynos_spi *regs;  	int inited;		/* 1 if this bus is ready for use */  	int node; +	uint deactivate_delay_us;	/* Delay to wait after deactivate */  };  /* A list of spi buses that we know about */ @@ -40,6 +41,8 @@ struct exynos_spi_slave {  	enum periph_id periph_id;	/* Peripheral ID for this device */  	unsigned int fifo_size;  	int skip_preamble; +	struct spi_bus *bus;		/* Pointer to our SPI bus info */ +	ulong last_transaction_us;	/* Time of last transaction end */  };  static struct spi_bus *spi_get_bus(unsigned dev_index) @@ -85,6 +88,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,  	}  	bus = &spi_bus[busnum]; +	spi_slave->bus = bus;  	spi_slave->regs = bus->regs;  	spi_slave->mode = mode;  	spi_slave->periph_id = bus->periph_id; @@ -95,6 +99,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,  		spi_slave->fifo_size = 256;  	spi_slave->skip_preamble = 0; +	spi_slave->last_transaction_us = timer_get_us();  	spi_slave->freq = bus->frequency;  	if (max_hz) @@ -199,12 +204,29 @@ static void spi_get_fifo_levels(struct exynos_spi *regs,   *   * @param regs	SPI peripheral registers   * @param count	Number of bytes to transfer + * @param step	Number of bytes to transfer in each packet (1 or 4)   */ -static void spi_request_bytes(struct exynos_spi *regs, int count) +static void spi_request_bytes(struct exynos_spi *regs, int count, int step)  { +	/* For word address we need to swap bytes */ +	if (step == 4) { +		setbits_le32(®s->mode_cfg, +			     SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); +		count /= 4; +		setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | +			SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | +			SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); +	} else { +		/* Select byte access and clear the swap configuration */ +		clrbits_le32(®s->mode_cfg, +			     SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); +		writel(0, ®s->swap_cfg); +	} +  	assert(count && count < (1 << 16));  	setbits_le32(®s->ch_cfg, SPI_CH_RST);  	clrbits_le32(®s->ch_cfg, SPI_CH_RST); +  	writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);  } @@ -219,6 +241,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  	int toread;  	unsigned start = get_timer(0);  	int stopping; +	int step;  	out_bytes = in_bytes = todo; @@ -226,10 +249,19 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  					!(spi_slave->mode & SPI_SLAVE);  	/* +	 * Try to transfer words if we can. This helps read performance at +	 * SPI clock speeds above about 20MHz. +	 */ +	step = 1; +	if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && +	    !spi_slave->skip_preamble) +		step = 4; + +	/*  	 * If there's something to send, do a software reset and set a  	 * transaction size.  	 */ -	spi_request_bytes(regs, todo); +	spi_request_bytes(regs, todo, step);  	/*  	 * Bytes are transmitted/received in pairs. Wait to receive all the @@ -242,24 +274,42 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  		/* Keep the fifos full/empty. */  		spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); -		if (tx_lvl < spi_slave->fifo_size && out_bytes) { -			temp = txp ? *txp++ : 0xff; + +		/* +		 * Don't completely fill the txfifo, since we don't want our +		 * rxfifo to overflow, and it may already contain data. +		 */ +		while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) { +			if (!txp) +				temp = -1; +			else if (step == 4) +				temp = *(uint32_t *)txp; +			else +				temp = *txp;  			writel(temp, ®s->tx_data); -			out_bytes--; +			out_bytes -= step; +			if (txp) +				txp += step; +			tx_lvl += step;  		} -		if (rx_lvl > 0) { -			temp = readl(®s->rx_data); -			if (spi_slave->skip_preamble) { -				if (temp == SPI_PREAMBLE_END_BYTE) { -					spi_slave->skip_preamble = 0; -					stopping = 0; +		if (rx_lvl >= step) { +			while (rx_lvl >= step) { +				temp = readl(®s->rx_data); +				if (spi_slave->skip_preamble) { +					if (temp == SPI_PREAMBLE_END_BYTE) { +						spi_slave->skip_preamble = 0; +						stopping = 0; +					} +				} else { +					if (rxp || stopping) { +						*rxp = temp; +						rxp += step; +					} +					in_bytes -= step;  				} -			} else { -				if (rxp || stopping) -					*rxp++ = temp; -				in_bytes--; +				toread -= step; +				rx_lvl -= step;  			} -			toread--;  		} else if (!toread) {  			/*  			 * We have run out of input data, but haven't read @@ -271,7 +321,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  			out_bytes = in_bytes;  			toread = in_bytes;  			txp = NULL; -			spi_request_bytes(regs, toread); +			spi_request_bytes(regs, toread, step);  		}  		if (spi_slave->skip_preamble && get_timer(start) > 100) {  			printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", @@ -315,10 +365,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  	if ((flags & SPI_XFER_BEGIN))  		spi_cs_activate(slave); -	/* Exynos SPI limits each transfer to 65535 bytes */ +	/* +	 * Exynos SPI limits each transfer to 65535 transfers. To keep +	 * things simple, allow a maximum of 65532 bytes. We could allow +	 * more in word mode, but the performance difference is small. +	 */  	bytelen =  bitlen / 8;  	for (upto = 0; !ret && upto < bytelen; upto += todo) { -		todo = min(bytelen - upto, (1 << 16) - 1); +		todo = min(bytelen - upto, (1 << 16) - 4);  		ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);  		if (ret)  			break; @@ -359,9 +413,22 @@ void spi_cs_activate(struct spi_slave *slave)  {  	struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); +	/* If it's too soon to do another transaction, wait */ +	if (spi_slave->bus->deactivate_delay_us && +	    spi_slave->last_transaction_us) { +		ulong delay_us;		/* The delay completed so far */ +		delay_us = timer_get_us() - spi_slave->last_transaction_us; +		if (delay_us < spi_slave->bus->deactivate_delay_us) +			udelay(spi_slave->bus->deactivate_delay_us - delay_us); +	} +  	clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);  	debug("Activate CS, bus %d\n", spi_slave->slave.bus);  	spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; + +	/* Remember time of this transaction so we can honour the bus delay */ +	if (spi_slave->bus->deactivate_delay_us) +		spi_slave->last_transaction_us = timer_get_us();  }  /** @@ -411,6 +478,8 @@ static int spi_get_config(const void *blob, int node, struct spi_bus *bus)  	/* Use 500KHz as a suitable default */  	bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",  					500000); +	bus->deactivate_delay_us = fdtdec_get_int(blob, node, +					"spi-deactivate-delay", 0);  	return 0;  } |