spi: Fixes for v6.5
A bunch of fixes for the Qualcomm QSPI driver, fixing multiple issues with the newly added DMA mode - it had a number of issues exposed when tested in a wider range of use cases, both race condition style issues and issues with different inputs to those that had been used in test. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmTGiFsACgkQJNaLcl1U h9ACTgf/WgvJ+Yei8UVtLYnX5SREfiKt0DV9/0fa15/JB1H24WtpaELdJkvzds04 3zypksuI9TsQiw6Cm8WwYb4XQdPx8I+996czKrG2fnMwOruyzUTAOd6XHiEAnLuO 8YwJDoN0R7qdVRTwiTqqdl7EyYHRoeUaPX0j5DB4YSVRsaV/9FRQykEMfUBa2//D eRcOwoXcwZOghFBPXiFQrV6DDaStFk1a04PhpA8RCxPk7bPOQtZi5emLfnLcSJOr CHFfCepYlWTRgioXg+sihWXqapc+zpiIRqRtAfkcAFfs0k4O59bzGVrKxCgmtjeW r8oKqnfckrDmPThvYPBNP7UojQ5i7w== =q83F -----END PGP SIGNATURE----- Merge tag 'spi-fix-v6.5-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi fixes from Mark Brown: "A bunch of fixes for the Qualcomm QSPI driver, fixing multiple issues with the newly added DMA mode - it had a number of issues exposed when tested in a wider range of use cases, both race condition style issues and issues with different inputs to those that had been used in test" * tag 'spi-fix-v6.5-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: spi: spi-qcom-qspi: Add mem_ops to avoid PIO for badly sized reads spi: spi-qcom-qspi: Fallback to PIO for xfers that aren't multiples of 4 bytes spi: spi-qcom-qspi: Add DMA_CHAIN_DONE to ALL_IRQS spi: spi-qcom-qspi: Call dma_wmb() after setting up descriptors spi: spi-qcom-qspi: Use GFP_ATOMIC flag while allocating for descriptor spi: spi-qcom-qspi: Ignore disabled interrupts' status in isr
This commit is contained in:
commit
d5bb4b89ac
|
@ -69,7 +69,7 @@
|
||||||
WR_FIFO_OVERRUN)
|
WR_FIFO_OVERRUN)
|
||||||
#define QSPI_ALL_IRQS (QSPI_ERR_IRQS | RESP_FIFO_RDY | \
|
#define QSPI_ALL_IRQS (QSPI_ERR_IRQS | RESP_FIFO_RDY | \
|
||||||
WR_FIFO_EMPTY | WR_FIFO_FULL | \
|
WR_FIFO_EMPTY | WR_FIFO_FULL | \
|
||||||
TRANSACTION_DONE)
|
TRANSACTION_DONE | DMA_CHAIN_DONE)
|
||||||
|
|
||||||
#define PIO_XFER_CTRL 0x0014
|
#define PIO_XFER_CTRL 0x0014
|
||||||
#define REQUEST_COUNT_MSK 0xffff
|
#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;
|
dma_addr_t dma_cmd_desc;
|
||||||
|
|
||||||
/* allocate for dma cmd descriptor */
|
/* allocate for dma cmd descriptor */
|
||||||
virt_cmd_desc = dma_pool_alloc(ctrl->dma_cmd_pool, GFP_KERNEL | __GFP_ZERO, &dma_cmd_desc);
|
virt_cmd_desc = dma_pool_alloc(ctrl->dma_cmd_pool, GFP_ATOMIC | __GFP_ZERO, &dma_cmd_desc);
|
||||||
if (!virt_cmd_desc)
|
if (!virt_cmd_desc) {
|
||||||
return -ENOMEM;
|
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->virt_cmd_desc[ctrl->n_cmd_desc] = virt_cmd_desc;
|
||||||
ctrl->dma_cmd_desc[ctrl->n_cmd_desc] = dma_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++) {
|
for (i = 0; i < sgt->nents; i++) {
|
||||||
dma_ptr_sg = sg_dma_address(sgt->sgl + 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)) {
|
if (!IS_ALIGNED(dma_ptr_sg, QSPI_ALIGN_REQ)) {
|
||||||
dev_warn_once(ctrl->dev, "dma_address not aligned to %d\n", QSPI_ALIGN_REQ);
|
dev_warn_once(ctrl->dev, "dma_address not aligned to %d\n", QSPI_ALIGN_REQ);
|
||||||
return -EAGAIN;
|
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++) {
|
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);
|
ret = qcom_qspi_setup_dma_desc(ctrl, xfer);
|
||||||
if (ret != -EAGAIN) {
|
if (ret != -EAGAIN) {
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
|
dma_wmb();
|
||||||
qcom_qspi_dma_xfer(ctrl);
|
qcom_qspi_dma_xfer(ctrl);
|
||||||
|
}
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
dev_warn_once(ctrl->dev, "DMA failure, falling back to PIO\n");
|
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);
|
int_status = readl(ctrl->base + MSTR_INT_STATUS);
|
||||||
writel(int_status, 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 */
|
/* PIO mode handling */
|
||||||
if (ctrl->xfer.dir == QSPI_WRITE) {
|
if (ctrl->xfer.dir == QSPI_WRITE) {
|
||||||
if (int_status & WR_FIFO_EMPTY)
|
if (int_status & WR_FIFO_EMPTY)
|
||||||
|
@ -647,6 +666,30 @@ static irqreturn_t qcom_qspi_irq(int irq, void *dev_id)
|
||||||
return ret;
|
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)
|
static int qcom_qspi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
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"))
|
if (of_property_read_bool(pdev->dev.of_node, "iommus"))
|
||||||
master->can_dma = qcom_qspi_can_dma;
|
master->can_dma = qcom_qspi_can_dma;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
|
master->mem_ops = &qcom_qspi_mem_ops;
|
||||||
|
|
||||||
ret = devm_pm_opp_set_clkname(&pdev->dev, "core");
|
ret = devm_pm_opp_set_clkname(&pdev->dev, "core");
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
Loading…
Reference in New Issue