tegra124: add thread-friendly delays to SPI driver

This adds delay logic to the SPI driver which will calculate
delays based on the SPI clock speed (currently hard-coded at max
value) and the number of bytes remaining to be transferred.

The delay is used whenever bytes are being transmitted over the
SPI bus itself. Copying between memory and SPI FIFO is assumed
to be fast enough to just busy wait.

TODO: Calculate SPI speed properly.

BUG=none
BRANCH=none
TEST=built and booted on nyan

Change-Id: I8a74e5cacdae83de18fce084cf3b81f911508bd9
Signed-off-by: David Hendricks <dhendrix@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/173648
Reviewed-by: Gabe Black <gabeblack@chromium.org>
Commit-Queue: Gabe Black <gabeblack@chromium.org>
Tested-by: Gabe Black <gabeblack@chromium.org>
This commit is contained in:
David Hendricks 2013-10-18 13:00:31 -07:00 committed by chrome-internal-fetch
commit c1a321c8f6

View file

@ -215,6 +215,33 @@ static struct tegra_spi_channel * const to_tegra_spi(int bus) {
return &tegra_spi_channels[bus - 1];
}
static unsigned int tegra_spi_speed(unsigned int bus)
{
/* FIXME: implement this properly, for now use max value (50MHz) */
return 50000000;
}
/*
* This calls udelay() with a calculated value based on the SPI speed and
* number of bytes remaining to be transferred. It assumes that if the
* calculated delay period is less than MIN_DELAY_US then it is probably
* not worth the overhead of yielding.
*/
#define MIN_DELAY_US 250
static void tegra_spi_delay(struct tegra_spi_channel *spi,
unsigned int bytes_remaining)
{
unsigned int ns_per_byte, delay_us;
ns_per_byte = 1000000000 / (tegra_spi_speed(spi->slave.bus) / 8);
delay_us = (ns_per_byte * bytes_remaining) / 1000;
if (delay_us < MIN_DELAY_US)
return;
udelay(delay_us);
}
void spi_cs_activate(struct spi_slave *slave)
{
struct tegra_spi_regs *regs = to_tegra_spi(slave->bus)->regs;
@ -348,7 +375,7 @@ static int tegra_spi_fifo_receive(struct tegra_spi_channel *spi,
setbits_le32(&spi->regs->command1, SPI_CMD1_RX_EN);
while (remaining) {
unsigned int from_fifo;
unsigned int from_fifo, count;
from_fifo = MIN(in_bytes, SPI_MAX_TRANSFER_BYTES_FIFO);
remaining -= from_fifo;
@ -360,8 +387,8 @@ static int tegra_spi_fifo_receive(struct tegra_spi_channel *spi,
setbits_le32(&spi->regs->trans_status, SPI_STATUS_RDY);
setbits_le32(&spi->regs->command1, SPI_CMD1_GO);
/* FIXME: delay loops should be "thread" friendly */
while (spi_byte_count(spi) != from_fifo) {
while ((count = spi_byte_count(spi)) != from_fifo) {
tegra_spi_delay(spi, from_fifo - count);
if (fifo_error(spi))
goto done;
}
@ -412,9 +439,9 @@ static int tegra_spi_fifo_send(struct tegra_spi_channel *spi,
setbits_le32(&spi->regs->trans_status, SPI_STATUS_RDY);
setbits_le32(&spi->regs->command1, SPI_CMD1_GO);
/* FIXME: delay loops should be "thread" friendly */
while (!(read32(&spi->regs->fifo_status) &
SPI_FIFO_STATUS_TX_FIFO_EMPTY)) {
tegra_spi_delay(spi, to_fifo - spi_byte_count(spi));
if (fifo_error(spi))
goto done;
}
@ -501,14 +528,12 @@ static int tegra_spi_dma_receive(struct tegra_spi_channel *spi,
* from Rx FIFO */
dma_start(dma);
/* FIXME: delay loops should be "thread" friendly */
while (spi_byte_count(spi) != in_bytes)
;
tegra_spi_delay(spi, in_bytes - spi_byte_count(spi));
clrbits_le32(&spi->regs->command1, SPI_CMD1_RX_EN);
while ((read32(&dma->regs->dma_byte_sta) < in_bytes) ||
dma_busy(dma))
;
while ((read32(&dma->regs->dma_byte_sta) < in_bytes) || dma_busy(dma))
; /* this shouldn't take long, no udelay */
dma_stop(dma);
dma_release(dma);
@ -526,6 +551,7 @@ static int tegra_spi_dma_send(struct tegra_spi_channel *spi,
const u8 *dout, unsigned int out_bytes)
{
struct apb_dma_channel *dma;
unsigned int count;
dma = dma_claim();
if (!dma) {
@ -558,13 +584,12 @@ static int tegra_spi_dma_send(struct tegra_spi_channel *spi,
/* set DMA bit in SPI_DMA_CTL to start */
setbits_le32(&spi->regs->dma_ctl, SPI_DMA_CTL_DMA);
/* FIXME: delay loops should be "thread" friendly */
while ((read32(&dma->regs->dma_byte_sta) < out_bytes) ||
dma_busy(dma))
;
while ((read32(&dma->regs->dma_byte_sta) < out_bytes) || dma_busy(dma))
tegra_spi_delay(spi, out_bytes - spi_byte_count(spi));
dma_stop(dma);
while (spi_byte_count(spi) != out_bytes)
;
while ((count = spi_byte_count(spi)) != out_bytes)
tegra_spi_delay(spi, out_bytes - count);
clrbits_le32(&spi->regs->command1, SPI_CMD1_TX_EN);
dma_release(dma);