From 7ddc1c355673e112409626ad4174f2f561bb3c66 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Tue, 30 Jun 2020 14:14:41 +0800 Subject: [PATCH] spi: rockchip_sfc: Support dma xfer prepare Change-Id: I9c3285daf22775fa3ad72e41abcd205c4caaaaa4 Signed-off-by: Jon Lin --- drivers/spi/rockchip_sfc.c | 55 +++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/spi/rockchip_sfc.c b/drivers/spi/rockchip_sfc.c index 7c3f5125cb..9113d7ac12 100644 --- a/drivers/spi/rockchip_sfc.c +++ b/drivers/spi/rockchip_sfc.c @@ -132,6 +132,8 @@ struct rockchip_sfc { unsigned int mode; unsigned int speed_hz; u32 max_iosize; + bool prepare; + u32 last_prepare_size; u32 cmd; u32 addr; u8 addr_bits; @@ -214,6 +216,29 @@ static int rockchip_sfc_reset(struct rockchip_sfc *sfc) return ret; } +static int rockchip_sfc_dma_xfer_wait_finished(struct rockchip_sfc *sfc) +{ + struct rockchip_sfc_reg *regs = sfc->regbase; + int timeout = sfc->last_prepare_size * 10; + unsigned long tbase; + int ret = 0; + int risr; + + tbase = get_timer(0); + do { + udelay(1); + risr = readl(®s->risr); + if (get_timer(tbase) > timeout) { + debug("dma timeout\n"); + ret = -ETIMEDOUT; + break; + } + } while (!(risr & TRANS_FINISH_INT)); + sfc->last_prepare_size = 0; + + return ret; +} + /* The SFC_CTRL register is a global control register, * when the controller is in busy state(SFC_SR), * SFC_CTRL cannot be set. @@ -225,6 +250,9 @@ static int rockchip_sfc_wait_idle(struct rockchip_sfc *sfc, unsigned long tbase = get_timer(0); u32 sr, fsr; + if (sfc->last_prepare_size && rockchip_sfc_dma_xfer_wait_finished(sfc)) + return -ETIMEDOUT; + while (1) { sr = readl(®s->sr); fsr = readl(®s->fsr); @@ -353,6 +381,26 @@ static int rockchip_sfc_dma_xfer(struct rockchip_sfc *sfc, void *buffer, return ret; } +static int rockchip_sfc_dma_xfer_prepare(struct rockchip_sfc *sfc, + void *buffer, size_t trb) +{ + struct rockchip_sfc_reg *regs = sfc->regbase; + + SFC_DBG("sfc_dma_xfer_prepar enter\n"); + + rockchip_sfc_setup_xfer(sfc, trb); + sfc->last_prepare_size = trb; + + flush_dcache_range((unsigned long)buffer, + (unsigned long)buffer + trb); + + writel(0xFFFFFFFF, ®s->iclr); + writel((unsigned long)buffer, ®s->dmaaddr); + writel(SFC_DMA_START, ®s->dmatr); + + return 0; +} + static int rockchip_sfc_wait_fifo_ready(struct rockchip_sfc *sfc, int rw, u32 timeout) { @@ -471,7 +519,10 @@ static int rockchip_sfc_read(struct rockchip_sfc *sfc, u32 offset, while (dma_trans) { trb = min_t(size_t, dma_trans, sfc->max_iosize); - ret = rockchip_sfc_dma_xfer(sfc, buf, trb); + if (sfc->prepare) + ret = rockchip_sfc_dma_xfer_prepare(sfc, buf, len); + else + ret = rockchip_sfc_dma_xfer(sfc, buf, trb); if (ret < 0) return ret; dma_trans -= trb; @@ -581,6 +632,8 @@ static int rockchip_sfc_xfer(struct udevice *dev, unsigned int bitlen, data_buf = NULL; } + sfc->prepare = flags & SPI_XFER_PREPARE ? true : false; + if (sfc->cmd == 0x9f && len == 4) { /* SPI Nand read id */ sfc->addr_bits = SFC_ADDR_XBITS;