From cf553bf20e51951110f88501577c7fe8bbf68386 Mon Sep 17 00:00:00 2001 From: T Karthik Reddy Date: Thu, 12 May 2022 04:05:32 -0600 Subject: [PATCH] arm64: versal: Add versal specific cadence ospi driver Add support for cadence ospi driver for Versal platform. This driver provides support for DMA read operation which utilizes cadence qspi driver. If "cdns,is-dma" DT property is specified use dma for read operation from cadence_qspi driver. As cadence_qspi_apb_dma_read() is defined in cadence_ospi_versal driver add a weak function defination in cadence_qspi driver. Signed-off-by: T Karthik Reddy Signed-off-by: Ashok Reddy Soma Link: https://lore.kernel.org/r/20220512100535.16364-3-ashok.reddy.soma@xilinx.com Signed-off-by: Michal Simek --- MAINTAINERS | 1 + configs/xilinx_versal_virt_defconfig | 2 + drivers/spi/Kconfig | 8 ++ drivers/spi/Makefile | 1 + drivers/spi/cadence_ospi_versal.c | 127 +++++++++++++++++++++++++++ drivers/spi/cadence_qspi.c | 16 +++- drivers/spi/cadence_qspi.h | 5 ++ drivers/spi/cadence_qspi_apb.c | 3 +- 8 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 drivers/spi/cadence_ospi_versal.c diff --git a/MAINTAINERS b/MAINTAINERS index bfa3bfbc1f..4b74866c71 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -640,6 +640,7 @@ T: git https://source.denx.de/u-boot/custodians/u-boot-microblaze.git F: arch/arm/mach-versal/ F: drivers/net/xilinx_axi_mrmac.* F: drivers/soc/soc_xilinx_versal.c +F: drivers/spi/cadence_ospi_versal.c F: drivers/watchdog/xilinx_wwdt.c N: (? +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cadence_qspi.h" +#include + +#define CMD_4BYTE_READ 0x13 +#define CMD_4BYTE_FAST_READ 0x0C + +int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat, + const struct spi_mem_op *op) +{ + u32 reg, ret, rx_rem, n_rx, bytes_to_dma, data; + u8 opcode, addr_bytes, *rxbuf, dummy_cycles; + + n_rx = op->data.nbytes; + rxbuf = op->data.buf.in; + rx_rem = n_rx % 4; + bytes_to_dma = n_rx - rx_rem; + + if (bytes_to_dma) { + reg = readl(plat->regbase + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_ENBL_DMA; + writel(reg, plat->regbase + CQSPI_REG_CONFIG); + + writel(bytes_to_dma, plat->regbase + CQSPI_REG_INDIRECTRDBYTES); + + writel(CQSPI_DFLT_INDIR_TRIG_ADDR_RANGE, + plat->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE); + writel(CQSPI_DFLT_DMA_PERIPH_CFG, + plat->regbase + CQSPI_REG_DMA_PERIPH_CFG); + writel((unsigned long)rxbuf, plat->regbase + + CQSPI_DMA_DST_ADDR_REG); + writel(plat->trigger_address, plat->regbase + + CQSPI_DMA_SRC_RD_ADDR_REG); + writel(bytes_to_dma, plat->regbase + + CQSPI_DMA_DST_SIZE_REG); + flush_dcache_range((unsigned long)rxbuf, + (unsigned long)rxbuf + bytes_to_dma); + writel(CQSPI_DFLT_DST_CTRL_REG_VAL, + plat->regbase + CQSPI_DMA_DST_CTRL_REG); + + /* Start the indirect read transfer */ + writel(CQSPI_REG_INDIRECTRD_START, plat->regbase + + CQSPI_REG_INDIRECTRD); + /* Wait for dma to complete transfer */ + ret = cadence_qspi_apb_wait_for_dma_cmplt(plat); + if (ret) + return ret; + + /* Clear indirect completion status */ + writel(CQSPI_REG_INDIRECTRD_DONE, plat->regbase + + CQSPI_REG_INDIRECTRD); + rxbuf += bytes_to_dma; + } + + if (rx_rem) { + reg = readl(plat->regbase + CQSPI_REG_CONFIG); + reg &= ~CQSPI_REG_CONFIG_ENBL_DMA; + writel(reg, plat->regbase + CQSPI_REG_CONFIG); + + reg = readl(plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR); + reg += bytes_to_dma; + writel(reg, plat->regbase + CQSPI_REG_CMDADDRESS); + + addr_bytes = readl(plat->regbase + CQSPI_REG_SIZE) & + CQSPI_REG_SIZE_ADDRESS_MASK; + + opcode = CMD_4BYTE_FAST_READ; + dummy_cycles = 8; + writel((dummy_cycles << CQSPI_REG_RD_INSTR_DUMMY_LSB) | opcode, + plat->regbase + CQSPI_REG_RD_INSTR); + + reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; + reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); + reg |= (addr_bytes & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) << + CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; + reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); + dummy_cycles = (readl(plat->regbase + CQSPI_REG_RD_INSTR) >> + CQSPI_REG_RD_INSTR_DUMMY_LSB) & + CQSPI_REG_RD_INSTR_DUMMY_MASK; + reg |= (dummy_cycles & CQSPI_REG_CMDCTRL_DUMMY_MASK) << + CQSPI_REG_CMDCTRL_DUMMY_LSB; + reg |= (((rx_rem - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) << + CQSPI_REG_CMDCTRL_RD_BYTES_LSB); + ret = cadence_qspi_apb_exec_flash_cmd(plat->regbase, reg); + if (ret) + return ret; + + data = readl(plat->regbase + CQSPI_REG_CMDREADDATALOWER); + memcpy(rxbuf, &data, rx_rem); + } + + return 0; +} + +int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_plat *plat) +{ + u32 timeout = CQSPI_DMA_TIMEOUT; + + while (!(readl(plat->regbase + CQSPI_DMA_DST_I_STS_REG) & + CQSPI_DMA_DST_I_STS_DONE) && timeout--) + udelay(1); + + if (!timeout) { + printf("DMA timeout\n"); + return -ETIMEDOUT; + } + + writel(readl(plat->regbase + CQSPI_DMA_DST_I_STS_REG), + plat->regbase + CQSPI_DMA_DST_I_STS_REG); + return 0; +} diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 7209bb43a7..f1d3050d07 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -27,6 +27,12 @@ #define CQSPI_READ 2 #define CQSPI_WRITE 3 +__weak int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat, + const struct spi_mem_op *op) +{ + return 0; +} + static int cadence_spi_write_speed(struct udevice *bus, uint hz) { struct cadence_spi_plat *plat = dev_get_plat(bus); @@ -288,8 +294,12 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi, break; case CQSPI_READ: err = cadence_qspi_apb_read_setup(plat, op); - if (!err) - err = cadence_qspi_apb_read_execute(plat, op); + if (!err) { + if (plat->is_dma) + err = cadence_qspi_apb_dma_read(plat, op); + else + err = cadence_qspi_apb_read_execute(plat, op); + } break; case CQSPI_WRITE: err = cadence_qspi_apb_write_setup(plat, op); @@ -342,6 +352,8 @@ static int cadence_spi_of_to_plat(struct udevice *bus) if (plat->ahbsize >= SZ_8M) plat->use_dac_mode = true; + plat->is_dma = dev_read_bool(bus, "cdns,is-dma"); + /* All other paramters are embedded in the child node */ subnode = dev_read_first_subnode(bus); if (!ofnode_valid(subnode)) { diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 4c2148136b..a201ed7c4e 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -224,6 +224,7 @@ struct cadence_spi_plat { u8 addr_width; u8 data_width; bool dtr; + bool is_dma; }; struct cadence_spi_priv { @@ -278,5 +279,9 @@ void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy); void cadence_qspi_apb_readdata_capture(void *reg_base, unsigned int bypass, unsigned int delay); unsigned int cm_get_qspi_controller_clk_hz(void); +int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat, + const struct spi_mem_op *op); +int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_plat *plat); +int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg); #endif /* __CADENCE_QSPI_H__ */ diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index ac8b1bef7f..b11bd2d2c6 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -336,8 +336,7 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_plat *plat) cadence_qspi_apb_controller_enable(plat->regbase); } -static int cadence_qspi_apb_exec_flash_cmd(void *reg_base, - unsigned int reg) +int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg) { unsigned int retry = CQSPI_REG_RETRY;