diff options
Diffstat (limited to 'drivers/spi/omap2_mcspi.c')
-rw-r--r-- | drivers/spi/omap2_mcspi.c | 153 |
1 files changed, 123 insertions, 30 deletions
diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index e0de0d0..b3a94ca 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -38,7 +38,7 @@ #include <plat/dma.h> #include <plat/clock.h> - +#include <plat/mcspi.h> #define OMAP2_MCSPI_MAX_FREQ 48000000 @@ -113,7 +113,7 @@ struct omap2_mcspi_dma { /* use PIO for small transfers, avoiding DMA setup/teardown overhead and * cache operations; better heuristics consider wordsize and bitrate. */ -#define DMA_MIN_BYTES 8 +#define DMA_MIN_BYTES 160 struct omap2_mcspi { @@ -229,6 +229,8 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); + /* Flash post-writes */ + mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); } static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) @@ -303,11 +305,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) unsigned int count, c; unsigned long base, tx_reg, rx_reg; int word_len, data_type, element_count; + int elements; + u32 l; u8 * rx; const u8 * tx; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + l = mcspi_cached_chconf0(spi); count = xfer->len; c = count; @@ -346,8 +351,12 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) } if (rx != NULL) { + elements = element_count - 1; + if (l & OMAP2_MCSPI_CHCONF_TURBO) + elements--; + omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, - data_type, element_count - 1, 1, + data_type, elements, 1, OMAP_DMA_SYNC_ELEMENT, mcspi_dma->dma_rx_sync_dev, 1); @@ -379,17 +388,42 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) wait_for_completion(&mcspi_dma->dma_rx_completion); dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); omap2_mcspi_set_enable(spi, 0); + + if (l & OMAP2_MCSPI_CHCONF_TURBO) { + + if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) + & OMAP2_MCSPI_CHSTAT_RXS)) { + u32 w; + + w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); + if (word_len <= 8) + ((u8 *)xfer->rx_buf)[elements++] = w; + else if (word_len <= 16) + ((u16 *)xfer->rx_buf)[elements++] = w; + else /* word_len <= 32 */ + ((u32 *)xfer->rx_buf)[elements++] = w; + } else { + dev_err(&spi->dev, + "DMA RX penultimate word empty"); + count -= (word_len <= 8) ? 2 : + (word_len <= 16) ? 4 : + /* word_len <= 32 */ 8; + omap2_mcspi_set_enable(spi, 1); + return count; + } + } + if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) & OMAP2_MCSPI_CHSTAT_RXS)) { u32 w; w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); if (word_len <= 8) - ((u8 *)xfer->rx_buf)[element_count - 1] = w; + ((u8 *)xfer->rx_buf)[elements] = w; else if (word_len <= 16) - ((u16 *)xfer->rx_buf)[element_count - 1] = w; + ((u16 *)xfer->rx_buf)[elements] = w; else /* word_len <= 32 */ - ((u32 *)xfer->rx_buf)[element_count - 1] = w; + ((u32 *)xfer->rx_buf)[elements] = w; } else { dev_err(&spi->dev, "DMA RX last word empty"); count -= (word_len <= 8) ? 1 : @@ -433,7 +467,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len = cs->word_len; l = mcspi_cached_chconf0(spi); - l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; /* We store the pre-calculated register addresses on stack to speed * up the transfer loop. */ @@ -468,11 +501,26 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "RXS timed out\n"); goto out; } - /* prevent last RX_ONLY read from triggering - * more word i/o: switch to rx+tx - */ - if (c == 0 && tx == NULL) - mcspi_write_chconf0(spi, l); + + if (c == 1 && tx == NULL && + (l & OMAP2_MCSPI_CHCONF_TURBO)) { + omap2_mcspi_set_enable(spi, 0); + *rx++ = __raw_readl(rx_reg); +#ifdef VERBOSE + dev_dbg(&spi->dev, "read-%d %02x\n", + word_len, *(rx - 1)); +#endif + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, + "RXS timed out\n"); + goto out; + } + c = 0; + } else if (c == 0 && tx == NULL) { + omap2_mcspi_set_enable(spi, 0); + } + *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %02x\n", @@ -506,11 +554,26 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "RXS timed out\n"); goto out; } - /* prevent last RX_ONLY read from triggering - * more word i/o: switch to rx+tx - */ - if (c == 0 && tx == NULL) - mcspi_write_chconf0(spi, l); + + if (c == 2 && tx == NULL && + (l & OMAP2_MCSPI_CHCONF_TURBO)) { + omap2_mcspi_set_enable(spi, 0); + *rx++ = __raw_readl(rx_reg); +#ifdef VERBOSE + dev_dbg(&spi->dev, "read-%d %04x\n", + word_len, *(rx - 1)); +#endif + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, + "RXS timed out\n"); + goto out; + } + c = 0; + } else if (c == 0 && tx == NULL) { + omap2_mcspi_set_enable(spi, 0); + } + *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", @@ -544,11 +607,26 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "RXS timed out\n"); goto out; } - /* prevent last RX_ONLY read from triggering - * more word i/o: switch to rx+tx - */ - if (c == 0 && tx == NULL) - mcspi_write_chconf0(spi, l); + + if (c == 4 && tx == NULL && + (l & OMAP2_MCSPI_CHCONF_TURBO)) { + omap2_mcspi_set_enable(spi, 0); + *rx++ = __raw_readl(rx_reg); +#ifdef VERBOSE + dev_dbg(&spi->dev, "read-%d %08x\n", + word_len, *(rx - 1)); +#endif + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, + "RXS timed out\n"); + goto out; + } + c = 0; + } else if (c == 0 && tx == NULL) { + omap2_mcspi_set_enable(spi, 0); + } + *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %08x\n", @@ -568,6 +646,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) dev_err(&spi->dev, "EOT timed out\n"); } out: + omap2_mcspi_set_enable(spi, 1); return count - c; } @@ -755,7 +834,6 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) struct omap2_mcspi_cs *cs; mcspi = spi_master_get_devdata(spi->master); - mcspi_dma = &mcspi->dma_channels[spi->chip_select]; if (spi->controller_state) { /* Unlink controller state from context save list */ @@ -765,13 +843,17 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) kfree(spi->controller_state); } - if (mcspi_dma->dma_rx_channel != -1) { - omap_free_dma(mcspi_dma->dma_rx_channel); - mcspi_dma->dma_rx_channel = -1; - } - if (mcspi_dma->dma_tx_channel != -1) { - omap_free_dma(mcspi_dma->dma_tx_channel); - mcspi_dma->dma_tx_channel = -1; + if (spi->chip_select < spi->master->num_chipselect) { + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + if (mcspi_dma->dma_rx_channel != -1) { + omap_free_dma(mcspi_dma->dma_rx_channel); + mcspi_dma->dma_rx_channel = -1; + } + if (mcspi_dma->dma_tx_channel != -1) { + omap_free_dma(mcspi_dma->dma_tx_channel); + mcspi_dma->dma_tx_channel = -1; + } } } @@ -797,6 +879,7 @@ static void omap2_mcspi_work(struct work_struct *work) struct spi_transfer *t = NULL; int cs_active = 0; struct omap2_mcspi_cs *cs; + struct omap2_mcspi_device_config *cd; int par_override = 0; int status = 0; u32 chconf; @@ -809,6 +892,7 @@ static void omap2_mcspi_work(struct work_struct *work) spi = m->spi; cs = spi->controller_state; + cd = spi->controller_data; omap2_mcspi_set_enable(spi, 1); list_for_each_entry(t, &m->transfers, transfer_list) { @@ -832,10 +916,19 @@ static void omap2_mcspi_work(struct work_struct *work) chconf = mcspi_cached_chconf0(spi); chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; + if (t->tx_buf == NULL) chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; else if (t->rx_buf == NULL) chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; + + if (cd && cd->turbo_mode && t->tx_buf == NULL) { + /* Turbo mode is for more than one word */ + if (t->len > ((cs->word_len + 7) >> 3)) + chconf |= OMAP2_MCSPI_CHCONF_TURBO; + } + mcspi_write_chconf0(spi, chconf); if (t->len) { |