Commit 37ffab81 authored by Keiji Hayashibara's avatar Keiji Hayashibara Committed by Mark Brown

spi: uniphier: introduce polling mode

Introduce new polling mode for short size transfer. Either the estimated
transfer time is estimated to exceed 200us, or polling loop actually exceeds
200us, it switches to irq mode.
Signed-off-by: default avatarKeiji Hayashibara <hayashibara.keiji@socionext.com>
Link: https://lore.kernel.org/r/1567488661-11428-4-git-send-email-hayashibara.keiji@socionext.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 151d0eaf
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#define SSI_TIMEOUT_MS 2000 #define SSI_TIMEOUT_MS 2000
#define SSI_POLL_TIMEOUT_US 200
#define SSI_MAX_CLK_DIVIDER 254 #define SSI_MAX_CLK_DIVIDER 254
#define SSI_MIN_CLK_DIVIDER 4 #define SSI_MIN_CLK_DIVIDER 4
...@@ -289,21 +291,23 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv) ...@@ -289,21 +291,23 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv)
static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv)
{ {
unsigned int tx_count; unsigned int fifo_threshold, fill_bytes;
u32 val; u32 val;
tx_count = DIV_ROUND_UP(priv->tx_bytes, fifo_threshold = DIV_ROUND_UP(priv->rx_bytes,
bytes_per_word(priv->bits_per_word)); bytes_per_word(priv->bits_per_word));
tx_count = min(tx_count, SSI_FIFO_DEPTH); fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH);
fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes);
/* set fifo threshold */ /* set fifo threshold */
val = readl(priv->base + SSI_FC); val = readl(priv->base + SSI_FC);
val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK);
val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold);
val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold);
writel(val, priv->base + SSI_FC); writel(val, priv->base + SSI_FC);
while (tx_count--) while (fill_bytes--)
uniphier_spi_send(priv); uniphier_spi_send(priv);
} }
...@@ -322,7 +326,7 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) ...@@ -322,7 +326,7 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable)
writel(val, priv->base + SSI_FPS); writel(val, priv->base + SSI_FPS);
} }
static int uniphier_spi_transfer_one(struct spi_master *master, static int uniphier_spi_transfer_one_irq(struct spi_master *master,
struct spi_device *spi, struct spi_device *spi,
struct spi_transfer *t) struct spi_transfer *t)
{ {
...@@ -330,12 +334,6 @@ static int uniphier_spi_transfer_one(struct spi_master *master, ...@@ -330,12 +334,6 @@ static int uniphier_spi_transfer_one(struct spi_master *master,
struct device *dev = master->dev.parent; struct device *dev = master->dev.parent;
unsigned long time_left; unsigned long time_left;
/* Terminate and return success for 0 byte length transfer */
if (!t->len)
return 0;
uniphier_spi_setup_transfer(spi, t);
reinit_completion(&priv->xfer_done); reinit_completion(&priv->xfer_done);
uniphier_spi_fill_tx_fifo(priv); uniphier_spi_fill_tx_fifo(priv);
...@@ -355,6 +353,59 @@ static int uniphier_spi_transfer_one(struct spi_master *master, ...@@ -355,6 +353,59 @@ static int uniphier_spi_transfer_one(struct spi_master *master,
return priv->error; return priv->error;
} }
static int uniphier_spi_transfer_one_poll(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *t)
{
struct uniphier_spi_priv *priv = spi_master_get_devdata(master);
int loop = SSI_POLL_TIMEOUT_US * 10;
while (priv->tx_bytes) {
uniphier_spi_fill_tx_fifo(priv);
while ((priv->rx_bytes - priv->tx_bytes) > 0) {
while (!(readl(priv->base + SSI_SR) & SSI_SR_RNE)
&& loop--)
ndelay(100);
if (loop == -1)
goto irq_transfer;
uniphier_spi_recv(priv);
}
}
return 0;
irq_transfer:
return uniphier_spi_transfer_one_irq(master, spi, t);
}
static int uniphier_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *t)
{
struct uniphier_spi_priv *priv = spi_master_get_devdata(master);
unsigned long threshold;
/* Terminate and return success for 0 byte length transfer */
if (!t->len)
return 0;
uniphier_spi_setup_transfer(spi, t);
/*
* If the transfer operation will take longer than
* SSI_POLL_TIMEOUT_US, it should use irq.
*/
threshold = DIV_ROUND_UP(SSI_POLL_TIMEOUT_US * priv->speed_hz,
USEC_PER_SEC * BITS_PER_BYTE);
if (t->len > threshold)
return uniphier_spi_transfer_one_irq(master, spi, t);
else
return uniphier_spi_transfer_one_poll(master, spi, t);
}
static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master)
{ {
struct uniphier_spi_priv *priv = spi_master_get_devdata(master); struct uniphier_spi_priv *priv = spi_master_get_devdata(master);
......
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