diff options
Diffstat (limited to 'drivers/spi/spi-omap2-mcspi.c')
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 137 |
1 files changed, 129 insertions, 8 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index ddf1c684bcc7..a0c9fea908f5 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -53,6 +53,8 @@ /* per-register bitmasks: */ #define OMAP2_MCSPI_IRQSTATUS_EOW BIT(17) +#define OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY BIT(0) +#define OMAP2_MCSPI_IRQSTATUS_RX0_FULL BIT(2) #define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2) @@ -291,7 +293,7 @@ static void omap2_mcspi_set_mode(struct spi_controller *ctlr) } static void omap2_mcspi_set_fifo(const struct spi_device *spi, - struct spi_transfer *t, int enable) + struct spi_transfer *t, int enable, int dma_enabled) { struct spi_controller *ctlr = spi->controller; struct omap2_mcspi_cs *cs = spi->controller_state; @@ -312,20 +314,28 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH / 2; else max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH; - - wcnt = t->len / bytes_per_word; + if (dma_enabled) + wcnt = t->len / bytes_per_word; + else + wcnt = 0; if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT) goto disable_fifo; xferlevel = wcnt << 16; if (t->rx_buf != NULL) { chconf |= OMAP2_MCSPI_CHCONF_FFER; - xferlevel |= (bytes_per_word - 1) << 8; + if (dma_enabled) + xferlevel |= (bytes_per_word - 1) << 8; + else + xferlevel |= (max_fifo_depth - 1) << 8; } if (t->tx_buf != NULL) { chconf |= OMAP2_MCSPI_CHCONF_FFET; - xferlevel |= bytes_per_word - 1; + if (dma_enabled) + xferlevel |= bytes_per_word - 1; + else + xferlevel |= (max_fifo_depth - 1); } mcspi_write_reg(ctlr, OMAP2_MCSPI_XFERLEVEL, xferlevel); @@ -882,6 +892,113 @@ out: return count - c; } +static unsigned +omap2_mcspi_txrx_piofifo(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi *mcspi; + unsigned int count, c; + unsigned int iter, cwc; + int last_request; + void __iomem *base = cs->base; + void __iomem *tx_reg; + void __iomem *rx_reg; + void __iomem *chstat_reg; + void __iomem *irqstat_reg; + int word_len, bytes_per_word; + u8 *rx; + const u8 *tx; + + mcspi = spi_controller_get_devdata(spi->controller); + count = xfer->len; + c = count; + word_len = cs->word_len; + bytes_per_word = mcspi_bytes_per_word(word_len); + + /* + * We store the pre-calculated register addresses on stack to speed + * up the transfer loop. + */ + tx_reg = base + OMAP2_MCSPI_TX0; + rx_reg = base + OMAP2_MCSPI_RX0; + chstat_reg = base + OMAP2_MCSPI_CHSTAT0; + irqstat_reg = base + OMAP2_MCSPI_IRQSTATUS; + + if (c < (word_len >> 3)) + return 0; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + do { + /* calculate number of words in current iteration */ + cwc = min((unsigned int)mcspi->fifo_depth / bytes_per_word, + c / bytes_per_word); + last_request = cwc != (mcspi->fifo_depth / bytes_per_word); + if (tx) { + if (mcspi_wait_for_reg_bit(irqstat_reg, + OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY) < 0) { + dev_err(&spi->dev, "TX Empty timed out\n"); + goto out; + } + writel_relaxed(OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY, irqstat_reg); + + for (iter = 0; iter < cwc; iter++, tx += bytes_per_word) { + if (bytes_per_word == 1) + writel_relaxed(*tx, tx_reg); + else if (bytes_per_word == 2) + writel_relaxed(*((u16 *)tx), tx_reg); + else if (bytes_per_word == 4) + writel_relaxed(*((u32 *)tx), tx_reg); + } + } + + if (rx) { + if (!last_request && + mcspi_wait_for_reg_bit(irqstat_reg, + OMAP2_MCSPI_IRQSTATUS_RX0_FULL) < 0) { + dev_err(&spi->dev, "RX_FULL timed out\n"); + goto out; + } + writel_relaxed(OMAP2_MCSPI_IRQSTATUS_RX0_FULL, irqstat_reg); + + for (iter = 0; iter < cwc; iter++, rx += bytes_per_word) { + if (last_request && + mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, "RXS timed out\n"); + goto out; + } + if (bytes_per_word == 1) + *rx = readl_relaxed(rx_reg); + else if (bytes_per_word == 2) + *((u16 *)rx) = readl_relaxed(rx_reg); + else if (bytes_per_word == 4) + *((u32 *)rx) = readl_relaxed(rx_reg); + } + } + + if (last_request) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0) { + dev_err(&spi->dev, "EOT timed out\n"); + goto out; + } + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXFFE) < 0) { + dev_err(&spi->dev, "TXFFE timed out\n"); + goto out; + } + omap2_mcspi_set_enable(spi, 0); + } + c -= cwc * bytes_per_word; + } while (c >= bytes_per_word); + +out: + omap2_mcspi_set_enable(spi, 1); + return count - c; +} + static u32 omap2_mcspi_calc_divisor(u32 speed_hz, u32 ref_clk_hz) { u32 div; @@ -1206,7 +1323,9 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && ctlr->cur_msg_mapped && ctlr->can_dma(ctlr, spi, t)) - omap2_mcspi_set_fifo(spi, t, 1); + omap2_mcspi_set_fifo(spi, t, 1, 1); + else if (t->len > OMAP2_MCSPI_MAX_FIFODEPTH) + omap2_mcspi_set_fifo(spi, t, 1, 0); omap2_mcspi_set_enable(spi, 1); @@ -1219,6 +1338,8 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, ctlr->cur_msg_mapped && ctlr->can_dma(ctlr, spi, t)) count = omap2_mcspi_txrx_dma(spi, t); + else if (mcspi->fifo_depth > 0) + count = omap2_mcspi_txrx_piofifo(spi, t); else count = omap2_mcspi_txrx_pio(spi, t); @@ -1231,7 +1352,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, omap2_mcspi_set_enable(spi, 0); if (mcspi->fifo_depth > 0) - omap2_mcspi_set_fifo(spi, t, 0); + omap2_mcspi_set_fifo(spi, t, 0, 0); out: /* Restore defaults if they were overriden */ @@ -1254,7 +1375,7 @@ out: omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH)); if (mcspi->fifo_depth > 0 && t) - omap2_mcspi_set_fifo(spi, t, 0); + omap2_mcspi_set_fifo(spi, t, 0, 0); return status; } |