summaryrefslogtreecommitdiffstats
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.c109
1 files changed, 81 insertions, 28 deletions
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 350b3dab3a05..05ebb03d319f 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -42,6 +42,7 @@ 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)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@@ -102,6 +103,8 @@ struct cqspi_st {
bool apb_ahb_hazard;
bool is_jh7110; /* Flag for StarFive JH7110 SoC */
+
+ const struct cqspi_driver_platdata *ddata;
};
struct cqspi_driver_platdata {
@@ -117,6 +120,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
@@ -295,13 +299,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 +352,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 +449,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 +507,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 +718,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 +742,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 +797,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 +805,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;
@@ -1084,8 +1106,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 +1380,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;
@@ -1511,8 +1530,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 +1561,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 +1594,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)
@@ -1731,6 +1769,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,7 +1861,6 @@ 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,
@@ -1864,7 +1902,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;
@@ -1947,7 +1988,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;
@@ -2012,6 +2055,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 +2094,10 @@ 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,
+ },
{ /* end of table */ }
};