aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-07-04 10:10:49 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-07-04 10:10:49 -0700
commita1d8128f701682d34d9308c9e6b7385c0ffa4b4b (patch)
treedffd00ec6bc88289bdcad079a62b55a5dc5274ac /drivers
parentMerge tag 'platform-drivers-x86-v6.16-3' of git://git.kernel.org/pub/scm/linu... (diff)
parentspi: cadence-quadspi: fix cleanup of rx_chan on failure paths (diff)
downloadlinux-a1d8128f701682d34d9308c9e6b7385c0ffa4b4b.tar.gz
linux-a1d8128f701682d34d9308c9e6b7385c0ffa4b4b.zip
Merge tag 'spi-fix-v6.16-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi fixes from Mark Brown: "As well as a few driver specific fixes we've got a core change here which raises the hard coded limit on the number of devices we can support on one SPI bus since some FPGA based systems are running into the existing limit. This is not a good solution but it's one suitable for this point in the release cycle, we should dynamically size the relevant data structures which I hope will happen in the next couple of merge windows. We also pull in a MTD fix for the Qualcomm SNAND driver, the two fixes cover the same issue and merging them together minimises bisection issues" * tag 'spi-fix-v6.16-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: spi: cadence-quadspi: fix cleanup of rx_chan on failure paths spi: spi-fsl-dspi: Clear completion counter before initiating transfer spi: Raise limit on number of chip selects to 24 mtd: nand: qpic_common: prevent out of bounds access of BAM arrays spi: spi-qpic-snand: reallocate BAM transactions
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/qpic_common.c30
-rw-r--r--drivers/spi/spi-cadence-quadspi.c5
-rw-r--r--drivers/spi/spi-fsl-dspi.c11
-rw-r--r--drivers/spi/spi-qpic-snand.c16
4 files changed, 52 insertions, 10 deletions
diff --git a/drivers/mtd/nand/qpic_common.c b/drivers/mtd/nand/qpic_common.c
index 4dc4d65e7d32..8e604cc22ca3 100644
--- a/drivers/mtd/nand/qpic_common.c
+++ b/drivers/mtd/nand/qpic_common.c
@@ -57,14 +57,15 @@ qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc)
bam_txn_buf += sizeof(*bam_txn);
bam_txn->bam_ce = bam_txn_buf;
- bam_txn_buf +=
- sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
+ bam_txn->bam_ce_nitems = QPIC_PER_CW_CMD_ELEMENTS * num_cw;
+ bam_txn_buf += sizeof(*bam_txn->bam_ce) * bam_txn->bam_ce_nitems;
bam_txn->cmd_sgl = bam_txn_buf;
- bam_txn_buf +=
- sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
+ bam_txn->cmd_sgl_nitems = QPIC_PER_CW_CMD_SGL * num_cw;
+ bam_txn_buf += sizeof(*bam_txn->cmd_sgl) * bam_txn->cmd_sgl_nitems;
bam_txn->data_sgl = bam_txn_buf;
+ bam_txn->data_sgl_nitems = QPIC_PER_CW_DATA_SGL * num_cw;
init_completion(&bam_txn->txn_done);
@@ -238,6 +239,11 @@ int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
struct bam_transaction *bam_txn = nandc->bam_txn;
u32 offset;
+ if (bam_txn->bam_ce_pos + size > bam_txn->bam_ce_nitems) {
+ dev_err(nandc->dev, "BAM %s array is full\n", "CE");
+ return -EINVAL;
+ }
+
bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
/* fill the command desc */
@@ -258,6 +264,12 @@ int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
/* use the separate sgl after this command */
if (flags & NAND_BAM_NEXT_SGL) {
+ if (bam_txn->cmd_sgl_pos >= bam_txn->cmd_sgl_nitems) {
+ dev_err(nandc->dev, "BAM %s array is full\n",
+ "CMD sgl");
+ return -EINVAL;
+ }
+
bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
bam_ce_size = (bam_txn->bam_ce_pos -
bam_txn->bam_ce_start) *
@@ -297,10 +309,20 @@ int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
struct bam_transaction *bam_txn = nandc->bam_txn;
if (read) {
+ if (bam_txn->rx_sgl_pos >= bam_txn->data_sgl_nitems) {
+ dev_err(nandc->dev, "BAM %s array is full\n", "RX sgl");
+ return -EINVAL;
+ }
+
sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
vaddr, size);
bam_txn->rx_sgl_pos++;
} else {
+ if (bam_txn->tx_sgl_pos >= bam_txn->data_sgl_nitems) {
+ dev_err(nandc->dev, "BAM %s array is full\n", "TX sgl");
+ return -EINVAL;
+ }
+
sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
vaddr, size);
bam_txn->tx_sgl_pos++;
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index aa1932ba17cb..d3c78f59b22c 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -1960,11 +1960,6 @@ static int cqspi_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
- if (cqspi->rx_chan) {
- dma_release_channel(cqspi->rx_chan);
- goto probe_setup_failed;
- }
-
pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev);
pm_runtime_get_noresume(dev);
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 863781ba6c16..0dcd49114095 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -983,11 +983,20 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) {
status = dspi_dma_xfer(dspi);
} else {
+ /*
+ * Reinitialize the completion before transferring data
+ * to avoid the case where it might remain in the done
+ * state due to a spurious interrupt from a previous
+ * transfer. This could falsely signal that the current
+ * transfer has completed.
+ */
+ if (dspi->irq)
+ reinit_completion(&dspi->xfer_done);
+
dspi_fifo_write(dspi);
if (dspi->irq) {
wait_for_completion(&dspi->xfer_done);
- reinit_completion(&dspi->xfer_done);
} else {
do {
status = dspi_poll(dspi);
diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c
index 77d9cc65477a..f2e1a27b410d 100644
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -315,6 +315,22 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
mtd_set_ooblayout(mtd, &qcom_spi_ooblayout);
+ /*
+ * Free the temporary BAM transaction allocated initially by
+ * qcom_nandc_alloc(), and allocate a new one based on the
+ * updated max_cwperpage value.
+ */
+ qcom_free_bam_transaction(snandc);
+
+ snandc->max_cwperpage = cwperpage;
+
+ snandc->bam_txn = qcom_alloc_bam_transaction(snandc);
+ if (!snandc->bam_txn) {
+ dev_err(snandc->dev, "failed to allocate BAM transaction\n");
+ ret = -ENOMEM;
+ goto err_free_ecc_cfg;
+ }
+
ecc_cfg->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_data) |
FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 1) |