提交 d33f473d 编写于 作者: I Illia Smyrnov 提交者: Mark Brown

spi: omap2-mcspi: Add FIFO buffer support

The MCSPI controller has a built-in FIFO buffer to unload the DMA or interrupt
handler and improve data throughput. This patch adds FIFO buffer support for SPI
transfers in DMA mode.

For SPI transfers in DMA mode, the largest possible FIFO buffer size will be
calculated and set up. The FIFO won't be used for the SPI transfers in DMA mode
if: calculated FIFO buffer size is less then 2 bytes or the FIFO buffer size
isn't multiple of the SPI word length.
Signed-off-by: NIllia Smyrnov <illia.smyrnov@ti.com>
Signed-off-by: NMark Brown <broonie@linaro.org>
上级 56cd5c15
...@@ -38,12 +38,15 @@ ...@@ -38,12 +38,15 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/gcd.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/platform_data/spi-omap2-mcspi.h> #include <linux/platform_data/spi-omap2-mcspi.h>
#define OMAP2_MCSPI_MAX_FREQ 48000000 #define OMAP2_MCSPI_MAX_FREQ 48000000
#define OMAP2_MCSPI_MAX_FIFODEPTH 64
#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF
#define SPI_AUTOSUSPEND_TIMEOUT 2000 #define SPI_AUTOSUSPEND_TIMEOUT 2000
#define OMAP2_MCSPI_REVISION 0x00 #define OMAP2_MCSPI_REVISION 0x00
...@@ -53,6 +56,7 @@ ...@@ -53,6 +56,7 @@
#define OMAP2_MCSPI_WAKEUPENABLE 0x20 #define OMAP2_MCSPI_WAKEUPENABLE 0x20
#define OMAP2_MCSPI_SYST 0x24 #define OMAP2_MCSPI_SYST 0x24
#define OMAP2_MCSPI_MODULCTRL 0x28 #define OMAP2_MCSPI_MODULCTRL 0x28
#define OMAP2_MCSPI_XFERLEVEL 0x7c
/* per-channel banks, 0x14 bytes each, first is: */ /* per-channel banks, 0x14 bytes each, first is: */
#define OMAP2_MCSPI_CHCONF0 0x2c #define OMAP2_MCSPI_CHCONF0 0x2c
...@@ -62,6 +66,7 @@ ...@@ -62,6 +66,7 @@
#define OMAP2_MCSPI_RX0 0x3c #define OMAP2_MCSPI_RX0 0x3c
/* per-register bitmasks: */ /* per-register bitmasks: */
#define OMAP2_MCSPI_IRQSTATUS_EOW BIT(17)
#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)
...@@ -82,10 +87,13 @@ ...@@ -82,10 +87,13 @@
#define OMAP2_MCSPI_CHCONF_IS BIT(18) #define OMAP2_MCSPI_CHCONF_IS BIT(18)
#define OMAP2_MCSPI_CHCONF_TURBO BIT(19) #define OMAP2_MCSPI_CHCONF_TURBO BIT(19)
#define OMAP2_MCSPI_CHCONF_FORCE BIT(20) #define OMAP2_MCSPI_CHCONF_FORCE BIT(20)
#define OMAP2_MCSPI_CHCONF_FFET BIT(27)
#define OMAP2_MCSPI_CHCONF_FFER BIT(28)
#define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_RXS BIT(0)
#define OMAP2_MCSPI_CHSTAT_TXS BIT(1) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1)
#define OMAP2_MCSPI_CHSTAT_EOT BIT(2) #define OMAP2_MCSPI_CHSTAT_EOT BIT(2)
#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3)
#define OMAP2_MCSPI_CHCTRL_EN BIT(0) #define OMAP2_MCSPI_CHCTRL_EN BIT(0)
...@@ -128,6 +136,7 @@ struct omap2_mcspi { ...@@ -128,6 +136,7 @@ struct omap2_mcspi {
struct omap2_mcspi_dma *dma_channels; struct omap2_mcspi_dma *dma_channels;
struct device *dev; struct device *dev;
struct omap2_mcspi_regs ctx; struct omap2_mcspi_regs ctx;
int fifo_depth;
unsigned int pin_dir:1; unsigned int pin_dir:1;
}; };
...@@ -257,6 +266,58 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) ...@@ -257,6 +266,58 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master)
ctx->modulctrl = l; ctx->modulctrl = l;
} }
static void omap2_mcspi_set_fifo(const struct spi_device *spi,
struct spi_transfer *t, int enable)
{
struct spi_master *master = spi->master;
struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi *mcspi;
unsigned int wcnt;
int fifo_depth, bytes_per_word;
u32 chconf, xferlevel;
mcspi = spi_master_get_devdata(master);
chconf = mcspi_cached_chconf0(spi);
if (enable) {
bytes_per_word = mcspi_bytes_per_word(cs->word_len);
if (t->len % bytes_per_word != 0)
goto disable_fifo;
fifo_depth = gcd(t->len, OMAP2_MCSPI_MAX_FIFODEPTH);
if (fifo_depth < 2 || fifo_depth % bytes_per_word != 0)
goto disable_fifo;
wcnt = t->len / bytes_per_word;
if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT)
goto disable_fifo;
xferlevel = wcnt << 16;
if (t->rx_buf != NULL) {
chconf |= OMAP2_MCSPI_CHCONF_FFER;
xferlevel |= (fifo_depth - 1) << 8;
} else {
chconf |= OMAP2_MCSPI_CHCONF_FFET;
xferlevel |= fifo_depth - 1;
}
mcspi_write_reg(master, OMAP2_MCSPI_XFERLEVEL, xferlevel);
mcspi_write_chconf0(spi, chconf);
mcspi->fifo_depth = fifo_depth;
return;
}
disable_fifo:
if (t->rx_buf != NULL)
chconf &= ~OMAP2_MCSPI_CHCONF_FFER;
else
chconf &= ~OMAP2_MCSPI_CHCONF_FFET;
mcspi_write_chconf0(spi, chconf);
mcspi->fifo_depth = 0;
}
static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
{ {
struct spi_master *spi_cntrl = mcspi->master; struct spi_master *spi_cntrl = mcspi->master;
...@@ -373,7 +434,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, ...@@ -373,7 +434,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
{ {
struct omap2_mcspi *mcspi; struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_dma *mcspi_dma;
unsigned int count; unsigned int count, dma_count;
u32 l; u32 l;
int elements = 0; int elements = 0;
int word_len, element_count; int word_len, element_count;
...@@ -381,6 +442,11 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, ...@@ -381,6 +442,11 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
mcspi = spi_master_get_devdata(spi->master); mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select]; mcspi_dma = &mcspi->dma_channels[spi->chip_select];
count = xfer->len; count = xfer->len;
dma_count = xfer->len;
if (mcspi->fifo_depth == 0)
dma_count -= es;
word_len = cs->word_len; word_len = cs->word_len;
l = mcspi_cached_chconf0(spi); l = mcspi_cached_chconf0(spi);
...@@ -394,16 +460,15 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, ...@@ -394,16 +460,15 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
if (mcspi_dma->dma_rx) { if (mcspi_dma->dma_rx) {
struct dma_async_tx_descriptor *tx; struct dma_async_tx_descriptor *tx;
struct scatterlist sg; struct scatterlist sg;
size_t len = xfer->len - es;
dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
if (l & OMAP2_MCSPI_CHCONF_TURBO) if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0)
len -= es; dma_count -= es;
sg_init_table(&sg, 1); sg_init_table(&sg, 1);
sg_dma_address(&sg) = xfer->rx_dma; sg_dma_address(&sg) = xfer->rx_dma;
sg_dma_len(&sg) = len; sg_dma_len(&sg) = dma_count;
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT |
...@@ -423,6 +488,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, ...@@ -423,6 +488,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
wait_for_completion(&mcspi_dma->dma_rx_completion); wait_for_completion(&mcspi_dma->dma_rx_completion);
dma_unmap_single(mcspi->dev, xfer->rx_dma, count, dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
if (mcspi->fifo_depth > 0)
return count;
omap2_mcspi_set_enable(spi, 0); omap2_mcspi_set_enable(spi, 0);
elements = element_count - 1; elements = element_count - 1;
...@@ -481,7 +550,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -481,7 +550,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
struct dma_slave_config cfg; struct dma_slave_config cfg;
enum dma_slave_buswidth width; enum dma_slave_buswidth width;
unsigned es; unsigned es;
u32 burst;
void __iomem *chstat_reg; void __iomem *chstat_reg;
void __iomem *irqstat_reg;
int wait_res;
mcspi = spi_master_get_devdata(spi->master); mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select]; mcspi_dma = &mcspi->dma_channels[spi->chip_select];
...@@ -499,19 +571,27 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -499,19 +571,27 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
es = 4; es = 4;
} }
count = xfer->len;
burst = 1;
if (mcspi->fifo_depth > 0) {
if (count > mcspi->fifo_depth)
burst = mcspi->fifo_depth / es;
else
burst = count / es;
}
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
cfg.src_addr_width = width; cfg.src_addr_width = width;
cfg.dst_addr_width = width; cfg.dst_addr_width = width;
cfg.src_maxburst = 1; cfg.src_maxburst = burst;
cfg.dst_maxburst = 1; cfg.dst_maxburst = burst;
rx = xfer->rx_buf; rx = xfer->rx_buf;
tx = xfer->tx_buf; tx = xfer->tx_buf;
count = xfer->len;
if (tx != NULL) if (tx != NULL)
omap2_mcspi_tx_dma(spi, xfer, cfg); omap2_mcspi_tx_dma(spi, xfer, cfg);
...@@ -519,18 +599,38 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -519,18 +599,38 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); count = omap2_mcspi_rx_dma(spi, xfer, cfg, es);
if (tx != NULL) { if (tx != NULL) {
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
wait_for_completion(&mcspi_dma->dma_tx_completion); wait_for_completion(&mcspi_dma->dma_tx_completion);
dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (mcspi->fifo_depth > 0) {
irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS;
if (mcspi_wait_for_reg_bit(irqstat_reg,
OMAP2_MCSPI_IRQSTATUS_EOW) < 0)
dev_err(&spi->dev, "EOW timed out\n");
mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS,
OMAP2_MCSPI_IRQSTATUS_EOW);
}
/* for TX_ONLY mode, be sure all words have shifted out */ /* for TX_ONLY mode, be sure all words have shifted out */
if (rx == NULL) { if (rx == NULL) {
if (mcspi_wait_for_reg_bit(chstat_reg, chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
OMAP2_MCSPI_CHSTAT_TXS) < 0) if (mcspi->fifo_depth > 0) {
dev_err(&spi->dev, "TXS timed out\n"); wait_res = mcspi_wait_for_reg_bit(chstat_reg,
else if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXFFE);
OMAP2_MCSPI_CHSTAT_EOT) < 0) if (wait_res < 0)
dev_err(&spi->dev, "TXFFE timed out\n");
} else {
wait_res = mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_TXS);
if (wait_res < 0)
dev_err(&spi->dev, "TXS timed out\n");
}
if (wait_res >= 0 &&
(mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_EOT) < 0))
dev_err(&spi->dev, "EOT timed out\n"); dev_err(&spi->dev, "EOT timed out\n");
} }
} }
...@@ -957,7 +1057,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) ...@@ -957,7 +1057,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
cs = spi->controller_state; cs = spi->controller_state;
cd = spi->controller_data; cd = spi->controller_data;
omap2_mcspi_set_enable(spi, 1); omap2_mcspi_set_enable(spi, 0);
list_for_each_entry(t, &m->transfers, transfer_list) { list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
status = -EINVAL; status = -EINVAL;
...@@ -1005,6 +1105,12 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) ...@@ -1005,6 +1105,12 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
if (t->len) { if (t->len) {
unsigned count; unsigned count;
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
(m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
omap2_mcspi_set_fifo(spi, t, 1);
omap2_mcspi_set_enable(spi, 1);
/* RX_ONLY mode needs dummy data in TX reg */ /* RX_ONLY mode needs dummy data in TX reg */
if (t->tx_buf == NULL) if (t->tx_buf == NULL)
__raw_writel(0, cs->base __raw_writel(0, cs->base
...@@ -1031,6 +1137,11 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) ...@@ -1031,6 +1137,11 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
omap2_mcspi_force_cs(spi, 0); omap2_mcspi_force_cs(spi, 0);
cs_active = 0; cs_active = 0;
} }
omap2_mcspi_set_enable(spi, 0);
if (mcspi->fifo_depth > 0)
omap2_mcspi_set_fifo(spi, t, 0);
} }
/* Restore defaults if they were overriden */ /* Restore defaults if they were overriden */
if (par_override) { if (par_override) {
...@@ -1051,8 +1162,10 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) ...@@ -1051,8 +1162,10 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
omap2_mcspi_set_enable(spi, 0); omap2_mcspi_set_enable(spi, 0);
m->status = status; if (mcspi->fifo_depth > 0 && t)
omap2_mcspi_set_fifo(spi, t, 0);
m->status = status;
} }
static int omap2_mcspi_transfer_one_message(struct spi_master *master, static int omap2_mcspi_transfer_one_message(struct spi_master *master,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册