[PATCH v3 1/3] spi: sifive: Fix QPP transfer
Bin Meng
bmeng.cn at gmail.com
Mon Apr 20 16:01:27 CEST 2020
Hi Jagan,
On Mon, Apr 20, 2020 at 8:52 PM Jagan Teki <jagan at amarulasolutions.com> wrote:
>
> For historical reasons the existing logic of filling tx fifo
What historical reasons?
> with data, rx fifo with NULL for tx transfer and filling rx
> fifo with data, tx fifo with NULL for rx transfer is not
> clear enough to support the Quad Page Program.
>
> SiFive SPI controllers have specific sets of watermark
> registers and SPI I/O directions bits in order to program
> SPI controllers clear enough to support all sets of operating
> modes.
>
> Here is the exact programing sequence that would follow on this
> patch and tested via SPI-NOR and MMC_SPI.
>
> - set the frame format proto, endian
> - set the frame format dir, set it for tx and clear it for rx
> - TX transfer:
> fill tx fifo with data.
> wait for TX watermark bit to clear.
> - TX transfer:
RX transfer ?
> fill tx fifo with 0xff.
rx fifo ?
> write nbytes to rx watermark register
> wait for rx watermark bit to clear.
> read the rx fifo data.
>
> So, this patch adopts this program sequence and fixes the existing
> I/O direction bit.
>
> Signed-off-by: Jagan Teki <jagan at amarulasolutions.com>
> ---
> Changes for v3:
> - new patch
>
> drivers/spi/spi-sifive.c | 57 ++++++++++++++++++++++++++--------------
> 1 file changed, 37 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
> index 336b683476..2a0b28dc08 100644
> --- a/drivers/spi/spi-sifive.c
> +++ b/drivers/spi/spi-sifive.c
> @@ -10,6 +10,7 @@
> #include <dm.h>
> #include <malloc.h>
> #include <spi.h>
> +#include <wait_bit.h>
> #include <asm/io.h>
> #include <linux/log2.h>
> #include <clk.h>
> @@ -127,8 +128,8 @@ static void sifive_spi_clear_cs(struct sifive_spi *spi)
> }
>
> static void sifive_spi_prep_transfer(struct sifive_spi *spi,
> - bool is_rx_xfer,
> - struct dm_spi_slave_platdata *slave_plat)
> + struct dm_spi_slave_platdata *slave_plat,
> + u8 *rx_ptr)
> {
> u32 cr;
>
> @@ -160,7 +161,7 @@ static void sifive_spi_prep_transfer(struct sifive_spi *spi,
>
> /* SPI direction in/out ? */
> cr &= ~SIFIVE_SPI_FMT_DIR;
> - if (!is_rx_xfer)
> + if (!rx_ptr)
> cr |= SIFIVE_SPI_FMT_DIR;
>
> writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
> @@ -191,13 +192,19 @@ static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
> writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
> }
>
> +static int sifive_spi_wait(struct sifive_spi *spi, u32 bit)
> +{
> + return wait_for_bit_le32(spi->regs + SIFIVE_SPI_REG_IP,
> + bit, true, 100, false);
> +}
> +
> static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
> const void *dout, void *din, unsigned long flags)
> {
> struct udevice *bus = dev->parent;
> struct sifive_spi *spi = dev_get_priv(bus);
> struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
> - const unsigned char *tx_ptr = dout;
> + const u8 *tx_ptr = dout;
> u8 *rx_ptr = din;
> u32 remaining_len;
> int ret;
> @@ -210,31 +217,37 @@ static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
> return ret;
> }
>
> - sifive_spi_prep_transfer(spi, true, slave_plat);
> + sifive_spi_prep_transfer(spi, slave_plat, rx_ptr);
>
> remaining_len = bitlen / 8;
>
> while (remaining_len) {
> - int n_words, tx_words, rx_words;
> -
> - n_words = min(remaining_len, spi->fifo_depth);
> + unsigned int n_words = min(remaining_len, spi->fifo_depth);
> + unsigned int tx_words, rx_words;
>
> /* Enqueue n_words for transmission */
> - if (tx_ptr) {
> - for (tx_words = 0; tx_words < n_words; ++tx_words) {
> - sifive_spi_tx(spi, tx_ptr);
> - sifive_spi_rx(spi, NULL);
> - tx_ptr++;
> - }
> + for (tx_words = 0; tx_words < n_words; tx_words++) {
> + if (!tx_ptr)
> + sifive_spi_tx(spi, NULL);
> + else
> + sifive_spi_tx(spi, tx_ptr++);
> }
>
> - /* Read out all the data from the RX FIFO */
> if (rx_ptr) {
> - for (rx_words = 0; rx_words < n_words; ++rx_words) {
> - sifive_spi_tx(spi, NULL);
> - sifive_spi_rx(spi, rx_ptr);
> - rx_ptr++;
> - }
> + /* Wait for transmission + reception to complete */
> + writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
> + ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);
> + if (ret)
> + return ret;
> +
> + /* Read out all the data from the RX FIFO */
> + for (rx_words = 0; rx_words < n_words; rx_words++)
> + sifive_spi_rx(spi, rx_ptr++);
> + } else {
> + /* Wait for transmission to complete */
> + ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM);
> + if (ret)
> + return ret;
> }
>
> remaining_len -= n_words;
> @@ -314,6 +327,10 @@ static void sifive_spi_init_hw(struct sifive_spi *spi)
> /* Watermark interrupts are disabled by default */
> writel(0, spi->regs + SIFIVE_SPI_REG_IE);
>
> + /* Default watermark FIFO threshold values */
> + writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK);
> + writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK);
> +
> /* Set CS/SCK Delays and Inactive Time to defaults */
> writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
> spi->regs + SIFIVE_SPI_REG_DELAY0);
Regards,
Bin
More information about the U-Boot
mailing list