spi: rockchip: Support cpu polling to complete transmission

The default is DMA and IRQ transmission mode. You can change the
transmission mode to only support cpu polling transmission by adding
"rockchip,poll-only" to the device-tree node.

Change-Id: Icee3f4e899533ee51caab68fb85ec45f64b89d91
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
This commit is contained in:
Jon Lin
2023-01-29 16:43:11 +08:00
parent f4708995d0
commit db45871299

View File

@ -181,6 +181,12 @@
#define ROCKCHIP_SPI_REGISTER_SIZE 0x1000
enum rockchip_spi_xfer_mode {
ROCKCHIP_SPI_DMA,
ROCKCHIP_SPI_IRQ,
ROCKCHIP_SPI_POLL,
};
struct rockchip_spi_quirks {
u32 max_baud_div_in_cpha;
};
@ -213,6 +219,7 @@ struct rockchip_spi {
u8 n_bytes;
u8 rsd;
u8 csm;
bool poll; /* only support transfer data by cpu polling */
bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
@ -233,7 +240,7 @@ static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR);
}
static inline void wait_for_idle(struct rockchip_spi *rs, bool slave_mode)
static inline void wait_for_tx_idle(struct rockchip_spi *rs, bool slave_mode)
{
unsigned long timeout = jiffies + msecs_to_jiffies(5);
u32 busy = SR_BUSY;
@ -397,8 +404,6 @@ static int rockchip_spi_prepare_irq(struct rockchip_spi *rs,
struct spi_controller *ctlr,
struct spi_transfer *xfer)
{
rs->tx = xfer->tx_buf;
rs->rx = xfer->rx_buf;
rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
rs->rx_left = xfer->len / rs->n_bytes;
@ -446,7 +451,7 @@ static void rockchip_spi_dma_txcb(void *data)
return;
/* Wait until the FIFO data completely. */
wait_for_idle(rs, ctlr->slave);
wait_for_tx_idle(rs, ctlr->slave);
spi_enable_chip(rs, false);
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
@ -474,9 +479,6 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
atomic_set(&rs->state, 0);
rs->tx = xfer->tx_buf;
rs->rx = xfer->rx_buf;
rxdesc = NULL;
if (xfer->rx_buf) {
struct dma_slave_config rxconf = {
@ -546,9 +548,59 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
return 1;
}
static void rockchip_spi_config(struct rockchip_spi *rs,
static int rockchip_spi_pio_transfer(struct rockchip_spi *rs,
struct spi_controller *ctlr, struct spi_transfer *xfer)
{
unsigned long time, timeout;
u32 speed_hz = xfer->speed_hz;
unsigned long long ms;
int ret = 0;
if (!speed_hz)
speed_hz = 100000;
ms = 8LL * 1000LL * xfer->len;
do_div(ms, speed_hz);
ms += ms + 200; /* some tolerance */
if (ms > UINT_MAX || ctlr->slave)
ms = UINT_MAX;
timeout = jiffies + msecs_to_jiffies(ms);
time = jiffies;
rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
rs->rx_left = rs->rx ? xfer->len / rs->n_bytes : 0;
spi_enable_chip(rs, true);
while (rs->tx_left || rs->rx_left) {
if (rs->tx)
rockchip_spi_pio_writer(rs);
if (rs->rx)
rockchip_spi_pio_reader(rs);
cpu_relax();
if (time_after(time, timeout)) {
ret = -EIO;
goto out;
}
};
/* If tx, wait until the FIFO data completely. */
if (rs->tx)
wait_for_tx_idle(rs, ctlr->slave);
out:
spi_enable_chip(rs, false);
return ret;
}
static int rockchip_spi_config(struct rockchip_spi *rs,
struct spi_device *spi, struct spi_transfer *xfer,
bool use_dma, bool slave_mode)
enum rockchip_spi_xfer_mode xfer_mode, bool slave_mode)
{
u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET
| CR0_BHT_8BIT << CR0_BHT_OFFSET
@ -569,12 +621,23 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
if (spi->mode & SPI_CS_HIGH)
cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
if (xfer->rx_buf && xfer->tx_buf)
if (xfer->rx_buf && xfer->tx_buf) {
cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
else if (xfer->rx_buf)
} else if (xfer->rx_buf) {
cr0 |= CR0_XFM_RO << CR0_XFM_OFFSET;
else if (use_dma)
cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET;
} else if (xfer->tx_buf) {
/*
* Use the water line of rx fifo in full duplex mode to trigger
* the interruption of tx irq transmission completion.
*/
if (xfer_mode == ROCKCHIP_SPI_IRQ)
cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
else
cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET;
} else {
dev_err(rs->dev, "no transmission buffer\n");
return -EINVAL;
}
switch (xfer->bits_per_word) {
case 4:
@ -597,7 +660,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
unreachable();
}
if (use_dma) {
if (xfer_mode == ROCKCHIP_SPI_DMA) {
if (xfer->tx_buf)
dmacr |= TF_DMA_EN;
if (xfer->rx_buf)
@ -654,6 +717,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
writel_relaxed(2 * DIV_ROUND_UP(rs->freq, 2 * xfer->speed_hz),
rs->regs + ROCKCHIP_SPI_BAUDR);
rs->speed_hz = xfer->speed_hz;
return 0;
}
static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
@ -719,7 +784,9 @@ static int rockchip_spi_transfer_one(
struct spi_transfer *xfer)
{
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
int ret;
bool use_dma;
enum rockchip_spi_xfer_mode xfer_mode;
/* Zero length transfers won't trigger an interrupt on completion */
if (!xfer->len) {
@ -742,14 +809,31 @@ static int rockchip_spi_transfer_one(
rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;
rs->xfer = xfer;
use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
if (rs->poll) {
xfer_mode = ROCKCHIP_SPI_POLL;
} else {
use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
if (use_dma)
xfer_mode = ROCKCHIP_SPI_DMA;
else
xfer_mode = ROCKCHIP_SPI_IRQ;
}
rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
ret = rockchip_spi_config(rs, spi, xfer, xfer_mode, ctlr->slave);
if (ret)
return ret;
if (use_dma)
rs->tx = xfer->tx_buf;
rs->rx = xfer->rx_buf;
switch (xfer_mode) {
case ROCKCHIP_SPI_POLL:
return rockchip_spi_pio_transfer(rs, ctlr, xfer);
case ROCKCHIP_SPI_DMA:
return rockchip_spi_prepare_dma(rs, ctlr, xfer);
return rockchip_spi_prepare_irq(rs, ctlr, xfer);
default:
return rockchip_spi_prepare_irq(rs, ctlr, xfer);
}
}
static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
@ -1042,6 +1126,8 @@ static int rockchip_spi_probe(struct platform_device *pdev)
ctlr->can_dma = rockchip_spi_can_dma;
}
rs->poll = device_property_read_bool(&pdev->dev, "rockchip,poll-only");
switch (rs->version) {
case ROCKCHIP_SPI_VER2_TYPE1:
case ROCKCHIP_SPI_VER2_TYPE2:
@ -1084,6 +1170,8 @@ static int rockchip_spi_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "register misc device %s\n", misc_name);
}
dev_info(rs->dev, "probed, poll=%d, rsd=%d\n", rs->poll, rs->rsd);
return 0;
err_free_dma_rx: