From db45871299f836313cbafb72b01ed2f66020222c Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Sun, 29 Jan 2023 16:43:11 +0800 Subject: [PATCH] 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 --- drivers/spi/spi-rockchip.c | 126 +++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 54ca91585711..686749edfd1b 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -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: