From 102a0743326a03cd1a1202ceda21e175b7d3575c Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 20 Feb 2024 01:20:52 +0000 Subject: [PATCH] add new system file --- kernel/drivers/spi/spi-qup.c | 149 ++++++++++++++++++++++++++++++++----------------- 1 files changed, 96 insertions(+), 53 deletions(-) diff --git a/kernel/drivers/spi/spi-qup.c b/kernel/drivers/spi/spi-qup.c index cb74fd1..2cc9bb4 100644 --- a/kernel/drivers/spi/spi-qup.c +++ b/kernel/drivers/spi/spi-qup.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008-2014, The Linux foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License rev 2 and - * only rev 2 as published by the free Software foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or fITNESS fOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/clk.h> @@ -281,6 +273,9 @@ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, controller->base + QUP_OPERATIONAL); + if (!remainder) + goto exit; + if (is_block_mode) { num_words = (remainder > words_per_block) ? words_per_block : remainder; @@ -310,11 +305,13 @@ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be * present and this is used to determine if transaction is complete */ - *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); - if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG) - writel_relaxed(QUP_OP_IN_SERVICE_FLAG, - controller->base + QUP_OPERATIONAL); - +exit: + if (!remainder) { + *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); + if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG) + writel_relaxed(QUP_OP_IN_SERVICE_FLAG, + controller->base + QUP_OPERATIONAL); + } } static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words) @@ -361,6 +358,10 @@ /* ACK by clearing service flag */ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, controller->base + QUP_OPERATIONAL); + + /* make sure the interrupt is valid */ + if (!remainder) + return; if (is_block_mode) { num_words = (remainder > words_per_block) ? @@ -575,10 +576,24 @@ return 0; } +static bool spi_qup_data_pending(struct spi_qup *controller) +{ + unsigned int remainder_tx, remainder_rx; + + remainder_tx = DIV_ROUND_UP(spi_qup_len(controller) - + controller->tx_bytes, controller->w_size); + + remainder_rx = DIV_ROUND_UP(spi_qup_len(controller) - + controller->rx_bytes, controller->w_size); + + return remainder_tx || remainder_rx; +} + static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) { struct spi_qup *controller = dev_id; u32 opflags, qup_err, spi_err; + unsigned long flags; int error = 0; qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); @@ -610,6 +625,11 @@ error = -EIO; } + spin_lock_irqsave(&controller->lock, flags); + if (!controller->error) + controller->error = error; + spin_unlock_irqrestore(&controller->lock, flags); + if (spi_qup_is_dma_xfer(controller->mode)) { writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); } else { @@ -618,10 +638,21 @@ if (opflags & QUP_OP_OUT_SERVICE_FLAG) spi_qup_write(controller); + + if (!spi_qup_data_pending(controller)) + complete(&controller->done); } - if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error) + if (error) complete(&controller->done); + + if (opflags & QUP_OP_MAX_INPUT_DONE_FLAG) { + if (!spi_qup_is_dma_xfer(controller->mode)) { + if (spi_qup_data_pending(controller)) + return IRQ_HANDLED; + } + complete(&controller->done); + } return IRQ_HANDLED; } @@ -817,7 +848,7 @@ { struct spi_qup *controller = spi_master_get_devdata(master); unsigned long timeout, flags; - int ret = -EIO; + int ret; ret = spi_qup_io_prep(spi, xfer); if (ret) @@ -842,10 +873,6 @@ else ret = spi_qup_do_pio(spi, xfer, timeout); - if (ret) - goto exit; - -exit: spi_qup_set_state(controller, QUP_STATE_RESET); spin_lock_irqsave(&controller->lock, flags); if (!ret) @@ -905,11 +932,11 @@ int ret; /* allocate dma resources, if available */ - master->dma_rx = dma_request_slave_channel_reason(dev, "rx"); + master->dma_rx = dma_request_chan(dev, "rx"); if (IS_ERR(master->dma_rx)) return PTR_ERR(master->dma_rx); - master->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + master->dma_tx = dma_request_chan(dev, "tx"); if (IS_ERR(master->dma_tx)) { ret = PTR_ERR(master->dma_tx); goto err_tx; @@ -1003,23 +1030,8 @@ return -ENXIO; } - ret = clk_prepare_enable(cclk); - if (ret) { - dev_err(dev, "cannot enable core clock\n"); - return ret; - } - - ret = clk_prepare_enable(iclk); - if (ret) { - clk_disable_unprepare(cclk); - dev_err(dev, "cannot enable iface clock\n"); - return ret; - } - master = spi_alloc_master(dev, sizeof(struct spi_qup)); if (!master) { - clk_disable_unprepare(cclk); - clk_disable_unprepare(iclk); dev_err(dev, "cannot allocate master\n"); return -ENOMEM; } @@ -1065,6 +1077,19 @@ spin_lock_init(&controller->lock); init_completion(&controller->done); + ret = clk_prepare_enable(cclk); + if (ret) { + dev_err(dev, "cannot enable core clock\n"); + goto error_dma; + } + + ret = clk_prepare_enable(iclk); + if (ret) { + clk_disable_unprepare(cclk); + dev_err(dev, "cannot enable iface clock\n"); + goto error_dma; + } + iomode = readl_relaxed(base + QUP_IO_M_MODES); size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); @@ -1094,7 +1119,7 @@ ret = spi_qup_set_state(controller, QUP_STATE_RESET); if (ret) { dev_err(dev, "cannot set RESET state\n"); - goto error_dma; + goto error_clk; } writel_relaxed(0, base + QUP_OPERATIONAL); @@ -1118,7 +1143,7 @@ ret = devm_request_irq(dev, irq, spi_qup_qup_irq, IRQF_TRIGGER_HIGH, pdev->name, controller); if (ret) - goto error_dma; + goto error_clk; pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(dev); @@ -1133,11 +1158,12 @@ disable_pm: pm_runtime_disable(&pdev->dev); +error_clk: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); error_dma: spi_qup_release_dma(master); error: - clk_disable_unprepare(cclk); - clk_disable_unprepare(iclk); spi_master_put(master); return ret; } @@ -1172,8 +1198,10 @@ return ret; ret = clk_prepare_enable(controller->cclk); - if (ret) + if (ret) { + clk_disable_unprepare(controller->iclk); return ret; + } /* Disable clocks auto gaiting */ config = readl_relaxed(controller->base + QUP_CONFIG); @@ -1219,14 +1247,25 @@ return ret; ret = clk_prepare_enable(controller->cclk); - if (ret) + if (ret) { + clk_disable_unprepare(controller->iclk); return ret; + } ret = spi_qup_set_state(controller, QUP_STATE_RESET); if (ret) - return ret; + goto disable_clk; - return spi_master_resume(master); + ret = spi_master_resume(master); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + return ret; } #endif /* CONFIG_PM_SLEEP */ @@ -1237,17 +1276,21 @@ int ret; ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) - return ret; - ret = spi_qup_set_state(controller, QUP_STATE_RESET); - if (ret) - return ret; + if (ret >= 0) { + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + dev_warn(&pdev->dev, "failed to reset controller (%pe)\n", + ERR_PTR(ret)); + + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + } else { + dev_warn(&pdev->dev, "failed to resume, skip hw disable (%pe)\n", + ERR_PTR(ret)); + } spi_qup_release_dma(master); - - clk_disable_unprepare(controller->cclk); - clk_disable_unprepare(controller->iclk); pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); -- Gitblit v1.6.2