From patchwork Tue Oct 29 12:58:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?QmVuY2UgQ3PDs2vDoXM=?= X-Patchwork-Id: 839806 Received: from fw2.prolan.hu (fw2.prolan.hu [193.68.50.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 49DDD20606A; Tue, 29 Oct 2024 13:00:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.68.50.107 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730206819; cv=none; b=BujRCHemSQ8HRpPjnrgkQOxfqY50VGTiTh2A1WqR7loP4iOPqX6K/PlWd+IHfh/PkYJN36QWO6yAvTslRpcFy3ol+UNJNP0KLXAK0cF3Ir79+U8Ue8mKsbb3LLDZYBQfUrmTftN+PNonTVByehKE8ISZYr9qfcqcn+ipNxqUmjw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730206819; c=relaxed/simple; bh=qo248CgBS1tnumaycZ4qKStoReKnTQht5963gMX/YBI=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=e8D61O0JFMNhUO7bYclInrOX9TSM5n0CYiDM9eLQAa3IGkUbvL9XALxaE+f7j8inlT8v7XxetpQZnH940RscTTeHJZQsk2+mev09Zq1TjFc+vH/20qNhQn5ixwaw2D2LmF8nWmGerOn+hc+yMgUfGVuUu0PpqhQzXqPMN7ZyZ2Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=prolan.hu; spf=pass smtp.mailfrom=prolan.hu; dkim=pass (4096-bit key) header.d=prolan.hu header.i=@prolan.hu header.b=FnSjRBho; arc=none smtp.client-ip=193.68.50.107 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=prolan.hu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=prolan.hu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (4096-bit key) header.d=prolan.hu header.i=@prolan.hu header.b="FnSjRBho" Received: from proxmox-mailgw.intranet.prolan.hu (localhost.localdomain [127.0.0.1]) by proxmox-mailgw.intranet.prolan.hu (Proxmox) with ESMTP id 0ADC2A09F1; Tue, 29 Oct 2024 14:00:06 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=prolan.hu; h=cc :cc:content-transfer-encoding:content-type:content-type:date :from:from:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=mail; bh=WgY432aLhx2lOqFgoq9u CcBSfszhH81w0TpnR0b3JZE=; b=FnSjRBhoWEfczvd6I8RFtVpzB6Md9cMObODc ZRRg6d1CAwAcauUtPqRxEWPbo2aMa/e+4DVzzsRYzVZIdM/sZORoj7Tp09BH+xOK vJgrKeeDpjowO02V6tBzY18fGPlxx5dfKZtnADkyRxJVbYdgAaZ4FOnlXwrRJaTl 7JYy02XA2bKct4aUeAbezam4Bo6TxF/Gi/YlwgEFL6kUtKZhpr5TfgAoBMeWlbwj mD5Bh9uBTItxi66vybZRRQ4BMEkKSWPLI4K26cyQHaEh6b93auQqBfm/7DS393WL fj4fYF5JVoeXz3/WYpcYPOGf0MnVtE5kVod0yYaNM5ISf6OokHSKD2a44nept+uX ANH7vX9arnvNIBDKitTXenUktlFuGk2ocddFM/90lJoBrt2d5Sif7PLMRX8QuhF6 8LtmbHhN6ULZ3XLHZJC2cqIX6G7JH8lzuEG0Gawh1BcQyy0OOsAwmb45utuqf2fg o4BtKSGNY54cbS7isYzoBdfqRxeiveDo0kyAl5bxZrL6tGY4Ttnuv7fn7OCZ6kro J8ESDF4XICojneHS8Rik6JdQ10ST8aMHgPq/Iah/TXfb94MPu3/I03WdxaTjrBik 9UUx5vkQpYAgqNZt8LFm5NZaZaZMUeyf3Q4jonvF9/y6pBG+1hvDJTOxiMr6tutW fQzXP1o= From: =?utf-8?b?Q3PDs2vDoXMsIEJlbmNl?= To: , , CC: =?utf-8?b?Q3PDs2vDoXMsIEJlbmNl?= , "Tudor Ambarus" , Varshini Rajendran , Mark Brown , "Nicolas Ferre" , Alexandre Belloni , Claudiu Beznea Subject: [PATCH] spi: atmel-quadspi: Create `atmel_qspi_ops` to support newer SoC families Date: Tue, 29 Oct 2024 13:58:44 +0100 Message-ID: <20241029125843.2384307-1-csokas.bence@prolan.hu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-spi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ESET-AS: R=OK; S=0; OP=CALC; TIME=1730206804; VERSION=7978; MC=582406864; ID=190610; TRN=0; CRV=0; IPC=; SP=0; SIPS=0; PI=3; F=0 X-ESET-Antispam: OK X-EsetResult: clean, is OK X-EsetId: 37303A2980D94855667463 Refactor the code to introduce an ops struct, to prepare for merging support for later SoCs, such as SAMA7G5. This code was based on the vendor's kernel (linux4microchip). Cc'ing original contributors. Cc: Tudor Ambarus Cc: Varshini Rajendran Signed-off-by: Csókás, Bence --- drivers/spi/atmel-quadspi.c | 111 +++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 34 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 95cdfc28361e..20c4bb9d1dbe 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -138,11 +138,15 @@ #define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8) #define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC) +#define ATMEL_QSPI_TIMEOUT 1000 /* ms */ + struct atmel_qspi_caps { bool has_qspick; bool has_ricr; }; +struct atmel_qspi_ops; + struct atmel_qspi { void __iomem *regs; void __iomem *mem; @@ -150,13 +154,22 @@ struct atmel_qspi { struct clk *qspick; struct platform_device *pdev; const struct atmel_qspi_caps *caps; + const struct atmel_qspi_ops *ops; resource_size_t mmap_size; u32 pending; + u32 irq_mask; u32 mr; u32 scr; struct completion cmd_completion; }; +struct atmel_qspi_ops { + int (*set_cfg)(struct atmel_qspi *aq, const struct spi_mem_op *op, + u32 *offset); + int (*transfer)(struct spi_mem *mem, const struct spi_mem_op *op, + u32 offset); +}; + struct atmel_qspi_mode { u8 cmd_buswidth; u8 addr_buswidth; @@ -404,10 +417,60 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, return 0; } +static int atmel_qspi_wait_for_completion(struct atmel_qspi *aq, u32 irq_mask) +{ + int err = 0; + u32 sr; + + /* Poll INSTRuction End status */ + sr = atmel_qspi_read(aq, QSPI_SR); + if ((sr & irq_mask) == irq_mask) + return 0; + + /* Wait for INSTRuction End interrupt */ + reinit_completion(&aq->cmd_completion); + aq->pending = sr & irq_mask; + aq->irq_mask = irq_mask; + atmel_qspi_write(irq_mask, aq, QSPI_IER); + if (!wait_for_completion_timeout(&aq->cmd_completion, + msecs_to_jiffies(ATMEL_QSPI_TIMEOUT))) + err = -ETIMEDOUT; + atmel_qspi_write(irq_mask, aq, QSPI_IDR); + + return err; +} + +static int atmel_qspi_transfer(struct spi_mem *mem, + const struct spi_mem_op *op, u32 offset) +{ + struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->master); + + /* Skip to the final steps if there is no data */ + if (!op->data.nbytes) + return atmel_qspi_wait_for_completion(aq, + QSPI_SR_CMD_COMPLETED); + + /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */ + (void)atmel_qspi_read(aq, QSPI_IFR); + + /* Send/Receive data */ + if (op->data.dir == SPI_MEM_DATA_IN) + memcpy_fromio(op->data.buf.in, aq->mem + offset, + op->data.nbytes); + else + memcpy_toio(aq->mem + offset, op->data.buf.out, + op->data.nbytes); + + /* Release the chip-select */ + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); + + return atmel_qspi_wait_for_completion(aq, QSPI_SR_CMD_COMPLETED); +} + static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->controller); - u32 sr, offset; + u32 offset; int err; /* @@ -416,46 +479,20 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) * when the flash memories overrun the controller's memory space. */ if (op->addr.val + op->data.nbytes > aq->mmap_size) - return -ENOTSUPP; + return -EOPNOTSUPP; + + if (op->addr.nbytes > 4) + return -EOPNOTSUPP; err = pm_runtime_resume_and_get(&aq->pdev->dev); if (err < 0) return err; - err = atmel_qspi_set_cfg(aq, op, &offset); + err = aq->ops->set_cfg(aq, op, &offset); if (err) goto pm_runtime_put; - /* Skip to the final steps if there is no data */ - if (op->data.nbytes) { - /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */ - (void)atmel_qspi_read(aq, QSPI_IFR); - - /* Send/Receive data */ - if (op->data.dir == SPI_MEM_DATA_IN) - memcpy_fromio(op->data.buf.in, aq->mem + offset, - op->data.nbytes); - else - memcpy_toio(aq->mem + offset, op->data.buf.out, - op->data.nbytes); - - /* Release the chip-select */ - atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); - } - - /* Poll INSTRuction End status */ - sr = atmel_qspi_read(aq, QSPI_SR); - if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED) - goto pm_runtime_put; - - /* Wait for INSTRuction End interrupt */ - reinit_completion(&aq->cmd_completion); - aq->pending = sr & QSPI_SR_CMD_COMPLETED; - atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IER); - if (!wait_for_completion_timeout(&aq->cmd_completion, - msecs_to_jiffies(1000))) - err = -ETIMEDOUT; - atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR); + err = aq->ops->transfer(mem, op, offset); pm_runtime_put: pm_runtime_mark_last_busy(&aq->pdev->dev); @@ -571,12 +608,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id) return IRQ_NONE; aq->pending |= pending; - if ((aq->pending & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED) + if ((aq->pending & aq->irq_mask) == aq->irq_mask) complete(&aq->cmd_completion); return IRQ_HANDLED; } +static const struct atmel_qspi_ops atmel_qspi_ops = { + .set_cfg = atmel_qspi_set_cfg, + .transfer = atmel_qspi_transfer, +}; + static int atmel_qspi_probe(struct platform_device *pdev) { struct spi_controller *ctrl; @@ -601,6 +643,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) init_completion(&aq->cmd_completion); aq->pdev = pdev; + aq->ops = &atmel_qspi_ops; /* Map the registers */ aq->regs = devm_platform_ioremap_resource_byname(pdev, "qspi_base");