Commit 75223bbe authored by Vaishnav Achath's avatar Vaishnav Achath Committed by Mark Brown

spi: omap2-mcspi: Add FIFO support without DMA

Currently, the built-in 64-byte FIFO on the MCSPI controller is not
enabled in PIO mode and is used only when DMA is enabled. Enable the
FIFO in PIO mode by default for transactions larger than the FIFO depth
and fallback only if FIFO is not available. When DMA is not enabled,
it is efficient to enable the RX FIFO almost full and TX FIFO almost
empty events after each FIFO fill instead of each word.

Update omap2_mcspi_set_fifo() to enable the events accordingly and
also rely on OMAP2_MCSPI_CHSTAT_RXS for the last transfer instead of the
FIFO events to handle the case when the transfer size is not a multiple
of FIFO depth.

See J721E Technical Reference Manual (SPRUI1C), section 12.1.5
for further details: http://www.ti.com/lit/pdf/spruil1Signed-off-by: default avatarVaishnav Achath <vaishnav.a@ti.com>
Link: https://lore.kernel.org/r/20231013092629.19005-1-vaishnav.a@ti.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 69222501
...@@ -53,6 +53,8 @@ ...@@ -53,6 +53,8 @@
/* per-register bitmasks: */ /* per-register bitmasks: */
#define OMAP2_MCSPI_IRQSTATUS_EOW BIT(17) #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_SINGLE BIT(0)
#define OMAP2_MCSPI_MODULCTRL_MS BIT(2) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2)
...@@ -291,7 +293,7 @@ static void omap2_mcspi_set_mode(struct spi_controller *ctlr) ...@@ -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, 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 spi_controller *ctlr = spi->controller;
struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_cs *cs = spi->controller_state;
...@@ -312,20 +314,28 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, ...@@ -312,20 +314,28 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi,
max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH / 2; max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH / 2;
else else
max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH; max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH;
if (dma_enabled)
wcnt = t->len / bytes_per_word; wcnt = t->len / bytes_per_word;
else
wcnt = 0;
if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT) if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT)
goto disable_fifo; goto disable_fifo;
xferlevel = wcnt << 16; xferlevel = wcnt << 16;
if (t->rx_buf != NULL) { if (t->rx_buf != NULL) {
chconf |= OMAP2_MCSPI_CHCONF_FFER; chconf |= OMAP2_MCSPI_CHCONF_FFER;
if (dma_enabled)
xferlevel |= (bytes_per_word - 1) << 8; xferlevel |= (bytes_per_word - 1) << 8;
else
xferlevel |= (max_fifo_depth - 1) << 8;
} }
if (t->tx_buf != NULL) { if (t->tx_buf != NULL) {
chconf |= OMAP2_MCSPI_CHCONF_FFET; chconf |= OMAP2_MCSPI_CHCONF_FFET;
if (dma_enabled)
xferlevel |= bytes_per_word - 1; xferlevel |= bytes_per_word - 1;
else
xferlevel |= (max_fifo_depth - 1);
} }
mcspi_write_reg(ctlr, OMAP2_MCSPI_XFERLEVEL, xferlevel); mcspi_write_reg(ctlr, OMAP2_MCSPI_XFERLEVEL, xferlevel);
...@@ -882,6 +892,113 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -882,6 +892,113 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
return count - c; 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) static u32 omap2_mcspi_calc_divisor(u32 speed_hz, u32 ref_clk_hz)
{ {
u32 div; u32 div;
...@@ -1206,7 +1323,9 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, ...@@ -1206,7 +1323,9 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
ctlr->cur_msg_mapped && ctlr->cur_msg_mapped &&
ctlr->can_dma(ctlr, spi, t)) 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); omap2_mcspi_set_enable(spi, 1);
...@@ -1219,6 +1338,8 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, ...@@ -1219,6 +1338,8 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
ctlr->cur_msg_mapped && ctlr->cur_msg_mapped &&
ctlr->can_dma(ctlr, spi, t)) ctlr->can_dma(ctlr, spi, t))
count = omap2_mcspi_txrx_dma(spi, t); count = omap2_mcspi_txrx_dma(spi, t);
else if (mcspi->fifo_depth > 0)
count = omap2_mcspi_txrx_piofifo(spi, t);
else else
count = omap2_mcspi_txrx_pio(spi, t); count = omap2_mcspi_txrx_pio(spi, t);
...@@ -1231,7 +1352,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, ...@@ -1231,7 +1352,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
omap2_mcspi_set_enable(spi, 0); omap2_mcspi_set_enable(spi, 0);
if (mcspi->fifo_depth > 0) if (mcspi->fifo_depth > 0)
omap2_mcspi_set_fifo(spi, t, 0); omap2_mcspi_set_fifo(spi, t, 0, 0);
out: out:
/* Restore defaults if they were overriden */ /* Restore defaults if they were overriden */
...@@ -1254,7 +1375,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, ...@@ -1254,7 +1375,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH)); omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
if (mcspi->fifo_depth > 0 && t) if (mcspi->fifo_depth > 0 && t)
omap2_mcspi_set_fifo(spi, t, 0); omap2_mcspi_set_fifo(spi, t, 0, 0);
return status; return status;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment