spi: cadence-qspi: Add Renesas RZ/N1 support

Merge series from "Miquel Raynal (Schneider Electric)" <miquel.raynal@bootlin.com>:

This series adds support for the QSPI controller available on Renesas
RZ/N1S and RZ/N1D SoC. It has been tested with a custom board (see last
SPI patch for details), but has been tested by Wolfram (thank you!) on
the DB board.
Link: https://lore.kernel.org/linux-devicetree/20260116114852.52948-2-wsa+renesas@sang-engineering.com/

Adding support for this SoC required a few adaptations in the Cadence
QSPI driver. The bulk of the work is in the few last patches. Everything
else is just misc style fixes and improvements which bothered me while I
was wandering.

In order to support all constraints, I sometimes used a new quirk (for
the write protection feature and the "no indirect mode"), and sometimes
used the compatible directly. The ones I thought might not be RZ/N1
specific have been implemented under the form of a quirk, in order to
ease their reuse. The other adaptations, which I believe are more
Renesas specific, have been handled using the compatible. This is all
very arbitrary, and can be discussed.
This commit is contained in:
Mark Brown 2026-01-29 00:24:48 +00:00
commit 65ce1155f9
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 72 additions and 59 deletions

View File

@ -172,7 +172,7 @@ unevaluatedProperties: false
examples:
- |
qspi: spi@ff705000 {
spi@ff705000 {
compatible = "intel,socfpga-qspi", "cdns,qspi-nor";
#address-cells = <1>;
#size-cells = <0>;

View File

@ -40,13 +40,15 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_DEVICE_CS_CNT_MAX);
#define CQSPI_DISABLE_DAC_MODE BIT(1)
#define CQSPI_SUPPORT_EXTERNAL_DMA BIT(2)
#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3)
#define CQSPI_SLOW_SRAM BIT(4)
#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)
#define CQSPI_DISABLE_RUNTIME_PM BIT(10)
#define CQSPI_NO_INDIRECT_MODE BIT(11)
#define CQSPI_HAS_WR_PROTECT BIT(12)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@ -219,6 +221,8 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_IRQSTATUS 0x40
#define CQSPI_REG_IRQMASK 0x44
#define CQSPI_REG_WR_PROT_CTRL 0x58
#define CQSPI_REG_INDIRECTRD 0x60
#define CQSPI_REG_INDIRECTRD_START_MASK BIT(0)
#define CQSPI_REG_INDIRECTRD_CANCEL_MASK BIT(1)
@ -374,17 +378,12 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
/* Clear interrupt */
writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS);
if (cqspi->use_dma_read && ddata && ddata->get_dma_status) {
if (ddata->get_dma_status(cqspi)) {
complete(&cqspi->transfer_complete);
return IRQ_HANDLED;
}
}
else if (!cqspi->slow_sram)
irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR;
else
if (cqspi->use_dma_read && ddata && ddata->get_dma_status)
irq_status = ddata->get_dma_status(cqspi);
else if (cqspi->slow_sram)
irq_status &= CQSPI_IRQ_MASK_RD_SLOW_SRAM | CQSPI_IRQ_MASK_WR;
else
irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR;
if (irq_status)
complete(&cqspi->transfer_complete);
@ -1263,7 +1262,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi)
reg = readl(reg_base + CQSPI_REG_CONFIG);
reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB);
reg |= (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB;
reg |= div << CQSPI_REG_CONFIG_BAUD_LSB;
writel(reg, reg_base + CQSPI_REG_CONFIG);
}
@ -1430,7 +1429,8 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
if (ret)
return ret;
if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size))
if ((cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size)) ||
(cqspi->ddata && cqspi->ddata->quirks & CQSPI_NO_INDIRECT_MODE))
return cqspi_direct_read_execute(f_pdata, buf, from, len);
if (cqspi->use_dma_read && ddata && ddata->indirect_read_dma &&
@ -1536,6 +1536,10 @@ static bool cqspi_supports_mem_op(struct spi_mem *mem,
return false;
if (op->data.nbytes && op->data.buswidth != 8)
return false;
/* A single opcode is supported, it will be repeated */
if ((op->cmd.opcode >> 8) != (op->cmd.opcode & 0xFF))
return false;
} else if (!all_false) {
/* Mixed DTR modes are not supported. */
return false;
@ -1594,10 +1598,8 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi)
cqspi->fifo_depth = 0;
}
if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width)) {
dev_err(dev, "couldn't determine fifo-width\n");
return -ENXIO;
}
if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width))
cqspi->fifo_width = 4;
if (of_property_read_u32(np, "cdns,trigger-address",
&cqspi->trigger_address)) {
@ -1627,19 +1629,24 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
/* Disable all interrupts. */
writel(0, cqspi->iobase + CQSPI_REG_IRQMASK);
/* Configure the SRAM split to 1:1 . */
writel(cqspi->fifo_depth / 2, cqspi->iobase + CQSPI_REG_SRAMPARTITION);
if (!(cqspi->ddata && cqspi->ddata->quirks & CQSPI_NO_INDIRECT_MODE)) {
/* Configure the SRAM split to 1:1 . */
writel(cqspi->fifo_depth / 2, cqspi->iobase + CQSPI_REG_SRAMPARTITION);
/* Load indirect trigger address. */
writel(cqspi->trigger_address,
cqspi->iobase + CQSPI_REG_INDIRECTTRIGGER);
/* Load indirect trigger address. */
writel(cqspi->trigger_address,
cqspi->iobase + CQSPI_REG_INDIRECTTRIGGER);
/* Program read watermark -- 1/2 of the FIFO. */
writel(cqspi->fifo_depth * cqspi->fifo_width / 2,
cqspi->iobase + CQSPI_REG_INDIRECTRDWATERMARK);
/* Program write watermark -- 1/8 of the FIFO. */
writel(cqspi->fifo_depth * cqspi->fifo_width / 8,
cqspi->iobase + CQSPI_REG_INDIRECTWRWATERMARK);
}
/* Program read watermark -- 1/2 of the FIFO. */
writel(cqspi->fifo_depth * cqspi->fifo_width / 2,
cqspi->iobase + CQSPI_REG_INDIRECTRDWATERMARK);
/* Program write watermark -- 1/8 of the FIFO. */
writel(cqspi->fifo_depth * cqspi->fifo_width / 8,
cqspi->iobase + CQSPI_REG_INDIRECTWRWATERMARK);
/* Disable write protection at controller level */
if (cqspi->ddata && cqspi->ddata->quirks & CQSPI_HAS_WR_PROTECT)
writel(0, cqspi->iobase + CQSPI_REG_WR_PROT_CTRL);
/* Disable direct access controller */
if (!cqspi->use_direct_mode) {
@ -1890,7 +1897,7 @@ static int cqspi_probe(struct platform_device *pdev)
ret = clk_prepare_enable(cqspi->clk);
if (ret) {
dev_err(dev, "Cannot enable QSPI clock.\n");
goto probe_clk_failed;
goto disable_rpm;
}
/* Obtain QSPI reset control */
@ -1898,14 +1905,14 @@ static int cqspi_probe(struct platform_device *pdev)
if (IS_ERR(rstc)) {
ret = PTR_ERR(rstc);
dev_err(dev, "Cannot get QSPI reset.\n");
goto probe_reset_failed;
goto disable_clk;
}
rstc_ocp = devm_reset_control_get_optional_exclusive(dev, "qspi-ocp");
if (IS_ERR(rstc_ocp)) {
ret = PTR_ERR(rstc_ocp);
dev_err(dev, "Cannot get QSPI OCP reset.\n");
goto probe_reset_failed;
goto disable_clk;
}
if (of_device_is_compatible(pdev->dev.of_node, "starfive,jh7110-qspi")) {
@ -1913,7 +1920,7 @@ static int cqspi_probe(struct platform_device *pdev)
if (IS_ERR(rstc_ref)) {
ret = PTR_ERR(rstc_ref);
dev_err(dev, "Cannot get QSPI REF reset.\n");
goto probe_reset_failed;
goto disable_clk;
}
reset_control_assert(rstc_ref);
reset_control_deassert(rstc_ref);
@ -1955,7 +1962,7 @@ static int cqspi_probe(struct platform_device *pdev)
if (ddata->jh7110_clk_init) {
ret = cqspi_jh7110_clk_init(pdev, cqspi);
if (ret)
goto probe_reset_failed;
goto disable_clk;
}
if (ddata->quirks & CQSPI_DISABLE_STIG_MODE)
cqspi->disable_stig_mode = true;
@ -1963,7 +1970,7 @@ static int cqspi_probe(struct platform_device *pdev)
if (ddata->quirks & CQSPI_DMA_SET_MASK) {
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
if (ret)
goto probe_reset_failed;
goto disable_clks;
}
}
@ -1974,7 +1981,7 @@ static int cqspi_probe(struct platform_device *pdev)
pdev->name, cqspi);
if (ret) {
dev_err(dev, "Cannot request IRQ.\n");
goto probe_reset_failed;
goto disable_clks;
}
cqspi_wait_idle(cqspi);
@ -2001,31 +2008,36 @@ static int cqspi_probe(struct platform_device *pdev)
ret = cqspi_request_mmap_dma(cqspi);
if (ret == -EPROBE_DEFER) {
dev_err_probe(&pdev->dev, ret, "Failed to request mmap DMA\n");
goto probe_setup_failed;
goto disable_controller;
}
}
ret = spi_register_controller(host);
if (ret) {
dev_err(&pdev->dev, "failed to register SPI ctlr %d\n", ret);
goto probe_setup_failed;
goto release_dma_chan;
}
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
pm_runtime_put_autosuspend(dev);
return 0;
probe_setup_failed:
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
pm_runtime_disable(dev);
release_dma_chan:
if (cqspi->rx_chan)
dma_release_channel(cqspi->rx_chan);
disable_controller:
cqspi_controller_enable(cqspi, 0);
probe_reset_failed:
disable_clks:
if (cqspi->is_jh7110)
cqspi_jh7110_disable_clk(pdev, cqspi);
disable_clk:
if (pm_runtime_get_sync(&pdev->dev) >= 0)
clk_disable_unprepare(cqspi->clk);
probe_clk_failed:
disable_rpm:
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
pm_runtime_disable(dev);
return ret;
}
@ -2034,6 +2046,7 @@ static void cqspi_remove(struct platform_device *pdev)
const struct cqspi_driver_platdata *ddata;
struct cqspi_st *cqspi = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int ret = 0;
ddata = of_device_get_match_data(dev);
@ -2043,18 +2056,21 @@ static void cqspi_remove(struct platform_device *pdev)
cqspi_wait_idle(cqspi);
spi_unregister_controller(cqspi->host);
cqspi_controller_enable(cqspi, 0);
if (cqspi->rx_chan)
dma_release_channel(cqspi->rx_chan);
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
if (pm_runtime_get_sync(&pdev->dev) >= 0)
clk_disable(cqspi->clk);
cqspi_controller_enable(cqspi, 0);
if (cqspi->is_jh7110)
cqspi_jh7110_disable_clk(pdev, cqspi);
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
ret = pm_runtime_get_sync(&pdev->dev);
if (ret >= 0)
clk_disable(cqspi->clk);
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@ -2134,26 +2150,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_DISABLE_STIG_MODE
| CQSPI_DISABLE_RUNTIME_PM,
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_NO_SUPPORT_WR_COMPLETION |
CQSPI_SLOW_SRAM | CQSPI_DISABLE_STIG_MODE |
CQSPI_DISABLE_RUNTIME_PM,
};
static const struct cqspi_driver_platdata versal_ospi = {
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA
| CQSPI_DMA_SET_MASK,
.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,
.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,
};
@ -2170,7 +2183,7 @@ static const struct cqspi_driver_platdata pensando_cdns_qspi = {
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,
CQSPI_RD_NO_IRQ,
};
static const struct of_device_id cqspi_dt_ids[] = {