patch 1: Add YAML for cadence-qspi devicetree cdocumentation.
patch 2: cadence-qspi controller driver to support QSPI-NAND flash
using existing spi-nand framework with legacy spi protocol.
Ramuthevar Vadivel Murugan (2):
dt-bindings: spi: Add support for cadence-qspi IP Intel LGM SoC
spi: cadence-qspi: Add QSPI support for Intel LGM SoC
.../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 +++
drivers/spi/Kconfig | 9 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-cadence-qspi-apb.c | 644 +++++++++++++++++++++
drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++
drivers/spi/spi-cadence-qspi.c | 461 +++++++++++++++
drivers/spi/spi-cadence-qspi.h | 73 +++
7 files changed, 1446 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
create mode 100644 drivers/spi/spi-cadence-qspi.c
create mode 100644 drivers/spi/spi-cadence-qspi.h
--
2.11.0
From: Ramuthevar Vadivel Murugan <[email protected]>
On Intel Lightening Mountain(LGM) SoCs one of the IP is Cadence QSPI.
SPI flash(NAND) memories interface through QSPI controller.
This patch introduces to specific support for QSPI NAND flash.
Existing cadence drivers do not support SPI-NAND, it only supports to
SPI-NOR and SPI devices. To state that is the driver for the same IP
but due to different SPI flash memory(NAND) need to write from scratch.
Signed-off-by: Ramuthevar Vadivel Murugan <[email protected]>
---
drivers/spi/Kconfig | 9 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-cadence-qspi-apb.c | 644 +++++++++++++++++++++++++++++++++++++
drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++++++
drivers/spi/spi-cadence-qspi.c | 461 ++++++++++++++++++++++++++
drivers/spi/spi-cadence-qspi.h | 73 +++++
6 files changed, 1362 insertions(+)
create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
create mode 100644 drivers/spi/spi-cadence-qspi.c
create mode 100644 drivers/spi/spi-cadence-qspi.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3a1d8f1170de..a3ece0977759 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -192,6 +192,15 @@ config SPI_CADENCE
This selects the Cadence SPI controller master driver
used by Xilinx Zynq and ZynqMP.
+config SPI_CADENCE_QSPI_FLASH
+ bool "Cadence QSPI controller support for QSPI-FLASH"
+ depends on X86
+ depends on OF
+ select MTD_SPI_NAND
+ help
+ This selects the Cadence SPI controller master driver
+ used by Intel lightning Moutaing(LGM) SoC's.
+
config SPI_CLPS711X
tristate "CLPS711X host SPI controller"
depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 63dcab552bcb..8bb6fd20d7e9 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o
+obj-$(CONFIG_SPI_CADENCE_QSPI_FLASH) += spi-cadence-qspi.o spi-cadence-qspi-apb.o
obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
diff --git a/drivers/spi/spi-cadence-qspi-apb.c b/drivers/spi/spi-cadence-qspi-apb.c
new file mode 100644
index 000000000000..476a898de752
--- /dev/null
+++ b/drivers/spi/spi-cadence-qspi-apb.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Cadence QSPI Controller
+ *
+ * Copyright (C) 2012 Altera Corporation
+ * Copyright (C) 2019 Intel Corporation
+ */
+#include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/unaligned/be_byteshift.h>
+
+#include "spi-cadence-qspi.h"
+#include "spi-cadence-qspi-apb.h"
+
+static unsigned int cadence_qspi_wait_idle(void __iomem *reg_base)
+{
+ unsigned int count = 0;
+ unsigned int timeout;
+
+ timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS);
+ while (cadence_qspi_check_timeout(timeout)) {
+ if (CQSPI_REG_IS_IDLE(reg_base)) {
+ count++;
+ if (count >= CQSPI_POLL_IDLE_RETRY)
+ return 1;
+ } else {
+ count = 0;
+ }
+ }
+ return 0;
+}
+
+static void cqspi_apb_readdata_capture(void __iomem *reg_base, u32 delay)
+{
+ u32 reg;
+
+ cqspi_apb_controller_enable(reg_base, 0);
+
+ reg = readl(reg_base + CQSPI_REG_READCAPTURE);
+ reg |= BIT(CQSPI_REG_READCAPTURE_BYPASS_LSB);
+ reg &= ~(CQSPI_REG_READCAPTURE_DELAY_MASK
+ << CQSPI_REG_READCAPTURE_DELAY_LSB);
+ reg |= ((delay & CQSPI_REG_READCAPTURE_DELAY_MASK)
+ << CQSPI_REG_READCAPTURE_DELAY_LSB);
+ writel(reg, reg_base + CQSPI_REG_READCAPTURE);
+
+ cqspi_apb_controller_enable(reg_base, 1);
+}
+
+static void cqspi_apb_config_baudrate_div(void __iomem *reg_base,
+ u32 ref_clk_hz, u32 sclk_hz)
+{
+ u32 reg, div;
+
+ cqspi_apb_controller_enable(reg_base, 0);
+
+ div = DIV_ROUND_UP(ref_clk_hz, 2 * sclk_hz) - 1;
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB);
+ div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
+
+ cqspi_apb_controller_enable(reg_base, 1);
+}
+
+static void cadence_qspi_apb_chipselect(void __iomem *reg_base, u32 chip_select)
+{
+ u32 reg;
+
+ cqspi_apb_controller_enable(reg_base, 0);
+
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
+ chip_select = 0xF & ~(1 << chip_select);
+ reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
+ reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
+
+ cqspi_apb_controller_enable(reg_base, 1);
+}
+
+static int cadence_qspi_apb_exec_flash_cmd(void __iomem *reg_base, u32 reg)
+{
+ u32 timeout;
+
+ writel(reg, reg_base + CQSPI_REG_CMDCTRL);
+ reg |= CQSPI_REG_CMDCTRL_EXECUTE_MASK;
+ writel(reg, reg_base + CQSPI_REG_CMDCTRL);
+
+ timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS);
+ while (cadence_qspi_check_timeout(timeout)) {
+ reg = readl(reg_base + CQSPI_REG_CMDCTRL) &
+ CQSPI_REG_CMDCTRL_INPROGRESS_MASK;
+ if (!reg)
+ break;
+ }
+ if (!cadence_qspi_wait_idle(reg_base) || reg != 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int cqspi_apb_command_read(void __iomem *reg_base, u32 txlen,
+ const u8 *txbuf, u32 addrlen,
+ const u8 *addrbuf, u32 rxlen, u8 *rxbuf)
+{
+ u32 addr_value, read_len, reg;
+ int ret;
+
+ if (!rxlen || rxlen > CQSPI_STIG_DATA_LEN_MAX || !rxbuf)
+ return -EINVAL;
+
+ reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB;
+ reg |= BIT(CQSPI_REG_CMDCTRL_RD_EN_LSB);
+ if (addrlen) {
+ reg |= BIT(CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
+ reg |= ((addrlen - 1) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
+ addr_value = get_unaligned_be32(&addrbuf[0]);
+ writel(addr_value, reg_base + CQSPI_REG_CMDADDRESS);
+ }
+ reg |= (((rxlen - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_RD_BYTES_LSB);
+ ret = cadence_qspi_apb_exec_flash_cmd(reg_base, reg);
+ if (ret != 0)
+ return ret;
+
+ reg = readl(reg_base + CQSPI_REG_CMDREADDATALOWER);
+ read_len = (rxlen > 4) ? 4 : rxlen;
+ memcpy(rxbuf, ®, read_len);
+ rxbuf += read_len;
+
+ if (rxlen > 4) {
+ reg = readl(reg_base + CQSPI_REG_CMDREADDATAUPPER);
+ read_len = rxlen - read_len;
+ memcpy(rxbuf, ®, read_len);
+ }
+
+ return 0;
+}
+
+static int cqspi_apb_command_write(void __iomem *reg_base, u32 txlen,
+ const u8 *txbuf, u32 addrlen,
+ const u8 *addrbuf, u32 datalen,
+ const u8 *databuf)
+{
+ u32 addr_value, reg, data = 0;
+
+ if (!txlen || !txbuf)
+ return -EINVAL;
+
+ reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB;
+ if (datalen != 0) {
+ reg |= BIT(CQSPI_REG_CMDCTRL_WR_EN_LSB);
+ reg |= ((datalen - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_WR_BYTES_LSB;
+ memcpy(&data, txbuf, datalen);
+ writel(data, reg_base + CQSPI_REG_CMDWRITEDATALOWER);
+ }
+ if (addrlen) {
+ reg |= BIT(CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
+ reg |= ((addrlen - 1) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
+ addr_value = get_unaligned_be32(&addrbuf[0]);
+ writel(addr_value, reg_base + CQSPI_REG_CMDADDRESS);
+ }
+
+ return cadence_qspi_apb_exec_flash_cmd(reg_base, reg);
+}
+
+static int cqspi_apb_indirect_read_setup(void __iomem *reg_base,
+ u32 ahb_phy_addr, u32 txlen,
+ const u8 *txbuf, u32 addrlen,
+ const u8 *addrbuf, u32 dummy_bytes,
+ u32 quad_mode)
+{
+ u32 addr_val, dummy_clk, reg;
+
+ writel(ahb_phy_addr, reg_base + CQSPI_REG_INDIRECTTRIGGER);
+ if (addrlen) {
+ addr_val = get_unaligned_be16(&addrbuf[0]);
+ writel(addr_val, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
+ }
+ reg = txbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB;
+ reg |= (quad_mode & CQSPI_REG_RD_INSTR_TYPE_DATA_MASK)
+ << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
+
+ if (dummy_bytes) {
+ if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
+ dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+ reg |= BIT(CQSPI_REG_RD_INSTR_MODE_EN_LSB);
+ writel(0xFF, reg_base + CQSPI_REG_MODE_BIT);
+ dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE;
+ dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
+ if (dummy_clk)
+ reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
+ << CQSPI_REG_RD_INSTR_DUMMY_LSB;
+ }
+ writel(reg, reg_base + CQSPI_REG_RD_INSTR);
+
+ reg = readl(reg_base + CQSPI_REG_SIZE);
+ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
+ reg |= (addrlen - 1);
+ writel(reg, reg_base + CQSPI_REG_SIZE);
+
+ return 0;
+}
+
+static int cqspi_apb_indirect_read_execute(struct struct_cqspi *cadence_qspi,
+ u32 rxlen, u8 *rxbuf)
+{
+ struct platform_device *pdev = cadence_qspi->pdev;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *reg_base = cadence_qspi->iobase;
+ void __iomem *ahb_base = cadence_qspi->qspi_ahb_virt;
+ u8 *rxbuf_end = rxbuf + rxlen;
+ int remaining = (int)rxlen;
+ u32 mod_bytes = rxlen % 4;
+ u32 bytes_to_read = 0;
+ u32 timeout;
+ int ret, status = 0;
+
+ writel(0, reg_base + CQSPI_REG_INDIRECTRDWATERMARK);
+ writel(0xa, reg_base + CQSPI_REG_INDIRECTTRIGGERADDRRANGE);
+ writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES);
+ /* flush previous writes before starting read operation */
+ mb();
+
+ writel((pdata->fifo_depth - CQSPI_REG_SRAM_RESV_WORDS),
+ reg_base + CQSPI_REG_SRAMPARTITION);
+ writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS);
+ writel(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK);
+
+ writel(CQSPI_REG_INDIRECTRD_START_MASK,
+ reg_base + CQSPI_REG_INDIRECTRD);
+ while (remaining > 0) {
+ bytes_to_read = CQSPI_GET_RD_SRAM_LEVEL(reg_base);
+
+ while (bytes_to_read != 0) {
+ unsigned int word_remain = round_down(remaining, 4);
+
+ bytes_to_read *= CQSPI_FIFO_WIDTH;
+ bytes_to_read = bytes_to_read > remaining ?
+ remaining : bytes_to_read;
+ bytes_to_read = round_down(bytes_to_read, 4);
+ if (bytes_to_read) {
+ ioread32_rep(ahb_base, rxbuf,
+ (bytes_to_read / 4));
+ } else if (!word_remain && mod_bytes) {
+ unsigned int temp = ioread32(ahb_base);
+
+ bytes_to_read = mod_bytes;
+ memcpy(rxbuf, &temp, min((unsigned int)
+ (rxbuf_end - rxbuf), bytes_to_read));
+ }
+
+ rxbuf += bytes_to_read;
+ remaining -= bytes_to_read;
+ bytes_to_read = CQSPI_GET_RD_SRAM_LEVEL(reg_base);
+ }
+ }
+
+ /* Check indirect done status */
+ timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS);
+ while (cadence_qspi_check_timeout(timeout)) {
+ status = readl(reg_base + CQSPI_REG_INDIRECTRD);
+ if (status & CQSPI_REG_INDIRECTRD_DONE_MASK)
+ break;
+ }
+ if (!(status & CQSPI_REG_INDIRECTRD_DONE_MASK)) {
+ ret = -ETIMEDOUT;
+ goto failrd;
+ }
+ writel(0, reg_base + CQSPI_REG_IRQMASK);
+ writel(CQSPI_REG_INDIRECTRD_DONE_MASK,
+ reg_base + CQSPI_REG_INDIRECTRD);
+ return 0;
+failrd:
+ writel(0, reg_base + CQSPI_REG_IRQMASK);
+ writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
+ reg_base + CQSPI_REG_INDIRECTRD);
+ return ret;
+}
+
+static int cqspi_apb_indirect_write_setup(void __iomem *reg_base,
+ u32 ahb_phy_addr, u32 txlen,
+ const u8 *txbuf, u32 addrlen,
+ const u8 *addrbuf, u32 quad_mode)
+{
+ u32 reg;
+
+ if (!txbuf)
+ return -EINVAL;
+
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~(CQSPI_REG_CONFIG_DIRECT_MASK);
+ reg &= ~(CQSPI_REG_CONFIG_DMA_MASK);
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
+
+ writel(ahb_phy_addr, reg_base + CQSPI_REG_INDIRECTTRIGGER);
+ reg = txbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB;
+ reg |= BIT(CQSPI_REG_WR_INSTR_WEL_DISABLE_POS);
+ if (quad_mode == QUAD_QIO) {
+ reg |= (quad_mode & CQSPI_REG_WR_INSTR_TYPE_ADDR_MASK) <<
+ CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB;
+ }
+ /* Configure the quad */
+ reg |= (quad_mode & CQSPI_REG_WR_INSTR_TYPE_DATA_MASK) <<
+ CQSPI_REG_WR_INSTR_TYPE_DATA_LSB;
+ writel(reg, reg_base + CQSPI_REG_WR_INSTR);
+
+ reg = get_unaligned_be32(&addrbuf[0]);
+ writel(reg, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR);
+ reg = readl(reg_base + CQSPI_REG_SIZE);
+ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
+ reg |= ((addrlen - 1) & CQSPI_REG_SIZE_ADDRESS_MASK);
+ writel(reg, reg_base + CQSPI_REG_SIZE);
+
+ return 0;
+}
+
+static int cqspi_apb_indirect_write_execute(struct struct_cqspi *cadence_qspi,
+ u32 txlen, const u8 *txbuf)
+{
+ struct platform_device *pdev = cadence_qspi->pdev;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ struct cqspi_flash_pdata *f_pdata =
+ &pdata->f_pdata[cadence_qspi->current_cs];
+ unsigned int *irq_status = &cadence_qspi->irq_status;
+ void __iomem *reg_base = cadence_qspi->iobase;
+ void __iomem *ahb_base = cadence_qspi->qspi_ahb_virt;
+ unsigned int page_size = f_pdata->page_size;
+ int remaining = (int)txlen;
+ u32 write_bytes, timeout, reg;
+ int ret, status = 0;
+
+ writel(0xa, reg_base + CQSPI_REG_INDIRECTTRIGGERADDRRANGE);
+ writel(0x0, reg_base + CQSPI_REG_INDIRECTWRWATERMARK);
+ reg = readl(reg_base + CQSPI_REG_SIZE);
+ reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB);
+ reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB);
+ reg |= (f_pdata->page_size << CQSPI_REG_SIZE_PAGE_LSB);
+ reg |= (f_pdata->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
+ writel(reg, reg_base + CQSPI_REG_SIZE);
+
+ writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES);
+ writel(CQSPI_REG_SRAM_PARTITION_WR, reg_base + CQSPI_REG_SRAMPARTITION);
+ /* Clear all interrupts. */
+ writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS);
+ writel(CQSPI_IRQ_MASK_WR, reg_base + CQSPI_REG_IRQMASK);
+ writel(CQSPI_REG_INDIRECTWR_START_MASK,
+ reg_base + CQSPI_REG_INDIRECTWR);
+
+ while (remaining > 0) {
+ size_t write_words, mod_bytes;
+
+ write_bytes = remaining > page_size ? page_size : remaining;
+ write_words = write_bytes / 4;
+ mod_bytes = write_bytes % 4;
+
+ if (write_words) {
+ iowrite32_rep(ahb_base, txbuf, write_words);
+ txbuf += (write_words * 4);
+ }
+ if (mod_bytes) {
+ unsigned int temp = 0xFFFFFFFF;
+
+ memcpy(&temp, txbuf, mod_bytes);
+ iowrite32(temp, ahb_base);
+ txbuf += mod_bytes;
+ }
+ ret = wait_event_interruptible_timeout(cadence_qspi->waitqueue,
+ *irq_status &
+ CQSPI_IRQ_MASK_WR,
+ CQSPI_TIMEOUT_MS);
+ if (!ret & (*irq_status & CQSPI_IRQ_STATUS_ERR)) {
+ ret = -ETIMEDOUT;
+ goto failwr;
+ } else {
+ ret = 0;
+ }
+ remaining -= write_bytes;
+ }
+
+ /* Check indirect done status */
+ timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS);
+ while (cadence_qspi_check_timeout(timeout)) {
+ status = readl(reg_base + CQSPI_REG_INDIRECTWR);
+ if (status & CQSPI_REG_INDIRECTWR_DONE_MASK)
+ break;
+ }
+ if (!(status & CQSPI_REG_INDIRECTWR_DONE_MASK)) {
+ ret = -ETIMEDOUT;
+ goto failwr;
+ }
+ return 0;
+
+failwr:
+ writel(0, reg_base + CQSPI_REG_IRQMASK);
+ writel(CQSPI_REG_INDIRECTWR_DONE_MASK,
+ reg_base + CQSPI_REG_INDIRECTWR);
+ if (ret)
+ writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
+ reg_base + CQSPI_REG_INDIRECTWR);
+ return ret;
+}
+
+void cqspi_apb_controller_enable(void __iomem *reg_base, bool enable)
+{
+ u32 reg;
+
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ if (enable)
+ reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
+ else
+ reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
+}
+
+unsigned int cadence_qspi_apb_is_controller_ready(void __iomem *reg_base)
+{
+ return cadence_qspi_wait_idle(reg_base);
+}
+
+void cadence_qspi_apb_controller_init(struct struct_cqspi *cadence_qspi)
+{
+ cqspi_apb_controller_enable(cadence_qspi->iobase, 0);
+ writel(0, cadence_qspi->iobase + CQSPI_REG_REMAP);
+ writel(0, cadence_qspi->iobase + CQSPI_REG_IRQMASK);
+ cqspi_apb_controller_enable(cadence_qspi->iobase, 1);
+}
+
+unsigned int calculate_ticks_for_ns(u32 ref_clk_hz, u32 ns_val)
+{
+ u32 ticks;
+
+ ticks = ref_clk_hz / 1000; /* kHz */
+ ticks = DIV_ROUND_UP(ticks * ns_val, 1000000);
+
+ return ticks;
+}
+
+static void cqspi_apb_delay(struct struct_cqspi *cqspi, u32 ref_clk, u32 sclk)
+{
+ void __iomem *iobase = cqspi->iobase;
+ struct platform_device *pdev = cqspi->pdev;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ struct cqspi_flash_pdata *f_pdata =
+ &pdata->f_pdata[cqspi->current_cs];
+ u32 tshsl, tchsh, tslch, tsd2d, tsclk;
+ u32 ref_clk_ns, sclk_ns, reg;
+
+ cqspi_apb_controller_enable(cqspi->iobase, 0);
+ ref_clk_ns = (1000000000) / ref_clk;
+ sclk_ns = (1000000000) / sclk;
+ tsclk = (ref_clk + sclk - 1) / sclk;
+ tshsl = calculate_ticks_for_ns(ref_clk, f_pdata->tshsl_ns);
+ if (tshsl < tsclk)
+ tshsl = tsclk;
+ tchsh = calculate_ticks_for_ns(ref_clk, f_pdata->tchsh_ns);
+ tslch = calculate_ticks_for_ns(ref_clk, f_pdata->tslch_ns);
+ tsd2d = calculate_ticks_for_ns(ref_clk, f_pdata->tsd2d_ns);
+
+ reg = ((tshsl & CQSPI_REG_DELAY_TSHSL_MASK)
+ << CQSPI_REG_DELAY_TSHSL_LSB);
+ reg |= ((tchsh & CQSPI_REG_DELAY_TCHSH_MASK)
+ << CQSPI_REG_DELAY_TCHSH_LSB);
+ reg |= ((tslch & CQSPI_REG_DELAY_TSLCH_MASK)
+ << CQSPI_REG_DELAY_TSLCH_LSB);
+ reg |= ((tsd2d & CQSPI_REG_DELAY_TSD2D_MASK)
+ << CQSPI_REG_DELAY_TSD2D_LSB);
+ writel(reg, iobase + CQSPI_REG_DELAY);
+ cqspi_apb_controller_enable(cqspi->iobase, 1);
+}
+
+static void cadence_qspi_switch_cs(struct struct_cqspi *cadence_qspi, u32 cs)
+{
+ struct platform_device *pdev = cadence_qspi->pdev;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ struct cqspi_flash_pdata *f_pdata = &pdata->f_pdata[cs];
+ void __iomem *iobase = cadence_qspi->iobase;
+ u32 reg;
+
+ cqspi_apb_controller_enable(iobase, 0);
+
+ reg = readl(iobase + CQSPI_REG_SIZE);
+ reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB);
+ reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB);
+ reg |= (f_pdata->page_size << CQSPI_REG_SIZE_PAGE_LSB);
+ reg |= (f_pdata->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
+ writel(reg, iobase + CQSPI_REG_SIZE);
+ cadence_qspi_apb_chipselect(iobase, cs);
+
+ cqspi_apb_controller_enable(iobase, 1);
+}
+
+int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi,
+ struct spi_device *spi, u32 n_trans,
+ struct spi_transfer **spi_xfer)
+{
+ struct platform_device *pdev = cadence_qspi->pdev;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ struct cqspi_flash_pdata *f_pdata;
+ void __iomem *iobase = cadence_qspi->iobase;
+ struct spi_transfer *cmd_xfer = NULL;
+ struct spi_transfer *data_xfer = NULL;
+ struct spi_transfer *dummy_xfer = NULL;
+ struct spi_transfer *addr_xfer = NULL;
+ int rdid_length, mode, ret, i;
+ u32 sclk;
+
+ for (i = 0; i < n_trans; i++)
+ dev_dbg(&pdev->dev, "xfer[%d] %d\n", i, spi_xfer[i]->len);
+
+ if (cadence_qspi->current_cs != spi->chip_select) {
+ cadence_qspi->current_cs = spi->chip_select;
+ cadence_qspi_switch_cs(cadence_qspi, spi->chip_select);
+ }
+
+ f_pdata = &pdata->f_pdata[cadence_qspi->current_cs];
+ rdid_length = 6;
+
+ switch (n_trans) {
+ case 1:
+ cmd_xfer = spi_xfer[0];
+ break;
+ case 2:
+ cmd_xfer = spi_xfer[0];
+ data_xfer = spi_xfer[1];
+ break;
+ case 3:
+ cmd_xfer = spi_xfer[0];
+ addr_xfer = spi_xfer[1];
+ data_xfer = spi_xfer[2];
+ break;
+ case 4:
+ cmd_xfer = spi_xfer[0];
+ addr_xfer = spi_xfer[1];
+ dummy_xfer = spi_xfer[2];
+ data_xfer = spi_xfer[3];
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported n_trans %u\n", n_trans);
+ return -EINVAL;
+ }
+
+ if (!data_xfer)
+ mode = STIG_WRITE_MODE;
+ else if (data_xfer->len > rdid_length)
+ mode = data_xfer->tx_buf ? IDC_WRITE_MODE : IDC_READ_MODE;
+ else
+ mode = data_xfer->tx_buf ? STIG_WRITE_MODE : STIG_READ_MODE;
+
+ sclk = cmd_xfer->speed_hz ? cmd_xfer->speed_hz : spi->max_speed_hz;
+ cqspi_apb_controller_enable(iobase, 0);
+ cqspi_apb_config_baudrate_div(iobase, pdata->master_ref_clk_hz, sclk);
+ cqspi_apb_delay(cadence_qspi, pdata->master_ref_clk_hz, sclk);
+ cqspi_apb_readdata_capture(iobase, f_pdata->read_delay);
+ cqspi_apb_controller_enable(iobase, 1);
+
+ switch (mode) {
+ case STIG_WRITE_MODE:
+ ret = cqspi_apb_command_write(iobase, cmd_xfer->len,
+ cmd_xfer->tx_buf,
+ addr_xfer ? addr_xfer->len : 0,
+ addr_xfer ?
+ addr_xfer->tx_buf : NULL,
+ data_xfer ? data_xfer->len : 0,
+ data_xfer ?
+ data_xfer->tx_buf : NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "command write error (%i)\n", ret);
+ return ret;
+ }
+ break;
+ case STIG_READ_MODE:
+ ret = cqspi_apb_command_read(iobase, cmd_xfer->len,
+ cmd_xfer->tx_buf,
+ addr_xfer ? addr_xfer->len : 0,
+ addr_xfer ?
+ addr_xfer->tx_buf : NULL,
+ data_xfer ? data_xfer->len : 0,
+ data_xfer ?
+ data_xfer->rx_buf : NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "command read error (%i)\n", ret);
+ return ret;
+ }
+ break;
+ case IDC_WRITE_MODE:
+ ret = cqspi_apb_indirect_write_setup(iobase,
+ pdata->qspi_ahb_phy &
+ pdata->qspi_ahb_mask,
+ cmd_xfer->len,
+ cmd_xfer->tx_buf,
+ addr_xfer ?
+ addr_xfer->len : 0,
+ addr_xfer ?
+ addr_xfer->tx_buf : NULL,
+ f_pdata->quad);
+ ret = cqspi_apb_indirect_write_execute(cadence_qspi,
+ data_xfer->len,
+ data_xfer->tx_buf);
+ if (ret) {
+ dev_err(&pdev->dev, "indirect write error (%i)\n", ret);
+ return ret;
+ }
+ break;
+ case IDC_READ_MODE:
+ ret = cqspi_apb_indirect_read_setup(iobase,
+ pdata->qspi_ahb_phy &
+ pdata->qspi_ahb_mask,
+ cmd_xfer->len,
+ cmd_xfer->tx_buf,
+ addr_xfer ?
+ addr_xfer->len : 0,
+ addr_xfer ?
+ addr_xfer->tx_buf : NULL,
+ dummy_xfer ?
+ dummy_xfer->len : 0,
+ f_pdata->quad);
+ ret = cqspi_apb_indirect_read_execute(cadence_qspi,
+ data_xfer->len,
+ data_xfer->rx_buf);
+ if (ret) {
+ dev_err(&pdev->dev, "indirect read error (%i)\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported mode %u\n", mode);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/spi/spi-cadence-qspi-apb.h b/drivers/spi/spi-cadence-qspi-apb.h
new file mode 100644
index 000000000000..e8e920b50105
--- /dev/null
+++ b/drivers/spi/spi-cadence-qspi-apb.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Cadence QSPI Controller
+ *
+ * Copyright (C) 2012 Altera Corporation
+ * Copyright (C) 2019 Intel Corporation
+ */
+#ifndef __CADENCE_QSPI_APB_H__
+#define __CADENCE_QSPI_APB_H__
+#include "spi-cadence-qspi.h"
+
+/* Operation timeout value */
+#define CQSPI_TIMEOUT_MS 5000
+#define CQSPI_POLL_IDLE_RETRY 3
+#define CQSPI_FIFO_WIDTH 4
+
+/* Controller sram size in word */
+#define CQSPI_REG_SRAM_RESV_WORDS 2
+#define CQSPI_REG_SRAM_PARTITION_WR 1
+#define CQSPI_REG_SRAM_THRESHOLD_BYTES 50
+
+/* Instruction type */
+#define CQSPI_INST_TYPE_SINGLE 0
+#define CQSPI_INST_TYPE_DUAL 1
+#define CQSPI_INST_TYPE_QUAD 2
+#define CQSPI_DUMMY_CLKS_PER_BYTE 8
+#define CQSPI_DUMMY_BYTES_MAX 4
+#define CQSPI_STIG_DATA_LEN_MAX 8
+#define CQSPI_INDIRECTTRIGGER_ADDR_MASK 0xFFFFF
+
+/* Register map */
+#define CQSPI_REG_CONFIG 0x00
+#define CQSPI_REG_CONFIG_ENABLE_MASK BIT(0)
+#define CQSPI_REG_CONFIG_DIRECT_MASK BIT(7)
+#define CQSPI_REG_CONFIG_DECODE_MASK BIT(9)
+#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10
+#define CQSPI_REG_CONFIG_DMA_MASK BIT(15)
+#define CQSPI_REG_CONFIG_BAUD_LSB 19
+#define CQSPI_REG_CONFIG_IDLE_LSB 31
+#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
+#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
+#define CQSPI_REG_RD_INSTR 0x04
+#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0
+#define CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB 8
+#define CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB 12
+#define CQSPI_REG_RD_INSTR_TYPE_DATA_LSB 16
+#define CQSPI_REG_RD_INSTR_MODE_EN_LSB 20
+#define CQSPI_REG_RD_INSTR_DUMMY_LSB 24
+#define CQSPI_REG_RD_INSTR_TYPE_INSTR_MASK 0x3
+#define CQSPI_REG_RD_INSTR_TYPE_ADDR_MASK 0x3
+#define CQSPI_REG_RD_INSTR_TYPE_DATA_MASK 0x3
+#define CQSPI_REG_RD_INSTR_DUMMY_MASK 0x1F
+#define CQSPI_REG_WR_INSTR 0x08
+#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0
+#define CQSPI_REG_WR_INSTR_TYPE_DATA_MASK 0x3
+#define CQSPI_REG_WR_INSTR_TYPE_DATA_LSB 16
+#define CQSPI_REG_WR_INSTR_TYPE_ADDR_MASK 0x3
+#define CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB 12
+
+/*! Field WEL_DIS_FLD - wel_dis_fld */
+#define CQSPI_REG_WR_INSTR_WEL_DISABLE_POS 8
+#define CQSPI_REG_DELAY 0x0C
+#define CQSPI_REG_DELAY_TSLCH_LSB 0
+#define CQSPI_REG_DELAY_TCHSH_LSB 8
+#define CQSPI_REG_DELAY_TSD2D_LSB 16
+#define CQSPI_REG_DELAY_TSHSL_LSB 24
+#define CQSPI_REG_DELAY_TSLCH_MASK 0xFF
+#define CQSPI_REG_DELAY_TCHSH_MASK 0xFF
+#define CQSPI_REG_DELAY_TSD2D_MASK 0xFF
+#define CQSPI_REG_DELAY_TSHSL_MASK 0xFF
+#define CQSPI_REG_READCAPTURE 0x10
+#define CQSPI_REG_READCAPTURE_BYPASS_LSB 0
+#define CQSPI_REG_READCAPTURE_DELAY_LSB 1
+#define CQSPI_REG_READCAPTURE_DELAY_MASK 0xF
+#define CQSPI_REG_SIZE 0x14
+#define CQSPI_REG_SIZE_ADDRESS_LSB 0
+#define CQSPI_REG_SIZE_PAGE_LSB 4
+#define CQSPI_REG_SIZE_BLOCK_LSB 16
+#define CQSPI_REG_SIZE_ADDRESS_MASK 0xF
+#define CQSPI_REG_SIZE_PAGE_MASK 0xFFF
+#define CQSPI_REG_SIZE_BLOCK_MASK 0x3F
+#define CQSPI_REG_SRAMPARTITION 0x18
+#define CQSPI_REG_INDIRECTTRIGGER 0x1C
+#define CQSPI_REG_REMAP 0x24
+#define CQSPI_REG_MODE_BIT 0x28
+#define CQSPI_REG_SDRAMLEVEL 0x2C
+#define CQSPI_REG_SDRAMLEVEL_RD_LSB 0
+#define CQSPI_REG_SDRAMLEVEL_WR_LSB 16
+#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF
+#define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF
+
+#define CQSPI_REG_IRQSTATUS 0x40
+#define CQSPI_REG_IRQMASK 0x44
+#define CQSPI_REG_INDIRECTRD 0x60
+#define CQSPI_REG_INDIRECTRD_START_MASK BIT(0)
+#define CQSPI_REG_INDIRECTRD_CANCEL_MASK BIT(1)
+#define CQSPI_REG_INDIRECTRD_DONE_MASK BIT(5)
+#define CQSPI_REG_INDIRECTRDWATERMARK 0x64
+#define CQSPI_REG_INDIRECTRDSTARTADDR 0x68
+#define CQSPI_REG_INDIRECTRDBYTES 0x6C
+#define CQSPI_REG_INDIRECTTRIGGERADDRRANGE 0x80
+#define CQSPI_REG_CMDCTRL 0x90
+#define CQSPI_REG_CMDCTRL_EXECUTE_MASK BIT(0)
+#define CQSPI_REG_CMDCTRL_INPROGRESS_MASK BIT(1)
+#define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12
+#define CQSPI_REG_CMDCTRL_WR_EN_LSB 15
+#define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16
+#define CQSPI_REG_CMDCTRL_ADDR_EN_LSB 19
+#define CQSPI_REG_CMDCTRL_RD_BYTES_LSB 20
+#define CQSPI_REG_CMDCTRL_RD_EN_LSB 23
+#define CQSPI_REG_CMDCTRL_OPCODE_LSB 24
+#define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7
+#define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3
+#define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7
+#define CQSPI_REG_INDIRECTWR 0x70
+#define CQSPI_REG_INDIRECTWR_START_MASK BIT(0)
+#define CQSPI_REG_INDIRECTWR_CANCEL_MASK BIT(1)
+#define CQSPI_REG_INDIRECTWR_DONE_MASK BIT(5)
+#define CQSPI_REG_INDIRECTWRWATERMARK 0x74
+#define CQSPI_REG_INDIRECTWRSTARTADDR 0x78
+#define CQSPI_REG_INDIRECTWRBYTES 0x7C
+#define CQSPI_REG_CMDADDRESS 0x94
+#define CQSPI_REG_CMDREADDATALOWER 0xA0
+#define CQSPI_REG_CMDREADDATAUPPER 0xA4
+#define CQSPI_REG_CMDWRITEDATALOWER 0xA8
+#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC
+
+/* Interrupt status bits */
+#define CQSPI_REG_IRQ_MODE_ERR BIT(0)
+#define CQSPI_REG_IRQ_UNDERFLOW BIT(1)
+#define CQSPI_REG_IRQ_IND_COMP BIT(2)
+#define CQSPI_REG_IRQ_IND_RD_REJECT BIT(3)
+#define CQSPI_REG_IRQ_WR_PROTECTED_ERR BIT(4)
+#define CQSPI_REG_IRQ_ILLEGAL_AHB_ERR BIT(5)
+#define CQSPI_REG_IRQ_WATERMARK BIT(6)
+#define CQSPI_REG_IRQ_IND_RD_OVERFLOW BIT(12)
+
+#define CQSPI_IRQ_STATUS_ERR (CQSPI_REG_IRQ_MODE_ERR | \
+ CQSPI_REG_IRQ_IND_RD_REJECT | \
+ CQSPI_REG_IRQ_WR_PROTECTED_ERR | \
+ CQSPI_REG_IRQ_ILLEGAL_AHB_ERR)
+#define CQSPI_IRQ_MASK_RD (CQSPI_REG_IRQ_MODE_ERR | \
+ CQSPI_REG_IRQ_IND_RD_REJECT | \
+ CQSPI_REG_IRQ_WATERMARK | \
+ CQSPI_REG_IRQ_IND_RD_OVERFLOW | \
+ CQSPI_REG_IRQ_IND_COMP)
+#define CQSPI_IRQ_MASK_WR (CQSPI_REG_IRQ_MODE_ERR | \
+ CQSPI_REG_IRQ_WR_PROTECTED_ERR | \
+ CQSPI_REG_IRQ_IND_COMP | \
+ CQSPI_REG_IRQ_WATERMARK | \
+ CQSPI_REG_IRQ_UNDERFLOW)
+#define CQSPI_IRQ_STATUS_MASK (0xFFFFFFFF)
+#define CQSPI_REG_IS_IDLE(base) \
+ ((readl((base) + CQSPI_REG_CONFIG) >> \
+ CQSPI_REG_CONFIG_IDLE_LSB) & 0x1)
+#define CQSPI_CAL_DELAY(tdelay_ns, tref_ns, tsclk_ns) \
+ ((((tdelay_ns) - (tsclk_ns)) / (tref_ns)))
+#define CQSPI_GET_RD_SRAM_LEVEL(reg_base) \
+ (((readl(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \
+ CQSPI_REG_SDRAMLEVEL_RD_LSB) & CQSPI_REG_SDRAMLEVEL_RD_MASK)
+#define CQSPI_READ_IRQ_STATUS(reg_base) \
+ readl((reg_base) + CQSPI_REG_IRQSTATUS)
+#define CQSPI_CLEAR_IRQ(reg_base, status) \
+ writel(status, (reg_base) + CQSPI_REG_IRQSTATUS)
+
+unsigned int cadence_qspi_apb_is_controller_ready(void *reg_base_addr);
+void cadence_qspi_apb_controller_init(struct struct_cqspi *cadence_qspi);
+int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi,
+ struct spi_device *spi,
+ unsigned int n_trans,
+ struct spi_transfer **spi_xfer);
+void cqspi_apb_controller_enable(void *reg_base_addr, bool enable);
+
+#endif /* __CADENCE_QSPI_APB_H__ */
diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c
new file mode 100644
index 000000000000..ad85fe5894e3
--- /dev/null
+++ b/drivers/spi/spi-cadence-qspi.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Cadence QSPI Controller
+ * Copyright Altera Corporation (C) 2012-2014. All rights reserved.
+ * Copyright Altera Corporation (C) 2019. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/spi/spi.h>
+
+#include "spi-cadence-qspi.h"
+#include "spi-cadence-qspi-apb.h"
+
+#define CADENCE_QSPI_NAME "cdns-qspi-flash"
+
+unsigned int cadence_qspi_init_timeout(const unsigned long timeout_in_ms)
+{
+ return jiffies + msecs_to_jiffies(timeout_in_ms);
+}
+
+unsigned int cadence_qspi_check_timeout(const unsigned long timeout)
+{
+ return time_before(jiffies, timeout);
+}
+
+static irqreturn_t cadence_qspi_irq_handler(int this_irq, void *dev)
+{
+ struct struct_cqspi *cadence_qspi = dev;
+ u32 irq_status;
+
+ irq_status = CQSPI_READ_IRQ_STATUS(cadence_qspi->iobase);
+ if (!irq_status)
+ return IRQ_HANDLED;
+
+ cadence_qspi->irq_status = irq_status;
+
+ /* Clear interrupt */
+ CQSPI_CLEAR_IRQ(cadence_qspi->iobase, irq_status);
+ wake_up(&cadence_qspi->waitqueue);
+ return IRQ_HANDLED;
+}
+
+static void cadence_qspi_work(struct work_struct *work)
+{
+ struct struct_cqspi *cadence_qspi = container_of(work,
+ struct struct_cqspi,
+ work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&cadence_qspi->lock, flags);
+ while ((!list_empty(&cadence_qspi->msg_queue)) &&
+ cadence_qspi->running) {
+ struct spi_message *spi_msg;
+ struct spi_device *spi;
+ struct spi_transfer *spi_xfer;
+ struct spi_transfer *xfer[CQSPI_MAX_TRANS];
+ int status = 0;
+ int n_trans = 0;
+ int skip_xfer = 0;
+ unsigned int total_len = 0;
+
+ spi_msg = container_of(cadence_qspi->msg_queue.next,
+ struct spi_message, queue);
+ list_del_init(&spi_msg->queue);
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+ spi = spi_msg->spi;
+ list_for_each_entry(spi_xfer, &spi_msg->transfers,
+ transfer_list) {
+ if (n_trans >= CQSPI_MAX_TRANS) {
+ dev_err(&spi->dev, "err: spi_xfer exceed %d.\n",
+ CQSPI_MAX_TRANS);
+ /* Skip process the queue if number of
+ * transaction is greater than max 2.
+ */
+ skip_xfer = 1;
+ break;
+ }
+ xfer[n_trans++] = spi_xfer;
+ total_len += spi_xfer->len;
+ }
+
+ if (!skip_xfer) {
+ status = cadence_qspi_apb_process_queue(cadence_qspi,
+ spi,
+ n_trans, xfer);
+ if (!status)
+ spi_msg->actual_length = total_len;
+
+ spi_msg->status = status;
+ spi_msg->complete(spi_msg->context);
+ }
+ spin_lock_irqsave(&cadence_qspi->lock, flags);
+ }
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+}
+
+static int cadence_qspi_transfer(struct spi_device *spi,
+ struct spi_message *msg)
+{
+ struct struct_cqspi *cadence_qspi =
+ spi_master_get_devdata(spi->master);
+ struct spi_transfer *spi_xfer;
+ struct platform_device *pdev = cadence_qspi->pdev;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ unsigned long flags;
+
+ list_for_each_entry(spi_xfer, &msg->transfers, transfer_list) {
+ if (spi_xfer->speed_hz > (pdata->master_ref_clk_hz / 2)) {
+ dev_err(&spi->dev,
+ "speed_hz%d greater than maximum %dHz\n",
+ spi_xfer->speed_hz,
+ (pdata->master_ref_clk_hz / 2));
+ msg->status = -EINVAL;
+ return -EINVAL;
+ }
+ }
+ spin_lock_irqsave(&cadence_qspi->lock, flags);
+ if (!cadence_qspi->running) {
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+ return -ESHUTDOWN;
+ }
+ msg->status = -EINPROGRESS;
+ msg->actual_length = 0;
+ list_add_tail(&msg->queue, &cadence_qspi->msg_queue);
+ queue_work(cadence_qspi->workqueue, &cadence_qspi->work);
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+ return 0;
+}
+
+static int cadence_qspi_setup(struct spi_device *spi)
+{
+ if (spi->chip_select > spi->master->num_chipselect) {
+ dev_err(&spi->dev, "%d chip select is out of range\n",
+ spi->chip_select);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cadence_qspi_start_queue(struct struct_cqspi *cadence_qspi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cadence_qspi->lock, flags);
+ if (cadence_qspi->running) {
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+ return -EBUSY;
+ }
+ if (!cadence_qspi_apb_is_controller_ready(cadence_qspi->iobase)) {
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+ return -EBUSY;
+ }
+ cadence_qspi->running = true;
+ spin_unlock_irqrestore(&cadence_qspi->lock, flags);
+ queue_work(cadence_qspi->workqueue, &cadence_qspi->work);
+ return 0;
+}
+
+static int cadence_qspi_of_get_pdata(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *nc;
+ struct cqspi_platform_data *pdata = pdev->dev.platform_data;
+ struct cqspi_flash_pdata *f_pdata;
+ unsigned int cs;
+ unsigned int prop;
+
+ if (of_property_read_u32(np, "cdns,qspi-phyaddr", &prop)) {
+ dev_err(&pdev->dev,
+ "couldn't determine cdns,qspi-phyaddr\n");
+ return -ENXIO;
+ }
+ pdata->qspi_ahb_phy = prop;
+
+ if (of_property_read_u32(np, "cdns,qspi-physize", &prop)) {
+ dev_info(&pdev->dev,
+ "missing cdns,qspi-physize; default to 128 kB\n");
+ prop = 128;
+ }
+ pdata->qspi_ahb_size = prop * 1024;
+
+ if (of_property_read_u32(np, "cdns,qspi-phymask", &prop))
+ prop = CQSPI_INDIRECTTRIGGER_ADDR_MASK;
+
+ pdata->qspi_ahb_mask = prop;
+
+ if (of_property_read_u32(np, "bus-num", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine bus-num\n");
+ return -ENXIO;
+ }
+ pdata->bus_num = prop;
+ if (of_property_read_u32(np, "num-chipselect", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine num-chipselect\n");
+ return -ENXIO;
+ }
+ pdata->num_chipselect = prop;
+ if (of_property_read_u32(np, "cdns,fifo-depth", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine fifo-depth\n");
+ return -ENXIO;
+ }
+ pdata->fifo_depth = prop;
+
+ /* Get flash devices platform data */
+ for_each_child_of_node(np, nc) {
+ if (!of_device_is_available(nc))
+ continue;
+
+ if (of_property_read_u32(nc, "reg", &cs)) {
+ dev_err(&pdev->dev, "couldn't determine reg\n");
+ return -ENXIO;
+ }
+ f_pdata = &pdata->f_pdata[cs];
+
+ if (of_property_read_u32(nc, "spi-rx-bus-width", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine quad\n");
+ return -ENXIO;
+ }
+ f_pdata->quad = (prop == 4) ? QUAD_QIO : QUAD_SIO;
+
+ if (of_property_read_u32(nc, "page-size", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine page-size\n");
+ return -ENXIO;
+ }
+ f_pdata->page_size = prop;
+
+ if (of_property_read_u32(nc, "block-size", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine block-size\n");
+ return -ENXIO;
+ }
+ f_pdata->block_size = prop;
+
+ if (of_property_read_u32(nc, "cdns,read-delay", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine read-delay\n");
+ return -ENXIO;
+ }
+ f_pdata->read_delay = prop;
+
+ if (of_property_read_u32(nc, "cdns,tshsl-ns", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine tshsl-ns\n");
+ return -ENXIO;
+ }
+ f_pdata->tshsl_ns = prop;
+
+ if (of_property_read_u32(nc, "cdns,tsd2d-ns", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine tsd2d-ns\n");
+ return -ENXIO;
+ }
+ f_pdata->tsd2d_ns = prop;
+
+ if (of_property_read_u32(nc, "cdns,tchsh-ns", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine tchsh-ns\n");
+ return -ENXIO;
+ }
+ f_pdata->tchsh_ns = prop;
+
+ if (of_property_read_u32(nc, "cdns,tslch-ns", &prop)) {
+ dev_err(&pdev->dev, "couldn't determine tslch-ns\n");
+ return -ENXIO;
+ }
+ f_pdata->tslch_ns = prop;
+ }
+ return 0;
+}
+
+static int cadence_qspi_probe(struct platform_device *pdev)
+{
+ struct cqspi_platform_data *pdata;
+ struct struct_cqspi *cadence_qspi;
+ struct spi_master *master;
+ struct resource *res;
+ int status;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*cadence_qspi));
+ if (!master) {
+ dev_err(&pdev->dev, "spi_alloc_master failed\n");
+ return -ENOMEM;
+ }
+ master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA | SPI_TX_QUAD |
+ SPI_RX_QUAD | SPI_TX_DUAL | SPI_RX_DUAL;
+ master->setup = cadence_qspi_setup;
+ master->transfer = cadence_qspi_transfer;
+ master->dev.of_node = pdev->dev.of_node;
+ cadence_qspi = spi_master_get_devdata(master);
+ cadence_qspi->pdev = pdev;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cadence_qspi->iobase = devm_ioremap_resource(&pdev->dev, res);
+ if (!cadence_qspi->iobase) {
+ dev_err(&pdev->dev, "devm_ioremap_resource res 0 failed\n");
+ status = -EADDRNOTAVAIL;
+ goto err_ioremap;
+ }
+
+ pdata = kmalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ status = -ENOMEM;
+ goto err_pdata;
+ }
+ pdev->dev.platform_data = pdata;
+ cadence_qspi->clk = devm_clk_get(&pdev->dev, "qspi");
+ if (IS_ERR(cadence_qspi->clk)) {
+ dev_err(&pdev->dev, "cannot get qspi clk\n");
+ return PTR_ERR(cadence_qspi->clk);
+ }
+ cadence_qspi->fpi_clk = devm_clk_get(&pdev->dev, "freq");
+ if (IS_ERR(cadence_qspi->fpi_clk)) {
+ dev_err(&pdev->dev, "cannot get qspi fpi_clk\n");
+ return PTR_ERR(cadence_qspi->fpi_clk);
+ }
+ pdata->master_ref_clk_hz = clk_get_rate(cadence_qspi->fpi_clk);
+ status = clk_prepare_enable(cadence_qspi->clk);
+ if (status < 0) {
+ dev_err(&pdev->dev,
+ "failed to enable qspi clock: %d\n", status);
+ return status;
+ }
+
+ status = cadence_qspi_of_get_pdata(pdev);
+ if (status) {
+ dev_err(&pdev->dev, "Get platform data failed.\n");
+ goto err_of;
+ }
+
+ cadence_qspi->res = res;
+
+ /* request and remap ahb */
+ res = devm_request_mem_region(&pdev->dev, pdata->qspi_ahb_phy,
+ pdata->qspi_ahb_size, "ahb");
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request memory region\n");
+ status = -EADDRNOTAVAIL;
+ goto err_ahbremap;
+ }
+
+ cadence_qspi->qspi_ahb_virt = devm_ioremap(&pdev->dev,
+ pdata->qspi_ahb_phy,
+ pdata->qspi_ahb_size);
+ if (!cadence_qspi->qspi_ahb_virt) {
+ dev_err(&pdev->dev, "failed to remap memory region\n");
+ status = -EADDRNOTAVAIL;
+ goto err_ahbremap;
+ }
+
+ cadence_qspi->reset = devm_reset_control_get(&pdev->dev, "qspi");
+ if (IS_ERR(cadence_qspi->reset))
+ dev_err(&pdev->dev, "qspi get reset fail.\n");
+
+ reset_control_assert(cadence_qspi->reset);
+ reset_control_deassert(cadence_qspi->reset);
+
+ cadence_qspi->workqueue =
+ create_singlethread_workqueue(dev_name(master->dev.parent));
+ if (!cadence_qspi->workqueue) {
+ dev_err(&pdev->dev, "create_workqueue failed\n");
+ status = -ENOMEM;
+ goto err_wq;
+ }
+ cadence_qspi->running = false;
+ INIT_WORK(&cadence_qspi->work, cadence_qspi_work);
+ spin_lock_init(&cadence_qspi->lock);
+ INIT_LIST_HEAD(&cadence_qspi->msg_queue);
+ init_waitqueue_head(&cadence_qspi->waitqueue);
+ status = cadence_qspi_start_queue(cadence_qspi);
+ if (status) {
+ dev_err(&pdev->dev, "problem starting queue.\n");
+ goto err_start_q;
+ }
+ cadence_qspi->irq = platform_get_irq(pdev, 0);
+ if (cadence_qspi->irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ status = -ENXIO;
+ goto err_irq;
+ }
+ status = request_irq(cadence_qspi->irq, cadence_qspi_irq_handler,
+ 0, pdev->name, cadence_qspi);
+ if (status) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto err_irq;
+ }
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->num_chipselect;
+ platform_set_drvdata(pdev, master);
+ cadence_qspi_apb_controller_init(cadence_qspi);
+ cadence_qspi->current_cs = 0;
+ status = spi_register_master(master);
+ if (status) {
+ dev_err(&pdev->dev, "spi_register_master=%d failed\n", status);
+ goto err_of;
+ }
+ return 0;
+
+err_pdata:
+ free_irq(cadence_qspi->irq, cadence_qspi);
+err_start_q:
+err_irq:
+ destroy_workqueue(cadence_qspi->workqueue);
+err_of:
+err_wq:
+err_ahbremap:
+err_ioremap:
+ spi_master_put(master);
+ dev_err(&pdev->dev, "Cadence QSPI controller probe failed\n");
+ return status;
+}
+
+static int cadence_qspi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct struct_cqspi *cadence_qspi = spi_master_get_devdata(master);
+
+ cqspi_apb_controller_enable(cadence_qspi->iobase, 0);
+ platform_set_drvdata(pdev, NULL);
+ destroy_workqueue(cadence_qspi->workqueue);
+ free_irq(cadence_qspi->irq, cadence_qspi);
+ iounmap(cadence_qspi->iobase);
+ iounmap(cadence_qspi->qspi_ahb_virt);
+ release_mem_region(cadence_qspi->res->start,
+ resource_size(cadence_qspi->res));
+ kfree(pdev->dev.platform_data);
+ spi_unregister_master(master);
+ spi_master_put(master);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cadence_qspi_of_match[] = {
+ { .compatible = "cadence,qspi-nand",},
+ { /* end of table */}
+};
+MODULE_DEVICE_TABLE(of, cadence_qspi_of_match);
+#else
+#define cadence_qspi_of_match NULL
+#endif /* CONFIG_OF */
+
+static struct platform_driver cadence_qspi_platform_driver = {
+ .probe = cadence_qspi_probe,
+ .remove = cadence_qspi_remove,
+ .driver = {
+ .name = CADENCE_QSPI_NAME,
+ .of_match_table = cadence_qspi_of_match,
+ },
+};
+
+module_platform_driver(cadence_qspi_platform_driver);
+
+MODULE_DESCRIPTION("Cadence QSPI Controller Driver for flash");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" CADENCE_QSPI_NAME);
+MODULE_AUTHOR("Ley Foon Tan <[email protected]>");
diff --git a/drivers/spi/spi-cadence-qspi.h b/drivers/spi/spi-cadence-qspi.h
new file mode 100644
index 000000000000..77d768ff75c7
--- /dev/null
+++ b/drivers/spi/spi-cadence-qspi.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Cadence QSPI Controller
+ *
+ * Copyright Altera Corporation (C) 2012-2014. All rights reserved.
+ * Copyright Intel Corporation (C) 2019. All rights reserved.
+ */
+#include <linux/reset.h>
+#ifndef __CADENCE_QSPI_H__
+#define __CADENCE_QSPI_H__
+#define CQSPI_MAX_TRANS 10
+#define CQSPI_MAX_CHIP_SELECT 16
+#define DC_MODE 0
+#define STIG_READ_MODE 1
+#define STIG_WRITE_MODE 2
+#define STIG_WRITE_READ_MODE 3
+#define IDC_READ_MODE 4
+#define IDC_WRITE_MODE 5
+
+#define QUAD_SIO 0
+#define QUAD_DIO 1
+#define QUAD_QIO 2
+
+#define QUAD_LSB 4
+
+struct cqspi_flash_pdata {
+ unsigned int page_size;
+ unsigned int block_size;
+ unsigned int flash_type;
+ unsigned int quad;
+ unsigned int read_delay;
+ unsigned int tshsl_ns;
+ unsigned int tsd2d_ns;
+ unsigned int tchsh_ns;
+ unsigned int tslch_ns;
+};
+
+struct cqspi_platform_data {
+ unsigned int bus_num;
+ unsigned int num_chipselect;
+ unsigned int qspi_ahb_phy;
+ unsigned int qspi_ahb_size;
+ unsigned int qspi_ahb_mask;
+ unsigned int master_ref_clk_hz;
+ unsigned int fifo_depth;
+ struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIP_SELECT];
+};
+
+struct struct_cqspi {
+ struct platform_device *pdev;
+
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ wait_queue_head_t waitqueue;
+ struct list_head msg_queue;
+ struct clk *clk;
+ struct clk *fpi_clk;
+ struct reset_control *reset;
+ /* lock protects queue and registers */
+ spinlock_t lock;
+ void __iomem *iobase;
+ void __iomem *qspi_ahb_virt;
+ struct resource *res;
+ struct resource *res_ahb;
+ int irq;
+ unsigned int irq_status;
+ int current_cs;
+ u8 running;
+};
+
+unsigned int cadence_qspi_init_timeout(const unsigned long timeout_in_ms);
+unsigned int cadence_qspi_check_timeout(const unsigned long timeout);
+#endif /* __CADENCE_QSPI_H__ */
--
2.11.0
From: Ramuthevar Vadivel Murugan <[email protected]>
On Intel Lightening Mountain(LGM) SoCs QSPI controller support
to QSPI-NAND flash. This introduces to device tree binding
documentation for Cadence-QSPI controller and spi-nand flash.
Signed-off-by: Ramuthevar Vadivel Murugan <[email protected]>
---
.../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 ++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
diff --git a/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml b/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
new file mode 100644
index 000000000000..9aae4c1459cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/spi/cadence,qspi-nand.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Cadence QSPI Flash Controller on Intel's SoC
+
+maintainers:
+ - Ramuthevar Vadivel Murugan <[email protected]>
+
+allOf:
+ - $ref: "spi-controller.yaml#"
+
+description: |
+ The Cadence QSPI is a controller optimized for communication with SPI
+ FLASH memories, without DMA support on Intel's SoC.
+
+properties:
+ compatible:
+ const: cadence,lgm-qspi
+
+ reg:
+ maxItems: 1
+
+ fifo-depth:
+ maxItems: 1
+
+ fifo-width:
+ maxItems: 1
+
+ qspi-phyaddr:
+ maxItems: 1
+
+ qspi-phymask:
+ maxItems: 1
+
+ clocks:
+ maxItems: 2
+
+ clocks-names:
+ maxItems: 2
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - fifo-depth
+ - fifo-width
+ - qspi-phyaddr
+ - qspi-phymask
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+
+examples:
+ - |
+ qspi@ec000000 {
+ compatible = "cadence,qspi-nand";
+ reg = <0xec000000 0x100>;
+ fifo-depth = <128>;
+ fifo-width = <4>;
+ qspi-phyaddr = <0xf4000000>;
+ qspi-phymask = <0xffffffff>;
+ clocks = <&cgu0 LGM_CLK_QSPI>, <&cgu0 LGM_GCLK_QSPI>;
+ clock-names = "freq", "qspi";
+ resets = <&rcu0 0x10 1>;
+ reset-names = "qspi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ flash: flash@1 {
+ compatible = "spi-nand";
+ reg = <1>;
+ spi-max-frequency = <10000000>;
+ };
+ };
+
--
2.11.0
On Mon, Sep 16, 2019 at 03:38:43PM +0800, Ramuthevar,Vadivel MuruganX wrote:
> Existing cadence drivers do not support SPI-NAND, it only supports to
> SPI-NOR and SPI devices. To state that is the driver for the same IP
> but due to different SPI flash memory(NAND) need to write from scratch.
What makes you say you need to write a separate driver? It's perfectly
possible to support the normal and flash I/O mechanisms in a single
driver and as well as the maintainence issues obviously someone could
build a system with both flash and non-flash devices on the same SPI
controller.
Hi "Ramuthevar,Vadivel,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master]
[cannot apply to v5.3 next-20190915]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Ramuthevar-Vadivel-MuruganX/spi-cadence-qspi-Add-cadence-qspi-support-for-Intel-LGM-SoC/20190916-163033
config: i386-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.4.0-11) 7.4.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>
All warnings (new ones prefixed by >>):
drivers/spi/spi-cadence-qspi-apb.c: In function 'cqspi_apb_indirect_write_execute':
>> drivers/spi/spi-cadence-qspi-apb.c:382:7: warning: suggest parentheses around operand of '!' or change '&' to '&&' or '!' to '~' [-Wparentheses]
if (!ret & (*irq_status & CQSPI_IRQ_STATUS_ERR)) {
^~~~
vim +382 drivers/spi/spi-cadence-qspi-apb.c
327
328 static int cqspi_apb_indirect_write_execute(struct struct_cqspi *cadence_qspi,
329 u32 txlen, const u8 *txbuf)
330 {
331 struct platform_device *pdev = cadence_qspi->pdev;
332 struct cqspi_platform_data *pdata = pdev->dev.platform_data;
333 struct cqspi_flash_pdata *f_pdata =
334 &pdata->f_pdata[cadence_qspi->current_cs];
335 unsigned int *irq_status = &cadence_qspi->irq_status;
336 void __iomem *reg_base = cadence_qspi->iobase;
337 void __iomem *ahb_base = cadence_qspi->qspi_ahb_virt;
338 unsigned int page_size = f_pdata->page_size;
339 int remaining = (int)txlen;
340 u32 write_bytes, timeout, reg;
341 int ret, status = 0;
342
343 writel(0xa, reg_base + CQSPI_REG_INDIRECTTRIGGERADDRRANGE);
344 writel(0x0, reg_base + CQSPI_REG_INDIRECTWRWATERMARK);
345 reg = readl(reg_base + CQSPI_REG_SIZE);
346 reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB);
347 reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB);
348 reg |= (f_pdata->page_size << CQSPI_REG_SIZE_PAGE_LSB);
349 reg |= (f_pdata->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
350 writel(reg, reg_base + CQSPI_REG_SIZE);
351
352 writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES);
353 writel(CQSPI_REG_SRAM_PARTITION_WR, reg_base + CQSPI_REG_SRAMPARTITION);
354 /* Clear all interrupts. */
355 writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS);
356 writel(CQSPI_IRQ_MASK_WR, reg_base + CQSPI_REG_IRQMASK);
357 writel(CQSPI_REG_INDIRECTWR_START_MASK,
358 reg_base + CQSPI_REG_INDIRECTWR);
359
360 while (remaining > 0) {
361 size_t write_words, mod_bytes;
362
363 write_bytes = remaining > page_size ? page_size : remaining;
364 write_words = write_bytes / 4;
365 mod_bytes = write_bytes % 4;
366
367 if (write_words) {
368 iowrite32_rep(ahb_base, txbuf, write_words);
369 txbuf += (write_words * 4);
370 }
371 if (mod_bytes) {
372 unsigned int temp = 0xFFFFFFFF;
373
374 memcpy(&temp, txbuf, mod_bytes);
375 iowrite32(temp, ahb_base);
376 txbuf += mod_bytes;
377 }
378 ret = wait_event_interruptible_timeout(cadence_qspi->waitqueue,
379 *irq_status &
380 CQSPI_IRQ_MASK_WR,
381 CQSPI_TIMEOUT_MS);
> 382 if (!ret & (*irq_status & CQSPI_IRQ_STATUS_ERR)) {
383 ret = -ETIMEDOUT;
384 goto failwr;
385 } else {
386 ret = 0;
387 }
388 remaining -= write_bytes;
389 }
390
391 /* Check indirect done status */
392 timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS);
393 while (cadence_qspi_check_timeout(timeout)) {
394 status = readl(reg_base + CQSPI_REG_INDIRECTWR);
395 if (status & CQSPI_REG_INDIRECTWR_DONE_MASK)
396 break;
397 }
398 if (!(status & CQSPI_REG_INDIRECTWR_DONE_MASK)) {
399 ret = -ETIMEDOUT;
400 goto failwr;
401 }
402 return 0;
403
404 failwr:
405 writel(0, reg_base + CQSPI_REG_IRQMASK);
406 writel(CQSPI_REG_INDIRECTWR_DONE_MASK,
407 reg_base + CQSPI_REG_INDIRECTWR);
408 if (ret)
409 writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
410 reg_base + CQSPI_REG_INDIRECTWR);
411 return ret;
412 }
413
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi,
On 16/09/19 1:08 PM, Ramuthevar,Vadivel MuruganX wrote:
> patch 1: Add YAML for cadence-qspi devicetree cdocumentation.
> patch 2: cadence-qspi controller driver to support QSPI-NAND flash
> using existing spi-nand framework with legacy spi protocol.
Nope, you cannot have two drivers for the same IP (i.e Cadence QSPI)
just to support to different types of SPI memories. This is the reason
why spi_mem_ops was introduced.
Please rewrite this driver over to use spi_mem_ops (instead of using
generic SPI xfers) so that same driver supports both SPI-NOR and
SPI-NAND flashes. Once that's done drivers/mtd/spi-nor/cadence-quadspi.c
can be deleted.
There are few existing examples of spi_mem_ops users in drivers/spi/
(git grep spi_mem_ops) and materials here on how to write such a driver:
[1]
https://bootlin.com/blog/spi-mem-bringing-some-consistency-to-the-spi-memory-ecosystem/
[2] https://www.youtube.com/watch?v=PkWbuLM_gmU
>
> Ramuthevar Vadivel Murugan (2):
> dt-bindings: spi: Add support for cadence-qspi IP Intel LGM SoC
> spi: cadence-qspi: Add QSPI support for Intel LGM SoC
>
> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 +++
> drivers/spi/Kconfig | 9 +
> drivers/spi/Makefile | 1 +
> drivers/spi/spi-cadence-qspi-apb.c | 644 +++++++++++++++++++++
> drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++
> drivers/spi/spi-cadence-qspi.c | 461 +++++++++++++++
> drivers/spi/spi-cadence-qspi.h | 73 +++
> 7 files changed, 1446 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
> create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
> create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
> create mode 100644 drivers/spi/spi-cadence-qspi.c
> create mode 100644 drivers/spi/spi-cadence-qspi.h
>
--
Regards
Vignesh
Hi Vignesh,
Thank you for the review comments and suggestions.
On 17/9/2019 12:50 AM, Vignesh Raghavendra wrote:
> Hi,
>
> On 16/09/19 1:08 PM, Ramuthevar,Vadivel MuruganX wrote:
>> patch 1: Add YAML for cadence-qspi devicetree cdocumentation.
>> patch 2: cadence-qspi controller driver to support QSPI-NAND flash
>> using existing spi-nand framework with legacy spi protocol.
> Nope, you cannot have two drivers for the same IP (i.e Cadence QSPI)
> just to support to different types of SPI memories. This is the reason
> why spi_mem_ops was introduced.
>
> Please rewrite this driver over to use spi_mem_ops (instead of using
> generic SPI xfers) so that same driver supports both SPI-NOR and
> SPI-NAND flashes. Once that's done drivers/mtd/spi-nor/cadence-quadspi.c
> can be deleted.
>
> There are few existing examples of spi_mem_ops users in drivers/spi/
> (git grep spi_mem_ops) and materials here on how to write such a driver:
>
> [1]
> https://bootlin.com/blog/spi-mem-bringing-some-consistency-to-the-spi-memory-ecosystem/
> [2] https://www.youtube.com/watch?v=PkWbuLM_gmU
Agreed!, Surely let me go through the above link and put my effort to
rewrite the drivers as per your suggestions.
---
With Best Regards
Vadivel Murugan R
>> Ramuthevar Vadivel Murugan (2):
>> dt-bindings: spi: Add support for cadence-qspi IP Intel LGM SoC
>> spi: cadence-qspi: Add QSPI support for Intel LGM SoC
>>
>> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 +++
>> drivers/spi/Kconfig | 9 +
>> drivers/spi/Makefile | 1 +
>> drivers/spi/spi-cadence-qspi-apb.c | 644 +++++++++++++++++++++
>> drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++
>> drivers/spi/spi-cadence-qspi.c | 461 +++++++++++++++
>> drivers/spi/spi-cadence-qspi.h | 73 +++
>> 7 files changed, 1446 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
>> create mode 100644 drivers/spi/spi-cadence-qspi.c
>> create mode 100644 drivers/spi/spi-cadence-qspi.h
>>
On Tue, Sep 17, 2019 at 10:11:28AM +0800, Ramuthevar, Vadivel MuruganX wrote:
> *??? spi-cadence.c* in *drivers/spi/*, which supports very old legacy
> cadence-spi based devices(normal)
> *??? cadence-quadspi.c(drivers/mtd/spi-nor/)* : specific support to SPI-NOR
> flash with new spi-nor layer.
> ??? all the API's in this driver purely on spi-nor specific, so couldn't?
> proceed to adapt.
Are these completely separate IPs or are they just different versions of
the same IP?
Hi Mark,
?? Thank you for the review comments.
On 17/9/2019 11:36 PM, Mark Brown wrote:
> On Tue, Sep 17, 2019 at 10:11:28AM +0800, Ramuthevar, Vadivel MuruganX wrote:
>
>> *??? spi-cadence.c* in *drivers/spi/*, which supports very old legacy
>> cadence-spi based devices(normal)
>> *??? cadence-quadspi.c(drivers/mtd/spi-nor/)* : specific support to SPI-NOR
>> flash with new spi-nor layer.
>> ??? all the API's in this driver purely on spi-nor specific, so couldn't
>> proceed to adapt.
> Are these completely separate IPs or are they just different versions of
> the same IP?
These are same IPs , but different features Enabled/Disabled depends
upon the SoC vendors.
for e.g: Intel LGM SoC uses the same IP, but without DMA and Direct
access controller.
also dedicated support to flash devices.
Best regards
Vadivel
On Wed, Sep 18, 2019 at 01:59:06PM +0800, Ramuthevar, Vadivel MuruganX wrote:
> On 17/9/2019 11:36 PM, Mark Brown wrote:
> > On Tue, Sep 17, 2019 at 10:11:28AM +0800, Ramuthevar, Vadivel MuruganX wrote:
> > > *??? spi-cadence.c* in *drivers/spi/*, which supports very old legacy
> > > cadence-spi based devices(normal)
> > > *??? cadence-quadspi.c(drivers/mtd/spi-nor/)* : specific support to SPI-NOR
> > > flash with new spi-nor layer.
> > > ??? all the API's in this driver purely on spi-nor specific, so couldn't
> > > proceed to adapt.
> > Are these completely separate IPs or are they just different versions of
> > the same IP?
> These are same IPs , but different features Enabled/Disabled depends upon
> the SoC vendors.
> for e.g: Intel LGM SoC uses the same IP, but without DMA and Direct access
> controller.
> also dedicated support to flash devices.
If it's different versions of the same IP then everything should be in
one driver with the optional features enabled depending on what's in a
given system.
Hi Mark,
?? Thank you for the comments and queries.
On 18/9/2019 8:08 PM, Mark Brown wrote:
> On Wed, Sep 18, 2019 at 01:59:06PM +0800, Ramuthevar, Vadivel MuruganX wrote:
>> On 17/9/2019 11:36 PM, Mark Brown wrote:
>>> On Tue, Sep 17, 2019 at 10:11:28AM +0800, Ramuthevar, Vadivel MuruganX wrote:
>>>> *??? spi-cadence.c* in *drivers/spi/*, which supports very old legacy
>>>> cadence-spi based devices(normal)
>>>> *??? cadence-quadspi.c(drivers/mtd/spi-nor/)* : specific support to SPI-NOR
>>>> flash with new spi-nor layer.
>>>> ??? all the API's in this driver purely on spi-nor specific, so couldn't
>>>> proceed to adapt.
>>> Are these completely separate IPs or are they just different versions of
>>> the same IP?
>> These are same IPs , but different features Enabled/Disabled depends upon
>> the SoC vendors.
>> for e.g: Intel LGM SoC uses the same IP, but without DMA and Direct access
>> controller.
>> also dedicated support to flash devices.
> If it's different versions of the same IP then everything should be in
> one driver with the optional features enabled depending on what's in a
> given system.
Agreed!, I am trying to adapt the driver/mtd/spi-nor/cadence-quadspi.c
and newly sent patches
in a single driver file, also trying to use spi_mem_ops framework.
With Best Regards
Vadivel
On Mon, Sep 16, 2019 at 03:38:42PM +0800, Ramuthevar,Vadivel MuruganX wrote:
> From: Ramuthevar Vadivel Murugan <[email protected]>
>
> On Intel Lightening Mountain(LGM) SoCs QSPI controller support
> to QSPI-NAND flash. This introduces to device tree binding
> documentation for Cadence-QSPI controller and spi-nand flash.
>
> Signed-off-by: Ramuthevar Vadivel Murugan <[email protected]>
> ---
> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 ++++++++++++++++++++++
> 1 file changed, 84 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>
> diff --git a/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml b/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
> new file mode 100644
> index 000000000000..9aae4c1459cc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
> @@ -0,0 +1,84 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/spi/cadence,qspi-nand.yaml#"
> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: Cadence QSPI Flash Controller on Intel's SoC
> +
> +maintainers:
> + - Ramuthevar Vadivel Murugan <[email protected]>
> +
> +allOf:
> + - $ref: "spi-controller.yaml#"
> +
> +description: |
> + The Cadence QSPI is a controller optimized for communication with SPI
> + FLASH memories, without DMA support on Intel's SoC.
> +
> +properties:
> + compatible:
> + const: cadence,lgm-qspi
Vendor here should be 'intel'. Perhaps the binding should be shared too
like the driver.
Plus the vendor prefix for Cadence is cdns.
> +
> + reg:
> + maxItems: 1
> +
> + fifo-depth:
> + maxItems: 1
> +
This is vendor specific, so needs a vendor prefix, type, and
description.
> + fifo-width:
> + maxItems: 1
Same
> +
> + qspi-phyaddr:
> + maxItems: 1
Same
> +
> + qspi-phymask:
> + maxItems: 1
Same
> +
> + clocks:
> + maxItems: 2
Need to define what each clock is when there is more than 1.
> +
> + clocks-names:
> + maxItems: 2
Need to define the strings.
> +
> + resets:
> + maxItems: 1
> +
> + reset-names:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - reg
> + - fifo-depth
> + - fifo-width
> + - qspi-phyaddr
> + - qspi-phymask
> + - clocks
> + - clock-names
> + - resets
> + - reset-names
> +
> +examples:
> + - |
> + qspi@ec000000 {
spi@...
> + compatible = "cadence,qspi-nand";
> + reg = <0xec000000 0x100>;
> + fifo-depth = <128>;
> + fifo-width = <4>;
> + qspi-phyaddr = <0xf4000000>;
> + qspi-phymask = <0xffffffff>;
> + clocks = <&cgu0 LGM_CLK_QSPI>, <&cgu0 LGM_GCLK_QSPI>;
> + clock-names = "freq", "qspi";
> + resets = <&rcu0 0x10 1>;
> + reset-names = "qspi";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + flash: flash@1 {
> + compatible = "spi-nand";
> + reg = <1>;
> + spi-max-frequency = <10000000>;
> + };
> + };
> +
> --
> 2.11.0
>
Hi Rob,
Thank you for the review comments.
On 1/10/2019 6:36 AM, Rob Herring wrote:
> On Mon, Sep 16, 2019 at 03:38:42PM +0800, Ramuthevar,Vadivel MuruganX wrote:
>> From: Ramuthevar Vadivel Murugan <[email protected]>
>>
>> On Intel Lightening Mountain(LGM) SoCs QSPI controller support
>> to QSPI-NAND flash. This introduces to device tree binding
>> documentation for Cadence-QSPI controller and spi-nand flash.
>>
>> Signed-off-by: Ramuthevar Vadivel Murugan <[email protected]>
>> ---
>> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 ++++++++++++++++++++++
>> 1 file changed, 84 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml b/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>> new file mode 100644
>> index 000000000000..9aae4c1459cc
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>> @@ -0,0 +1,84 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: "http://devicetree.org/schemas/spi/cadence,qspi-nand.yaml#"
>> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
>> +
>> +title: Cadence QSPI Flash Controller on Intel's SoC
>> +
>> +maintainers:
>> + - Ramuthevar Vadivel Murugan <[email protected]>
>> +
>> +allOf:
>> + - $ref: "spi-controller.yaml#"
>> +
>> +description: |
>> + The Cadence QSPI is a controller optimized for communication with SPI
>> + FLASH memories, without DMA support on Intel's SoC.
>> +
>> +properties:
>> + compatible:
>> + const: cadence,lgm-qspi
> Vendor here should be 'intel'. Perhaps the binding should be shared too
> like the driver.
>
> Plus the vendor prefix for Cadence is cdns.
Agreed!, will update.
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + fifo-depth:
>> + maxItems: 1
>> +
> This is vendor specific, so needs a vendor prefix, type, and
> description.
agreed!
>> + fifo-width:
>> + maxItems: 1
> Same
>
>> +
>> + qspi-phyaddr:
>> + maxItems: 1
> Same
>
>> +
>> + qspi-phymask:
>> + maxItems: 1
> Same
will update all the above.
>> +
>> + clocks:
>> + maxItems: 2
> Need to define what each clock is when there is more than 1.
Sure, will update.
>> +
>> + clocks-names:
>> + maxItems: 2
> Need to define the strings.
Noted, will update.
>> +
>> + resets:
>> + maxItems: 1
>> +
>> + reset-names:
>> + maxItems: 1
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - fifo-depth
>> + - fifo-width
>> + - qspi-phyaddr
>> + - qspi-phymask
>> + - clocks
>> + - clock-names
>> + - resets
>> + - reset-names
>> +
>> +examples:
>> + - |
>> + qspi@ec000000 {
> spi@...
Controller is qspi , so that have updated.
With Best Regards
Vadivel Murugan R
>> + compatible = "cadence,qspi-nand";
>> + reg = <0xec000000 0x100>;
>> + fifo-depth = <128>;
>> + fifo-width = <4>;
>> + qspi-phyaddr = <0xf4000000>;
>> + qspi-phymask = <0xffffffff>;
>> + clocks = <&cgu0 LGM_CLK_QSPI>, <&cgu0 LGM_GCLK_QSPI>;
>> + clock-names = "freq", "qspi";
>> + resets = <&rcu0 0x10 1>;
>> + reset-names = "qspi";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + flash: flash@1 {
>> + compatible = "spi-nand";
>> + reg = <1>;
>> + spi-max-frequency = <10000000>;
>> + };
>> + };
>> +
>> --
>> 2.11.0
>>
HI Vignesh,
On 17/9/2019 12:50 AM, Vignesh Raghavendra wrote:
> Hi,
>
> On 16/09/19 1:08 PM, Ramuthevar,Vadivel MuruganX wrote:
>> patch 1: Add YAML for cadence-qspi devicetree cdocumentation.
>> patch 2: cadence-qspi controller driver to support QSPI-NAND flash
>> using existing spi-nand framework with legacy spi protocol.
> Nope, you cannot have two drivers for the same IP (i.e Cadence QSPI)
> just to support to different types of SPI memories. This is the reason
> why spi_mem_ops was introduced.
>
> Please rewrite this driver over to use spi_mem_ops (instead of using
> generic SPI xfers) so that same driver supports both SPI-NOR and
> SPI-NAND flashes. Once that's done drivers/mtd/spi-nor/cadence-quadspi.c
> can be deleted.
>
> There are few existing examples of spi_mem_ops users in drivers/spi/
> (git grep spi_mem_ops) and materials here on how to write such a driver:
>
> [1]
> https://bootlin.com/blog/spi-mem-bringing-some-consistency-to-the-spi-memory-ecosystem/
> [2] https://www.youtube.com/watch?v=PkWbuLM_gmU
As per Mark Brown and your suggestion, I have started adapting
cadence-qaudspi driver with spi_mem_ops framework to work
QSPI-NAND/NOR as a generic driver(completely removed the legacy
SPI-XFERS), is in progress on Intel LGM SoC.
QSPI-IP on Intel LGM do not have DMA support and also not part of QSPI
IP, so couldn't able to validate DMA related.
will adapt the DMA things which are existing in cadence-quadspi.c as it is.
currently TI and Altera SoC's use this Cadence-qspi IP , both are not
using DMA as per my understanding (correct me if it is wrong).
confirmed through device tree entry.
what is your opinion on DMA related stuff? also using macronix(QSPI-NOR)
flash/Micron(QSPI-NAND).
---
With Regards
Vadivel
>> Ramuthevar Vadivel Murugan (2):
>> dt-bindings: spi: Add support for cadence-qspi IP Intel LGM SoC
>> spi: cadence-qspi: Add QSPI support for Intel LGM SoC
>>
>> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 +++
>> drivers/spi/Kconfig | 9 +
>> drivers/spi/Makefile | 1 +
>> drivers/spi/spi-cadence-qspi-apb.c | 644 +++++++++++++++++++++
>> drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++
>> drivers/spi/spi-cadence-qspi.c | 461 +++++++++++++++
>> drivers/spi/spi-cadence-qspi.h | 73 +++
>> 7 files changed, 1446 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
>> create mode 100644 drivers/spi/spi-cadence-qspi.c
>> create mode 100644 drivers/spi/spi-cadence-qspi.h
>>
On 10/10/19 7:04 AM, Ramuthevar, Vadivel MuruganX wrote:
> HI Vignesh,
>
> On 17/9/2019 12:50 AM, Vignesh Raghavendra wrote:
>> Hi,
>>
>> On 16/09/19 1:08 PM, Ramuthevar,Vadivel MuruganX wrote:
>>> patch 1: Add YAML for cadence-qspi devicetree cdocumentation.
>>> patch 2: cadence-qspi controller driver to support QSPI-NAND flash
>>> using existing spi-nand framework with legacy spi protocol.
>> Nope, you cannot have two drivers for the same IP (i.e Cadence QSPI)
>> just to support to different types of SPI memories. This is the reason
>> why spi_mem_ops was introduced.
>>
>> Please rewrite this driver over to use spi_mem_ops (instead of using
>> generic SPI xfers) so that same driver supports both SPI-NOR and
>> SPI-NAND flashes. Once that's done drivers/mtd/spi-nor/cadence-quadspi.c
>> can be deleted.
>>
>> There are few existing examples of spi_mem_ops users in drivers/spi/
>> (git grep spi_mem_ops) and materials here on how to write such a driver:
>>
>> [1]
>> https://bootlin.com/blog/spi-mem-bringing-some-consistency-to-the-spi-memory-ecosystem/
>>
>> [2] https://www.youtube.com/watch?v=PkWbuLM_gmU
> As per Mark Brown and your suggestion, I have started adapting
> cadence-qaudspi driver with spi_mem_ops framework to work
> QSPI-NAND/NOR as a generic driver(completely removed the legacy
> SPI-XFERS), is in progress on Intel LGM SoC.
> QSPI-IP on Intel LGM do not have DMA support and also not part of QSPI
> IP, so couldn't able to validate DMA related.
> will adapt the DMA things which are existing in cadence-quadspi.c as it is.
>
Great, appreciate the effort!
> currently TI and Altera SoC's use this Cadence-qspi IP , both are not
> using DMA as per my understanding (correct me if it is wrong).
> confirmed through device tree entry.
>
TI platforms use DMA to read data from flash in memory mapped mode
(direct access controller) using mem-to-mem DMA channels. Mem-to-mem DMA
channels are requested as and when needed and are not part of DT
description (as they are not bound to a device)
> what is your opinion on DMA related stuff?
Not having DMA support would be a regression. Please keep the DAC + DMA
part as is. I can help you will all the DMA related testing...
Regards
Vignesh
> also using macronix(QSPI-NOR)
> flash/Micron(QSPI-NAND).
> ---
> With Regards
> Vadivel
>>> Ramuthevar Vadivel Murugan (2):
>>> dt-bindings: spi: Add support for cadence-qspi IP Intel LGM SoC
>>> spi: cadence-qspi: Add QSPI support for Intel LGM SoC
>>>
>>> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 +++
>>> drivers/spi/Kconfig | 9 +
>>> drivers/spi/Makefile | 1 +
>>> drivers/spi/spi-cadence-qspi-apb.c | 644
>>> +++++++++++++++++++++
>>> drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++
>>> drivers/spi/spi-cadence-qspi.c | 461
>>> +++++++++++++++
>>> drivers/spi/spi-cadence-qspi.h | 73 +++
>>> 7 files changed, 1446 insertions(+)
>>> create mode 100644
>>> Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
>>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
>>> create mode 100644 drivers/spi/spi-cadence-qspi.c
>>> create mode 100644 drivers/spi/spi-cadence-qspi.h
>>>
--
Regards
Vignesh
Hi Vignesh,
On 10/10/2019 12:18 PM, Vignesh Raghavendra wrote:
>
> On 10/10/19 7:04 AM, Ramuthevar, Vadivel MuruganX wrote:
>> HI Vignesh,
>>
>> On 17/9/2019 12:50 AM, Vignesh Raghavendra wrote:
>>> Hi,
>>>
>>> On 16/09/19 1:08 PM, Ramuthevar,Vadivel MuruganX wrote:
>>>> patch 1: Add YAML for cadence-qspi devicetree cdocumentation.
>>>> patch 2: cadence-qspi controller driver to support QSPI-NAND flash
>>>> using existing spi-nand framework with legacy spi protocol.
>>> Nope, you cannot have two drivers for the same IP (i.e Cadence QSPI)
>>> just to support to different types of SPI memories. This is the reason
>>> why spi_mem_ops was introduced.
>>>
>>> Please rewrite this driver over to use spi_mem_ops (instead of using
>>> generic SPI xfers) so that same driver supports both SPI-NOR and
>>> SPI-NAND flashes. Once that's done drivers/mtd/spi-nor/cadence-quadspi.c
>>> can be deleted.
>>>
>>> There are few existing examples of spi_mem_ops users in drivers/spi/
>>> (git grep spi_mem_ops) and materials here on how to write such a driver:
>>>
>>> [1]
>>> https://bootlin.com/blog/spi-mem-bringing-some-consistency-to-the-spi-memory-ecosystem/
>>>
>>> [2] https://www.youtube.com/watch?v=PkWbuLM_gmU
>> As per Mark Brown and your suggestion, I have started adapting
>> cadence-qaudspi driver with spi_mem_ops framework to work
>> QSPI-NAND/NOR as a generic driver(completely removed the legacy
>> SPI-XFERS), is in progress on Intel LGM SoC.
>> QSPI-IP on Intel LGM do not have DMA support and also not part of QSPI
>> IP, so couldn't able to validate DMA related.
>> will adapt the DMA things which are existing in cadence-quadspi.c as it is.
>>
> Great, appreciate the effort!
>
>> currently TI and Altera SoC's use this Cadence-qspi IP , both are not
>> using DMA as per my understanding (correct me if it is wrong).
>> confirmed through device tree entry.
>>
> TI platforms use DMA to read data from flash in memory mapped mode
> (direct access controller) using mem-to-mem DMA channels. Mem-to-mem DMA
> channels are requested as and when needed and are not part of DT
> description (as they are not bound to a device)
yes, understood now, Thanks!
>> what is your opinion on DMA related stuff?
> Not having DMA support would be a regression. Please keep the DAC + DMA
> part as is. I can help you will all the DMA related testing...
Sure, will keep DAC + DMA, as we discussed earlier use QUIRKS to
differentiate and follow the same.
---
With Regards
Vadivel
> Regards
> Vignesh
>
>> also using macronix(QSPI-NOR)
>> flash/Micron(QSPI-NAND).
>> ---
>> With Regards
>> Vadivel
>>>> Ramuthevar Vadivel Murugan (2):
>>>> dt-bindings: spi: Add support for cadence-qspi IP Intel LGM SoC
>>>> spi: cadence-qspi: Add QSPI support for Intel LGM SoC
>>>>
>>>> .../devicetree/bindings/spi/cadence,qspi-nand.yaml | 84 +++
>>>> drivers/spi/Kconfig | 9 +
>>>> drivers/spi/Makefile | 1 +
>>>> drivers/spi/spi-cadence-qspi-apb.c | 644
>>>> +++++++++++++++++++++
>>>> drivers/spi/spi-cadence-qspi-apb.h | 174 ++++++
>>>> drivers/spi/spi-cadence-qspi.c | 461
>>>> +++++++++++++++
>>>> drivers/spi/spi-cadence-qspi.h | 73 +++
>>>> 7 files changed, 1446 insertions(+)
>>>> create mode 100644
>>>> Documentation/devicetree/bindings/spi/cadence,qspi-nand.yaml
>>>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.c
>>>> create mode 100644 drivers/spi/spi-cadence-qspi-apb.h
>>>> create mode 100644 drivers/spi/spi-cadence-qspi.c
>>>> create mode 100644 drivers/spi/spi-cadence-qspi.h
>>>>