Hi all,
This series adds support for the embedded Cadence Quad SPI controller on
Mobileye EyeQ5. In our case "quad" is inappropriate as we support
octal. Most patches aim to improve performance and avoid interrupts.
Stuff to talk about:
- Both IRQ handler and read operation callbacks called
of_device_get_match_data(). This has a cost which we avoid.
- Add a flag to detect FIFO depth at runtime. The SRAMPARTITION
register is partially read-only: writel() then readl() to get the
depth.
This implies cdns,fifo-depth devicetree property is unused on this
platform.
- Add a flag to avoid IRQs for indirect reads. Hardware is way too fast
for sleeping to make sense.
- Add a flag to busywait in read operations. We avoid many hrtimer
interrupts this way. Fallback to sleeping, which shouldn't happen.
- End this by adding the compatible to the driver, add the controller
to the SoC devicetree and add an octal flash to the eval board
devicetree.
Dependencies:
- Devicetree:
- Patches [0][1] adding platform DTS, found in mips-next [2].
- System-controller series [3] for <&clocks> phandle.
- Driver: patches that fix this driver's runtime PM, see spi-next [4].
Have a nice day,
Théo
[0]: https://lore.kernel.org/lkml/[email protected]/
[1]: https://lore.kernel.org/linux-mips/[email protected]/
[2]: https://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git/log/
[3]: https://lore.kernel.org/lkml/[email protected]/
[4]: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git/log/
Signed-off-by: Théo Lebrun <[email protected]>
---
Théo Lebrun (11):
dt-bindings: qspi: cdns,qspi-nor: add mobileye,eyeq5-ospi compatible
spi: cadence-qspi: switch from legacy names to modern ones
spi: cadence-qspi: allow building for MIPS
spi: cadence-qspi: store device data pointer in private struct
spi: cadence-qspi: add FIFO depth detection quirk
spi: cadence-qspi: minimise register accesses on each op if !DTR
spi: cadence-qspi: add no-IRQ mode to indirect reads
spi: cadence-qspi: add early busywait to cqspi_wait_for_bit()
spi: cadence-qspi: add mobileye,eyeq5-ospi compatible
MIPS: mobileye: eyeq5: Add SPI-NOR controller node
MIPS: mobileye: eyeq5: add octal flash node to eval board DTS
.../devicetree/bindings/spi/cdns,qspi-nor.yaml | 13 ++-
arch/mips/boot/dts/mobileye/eyeq5-epm5.dts | 15 +++
arch/mips/boot/dts/mobileye/eyeq5.dtsi | 15 +++
drivers/spi/Kconfig | 2 +-
drivers/spi/spi-cadence-quadspi.c | 116 +++++++++++++++------
5 files changed, 130 insertions(+), 31 deletions(-)
---
base-commit: ecd5ff483c763ca368f815fafc954f6db9788143
change-id: 20240209-cdns-qspi-mbly-de2205a44ab3
Best regards,
--
Théo Lebrun <[email protected]>
The Cadence QSPI Controller driver is used on Mobileye EyeQ5 platform.
Allow building on MIPS.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index ddae0fde798e..77489dad2945 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -246,7 +246,7 @@ config SPI_CADENCE
config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller"
- depends on OF && (ARM || ARM64 || X86 || RISCV || COMPILE_TEST)
+ depends on OF && (ARM || ARM64 || X86 || RISCV || MIPS || COMPILE_TEST)
help
Enable support for the Cadence Quad SPI Flash controller.
--
2.44.0
Both spi_master_get_devdata() and the ->master field in struct
spi_device are part of the compatibility layer provided by
<linux/spi/spi.h>. Switch away from them.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 1f2d404c2cde..266a5d5dcfaf 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -1412,7 +1412,7 @@ static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op)
static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
int ret;
- struct cqspi_st *cqspi = spi_master_get_devdata(mem->spi->master);
+ struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller);
struct device *dev = &cqspi->pdev->dev;
ret = pm_runtime_resume_and_get(dev);
--
2.44.0
Declare a new mobileye,eyeq5-ospi compatible. Exploit quirk flags:
detect FIFO depth through SRAMPARTITION register; avoid IRQs during
read operations.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 2ad9c736f85f..046388cacc66 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -2060,6 +2060,13 @@ 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_DETECT_FIFO_DEPTH | CQSPI_RD_NO_IRQ |
+ CQSPI_BUSYWAIT_EARLY,
+};
+
static const struct of_device_id cqspi_dt_ids[] = {
{
.compatible = "cdns,qspi-nor",
@@ -2093,6 +2100,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 */ }
};
--
2.44.0
Support reads through polling, without any IRQ. The main reason is
performance; profiling shows that the first IRQ comes quickly on our
specific hardware. Once this IRQ arrives, we poll until all data is
retrieved. Avoid initial sleep to reduce IRQ count.
Hide this behavior behind a quirk flag.
This is confirmed through micro-benchmarks, but also end-to-end
performance tests. Mobileye EyeQ5, octal flash, reading 235M on a UBIFS
filesystem:
- No optimizations, ~10.34s, ~22.7 MB/s, 199230 IRQs
- CQSPI_SLOW_SRAM, ~10.34s, ~22.7 MB/s, 70284 IRQs
- CQSPI_RD_NO_IRQ, ~9.37s, ~25.1 MB/s, 521 IRQs
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 0fc452bab0ee..973ea4edbe3a 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -43,6 +43,7 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
#define CQSPI_SLOW_SRAM BIT(4)
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
#define CQSPI_DETECT_FIFO_DEPTH BIT(6)
+#define CQSPI_RD_NO_IRQ BIT(7)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@@ -703,6 +704,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;
@@ -726,17 +728,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;
@@ -778,7 +783,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);
--
2.44.0
cqspi_enable_dtr() is called for each operation, commands or not, reads
or writes. It writes CQSPI_REG_CONFIG then waits for idle (three
successful reads). Skip that in the no-DTR case if DTR is already
disabled.
It cannot be skipped in the DTR case as cqspi_setup_opcode_ext() writes
to a register and we must wait for idle state.
According to ftrace, the average cqspi_exec_mem_op() call goes from
85.4µs to 83.6µs when reading 235M over UBIFS on an octal flash.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index d178148788f0..0fc452bab0ee 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -492,8 +492,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);
--
2.44.0
If the CQSPI_BUSYWAIT_EARLY quirk flag is on, call
readl_relaxed_poll_timeout() with no sleep at the start of
cqspi_wait_for_bit(). If its short timeout expires, a sleeping
readl_relaxed_poll_timeout() call takes the relay.
Behavior is hidden behind a quirk flag to keep the previous behavior the
same on all platforms.
The reason is to avoid hrtimer interrupts on the system. All read
operations take less than 100µs.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 973ea4edbe3a..2ad9c736f85f 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -44,6 +44,7 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
#define CQSPI_DETECT_FIFO_DEPTH BIT(6)
#define CQSPI_RD_NO_IRQ BIT(7)
+#define CQSPI_BUSYWAIT_EARLY BIT(8)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@@ -110,7 +111,7 @@ struct cqspi_st {
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);
@@ -121,6 +122,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
@@ -299,13 +301,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 && ddata && ddata->quirks & CQSPI_BUSYWAIT_EARLY) {
+ 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)
@@ -435,8 +451,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");
@@ -791,8 +807,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;
@@ -1092,8 +1108,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;
--
2.44.0
Use hardware ability to read the FIFO depth thanks to
CQSPI_REG_SRAMPARTITION that is partially read-only. Keep current
behavior identical for existing compatibles.
Hide feature behind a flag. If unset and detected value is different
from the devicetree-provided value, warn.
Move probe cqspi->ddata assignment prior to cqspi_of_get_pdata() call.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 38 ++++++++++++++++++++++++++++++++------
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 02a80745fa6d..d178148788f0 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_DETECT_FIFO_DEPTH BIT(6)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@@ -1500,13 +1501,15 @@ static int cqspi_of_get_flash_pdata(struct platform_device *pdev,
static int cqspi_of_get_pdata(struct cqspi_st *cqspi)
{
+ const struct cqspi_driver_platdata *ddata = cqspi->ddata;
struct device *dev = &cqspi->pdev->dev;
struct device_node *np = dev->of_node;
u32 id[2];
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)) {
+ if (!(ddata && ddata->quirks & CQSPI_DETECT_FIFO_DEPTH) &&
+ of_property_read_u32(np, "cdns,fifo-depth", &cqspi->fifo_depth)) {
dev_err(dev, "couldn't determine fifo-depth\n");
return -ENXIO;
}
@@ -1538,8 +1541,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);
@@ -1573,8 +1574,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)
+{
+ const struct cqspi_driver_platdata *ddata = cqspi->ddata;
+ 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;
+
+ if (ddata && ddata->quirks & CQSPI_DETECT_FIFO_DEPTH) {
+ 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)
@@ -1728,6 +1750,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. */
@@ -1819,8 +1842,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);
- cqspi->ddata = ddata;
if (ddata) {
if (ddata->quirks & CQSPI_NEEDS_WR_DELAY)
cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC,
@@ -1862,7 +1883,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;
@@ -1945,7 +1969,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;
--
2.44.0
Add SPI-NOR octal flash node to evaluation board devicetree.
Signed-off-by: Théo Lebrun <[email protected]>
---
arch/mips/boot/dts/mobileye/eyeq5-epm5.dts | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
index 6898b2d8267d..0e5fee7b680c 100644
--- a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
+++ b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
@@ -21,3 +21,18 @@ memory@0 {
<0x8 0x02000000 0x0 0x7E000000>;
};
};
+
+&ospi {
+ flash0: flash@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0>; /* chip select */
+
+ spi-max-frequency = <40000000>;
+ spi-rx-bus-width = <8>;
+ cdns,read-delay = <1>;
+ cdns,tshsl-ns = <400>;
+ cdns,tsd2d-ns = <400>;
+ cdns,tchsh-ns = <125>;
+ cdns,tslch-ns = <50>;
+ };
+};
--
2.44.0
Add Cadence Quad SPI controller node to EyeQ5 SoC devicetree.
Octal is supported.
Signed-off-by: Théo Lebrun <[email protected]>
---
arch/mips/boot/dts/mobileye/eyeq5.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/mips/boot/dts/mobileye/eyeq5.dtsi b/arch/mips/boot/dts/mobileye/eyeq5.dtsi
index 8d4f65ec912d..1543c2b9bcb6 100644
--- a/arch/mips/boot/dts/mobileye/eyeq5.dtsi
+++ b/arch/mips/boot/dts/mobileye/eyeq5.dtsi
@@ -158,6 +158,21 @@ timer {
clocks = <&core0_clk>;
};
};
+
+ ospi: spi@2100000 {
+ compatible = "mobileye,eyeq5-ospi", "cdns,qspi-nor";
+ reg = <0 0x2100000 0x0 0x1000>,
+ <0 0x10000000 0x0 0x8000000>;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 20 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clocks EQ5C_DIV_OSPI>;
+ assigned-clocks = <&clocks EQ5C_DIV_OSPI>;
+ assigned-clock-rates = <167000000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cdns,fifo-width = <4>;
+ cdns,trigger-address = <0x00000000>;
+ };
};
};
--
2.44.0
Avoid of_device_get_match_data() call on each IRQ and each read
operation. Store pointer in `struct cqspi_st` device instance.
End-to-end performance measurements improve with this patch. On a given
octal flash, reading 235M over UBIFS is ~3.4% faster. During that read,
the average cqspi_exec_mem_op() call goes from 85.4µs to 80.7µs
according to ftrace. The worst case goes from 622.4µs to 615.2µs.
Signed-off-by: Théo Lebrun <[email protected]>
---
drivers/spi/spi-cadence-quadspi.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 266a5d5dcfaf..02a80745fa6d 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -102,6 +102,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 {
@@ -334,11 +336,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);
@@ -1358,16 +1357,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;
@@ -1823,7 +1819,8 @@ 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);
+ ddata = of_device_get_match_data(dev);
+ cqspi->ddata = ddata;
if (ddata) {
if (ddata->quirks & CQSPI_NEEDS_WR_DELAY)
cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC,
--
2.44.0
On 3/8/24 17:18, Théo Lebrun wrote:
> Add SPI-NOR octal flash node to evaluation board devicetree.
>
> Signed-off-by: Théo Lebrun <[email protected]>
> ---
> arch/mips/boot/dts/mobileye/eyeq5-epm5.dts | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
> index 6898b2d8267d..0e5fee7b680c 100644
> --- a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
> +++ b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
> @@ -21,3 +21,18 @@ memory@0 {
> <0x8 0x02000000 0x0 0x7E000000>;
> };
> };
> +
> +&ospi {
> + flash0: flash@0 {
> + compatible = "jedec,spi-nor";
> + reg = <0>; /* chip select */
> +
> + spi-max-frequency = <40000000>;
> + spi-rx-bus-width = <8>;
> + cdns,read-delay = <1>;
> + cdns,tshsl-ns = <400>;
> + cdns,tsd2d-ns = <400>;
> + cdns,tchsh-ns = <125>;
> + cdns,tslch-ns = <50>;
These cdns properties look bad, they bypass SPI NOR entirely. I'd check
if these timings can be determined at SFDP parsing time, then I'd pass
them to the SPI controller.
I see these properties are already accepted in the bindings file for few
years now. Something to improve later on.
On Mon, Mar 11 2024, Tudor Ambarus wrote:
> On 3/8/24 17:18, Théo Lebrun wrote:
>> Add SPI-NOR octal flash node to evaluation board devicetree.
>>
>> Signed-off-by: Théo Lebrun <[email protected]>
>> ---
>> arch/mips/boot/dts/mobileye/eyeq5-epm5.dts | 15 +++++++++++++++
>> 1 file changed, 15 insertions(+)
>>
>> diff --git a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
>> index 6898b2d8267d..0e5fee7b680c 100644
>> --- a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
>> +++ b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts
>> @@ -21,3 +21,18 @@ memory@0 {
>> <0x8 0x02000000 0x0 0x7E000000>;
>> };
>> };
>> +
>> +&ospi {
>> + flash0: flash@0 {
>> + compatible = "jedec,spi-nor";
>> + reg = <0>; /* chip select */
>> +
>> + spi-max-frequency = <40000000>;
>> + spi-rx-bus-width = <8>;
>> + cdns,read-delay = <1>;
>> + cdns,tshsl-ns = <400>;
>> + cdns,tsd2d-ns = <400>;
>> + cdns,tchsh-ns = <125>;
>> + cdns,tslch-ns = <50>;
>
> These cdns properties look bad, they bypass SPI NOR entirely. I'd check
> if these timings can be determined at SFDP parsing time, then I'd pass
> them to the SPI controller.
I agree, but I don't see any table in SFDP that describes these timings.
So I don't think there is a way to discover them.
>
> I see these properties are already accepted in the bindings file for few
> years now. Something to improve later on.
--
Regards,
Pratyush Yadav