]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
arm64: versal: Add versal specific cadence ospi driver
authorT Karthik Reddy <t.karthik.reddy@xilinx.com>
Thu, 12 May 2022 10:05:32 +0000 (04:05 -0600)
committerMichal Simek <michal.simek@amd.com>
Wed, 29 Jun 2022 13:58:48 +0000 (15:58 +0200)
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 <t.karthik.reddy@xilinx.com>
Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
Link: https://lore.kernel.org/r/20220512100535.16364-3-ashok.reddy.soma@xilinx.com
Signed-off-by: Michal Simek <michal.simek@amd.com>
MAINTAINERS
configs/xilinx_versal_virt_defconfig
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/cadence_ospi_versal.c [new file with mode: 0644]
drivers/spi/cadence_qspi.c
drivers/spi/cadence_qspi.h
drivers/spi/cadence_qspi_apb.c

index bfa3bfbc1f4996395df15ec88c2b36a4e5bfd869..4b74866c71f5f4053a54337a2d9b503c8f8be52d 100644 (file)
@@ -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:     (?<!uni)versal
 
index cb10394951cc2e42f5709b7c820cd11217756e9b..c50ea96afc7861309d87defdb70374c1d2d6b0d9 100644 (file)
@@ -106,6 +106,8 @@ CONFIG_XILINX_UARTLITE=y
 CONFIG_SOC_XILINX_VERSAL=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
+CONFIG_CADENCE_QSPI=y
+CONFIG_CADENCE_OSPI_VERSAL=y
 CONFIG_ZYNQ_SPI=y
 CONFIG_USB=y
 CONFIG_DM_USB_GADGET=y
index e48d72d74453f4573d4fa896f96589e41a1a519c..766d5636c09a9ab901504732a94f35653e53b758 100644 (file)
@@ -136,6 +136,14 @@ config CQSPI_REF_CLK
        int "Cadence QSPI reference clock value in Hz"
        depends on HAS_CQSPI_REF_CLK
 
+config CADENCE_OSPI_VERSAL
+       bool "Configure Versal OSPI"
+       depends on ARCH_VERSAL && CADENCE_QSPI
+       imply DM_GPIO
+       help
+         This option is used to enable Versal OSPI DMA operations which
+         are used for ospi flash read using cadence qspi controller.
+
 config CF_SPI
         bool "ColdFire SPI driver"
         help
index 8755408e62982d47b55891bbc4a92e5f5fe83ae9..4de77c260adf8ece653f87dbdfecdf4bdc92002d 100644 (file)
@@ -7,6 +7,7 @@
 ifdef CONFIG_$(SPL_TPL_)DM_SPI
 obj-y += spi-uclass.o
 obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
+obj-$(CONFIG_CADENCE_OSPI_VERSAL) += cadence_ospi_versal.o
 obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
 obj-$(CONFIG_SOFT_SPI) += soft_spi.o
 obj-$(CONFIG_SPI_MEM) += spi-mem.o
diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
new file mode 100644 (file)
index 0000000..4b13beb
--- /dev/null
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2018 Xilinx
+ *
+ * Cadence QSPI controller DMA operations
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <memalign.h>
+#include <wait_bit.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/cache.h>
+#include <cpu_func.h>
+#include <zynqmp_firmware.h>
+#include <asm/arch/hardware.h>
+#include "cadence_qspi.h"
+#include <dt-bindings/power/xlnx-versal-power.h>
+
+#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;
+}
index 7209bb43a776c7d274cce270915973c85b146f38..f1d3050d0767abe69453f7cb4714b3a0aa18d4a2 100644 (file)
 #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)) {
index 4c2148136b5f68e63553740ba1eeb86a10060c32..a201ed7c4e6ee1d04b30dcea44d71b62009c1d90 100644 (file)
@@ -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__ */
index ac8b1bef7fd132d15ba24bd089ebf6178b2c680c..b11bd2d2c61bf148eac9e29530a2722bd58fadcb 100644 (file)
@@ -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;