Commit 317272ee authored by Csókás, Bence's avatar Csókás, Bence Committed by Wentao Guan
Browse files

spi: atmel-quadspi: Create `atmel_qspi_ops` to support newer SoC families

stable inclusion
from stable-v6.6.78
commit 34e7a2360c1ca99e9cada7266016159a3e0da3f0
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBX1M5

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=34e7a2360c1ca99e9cada7266016159a3e0da3f0



--------------------------------

commit c0a0203cf57963792d59b3e4317a1d07b73df42a upstream.

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.

Signed-off-by: default avatarCsókás, Bence <csokas.bence@prolan.hu>
Link: https://patch.msgid.link/20241128174316.3209354-2-csokas.bence@prolan.hu


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 34e7a2360c1ca99e9cada7266016159a3e0da3f0)
Signed-off-by: default avatarWentao Guan <guanwentao@uniontech.com>
parent 5a7dd2bc
Loading
Loading
Loading
Loading
+77 −34
Original line number Diff line number Diff line
@@ -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,30 +417,39 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
	return 0;
}

static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
static int atmel_qspi_wait_for_completion(struct atmel_qspi *aq, u32 irq_mask)
{
	struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->controller);
	u32 sr, offset;
	int err;
	int err = 0;
	u32 sr;

	/*
	 * Check if the address exceeds the MMIO window size. An improvement
	 * would be to add support for regular SPI mode and fall back to it
	 * when the flash memories overrun the controller's memory space.
	 */
	if (op->addr.val + op->data.nbytes > aq->mmap_size)
		return -ENOTSUPP;
	/* 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);

	err = pm_runtime_resume_and_get(&aq->pdev->dev);
	if (err < 0)
	return err;
}

	err = atmel_qspi_set_cfg(aq, op, &offset);
	if (err)
		goto pm_runtime_put;
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->controller);

	/* Skip to the final steps if there is no data */
	if (op->data.nbytes) {
	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);

@@ -441,21 +463,36 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)

	/* Release the chip-select */
	atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);

	return atmel_qspi_wait_for_completion(aq, QSPI_SR_CMD_COMPLETED);
}

	/* Poll INSTRuction End status */
	sr = atmel_qspi_read(aq, QSPI_SR);
	if ((sr & QSPI_SR_CMD_COMPLETED) == 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 offset;
	int err;

	/*
	 * Check if the address exceeds the MMIO window size. An improvement
	 * would be to add support for regular SPI mode and fall back to it
	 * when the flash memories overrun the controller's memory space.
	 */
	if (op->addr.val + op->data.nbytes > aq->mmap_size)
		return -EOPNOTSUPP;

	if (op->addr.nbytes > 4)
		return -EOPNOTSUPP;

	err = pm_runtime_resume_and_get(&aq->pdev->dev);
	if (err < 0)
		return err;

	err = aq->ops->set_cfg(aq, op, &offset);
	if (err)
		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 */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base");