summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-qcom-qspi.c
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2023-08-04 00:29:50 +0300
committerJakub Kicinski <kuba@kernel.org>2023-08-04 00:34:37 +0300
commit35b1b1fd96388d5e3cf179bf36bd8a4153baf4a3 (patch)
tree0c0debbc24d6633ec6f8980f94e70f924aeee74e /drivers/spi/spi-qcom-qspi.c
parent36e68eadd303dce58018b503186a89bcb27d527e (diff)
parent999f6631866e9ea81add935b9c6ebaab0579d259 (diff)
downloadlinux-35b1b1fd96388d5e3cf179bf36bd8a4153baf4a3.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR. Conflicts: net/dsa/port.c 9945c1fb03a3 ("net: dsa: fix older DSA drivers using phylink") a88dd7538461 ("net: dsa: remove legacy_pre_march2020 detection") https://lore.kernel.org/all/20230731102254.2c9868ca@canb.auug.org.au/ net/xdp/xsk.c 3c5b4d69c358 ("net: annotate data-races around sk->sk_mark") b7f72a30e9ac ("xsk: introduce wrappers and helpers for supporting multi-buffer in Tx path") https://lore.kernel.org/all/20230731102631.39988412@canb.auug.org.au/ drivers/net/ethernet/broadcom/bnxt/bnxt.c 37b61cda9c16 ("bnxt: don't handle XDP in netpoll") 2b56b3d99241 ("eth: bnxt: handle invalid Tx completions more gracefully") https://lore.kernel.org/all/20230801101708.1dc7faac@canb.auug.org.au/ Adjacent changes: drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c 62da08331f1a ("net/mlx5e: Set proper IPsec source port in L4 selector") fbd517549c32 ("net/mlx5e: Add function to get IPsec offload namespace") drivers/net/ethernet/sfc/selftest.c 55c1528f9b97 ("sfc: fix field-spanning memcpy in selftest") ae9d445cd41f ("sfc: Miscellaneous comment removals") Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/spi/spi-qcom-qspi.c')
-rw-r--r--drivers/spi/spi-qcom-qspi.c54
1 files changed, 49 insertions, 5 deletions
diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c
index a8a683d6145c..1954c39b3d08 100644
--- a/drivers/spi/spi-qcom-qspi.c
+++ b/drivers/spi/spi-qcom-qspi.c
@@ -69,7 +69,7 @@
WR_FIFO_OVERRUN)
#define QSPI_ALL_IRQS (QSPI_ERR_IRQS | RESP_FIFO_RDY | \
WR_FIFO_EMPTY | WR_FIFO_FULL | \
- TRANSACTION_DONE)
+ TRANSACTION_DONE | DMA_CHAIN_DONE)
#define PIO_XFER_CTRL 0x0014
#define REQUEST_COUNT_MSK 0xffff
@@ -308,9 +308,11 @@ static int qcom_qspi_alloc_desc(struct qcom_qspi *ctrl, dma_addr_t dma_ptr,
dma_addr_t dma_cmd_desc;
/* allocate for dma cmd descriptor */
- virt_cmd_desc = dma_pool_alloc(ctrl->dma_cmd_pool, GFP_KERNEL | __GFP_ZERO, &dma_cmd_desc);
- if (!virt_cmd_desc)
- return -ENOMEM;
+ virt_cmd_desc = dma_pool_alloc(ctrl->dma_cmd_pool, GFP_ATOMIC | __GFP_ZERO, &dma_cmd_desc);
+ if (!virt_cmd_desc) {
+ dev_warn_once(ctrl->dev, "Couldn't find memory for descriptor\n");
+ return -EAGAIN;
+ }
ctrl->virt_cmd_desc[ctrl->n_cmd_desc] = virt_cmd_desc;
ctrl->dma_cmd_desc[ctrl->n_cmd_desc] = dma_cmd_desc;
@@ -355,10 +357,22 @@ static int qcom_qspi_setup_dma_desc(struct qcom_qspi *ctrl,
for (i = 0; i < sgt->nents; i++) {
dma_ptr_sg = sg_dma_address(sgt->sgl + i);
+ dma_len_sg = sg_dma_len(sgt->sgl + i);
if (!IS_ALIGNED(dma_ptr_sg, QSPI_ALIGN_REQ)) {
dev_warn_once(ctrl->dev, "dma_address not aligned to %d\n", QSPI_ALIGN_REQ);
return -EAGAIN;
}
+ /*
+ * When reading with DMA the controller writes to memory 1 word
+ * at a time. If the length isn't a multiple of 4 bytes then
+ * the controller can clobber the things later in memory.
+ * Fallback to PIO to be safe.
+ */
+ if (ctrl->xfer.dir == QSPI_READ && (dma_len_sg & 0x03)) {
+ dev_warn_once(ctrl->dev, "fallback to PIO for read of size %#010x\n",
+ dma_len_sg);
+ return -EAGAIN;
+ }
}
for (i = 0; i < sgt->nents; i++) {
@@ -441,8 +455,10 @@ static int qcom_qspi_transfer_one(struct spi_master *master,
ret = qcom_qspi_setup_dma_desc(ctrl, xfer);
if (ret != -EAGAIN) {
- if (!ret)
+ if (!ret) {
+ dma_wmb();
qcom_qspi_dma_xfer(ctrl);
+ }
goto exit;
}
dev_warn_once(ctrl->dev, "DMA failure, falling back to PIO\n");
@@ -603,6 +619,9 @@ static irqreturn_t qcom_qspi_irq(int irq, void *dev_id)
int_status = readl(ctrl->base + MSTR_INT_STATUS);
writel(int_status, ctrl->base + MSTR_INT_STATUS);
+ /* Ignore disabled interrupts */
+ int_status &= readl(ctrl->base + MSTR_INT_EN);
+
/* PIO mode handling */
if (ctrl->xfer.dir == QSPI_WRITE) {
if (int_status & WR_FIFO_EMPTY)
@@ -647,6 +666,30 @@ static irqreturn_t qcom_qspi_irq(int irq, void *dev_id)
return ret;
}
+static int qcom_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ /*
+ * If qcom_qspi_can_dma() is going to return false we don't need to
+ * adjust anything.
+ */
+ if (op->data.nbytes <= QSPI_MAX_BYTES_FIFO)
+ return 0;
+
+ /*
+ * When reading, the transfer needs to be a multiple of 4 bytes so
+ * shrink the transfer if that's not true. The caller will then do a
+ * second transfer to finish things up.
+ */
+ if (op->data.dir == SPI_MEM_DATA_IN && (op->data.nbytes & 0x3))
+ op->data.nbytes &= ~0x3;
+
+ return 0;
+}
+
+static const struct spi_controller_mem_ops qcom_qspi_mem_ops = {
+ .adjust_op_size = qcom_qspi_adjust_op_size,
+};
+
static int qcom_qspi_probe(struct platform_device *pdev)
{
int ret;
@@ -731,6 +774,7 @@ static int qcom_qspi_probe(struct platform_device *pdev)
if (of_property_read_bool(pdev->dev.of_node, "iommus"))
master->can_dma = qcom_qspi_can_dma;
master->auto_runtime_pm = true;
+ master->mem_ops = &qcom_qspi_mem_ops;
ret = devm_pm_opp_set_clkname(&pdev->dev, "core");
if (ret)