summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-cadence-quadspi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-cadence-quadspi.c')
-rw-r--r--drivers/spi/spi-cadence-quadspi.c205
1 files changed, 160 insertions, 45 deletions
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 350b3dab3a05..fe0f122f07b0 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -42,9 +42,14 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3)
#define CQSPI_SLOW_SRAM BIT(4)
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
+#define CQSPI_RD_NO_IRQ BIT(6)
+#define CQSPI_DMA_SET_MASK BIT(7)
+#define CQSPI_SUPPORT_DEVICE_RESET BIT(8)
+#define CQSPI_DISABLE_STIG_MODE BIT(9)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
+#define CQSPI_SUPPORTS_QUAD BIT(1)
#define CQSPI_OP_WIDTH(part) ((part).nbytes ? ilog2((part).buswidth) : 0)
@@ -102,11 +107,14 @@ struct cqspi_st {
bool apb_ahb_hazard;
bool is_jh7110; /* Flag for StarFive JH7110 SoC */
+ bool disable_stig_mode;
+
+ const struct cqspi_driver_platdata *ddata;
};
struct cqspi_driver_platdata {
u32 hwcaps_mask;
- u8 quirks;
+ u16 quirks;
int (*indirect_read_dma)(struct cqspi_flash_pdata *f_pdata,
u_char *rxbuf, loff_t from_addr, size_t n_rx);
u32 (*get_dma_status)(struct cqspi_st *cqspi);
@@ -117,6 +125,7 @@ struct cqspi_driver_platdata {
/* Operation timeout value */
#define CQSPI_TIMEOUT_MS 500
#define CQSPI_READ_TIMEOUT_MS 10
+#define CQSPI_BUSYWAIT_TIMEOUT_US 500
/* Runtime_pm autosuspend delay */
#define CQSPI_AUTOSUSPEND_TIMEOUT 2000
@@ -140,6 +149,8 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_CONFIG_IDLE_LSB 31
#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
+#define CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK BIT(5)
+#define CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK BIT(6)
#define CQSPI_REG_RD_INSTR 0x04
#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0
@@ -295,13 +306,27 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_VERSAL_DMA_VAL 0x602
-static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
+static int cqspi_wait_for_bit(const struct cqspi_driver_platdata *ddata,
+ void __iomem *reg, const u32 mask, bool clr,
+ bool busywait)
{
+ u64 timeout_us = CQSPI_TIMEOUT_MS * USEC_PER_MSEC;
u32 val;
+ if (busywait) {
+ int ret = readl_relaxed_poll_timeout(reg, val,
+ (((clr ? ~val : val) & mask) == mask),
+ 0, CQSPI_BUSYWAIT_TIMEOUT_US);
+
+ if (ret != -ETIMEDOUT)
+ return ret;
+
+ timeout_us -= CQSPI_BUSYWAIT_TIMEOUT_US;
+ }
+
return readl_relaxed_poll_timeout(reg, val,
(((clr ? ~val : val) & mask) == mask),
- 10, CQSPI_TIMEOUT_MS * 1000);
+ 10, timeout_us);
}
static bool cqspi_is_idle(struct cqspi_st *cqspi)
@@ -334,11 +359,8 @@ static u32 cqspi_get_versal_dma_status(struct cqspi_st *cqspi)
static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
{
struct cqspi_st *cqspi = dev;
+ const struct cqspi_driver_platdata *ddata = cqspi->ddata;
unsigned int irq_status;
- struct device *device = &cqspi->pdev->dev;
- const struct cqspi_driver_platdata *ddata;
-
- ddata = of_device_get_match_data(device);
/* Read interrupt status */
irq_status = readl(cqspi->iobase + CQSPI_REG_IRQSTATUS);
@@ -434,8 +456,8 @@ static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg)
writel(reg, reg_base + CQSPI_REG_CMDCTRL);
/* Polling for completion. */
- ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_CMDCTRL,
- CQSPI_REG_CMDCTRL_INPROGRESS_MASK, 1);
+ ret = cqspi_wait_for_bit(cqspi->ddata, reg_base + CQSPI_REG_CMDCTRL,
+ CQSPI_REG_CMDCTRL_INPROGRESS_MASK, 1, true);
if (ret) {
dev_err(&cqspi->pdev->dev,
"Flash command execution timed out.\n");
@@ -492,8 +514,11 @@ static int cqspi_enable_dtr(struct cqspi_flash_pdata *f_pdata,
if (ret)
return ret;
} else {
- reg &= ~CQSPI_REG_CONFIG_DTR_PROTO;
- reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE;
+ unsigned int mask = CQSPI_REG_CONFIG_DTR_PROTO | CQSPI_REG_CONFIG_DUAL_OPCODE;
+ /* Shortcut if DTR is already disabled. */
+ if ((reg & mask) == 0)
+ return 0;
+ reg &= ~mask;
}
writel(reg, reg_base + CQSPI_REG_CONFIG);
@@ -700,6 +725,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
const size_t n_rx)
{
struct cqspi_st *cqspi = f_pdata->cqspi;
+ bool use_irq = !(cqspi->ddata && cqspi->ddata->quirks & CQSPI_RD_NO_IRQ);
struct device *dev = &cqspi->pdev->dev;
void __iomem *reg_base = cqspi->iobase;
void __iomem *ahb_base = cqspi->ahb_base;
@@ -723,17 +749,20 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
* all the read interrupts disabled for max performance.
*/
- if (!cqspi->slow_sram)
+ if (use_irq && cqspi->slow_sram)
+ writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
+ else if (use_irq)
writel(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK);
else
- writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
+ writel(0, reg_base + CQSPI_REG_IRQMASK);
reinit_completion(&cqspi->transfer_complete);
writel(CQSPI_REG_INDIRECTRD_START_MASK,
reg_base + CQSPI_REG_INDIRECTRD);
while (remaining > 0) {
- if (!wait_for_completion_timeout(&cqspi->transfer_complete,
+ if (use_irq &&
+ !wait_for_completion_timeout(&cqspi->transfer_complete,
msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS)))
ret = -ETIMEDOUT;
@@ -775,7 +804,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
bytes_to_read = cqspi_get_rd_sram_level(cqspi);
}
- if (remaining > 0) {
+ if (use_irq && remaining > 0) {
reinit_completion(&cqspi->transfer_complete);
if (cqspi->slow_sram)
writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
@@ -783,8 +812,8 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
}
/* Check indirect done status */
- ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTRD,
- CQSPI_REG_INDIRECTRD_DONE_MASK, 0);
+ ret = cqspi_wait_for_bit(cqspi->ddata, reg_base + CQSPI_REG_INDIRECTRD,
+ CQSPI_REG_INDIRECTRD_DONE_MASK, 0, true);
if (ret) {
dev_err(dev, "Indirect read completion error (%i)\n", ret);
goto failrd;
@@ -808,6 +837,25 @@ failrd:
return ret;
}
+static void cqspi_device_reset(struct cqspi_st *cqspi)
+{
+ u32 reg;
+
+ reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
+ reg |= CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK;
+ writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
+ /*
+ * NOTE: Delay timing implementation is derived from
+ * spi_nor_hw_reset()
+ */
+ writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, cqspi->iobase + CQSPI_REG_CONFIG);
+ usleep_range(1, 5);
+ writel(reg | CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, cqspi->iobase + CQSPI_REG_CONFIG);
+ usleep_range(100, 150);
+ writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, cqspi->iobase + CQSPI_REG_CONFIG);
+ usleep_range(1000, 1200);
+}
+
static void cqspi_controller_enable(struct cqspi_st *cqspi, bool enable)
{
void __iomem *reg_base = cqspi->iobase;
@@ -1084,8 +1132,8 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
}
/* Check indirect done status */
- ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTWR,
- CQSPI_REG_INDIRECTWR_DONE_MASK, 0);
+ ret = cqspi_wait_for_bit(cqspi->ddata, reg_base + CQSPI_REG_INDIRECTWR,
+ CQSPI_REG_INDIRECTWR_DONE_MASK, 0, false);
if (ret) {
dev_err(dev, "Indirect write completion error (%i)\n", ret);
goto failwr;
@@ -1358,16 +1406,13 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
const struct spi_mem_op *op)
{
struct cqspi_st *cqspi = f_pdata->cqspi;
- struct device *dev = &cqspi->pdev->dev;
- const struct cqspi_driver_platdata *ddata;
+ const struct cqspi_driver_platdata *ddata = cqspi->ddata;
loff_t from = op->addr.val;
size_t len = op->data.nbytes;
u_char *buf = op->data.buf.in;
u64 dma_align = (u64)(uintptr_t)buf;
int ret;
- ddata = of_device_get_match_data(dev);
-
ret = cqspi_read_setup(f_pdata, op);
if (ret)
return ret;
@@ -1388,7 +1433,7 @@ static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op)
struct cqspi_flash_pdata *f_pdata;
f_pdata = &cqspi->f_pdata[spi_get_chipselect(mem->spi, 0)];
- cqspi_configure(f_pdata, mem->spi->max_speed_hz);
+ cqspi_configure(f_pdata, op->max_freq);
if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
/*
@@ -1397,7 +1442,8 @@ static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op)
* reads, prefer STIG mode for such small reads.
*/
if (!op->addr.nbytes ||
- op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
+ (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX &&
+ !cqspi->disable_stig_mode))
return cqspi_command_read(f_pdata, op);
return cqspi_read(f_pdata, op);
@@ -1511,8 +1557,8 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi)
cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs");
if (of_property_read_u32(np, "cdns,fifo-depth", &cqspi->fifo_depth)) {
- dev_err(dev, "couldn't determine fifo-depth\n");
- return -ENXIO;
+ /* Zero signals FIFO depth should be runtime detected. */
+ cqspi->fifo_depth = 0;
}
if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width)) {
@@ -1542,8 +1588,6 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
{
u32 reg;
- cqspi_controller_enable(cqspi, 0);
-
/* Configure the remap address register, no remap */
writel(0, cqspi->iobase + CQSPI_REG_REMAP);
@@ -1577,8 +1621,29 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
reg |= CQSPI_REG_CONFIG_DMA_MASK;
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
}
+}
- cqspi_controller_enable(cqspi, 1);
+static void cqspi_controller_detect_fifo_depth(struct cqspi_st *cqspi)
+{
+ struct device *dev = &cqspi->pdev->dev;
+ u32 reg, fifo_depth;
+
+ /*
+ * Bits N-1:0 are writable while bits 31:N are read as zero, with 2^N
+ * the FIFO depth.
+ */
+ writel(U32_MAX, cqspi->iobase + CQSPI_REG_SRAMPARTITION);
+ reg = readl(cqspi->iobase + CQSPI_REG_SRAMPARTITION);
+ fifo_depth = reg + 1;
+
+ /* FIFO depth of zero means no value from devicetree was provided. */
+ if (cqspi->fifo_depth == 0) {
+ cqspi->fifo_depth = fifo_depth;
+ dev_dbg(dev, "using FIFO depth of %u\n", fifo_depth);
+ } else if (fifo_depth != cqspi->fifo_depth) {
+ dev_warn(dev, "detected FIFO depth (%u) different from config (%u)\n",
+ fifo_depth, cqspi->fifo_depth);
+ }
}
static int cqspi_request_mmap_dma(struct cqspi_st *cqspi)
@@ -1593,6 +1658,12 @@ static int cqspi_request_mmap_dma(struct cqspi_st *cqspi)
int ret = PTR_ERR(cqspi->rx_chan);
cqspi->rx_chan = NULL;
+ if (ret == -ENODEV) {
+ /* DMA support is not mandatory */
+ dev_info(&cqspi->pdev->dev, "No Rx DMA available\n");
+ return 0;
+ }
+
return dev_err_probe(&cqspi->pdev->dev, ret, "No Rx DMA available\n");
}
init_completion(&cqspi->rx_dma_complete);
@@ -1617,6 +1688,7 @@ static const struct spi_controller_mem_ops cqspi_mem_ops = {
static const struct spi_controller_mem_caps cqspi_mem_caps = {
.dtr = true,
+ .per_op_freq = true,
};
static int cqspi_setup_flash(struct cqspi_st *cqspi)
@@ -1624,23 +1696,20 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
unsigned int max_cs = cqspi->num_chipselect - 1;
struct platform_device *pdev = cqspi->pdev;
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct cqspi_flash_pdata *f_pdata;
unsigned int cs;
int ret;
/* Get flash device data */
- for_each_available_child_of_node(dev->of_node, np) {
+ for_each_available_child_of_node_scoped(dev->of_node, np) {
ret = of_property_read_u32(np, "reg", &cs);
if (ret) {
dev_err(dev, "Couldn't determine chip select.\n");
- of_node_put(np);
return ret;
}
if (cs >= cqspi->num_chipselect) {
dev_err(dev, "Chip select %d out of range.\n", cs);
- of_node_put(np);
return -EINVAL;
} else if (cs < max_cs) {
max_cs = cs;
@@ -1651,10 +1720,8 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
f_pdata->cs = cs;
ret = cqspi_of_get_flash_pdata(pdev, f_pdata, np);
- if (ret) {
- of_node_put(np);
+ if (ret)
return ret;
- }
}
cqspi->num_chipselect = max_cs + 1;
@@ -1731,6 +1798,7 @@ static int cqspi_probe(struct platform_device *pdev)
cqspi->pdev = pdev;
cqspi->host = host;
cqspi->is_jh7110 = false;
+ cqspi->ddata = ddata = of_device_get_match_data(dev);
platform_set_drvdata(pdev, cqspi);
/* Obtain configuration from OF. */
@@ -1822,13 +1890,14 @@ static int cqspi_probe(struct platform_device *pdev)
/* write completion is supported by default */
cqspi->wr_completion = true;
- ddata = of_device_get_match_data(dev);
if (ddata) {
if (ddata->quirks & CQSPI_NEEDS_WR_DELAY)
cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC,
cqspi->master_ref_clk_hz);
if (ddata->hwcaps_mask & CQSPI_SUPPORTS_OCTAL)
host->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL;
+ if (ddata->hwcaps_mask & CQSPI_SUPPORTS_QUAD)
+ host->mode_bits |= SPI_TX_QUAD;
if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) {
cqspi->use_direct_mode = true;
cqspi->use_direct_mode_wr = true;
@@ -1847,9 +1916,10 @@ static int cqspi_probe(struct platform_device *pdev)
if (ret)
goto probe_reset_failed;
}
+ if (ddata->quirks & CQSPI_DISABLE_STIG_MODE)
+ cqspi->disable_stig_mode = true;
- if (of_device_is_compatible(pdev->dev.of_node,
- "xlnx,versal-ospi-1.0")) {
+ if (ddata->quirks & CQSPI_DMA_SET_MASK) {
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
if (ret)
goto probe_reset_failed;
@@ -1864,7 +1934,10 @@ static int cqspi_probe(struct platform_device *pdev)
}
cqspi_wait_idle(cqspi);
+ cqspi_controller_enable(cqspi, 0);
+ cqspi_controller_detect_fifo_depth(cqspi);
cqspi_controller_init(cqspi);
+ cqspi_controller_enable(cqspi, 1);
cqspi->current_cs = -1;
cqspi->sclk = 0;
@@ -1876,6 +1949,9 @@ static int cqspi_probe(struct platform_device *pdev)
host->num_chipselect = cqspi->num_chipselect;
+ if (ddata && (ddata->quirks & CQSPI_SUPPORT_DEVICE_RESET))
+ cqspi_device_reset(cqspi);
+
if (cqspi->use_direct_mode) {
ret = cqspi_request_mmap_dma(cqspi);
if (ret == -EPROBE_DEFER)
@@ -1947,7 +2023,9 @@ static int cqspi_runtime_resume(struct device *dev)
clk_prepare_enable(cqspi->clk);
cqspi_wait_idle(cqspi);
+ cqspi_controller_enable(cqspi, 0);
cqspi_controller_init(cqspi);
+ cqspi_controller_enable(cqspi, 1);
cqspi->current_cs = -1;
cqspi->sclk = 0;
@@ -1957,13 +2035,25 @@ static int cqspi_runtime_resume(struct device *dev)
static int cqspi_suspend(struct device *dev)
{
struct cqspi_st *cqspi = dev_get_drvdata(dev);
+ int ret;
- return spi_controller_suspend(cqspi->host);
+ ret = spi_controller_suspend(cqspi->host);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_suspend(dev);
}
static int cqspi_resume(struct device *dev)
{
struct cqspi_st *cqspi = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_force_resume failed on resume\n");
+ return ret;
+ }
return spi_controller_resume(cqspi->host);
}
@@ -1982,7 +2072,7 @@ static const struct cqspi_driver_platdata k2g_qspi = {
};
static const struct cqspi_driver_platdata am654_ospi = {
- .hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
+ .hwcaps_mask = CQSPI_SUPPORTS_OCTAL | CQSPI_SUPPORTS_QUAD,
.quirks = CQSPI_NEEDS_WR_DELAY,
};
@@ -1993,12 +2083,23 @@ static const struct cqspi_driver_platdata intel_lgm_qspi = {
static const struct cqspi_driver_platdata socfpga_qspi = {
.quirks = CQSPI_DISABLE_DAC_MODE
| CQSPI_NO_SUPPORT_WR_COMPLETION
- | CQSPI_SLOW_SRAM,
+ | CQSPI_SLOW_SRAM
+ | CQSPI_DISABLE_STIG_MODE,
};
static const struct cqspi_driver_platdata versal_ospi = {
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
- .quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA,
+ .quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA
+ | CQSPI_DMA_SET_MASK,
+ .indirect_read_dma = cqspi_versal_indirect_read_dma,
+ .get_dma_status = cqspi_get_versal_dma_status,
+};
+
+static const struct cqspi_driver_platdata versal2_ospi = {
+ .hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
+ .quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA
+ | CQSPI_DMA_SET_MASK
+ | CQSPI_SUPPORT_DEVICE_RESET,
.indirect_read_dma = cqspi_versal_indirect_read_dma,
.get_dma_status = cqspi_get_versal_dma_status,
};
@@ -2012,6 +2113,12 @@ static const struct cqspi_driver_platdata pensando_cdns_qspi = {
.quirks = CQSPI_NEEDS_APB_AHB_HAZARD_WAR | CQSPI_DISABLE_DAC_MODE,
};
+static const struct cqspi_driver_platdata mobileye_eyeq5_ospi = {
+ .hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
+ .quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_NO_SUPPORT_WR_COMPLETION |
+ CQSPI_RD_NO_IRQ,
+};
+
static const struct of_device_id cqspi_dt_ids[] = {
{
.compatible = "cdns,qspi-nor",
@@ -2045,6 +2152,14 @@ static const struct of_device_id cqspi_dt_ids[] = {
.compatible = "amd,pensando-elba-qspi",
.data = &pensando_cdns_qspi,
},
+ {
+ .compatible = "mobileye,eyeq5-ospi",
+ .data = &mobileye_eyeq5_ospi,
+ },
+ {
+ .compatible = "amd,versal2-ospi",
+ .data = &versal2_ospi,
+ },
{ /* end of table */ }
};
@@ -2052,7 +2167,7 @@ MODULE_DEVICE_TABLE(of, cqspi_dt_ids);
static struct platform_driver cqspi_platform_driver = {
.probe = cqspi_probe,
- .remove_new = cqspi_remove,
+ .remove = cqspi_remove,
.driver = {
.name = CQSPI_NAME,
.pm = pm_ptr(&cqspi_dev_pm_ops),