From a7e82204743c3095deeac73e3142152d325141f8 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:02 +0800
Subject: [PATCH 01/14] clk: aspeed: Get HCLK frequency support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

User can get correct HCLK frequency during driver probe stage
by adding the following configuration in the device tree.
"clocks = <&scu ASPEED_CLK_AHB>".

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/clk/aspeed/clk_ast2500.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/clk/aspeed/clk_ast2500.c b/drivers/clk/aspeed/clk_ast2500.c
index 623c6915b8..dc446ce9fb 100644
--- a/drivers/clk/aspeed/clk_ast2500.c
+++ b/drivers/clk/aspeed/clk_ast2500.c
@@ -30,6 +30,12 @@
 
 #define D2PLL_DEFAULT_RATE	(250 * 1000 * 1000)
 
+/*
+ * AXI/AHB clock selection, taken from Aspeed SDK
+ */
+#define SCU_HWSTRAP_AXIAHB_DIV_SHIFT    9
+#define SCU_HWSTRAP_AXIAHB_DIV_MASK     (0x7 << SCU_HWSTRAP_AXIAHB_DIV_SHIFT)
+
 DECLARE_GLOBAL_DATA_PTR;
 
 /*
@@ -86,6 +92,20 @@ static ulong ast2500_get_clkin(struct ast2500_scu *scu)
 			? 25 * 1000 * 1000 : 24 * 1000 * 1000;
 }
 
+static u32 ast2500_get_hclk(ulong clkin, struct ast2500_scu *scu)
+{
+	u32 hpll_reg = readl(&scu->h_pll_param);
+	ulong axi_div = 2;
+	u32 rate;
+	ulong ahb_div = 1 + ((readl(&scu->hwstrap)
+			      & SCU_HWSTRAP_AXIAHB_DIV_MASK)
+			     >> SCU_HWSTRAP_AXIAHB_DIV_SHIFT);
+
+	rate = ast2500_get_hpll_rate(clkin, hpll_reg);
+
+	return (rate / axi_div / ahb_div);
+}
+
 /**
  * Get current rate or uart clock
  *
@@ -147,6 +167,9 @@ static ulong ast2500_clk_get_rate(struct clk *clk)
 			rate = rate / apb_div;
 		}
 		break;
+	case ASPEED_CLK_AHB:
+		rate = ast2500_get_hclk(clkin, priv->scu);
+		break;
 	case ASPEED_CLK_SDIO:
 		{
 			ulong apb_div = 4 + 4 * ((readl(&priv->scu->clk_sel1)

From cf2051ac4cc57ba194215ae095591b477af309b6 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:03 +0800
Subject: [PATCH 02/14] pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add FWSPICS1 and SPI1CS1 in AST2500 pinctrl group.
On AST2500 EVB, FWSPICS1 can be supported by default.
An extra jumper, J45, should be configured before
enabling SPI1CS1.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/pinctrl/aspeed/pinctrl_ast2500.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/pinctrl/aspeed/pinctrl_ast2500.c b/drivers/pinctrl/aspeed/pinctrl_ast2500.c
index 3c2e10b88e..93920a6389 100644
--- a/drivers/pinctrl/aspeed/pinctrl_ast2500.c
+++ b/drivers/pinctrl/aspeed/pinctrl_ast2500.c
@@ -61,6 +61,8 @@ static const struct ast2500_group_config ast2500_groups[] = {
 	{ "MDIO2", 5, (1 << 2) },
 	{ "SD1", 5, (1 << 0) },
 	{ "SD2", 5, (1 << 1) },
+	{ "FWSPICS1", 3, (1 << 24) },
+	{ "SPI1CS1", 1, (1 << 15) },
 };
 
 static int ast2500_pinctrl_get_groups_count(struct udevice *dev)

From 4daa6bb6f76d3124e73cef22c98e7269140f42b0 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:04 +0800
Subject: [PATCH 03/14] spi: aspeed: Add ASPEED SPI controller driver

Add ASPEED BMC FMC/SPI memory controller driver with
spi-mem interface for AST2500 and AST2600 platform.

There are three SPI memory controllers embedded in an ASPEED SoC.
- FMC: Named as Firmware Memory Controller. After AC on, MCU ROM
       fetches initial device boot image from FMC chip select(CS) 0.

- SPI1: Play the role of a SPI Master controller. Or, there is a
        dedicated path for HOST(X86) to access its BIOS flash mounted
        under BMC. spi-aspeed-smc.c implements the control sequence when
        SPI1 is a SPI master.

- SPI2: It is a pure SPI flash controller. For most scenarios, flashes
        mounted under it are for pure storage purpose.

ASPEED SPI controller supports 1-1-1, 1-1-2 and 1-1-4 SPI flash mode.
Three types of command mode are supported, normal mode, command
read/write mode and user mode.
- Normal mode: Default mode. After power on, normal read command 03h or
               13h is used to fetch boot image from SPI flash.
               - AST2500: Only 03h command can be used after power on
                          or reset.
               - AST2600: If FMC04[6:4] is set, 13h command is used,
                          otherwise, 03h command.
               The address length is decided by FMC04[2:0].

- Command mode: SPI controller can send command and address
                automatically when CPU read/write the related remapped
                or decoded address area. The command used by this mode
                can be configured by FMC10/14/18[23:16]. Also, the
                address length is decided by FMC04[2:0]. This mode will
                be implemented in the following patch series.

- User mode: It is a traditional and pure SPI operation, where
             SPI transmission is controlled by CPU. It is the main
             mode in this patch.

Each SPI controller in ASPEED SoC has its own decoded address mapping.
Within each SPI controller decoded address, driver can assign a specific
address region for each CS of a SPI controller. The decoded address
cannot overlap to each other. With normal mode and command mode, the
decoded address accessed by the CPU determines which CS is active.
When user mode is adopted, the CS decoded address is a FIFO, CPU can
send/receive any SPI transmission by accessing the related decoded
address for the target CS.

This patch only implements user mode initially. Command read/write
mode will be implemented in the following patches.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 MAINTAINERS                  |   7 +
 drivers/spi/Kconfig          |   8 +
 drivers/spi/Makefile         |   1 +
 drivers/spi/spi-aspeed-smc.c | 603 +++++++++++++++++++++++++++++++++++
 4 files changed, 619 insertions(+)
 create mode 100644 drivers/spi/spi-aspeed-smc.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 36a2b69fcb..1ebcd368a6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -724,6 +724,13 @@ S:	Maintained
 F:	drivers/pci/pcie_phytium.c
 F:	arch/arm/dts/phytium-durian.dts
 
+ASPEED FMC SPI DRIVER
+M:	Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
+M:	Cédric Le Goater <clg@kaod.org>
+R:	Aspeed BMC SW team <BMC-SW@aspeedtech.com>
+S:	Maintained
+F:	drivers/spi/spi-aspeed-smc.c
+
 BINMAN
 M:	Simon Glass <sjg@chromium.org>
 M:	Alper Nebi Yasak <alpernebiyasak@gmail.com>
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 75b794548b..8ca9274ef4 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -401,6 +401,14 @@ config SANDBOX_SPI
 		};
 	  };
 
+config SPI_ASPEED_SMC
+	bool "ASPEED SPI flash controller driver"
+	depends on DM_SPI && SPI_MEM
+	default n
+	help
+	  Enable ASPEED SPI flash controller driver for AST2500
+	  and AST2600 SoCs.
+
 config SPI_SIFIVE
 	bool "SiFive SPI driver"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 4de77c260a..7ba953d2df 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
 obj-$(CONFIG_CADENCE_OSPI_VERSAL) += cadence_ospi_versal.o
 obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
 obj-$(CONFIG_SOFT_SPI) += soft_spi.o
+obj-$(CONFIG_SPI_ASPEED_SMC) += spi-aspeed-smc.o
 obj-$(CONFIG_SPI_MEM) += spi-mem.o
 obj-$(CONFIG_TI_QSPI) += ti_qspi.o
 obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
new file mode 100644
index 0000000000..25b490bcf4
--- /dev/null
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ASPEED FMC/SPI Controller driver
+ *
+ * Copyright (c) 2022 ASPEED Corporation.
+ * Copyright (c) 2022 IBM Corporation.
+ *
+ * Author:
+ *     Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
+ *     Cedric Le Goater <clg@kaod.org>
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/sizes.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi-mem.h>
+
+#define ASPEED_SPI_MAX_CS       3
+
+#define CTRL_IO_SINGLE_DATA     0
+#define CTRL_IO_QUAD_DATA       BIT(30)
+#define CTRL_IO_DUAL_DATA       BIT(29)
+
+#define CTRL_IO_MODE_USER       GENMASK(1, 0)
+#define CTRL_IO_MODE_CMD_READ   BIT(0)
+#define CTRL_IO_MODE_CMD_WRITE  BIT(1)
+#define CTRL_STOP_ACTIVE        BIT(2)
+
+struct aspeed_spi_regs {
+	u32 conf;                       /* 0x00 CE Type Setting */
+	u32 ctrl;                       /* 0x04 CE Control */
+	u32 intr_ctrl;                  /* 0x08 Interrupt Control and Status */
+	u32 cmd_ctrl;                   /* 0x0c Command Control */
+	u32 ce_ctrl[ASPEED_SPI_MAX_CS]; /* 0x10 .. 0x18 CEx Control */
+	u32 _reserved0[5];              /* .. */
+	u32 segment_addr[ASPEED_SPI_MAX_CS]; /* 0x30 .. 0x38 Segment Address */
+	u32 _reserved1[5];		/* .. */
+	u32 soft_rst_cmd_ctrl;          /* 0x50 Auto Soft-Reset Command Control */
+	u32 _reserved2[11];             /* .. */
+	u32 dma_ctrl;                   /* 0x80 DMA Control/Status */
+	u32 dma_flash_addr;             /* 0x84 DMA Flash Side Address */
+	u32 dma_dram_addr;              /* 0x88 DMA DRAM Side Address */
+	u32 dma_len;                    /* 0x8c DMA Length Register */
+	u32 dma_checksum;               /* 0x90 Checksum Calculation Result */
+	u32 timings[ASPEED_SPI_MAX_CS]; /* 0x94 Read Timing Compensation */
+};
+
+struct aspeed_spi_plat {
+	u8 max_cs;
+	void __iomem *ahb_base; /* AHB address base for all flash devices. */
+	fdt_size_t ahb_sz; /* Overall AHB window size for all flash device. */
+};
+
+struct aspeed_spi_flash {
+	void __iomem *ahb_base;
+	u32 ahb_decoded_sz;
+	u32 ce_ctrl_user;
+	u32 ce_ctrl_read;
+};
+
+struct aspeed_spi_priv {
+	u32 num_cs;
+	struct aspeed_spi_regs *regs;
+	struct aspeed_spi_info *info;
+	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
+};
+
+struct aspeed_spi_info {
+	u32 io_mode_mask;
+	u32 max_bus_width;
+	u32 min_decoded_sz;
+	void (*set_4byte)(struct udevice *bus, u32 cs);
+	u32 (*segment_start)(struct udevice *bus, u32 reg);
+	u32 (*segment_end)(struct udevice *bus, u32 reg);
+	u32 (*segment_reg)(u32 start, u32 end);
+};
+
+static u32 aspeed_spi_get_io_mode(u32 bus_width)
+{
+	switch (bus_width) {
+	case 1:
+		return CTRL_IO_SINGLE_DATA;
+	case 2:
+		return CTRL_IO_DUAL_DATA;
+	case 4:
+		return CTRL_IO_QUAD_DATA;
+	default:
+		/* keep in default value */
+		return CTRL_IO_SINGLE_DATA;
+	}
+}
+
+static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	u32 start_offset = ((reg >> 16) & 0xff) << 23;
+
+	if (start_offset == 0)
+		return (u32)plat->ahb_base;
+
+	return (u32)plat->ahb_base + start_offset;
+}
+
+static u32 ast2500_spi_segment_end(struct udevice *bus, u32 reg)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	u32 end_offset = ((reg >> 24) & 0xff) << 23;
+
+	/* Meaningless end_offset, set to physical ahb base. */
+	if (end_offset == 0)
+		return (u32)plat->ahb_base;
+
+	return (u32)plat->ahb_base + end_offset;
+}
+
+static u32 ast2500_spi_segment_reg(u32 start, u32 end)
+{
+	if (start == end)
+		return 0;
+
+	return ((((start) >> 23) & 0xff) << 16) | ((((end) >> 23) & 0xff) << 24);
+}
+
+static void ast2500_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 reg_val;
+
+	reg_val = readl(&priv->regs->ctrl);
+	reg_val |= 0x1 << cs;
+	writel(reg_val, &priv->regs->ctrl);
+}
+
+static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	u32 start_offset = (reg << 16) & 0x0ff00000;
+
+	if (start_offset == 0)
+		return (u32)plat->ahb_base;
+
+	return (u32)plat->ahb_base + start_offset;
+}
+
+static u32 ast2600_spi_segment_end(struct udevice *bus, u32 reg)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	u32 end_offset = reg & 0x0ff00000;
+
+	/* Meaningless end_offset, set to physical ahb base. */
+	if (end_offset == 0)
+		return (u32)plat->ahb_base;
+
+	return (u32)plat->ahb_base + end_offset + 0x100000;
+}
+
+static u32 ast2600_spi_segment_reg(u32 start, u32 end)
+{
+	if (start == end)
+		return 0;
+
+	return ((start & 0x0ff00000) >> 16) | ((end - 0x100000) & 0x0ff00000);
+}
+
+static void ast2600_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 reg_val;
+
+	reg_val = readl(&priv->regs->ctrl);
+	reg_val |= 0x11 << cs;
+	writel(reg_val, &priv->regs->ctrl);
+}
+
+static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf,
+				    size_t len)
+{
+	size_t offset = 0;
+
+	if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) &&
+	    IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
+		readsl(ahb_base, buf, len >> 2);
+		offset = len & ~0x3;
+		len -= offset;
+	}
+
+	readsb(ahb_base, (u8 *)buf + offset, len);
+
+	return 0;
+}
+
+static int aspeed_spi_write_to_ahb(void __iomem *ahb_base, const void *buf,
+				   size_t len)
+{
+	size_t offset = 0;
+
+	if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) &&
+	    IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
+		writesl(ahb_base, buf, len >> 2);
+		offset = len & ~0x3;
+		len -= offset;
+	}
+
+	writesb(ahb_base, (u8 *)buf + offset, len);
+
+	return 0;
+}
+
+/*
+ * Currently, only support 1-1-1, 1-1-2 or 1-1-4
+ * SPI NOR flash operation format.
+ */
+static bool aspeed_spi_supports_op(struct spi_slave *slave,
+				   const struct spi_mem_op *op)
+{
+	struct udevice *bus = slave->dev->parent;
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+
+	if (op->cmd.buswidth > 1)
+		return false;
+
+	if (op->addr.nbytes != 0) {
+		if (op->addr.buswidth > 1)
+			return false;
+		if (op->addr.nbytes < 3 || op->addr.nbytes > 4)
+			return false;
+	}
+
+	if (op->dummy.nbytes != 0) {
+		if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
+			return false;
+	}
+
+	if (op->data.nbytes != 0 &&
+	    op->data.buswidth > priv->info->max_bus_width)
+		return false;
+
+	if (!spi_mem_default_supports_op(slave, op))
+		return false;
+
+	return true;
+}
+
+static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
+					const struct spi_mem_op *op)
+{
+	struct udevice *dev = slave->dev;
+	struct udevice *bus = dev->parent;
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
+	u32 cs = slave_plat->cs;
+	u32 ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+	u32 ce_ctrl_val;
+	struct aspeed_spi_flash *flash = &priv->flashes[cs];
+	u8 dummy_data[16] = {0};
+	u8 addr[4] = {0};
+	int i;
+
+	dev_dbg(dev, "cmd:%x(%d),addr:%llx(%d),dummy:%d(%d),data_len:0x%x(%d)\n",
+		op->cmd.opcode, op->cmd.buswidth, op->addr.val,
+		op->addr.buswidth, op->dummy.nbytes, op->dummy.buswidth,
+		op->data.nbytes, op->data.buswidth);
+
+	/*
+	 * Set controller to 4-byte address mode
+	 * if flash is in 4-byte address mode.
+	 */
+	if (op->cmd.opcode == SPINOR_OP_EN4B)
+		priv->info->set_4byte(bus, cs);
+
+	/* Start user mode */
+	ce_ctrl_val = flash->ce_ctrl_user;
+	writel(ce_ctrl_val, ce_ctrl_reg);
+	ce_ctrl_val &= (~CTRL_STOP_ACTIVE);
+	writel(ce_ctrl_val, ce_ctrl_reg);
+
+	/* Send command */
+	aspeed_spi_write_to_ahb(flash->ahb_base, &op->cmd.opcode, 1);
+
+	/* Send address */
+	for (i = op->addr.nbytes; i > 0; i--) {
+		addr[op->addr.nbytes - i] =
+			((u32)op->addr.val >> ((i - 1) * 8)) & 0xff;
+	}
+
+	/* Change io_mode */
+	ce_ctrl_val &= ~priv->info->io_mode_mask;
+	ce_ctrl_val |= aspeed_spi_get_io_mode(op->addr.buswidth);
+	writel(ce_ctrl_val, ce_ctrl_reg);
+	aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
+
+	/* Send dummy cycles */
+	aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data, op->dummy.nbytes);
+
+	/* Change io_mode */
+	ce_ctrl_val &= ~priv->info->io_mode_mask;
+	ce_ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
+	writel(ce_ctrl_val, ce_ctrl_reg);
+
+	/* Send data */
+	if (op->data.dir == SPI_MEM_DATA_OUT) {
+		aspeed_spi_write_to_ahb(flash->ahb_base, op->data.buf.out,
+					op->data.nbytes);
+	} else {
+		aspeed_spi_read_from_ahb(flash->ahb_base, op->data.buf.in,
+					 op->data.nbytes);
+	}
+
+	ce_ctrl_val |= CTRL_STOP_ACTIVE;
+	writel(ce_ctrl_val, ce_ctrl_reg);
+
+	/* Restore controller setting. */
+	writel(flash->ce_ctrl_read, ce_ctrl_reg);
+
+	return 0;
+}
+
+static struct aspeed_spi_flash *aspeed_spi_get_flash(struct udevice *dev)
+{
+	struct udevice *bus = dev->parent;
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 cs = slave_plat->cs;
+
+	if (cs >= plat->max_cs) {
+		dev_err(dev, "invalid CS %u\n", cs);
+		return NULL;
+	}
+
+	return &priv->flashes[cs];
+}
+
+static void aspeed_spi_decoded_base_calculate(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 cs;
+
+	priv->flashes[0].ahb_base = plat->ahb_base;
+
+	for (cs = 1; cs < plat->max_cs; cs++) {
+		priv->flashes[cs].ahb_base =
+				priv->flashes[cs - 1].ahb_base +
+				priv->flashes[cs - 1].ahb_decoded_sz;
+	}
+}
+
+static void aspeed_spi_decoded_range_set(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 decoded_reg_val;
+	u32 start_addr, end_addr;
+	u32 cs;
+
+	for (cs = 0; cs < plat->max_cs; cs++) {
+		start_addr = (u32)priv->flashes[cs].ahb_base;
+		end_addr = (u32)priv->flashes[cs].ahb_base +
+			   priv->flashes[cs].ahb_decoded_sz;
+
+		decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
+
+		writel(decoded_reg_val, &priv->regs->segment_addr[cs]);
+
+		dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
+			cs, decoded_reg_val, start_addr, end_addr);
+	}
+}
+
+static int aspeed_spi_decoded_range_config(struct udevice *bus)
+{
+	aspeed_spi_decoded_base_calculate(bus);
+	aspeed_spi_decoded_range_set(bus);
+
+	return 0;
+}
+
+/*
+ * Initialize SPI controller for each chip select.
+ * Here, only the minimum decode range is configured
+ * in order to get device (SPI NOR flash) information
+ * at the early stage.
+ */
+static int aspeed_spi_ctrl_init(struct udevice *bus)
+{
+	int ret;
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 cs;
+	u32 reg_val;
+	u32 decoded_sz;
+
+	/* Enable write capability for all CS. */
+	reg_val = readl(&priv->regs->conf);
+	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
+	       &priv->regs->conf);
+
+	memset(priv->flashes, 0x0,
+	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
+
+	/* Initial user mode. */
+	for (cs = 0; cs < priv->num_cs; cs++) {
+		priv->flashes[cs].ce_ctrl_user =
+				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
+	}
+
+	/* Assign basic AHB decoded size for each CS. */
+	for (cs = 0; cs < plat->max_cs; cs++) {
+		reg_val = readl(&priv->regs->segment_addr[cs]);
+		decoded_sz = priv->info->segment_end(bus, reg_val) -
+			     priv->info->segment_start(bus, reg_val);
+
+		if (decoded_sz < priv->info->min_decoded_sz)
+			decoded_sz = priv->info->min_decoded_sz;
+
+		priv->flashes[cs].ahb_decoded_sz = decoded_sz;
+	}
+
+	ret = aspeed_spi_decoded_range_config(bus);
+
+	return ret;
+}
+
+static const struct aspeed_spi_info ast2500_fmc_info = {
+	.io_mode_mask = 0x70000000,
+	.max_bus_width = 2,
+	.min_decoded_sz = 0x800000,
+	.set_4byte = ast2500_spi_chip_set_4byte,
+	.segment_start = ast2500_spi_segment_start,
+	.segment_end = ast2500_spi_segment_end,
+	.segment_reg = ast2500_spi_segment_reg,
+};
+
+/*
+ * There are some different between FMC and SPI controllers.
+ * For example, DMA operation, but this isn't implemented currently.
+ */
+static const struct aspeed_spi_info ast2500_spi_info = {
+	.io_mode_mask = 0x70000000,
+	.max_bus_width = 2,
+	.min_decoded_sz = 0x800000,
+	.set_4byte = ast2500_spi_chip_set_4byte,
+	.segment_start = ast2500_spi_segment_start,
+	.segment_end = ast2500_spi_segment_end,
+	.segment_reg = ast2500_spi_segment_reg,
+};
+
+static const struct aspeed_spi_info ast2600_fmc_info = {
+	.io_mode_mask = 0xf0000000,
+	.max_bus_width = 4,
+	.min_decoded_sz = 0x200000,
+	.set_4byte = ast2600_spi_chip_set_4byte,
+	.segment_start = ast2600_spi_segment_start,
+	.segment_end = ast2600_spi_segment_end,
+	.segment_reg = ast2600_spi_segment_reg,
+};
+
+static const struct aspeed_spi_info ast2600_spi_info = {
+	.io_mode_mask = 0xf0000000,
+	.max_bus_width = 4,
+	.min_decoded_sz = 0x200000,
+	.set_4byte = ast2600_spi_chip_set_4byte,
+	.segment_start = ast2600_spi_segment_start,
+	.segment_end = ast2600_spi_segment_end,
+	.segment_reg = ast2600_spi_segment_reg,
+};
+
+static int aspeed_spi_claim_bus(struct udevice *dev)
+{
+	struct udevice *bus = dev->parent;
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+
+	dev_dbg(bus, "%s: claim bus CS%u\n", bus->name, slave_plat->cs);
+
+	return 0;
+}
+
+static int aspeed_spi_release_bus(struct udevice *dev)
+{
+	struct udevice *bus = dev->parent;
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+
+	dev_dbg(bus, "%s: release bus CS%u\n", bus->name, slave_plat->cs);
+
+	if (!aspeed_spi_get_flash(dev))
+		return -ENODEV;
+
+	return 0;
+}
+
+static int aspeed_spi_set_mode(struct udevice *bus, uint mode)
+{
+	dev_dbg(bus, "%s: setting mode to %x\n", bus->name, mode);
+
+	return 0;
+}
+
+static int aspeed_spi_set_speed(struct udevice *bus, uint hz)
+{
+	dev_dbg(bus, "%s: setting speed to %u\n", bus->name, hz);
+	/*
+	 * ASPEED SPI controller supports multiple CS with different
+	 * clock frequency. We cannot distinguish which CS here.
+	 * Thus, the related implementation is postponed to claim_bus.
+	 */
+
+	return 0;
+}
+
+static int apseed_spi_of_to_plat(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+
+	priv->regs = (void __iomem *)devfdt_get_addr_index(bus, 0);
+	if ((u32)priv->regs == FDT_ADDR_T_NONE) {
+		dev_err(bus, "wrong ctrl base\n");
+		return -ENODEV;
+	}
+
+	plat->ahb_base =
+		(void __iomem *)devfdt_get_addr_size_index(bus, 1, &plat->ahb_sz);
+	if ((u32)plat->ahb_base == FDT_ADDR_T_NONE) {
+		dev_err(bus, "wrong AHB base\n");
+		return -ENODEV;
+	}
+
+	plat->max_cs = dev_read_u32_default(bus, "num-cs", ASPEED_SPI_MAX_CS);
+	if (plat->max_cs > ASPEED_SPI_MAX_CS)
+		return -EINVAL;
+
+	dev_dbg(bus, "ctrl_base = 0x%x, ahb_base = 0x%p, size = 0x%lx\n",
+		(u32)priv->regs, plat->ahb_base, plat->ahb_sz);
+	dev_dbg(bus, "max_cs = %d\n", plat->max_cs);
+
+	return 0;
+}
+
+static int aspeed_spi_probe(struct udevice *bus)
+{
+	int ret;
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct udevice *dev;
+
+	priv->info = (struct aspeed_spi_info *)dev_get_driver_data(bus);
+
+	priv->num_cs = 0;
+	for (device_find_first_child(bus, &dev); dev;
+	     device_find_next_child(&dev)) {
+		priv->num_cs++;
+	}
+
+	if (priv->num_cs > ASPEED_SPI_MAX_CS)
+		return -EINVAL;
+
+	ret = aspeed_spi_ctrl_init(bus);
+
+	return ret;
+}
+
+static const struct spi_controller_mem_ops aspeed_spi_mem_ops = {
+	.supports_op = aspeed_spi_supports_op,
+	.exec_op = aspeed_spi_exec_op_user_mode,
+};
+
+static const struct dm_spi_ops aspeed_spi_ops = {
+	.claim_bus = aspeed_spi_claim_bus,
+	.release_bus = aspeed_spi_release_bus,
+	.set_speed = aspeed_spi_set_speed,
+	.set_mode = aspeed_spi_set_mode,
+	.mem_ops = &aspeed_spi_mem_ops,
+};
+
+static const struct udevice_id aspeed_spi_ids[] = {
+	{ .compatible = "aspeed,ast2500-fmc", .data = (ulong)&ast2500_fmc_info, },
+	{ .compatible = "aspeed,ast2500-spi", .data = (ulong)&ast2500_spi_info, },
+	{ .compatible = "aspeed,ast2600-fmc", .data = (ulong)&ast2600_fmc_info, },
+	{ .compatible = "aspeed,ast2600-spi", .data = (ulong)&ast2600_spi_info, },
+	{ }
+};
+
+U_BOOT_DRIVER(aspeed_spi) = {
+	.name = "aspeed_spi_smc",
+	.id = UCLASS_SPI,
+	.of_match = aspeed_spi_ids,
+	.ops = &aspeed_spi_ops,
+	.of_to_plat = apseed_spi_of_to_plat,
+	.plat_auto = sizeof(struct aspeed_spi_plat),
+	.priv_auto = sizeof(struct aspeed_spi_priv),
+	.probe = aspeed_spi_probe,
+};

From 456f716f36578bcb86596b73750aa55d5851b4de Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:05 +0800
Subject: [PATCH 04/14] configs: aspeed: Enable SPI flash features

- Enable ASPEED SPI controller driver.
- Enable SPI flash memory configurations.
- Enable configurations for SPI flash manufacturers
  supported on both ASPEED AST2500 and AST2600 AVL.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 configs/evb-ast2500_defconfig | 13 +++++++++++++
 configs/evb-ast2600_defconfig | 13 +++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/configs/evb-ast2500_defconfig b/configs/evb-ast2500_defconfig
index 0bef043ea6..dfcf74ae01 100644
--- a/configs/evb-ast2500_defconfig
+++ b/configs/evb-ast2500_defconfig
@@ -39,6 +39,16 @@ CONFIG_SYS_I2C_ASPEED=y
 CONFIG_I2C_EEPROM=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ASPEED=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_SFDP_SUPPORT=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_SST=y
+CONFIG_SPI_FLASH_WINBOND=y
+# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
 CONFIG_PHY_REALTEK=y
 CONFIG_FTGMAC100=y
 CONFIG_PHY=y
@@ -47,6 +57,9 @@ CONFIG_RAM=y
 CONFIG_DM_RESET=y
 CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_SPI_ASPEED_SMC=y
 CONFIG_SYSRESET=y
 CONFIG_TIMER=y
 CONFIG_WDT=y
diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig
index 1284d51918..ebe58d7abd 100644
--- a/configs/evb-ast2600_defconfig
+++ b/configs/evb-ast2600_defconfig
@@ -87,6 +87,16 @@ CONFIG_SYS_I2C_ASPEED=y
 CONFIG_I2C_EEPROM=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ASPEED=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_SFDP_SUPPORT=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_SST=y
+CONFIG_SPI_FLASH_WINBOND=y
+# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
 CONFIG_PHY_REALTEK=y
 CONFIG_DM_MDIO=y
 CONFIG_FTGMAC100=y
@@ -98,6 +108,9 @@ CONFIG_SPL_RAM=y
 CONFIG_DM_RESET=y
 CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_SPI_ASPEED_SMC=y
 CONFIG_SYSRESET=y
 CONFIG_SPL_SYSRESET=y
 CONFIG_WDT=y

From 5150e908f54c3bd0fdd30eefdffaf49c826881ba Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:06 +0800
Subject: [PATCH 05/14] spi: aspeed: Support AST2400 platform

Although AST2400 is EOL officially, in order to achieve
sustainability and completeness, AST2400 part is added.

For AST2400,
- Five CSs are supported by FMC controller.
- SPI1 controller only supports single CS and there is
  no address segment address register. The CE control
  register of SPI1 is located at the offset 0x04 and
  the 4-byte address mode control bit is bit 13 of
  this register.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 105 ++++++++++++++++++++++++++++++++---
 1 file changed, 98 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 25b490bcf4..f858d36023 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -26,7 +26,7 @@
 #include <spi.h>
 #include <spi-mem.h>
 
-#define ASPEED_SPI_MAX_CS       3
+#define ASPEED_SPI_MAX_CS       5
 
 #define CTRL_IO_SINGLE_DATA     0
 #define CTRL_IO_QUAD_DATA       BIT(30)
@@ -42,10 +42,10 @@ struct aspeed_spi_regs {
 	u32 ctrl;                       /* 0x04 CE Control */
 	u32 intr_ctrl;                  /* 0x08 Interrupt Control and Status */
 	u32 cmd_ctrl;                   /* 0x0c Command Control */
-	u32 ce_ctrl[ASPEED_SPI_MAX_CS]; /* 0x10 .. 0x18 CEx Control */
-	u32 _reserved0[5];              /* .. */
-	u32 segment_addr[ASPEED_SPI_MAX_CS]; /* 0x30 .. 0x38 Segment Address */
-	u32 _reserved1[5];		/* .. */
+	u32 ce_ctrl[ASPEED_SPI_MAX_CS]; /* 0x10 .. 0x20 CEx Control */
+	u32 _reserved0[3];              /* .. */
+	u32 segment_addr[ASPEED_SPI_MAX_CS]; /* 0x30 .. 0x40 Segment Address */
+	u32 _reserved1[3];		/* .. */
 	u32 soft_rst_cmd_ctrl;          /* 0x50 Auto Soft-Reset Command Control */
 	u32 _reserved2[11];             /* .. */
 	u32 dma_ctrl;                   /* 0x80 DMA Control/Status */
@@ -86,6 +86,8 @@ struct aspeed_spi_info {
 	u32 (*segment_reg)(u32 start, u32 end);
 };
 
+static const struct aspeed_spi_info ast2400_spi_info;
+
 static u32 aspeed_spi_get_io_mode(u32 bus_width)
 {
 	switch (bus_width) {
@@ -101,6 +103,56 @@ static u32 aspeed_spi_get_io_mode(u32 bus_width)
 	}
 }
 
+static u32 ast2400_spi_segment_start(struct udevice *bus, u32 reg)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	u32 start_offset = ((reg >> 16) & 0xff) << 23;
+
+	if (start_offset == 0)
+		return (u32)plat->ahb_base;
+
+	return (u32)plat->ahb_base + start_offset;
+}
+
+static u32 ast2400_spi_segment_end(struct udevice *bus, u32 reg)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	u32 end_offset = ((reg >> 24) & 0xff) << 23;
+
+	/* Meaningless end_offset, set to physical ahb base. */
+	if (end_offset == 0)
+		return (u32)plat->ahb_base;
+
+	return (u32)plat->ahb_base + end_offset;
+}
+
+static u32 ast2400_spi_segment_reg(u32 start, u32 end)
+{
+	if (start == end)
+		return 0;
+
+	return ((((start) >> 23) & 0xff) << 16) | ((((end) >> 23) & 0xff) << 24);
+}
+
+static void ast2400_fmc_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 reg_val;
+
+	reg_val = readl(&priv->regs->ctrl);
+	reg_val |= 0x1 << cs;
+	writel(reg_val, &priv->regs->ctrl);
+}
+
+static void ast2400_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct aspeed_spi_flash *flash = &priv->flashes[cs];
+
+	flash->ce_ctrl_read |= BIT(13);
+	writel(flash->ce_ctrl_read, &priv->regs->ctrl);
+}
+
 static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
@@ -272,6 +324,9 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
 		op->addr.buswidth, op->dummy.nbytes, op->dummy.buswidth,
 		op->data.nbytes, op->data.buswidth);
 
+	if (priv->info == &ast2400_spi_info)
+		ce_ctrl_reg = (u32)&priv->regs->ctrl;
+
 	/*
 	 * Set controller to 4-byte address mode
 	 * if flash is in 4-byte address mode.
@@ -404,8 +459,12 @@ static int aspeed_spi_ctrl_init(struct udevice *bus)
 
 	/* Enable write capability for all CS. */
 	reg_val = readl(&priv->regs->conf);
-	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
-	       &priv->regs->conf);
+	if (priv->info == &ast2400_spi_info) {
+		writel(reg_val | BIT(0), &priv->regs->conf);
+	} else {
+		writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
+		       &priv->regs->conf);
+	}
 
 	memset(priv->flashes, 0x0,
 	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
@@ -416,6 +475,16 @@ static int aspeed_spi_ctrl_init(struct udevice *bus)
 				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
 	}
 
+	/*
+	 * SPI1 on AST2400 only supports CS0.
+	 * It is unnecessary to configure segment address register.
+	 */
+	if (priv->info == &ast2400_spi_info) {
+		priv->flashes[cs].ahb_base = plat->ahb_base;
+		priv->flashes[cs].ahb_decoded_sz = 0x10000000;
+		return 0;
+	}
+
 	/* Assign basic AHB decoded size for each CS. */
 	for (cs = 0; cs < plat->max_cs; cs++) {
 		reg_val = readl(&priv->regs->segment_addr[cs]);
@@ -433,6 +502,26 @@ static int aspeed_spi_ctrl_init(struct udevice *bus)
 	return ret;
 }
 
+static const struct aspeed_spi_info ast2400_fmc_info = {
+	.io_mode_mask = 0x70000000,
+	.max_bus_width = 2,
+	.min_decoded_sz = 0x800000,
+	.set_4byte = ast2400_fmc_chip_set_4byte,
+	.segment_start = ast2400_spi_segment_start,
+	.segment_end = ast2400_spi_segment_end,
+	.segment_reg = ast2400_spi_segment_reg,
+};
+
+static const struct aspeed_spi_info ast2400_spi_info = {
+	.io_mode_mask = 0x70000000,
+	.max_bus_width = 2,
+	.min_decoded_sz = 0x800000,
+	.set_4byte = ast2400_spi_chip_set_4byte,
+	.segment_start = ast2400_spi_segment_start,
+	.segment_end = ast2400_spi_segment_end,
+	.segment_reg = ast2400_spi_segment_reg,
+};
+
 static const struct aspeed_spi_info ast2500_fmc_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
@@ -584,6 +673,8 @@ static const struct dm_spi_ops aspeed_spi_ops = {
 };
 
 static const struct udevice_id aspeed_spi_ids[] = {
+	{ .compatible = "aspeed,ast2400-fmc", .data = (ulong)&ast2400_fmc_info, },
+	{ .compatible = "aspeed,ast2400-spi", .data = (ulong)&ast2400_spi_info, },
 	{ .compatible = "aspeed,ast2500-fmc", .data = (ulong)&ast2500_fmc_info, },
 	{ .compatible = "aspeed,ast2500-spi", .data = (ulong)&ast2500_spi_info, },
 	{ .compatible = "aspeed,ast2600-fmc", .data = (ulong)&ast2600_fmc_info, },

From d37b4f37ea40243ad3a126a152920339189afb52 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:07 +0800
Subject: [PATCH 06/14] arm: dts: aspeed: Update SPI flash node settings

For both AST2500 and AST2600, there are three
SPI controllers, FMC(Firmware Memory Controller),
SPI1 and SPI2. The clock source is HCLK. Following
is the basic information for ASPEED SPI controller.

AST2500:
  - FMC:
      CS number: 3
      controller reg: 0x1e620000 - 0x1e62ffff
      decoded address: 0x20000000 - 0x2fffffff

  - SPI1:
      CS number: 2
      controller reg: 0x1e630000 - 0x1e630fff
      decoded address: 0x30000000 - 0x37ffffff

  - SPI2:
      CS number: 2
      controller reg: 0x1e631000 - 0x1e631fff
      decoded address: 0x38000000 - 0x3fffffff

AST2600:
  - FMC:
      CS number: 3
      controller reg: 0x1e620000 - 0x1e62ffff
      decoded address: 0x20000000 - 0x2fffffff

  - SPI1:
      CS number: 2
      controller reg: 0x1e630000 - 0x1e630fff
      decoded address: 0x30000000 - 0x3fffffff

  - SPI2:
      CS number: 3
      controller reg: 0x1e631000 - 0x1e631fff
      decoded address: 0x50000000 - 0x5fffffff

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 arch/arm/dts/ast2500-evb.dts | 33 +++++++++++++++++++++++++++++++++
 arch/arm/dts/ast2500.dtsi    | 23 ++++++++++++++++-------
 arch/arm/dts/ast2600-evb.dts |  8 --------
 arch/arm/dts/ast2600.dtsi    | 34 +++++++++++++++++++---------------
 4 files changed, 68 insertions(+), 30 deletions(-)

diff --git a/arch/arm/dts/ast2500-evb.dts b/arch/arm/dts/ast2500-evb.dts
index cc577761fa..1fbacf985f 100644
--- a/arch/arm/dts/ast2500-evb.dts
+++ b/arch/arm/dts/ast2500-evb.dts
@@ -78,6 +78,39 @@
 	pinctrl-0 = <&pinctrl_sd2_default>;
 };
 
+&fmc {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_fwspics1_default>;
+
+	flash@0 {
+		status = "okay";
+		spi-max-frequency = <50000000>;
+		spi-tx-bus-width = <2>;
+		spi-rx-bus-width = <2>;
+	};
+
+	flash@1 {
+		status = "okay";
+		spi-max-frequency = <50000000>;
+		spi-tx-bus-width = <2>;
+		spi-rx-bus-width = <2>;
+	};
+};
+
+&spi1 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_spi1cs1_default>;
+
+	flash@0 {
+		status = "okay";
+		spi-max-frequency = <50000000>;
+		spi-tx-bus-width = <2>;
+		spi-rx-bus-width = <2>;
+	};
+};
+
 &i2c3 {
         status = "okay";
 
diff --git a/arch/arm/dts/ast2500.dtsi b/arch/arm/dts/ast2500.dtsi
index cea08e6f08..320d2e5340 100644
--- a/arch/arm/dts/ast2500.dtsi
+++ b/arch/arm/dts/ast2500.dtsi
@@ -57,23 +57,26 @@
 		ranges;
 
 		fmc: flash-controller@1e620000 {
-			reg = < 0x1e620000 0xc4
-				0x20000000 0x10000000 >;
+			reg = <0x1e620000 0xc4>, <0x20000000 0x10000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			compatible = "aspeed,ast2500-fmc";
+			clocks = <&scu ASPEED_CLK_AHB>;
+			num-cs = <3>;
 			status = "disabled";
-			interrupts = <19>;
+
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@1 {
 				reg = < 1 >;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@2 {
 				reg = < 2 >;
 				compatible = "jedec,spi-nor";
@@ -82,17 +85,20 @@
 		};
 
 		spi1: flash-controller@1e630000 {
-			reg = < 0x1e630000 0xc4
-				0x30000000 0x08000000 >;
+			reg = <0x1e630000 0xc4>, <0x30000000 0x08000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			compatible = "aspeed,ast2500-spi";
+			clocks = <&scu ASPEED_CLK_AHB>;
+			num-cs = <2>;
 			status = "disabled";
+
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@1 {
 				reg = < 1 >;
 				compatible = "jedec,spi-nor";
@@ -101,17 +107,20 @@
 		};
 
 		spi2: flash-controller@1e631000 {
-			reg = < 0x1e631000 0xc4
-				0x38000000 0x08000000 >;
+			reg = <0x1e631000 0xc4>, <0x38000000 0x08000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			compatible = "aspeed,ast2500-spi";
+			clocks = <&scu ASPEED_CLK_AHB>;
+			num-cs = <2>;
 			status = "disabled";
+
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@1 {
 				reg = < 1 >;
 				compatible = "jedec,spi-nor";
diff --git a/arch/arm/dts/ast2600-evb.dts b/arch/arm/dts/ast2600-evb.dts
index a9bba96816..a097f320e4 100644
--- a/arch/arm/dts/ast2600-evb.dts
+++ b/arch/arm/dts/ast2600-evb.dts
@@ -72,12 +72,10 @@
 
 &fmc {
 	status = "okay";
-
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_fmcquad_default>;
 
 	flash@0 {
-		compatible = "spi-flash", "sst,w25q256";
 		status = "okay";
 		spi-max-frequency = <50000000>;
 		spi-tx-bus-width = <4>;
@@ -85,7 +83,6 @@
 	};
 
 	flash@1 {
-		compatible = "spi-flash", "sst,w25q256";
 		status = "okay";
 		spi-max-frequency = <50000000>;
 		spi-tx-bus-width = <4>;
@@ -93,7 +90,6 @@
 	};
 
 	flash@2 {
-		compatible = "spi-flash", "sst,w25q256";
 		status = "okay";
 		spi-max-frequency = <50000000>;
 		spi-tx-bus-width = <4>;
@@ -103,14 +99,12 @@
 
 &spi1 {
 	status = "okay";
-
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_spi1_default &pinctrl_spi1abr_default
 			&pinctrl_spi1cs1_default &pinctrl_spi1wp_default
 			&pinctrl_spi1wp_default &pinctrl_spi1quad_default>;
 
 	flash@0 {
-		compatible = "spi-flash", "sst,w25q256";
 		status = "okay";
 		spi-max-frequency = <50000000>;
 		spi-tx-bus-width = <4>;
@@ -120,13 +114,11 @@
 
 &spi2 {
 	status = "okay";
-
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_spi2_default &pinctrl_spi2cs1_default
 			&pinctrl_spi2cs2_default &pinctrl_spi2quad_default>;
 
 	flash@0 {
-		compatible = "spi-flash", "sst,w25q256";
 		status = "okay";
 		spi-max-frequency = <50000000>;
 		spi-tx-bus-width = <4>;
diff --git a/arch/arm/dts/ast2600.dtsi b/arch/arm/dts/ast2600.dtsi
index ac8cd4d67d..8d91eedc17 100644
--- a/arch/arm/dts/ast2600.dtsi
+++ b/arch/arm/dts/ast2600.dtsi
@@ -129,74 +129,78 @@
 		};
 
 		fmc: flash-controller@1e620000 {
-			reg = < 0x1e620000 0xc4
-				0x20000000 0x10000000 >;
+			reg = <0x1e620000 0xc4>, <0x20000000 0x10000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			compatible = "aspeed,ast2600-fmc";
 			status = "disabled";
-			interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&scu ASPEED_CLK_AHB>;
 			num-cs = <3>;
+
 			flash@0 {
-				reg = < 0 >;
+				reg = <0>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@1 {
-				reg = < 1 >;
+				reg = <1>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@2 {
-				reg = < 2 >;
+				reg = <2>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
 		};
 
 		spi1: flash-controller@1e630000 {
-			reg = < 0x1e630000 0xc4
-				0x30000000 0x08000000 >;
+			reg = <0x1e630000 0xc4>, <0x30000000 0x10000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			compatible = "aspeed,ast2600-spi";
 			clocks = <&scu ASPEED_CLK_AHB>;
 			num-cs = <2>;
 			status = "disabled";
+
 			flash@0 {
-				reg = < 0 >;
+				reg = <0>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@1 {
-				reg = < 1 >;
+				reg = <1>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
 		};
 
 		spi2: flash-controller@1e631000 {
-			reg = < 0x1e631000 0xc4
-				0x50000000 0x08000000 >;
+			reg = <0x1e631000 0xc4>, <0x50000000 0x10000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			compatible = "aspeed,ast2600-spi";
 			clocks = <&scu ASPEED_CLK_AHB>;
 			num-cs = <3>;
 			status = "disabled";
+
 			flash@0 {
-				reg = < 0 >;
+				reg = <0>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@1 {
-				reg = < 1 >;
+				reg = <1>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};
+
 			flash@2 {
-				reg = < 2 >;
+				reg = <2>;
 				compatible = "jedec,spi-nor";
 				status = "disabled";
 			};

From f7e1de4c6a43beec438bce04571469145086efed Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:08 +0800
Subject: [PATCH 07/14] spi-mem: Add dirmap API from Linux

This adds the dirmap API originally introduced in
Linux commit aa167f3fed0c
("spi: spi-mem: Add a new API to support direct mapping").
This also includes several follow-up patches and fixes.

Changes from Linux include:
* Added Kconfig option
* Changed struct device to struct udevice
* Changed struct spi_mem to struct spi_slave

This patch is obtained from the following patch
https://patchwork.ozlabs.org/project/uboot/patch/20210205043924.149504-3-seanga2@gmail.com/
The corresponding Linux kernel SHA1 is aa167f3fed0c.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Signed-off-by: Sean Anderson <seanga2@gmail.com>
Acked-by: Pratyush Yadav <p.yadav@ti.com>
---
 drivers/spi/Kconfig   |  10 ++
 drivers/spi/spi-mem.c | 268 ++++++++++++++++++++++++++++++++++++++++++
 include/spi-mem.h     |  79 +++++++++++++
 3 files changed, 357 insertions(+)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8ca9274ef4..ac91d82258 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -40,6 +40,16 @@ config SPI_MEM
 	  This extension is meant to simplify interaction with SPI memories
 	  by providing an high-level interface to send memory-like commands.
 
+config SPI_DIRMAP
+	bool "SPI direct mapping"
+	depends on SPI_MEM
+	help
+	  Enable the SPI direct mapping API. Most modern SPI controllers can
+	  directly map a SPI memory (or a portion of the SPI memory) in the CPU
+	  address space. Most of the time this brings significant performance
+	  improvements as it automates the whole process of sending SPI memory
+	  operations every time a new region is accessed.
+
 if DM_SPI
 
 config ALTERA_SPI
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 9c1ede1b61..8e8995fc53 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -21,6 +21,8 @@
 #include <spi.h>
 #include <spi-mem.h>
 #include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
 #endif
 
 #ifndef __UBOOT__
@@ -491,6 +493,272 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
 }
 EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
 
+static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
+				      u64 offs, size_t len, void *buf)
+{
+	struct spi_mem_op op = desc->info.op_tmpl;
+	int ret;
+
+	op.addr.val = desc->info.offset + offs;
+	op.data.buf.in = buf;
+	op.data.nbytes = len;
+	ret = spi_mem_adjust_op_size(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	return op.data.nbytes;
+}
+
+static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
+				       u64 offs, size_t len, const void *buf)
+{
+	struct spi_mem_op op = desc->info.op_tmpl;
+	int ret;
+
+	op.addr.val = desc->info.offset + offs;
+	op.data.buf.out = buf;
+	op.data.nbytes = len;
+	ret = spi_mem_adjust_op_size(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	return op.data.nbytes;
+}
+
+/**
+ * spi_mem_dirmap_create() - Create a direct mapping descriptor
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * This function is creating a direct mapping descriptor which can then be used
+ * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
+ * If the SPI controller driver does not support direct mapping, this function
+ * falls back to an implementation using spi_mem_exec_op(), so that the caller
+ * doesn't have to bother implementing a fallback on his own.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_slave *slave,
+		      const struct spi_mem_dirmap_info *info)
+{
+	struct udevice *bus = slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+	struct spi_mem_dirmap_desc *desc;
+	int ret = -EOPNOTSUPP;
+
+	/* Make sure the number of address cycles is between 1 and 8 bytes. */
+	if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
+		return ERR_PTR(-EINVAL);
+
+	/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
+	if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
+		return ERR_PTR(-EINVAL);
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->slave = slave;
+	desc->info = *info;
+	if (ops->mem_ops && ops->mem_ops->dirmap_create)
+		ret = ops->mem_ops->dirmap_create(desc);
+
+	if (ret) {
+		desc->nodirmap = true;
+		if (!spi_mem_supports_op(desc->slave, &desc->info.op_tmpl))
+			ret = -EOPNOTSUPP;
+		else
+			ret = 0;
+	}
+
+	if (ret) {
+		kfree(desc);
+		return ERR_PTR(ret);
+	}
+
+	return desc;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
+
+/**
+ * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
+ * @desc: the direct mapping descriptor to destroy
+ *
+ * This function destroys a direct mapping descriptor previously created by
+ * spi_mem_dirmap_create().
+ */
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
+{
+	struct udevice *bus = desc->slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+
+	if (!desc->nodirmap && ops->mem_ops && ops->mem_ops->dirmap_destroy)
+		ops->mem_ops->dirmap_destroy(desc);
+
+	kfree(desc);
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
+
+#ifndef __UBOOT__
+static void devm_spi_mem_dirmap_release(struct udevice *dev, void *res)
+{
+	struct spi_mem_dirmap_desc *desc = *(struct spi_mem_dirmap_desc **)res;
+
+	spi_mem_dirmap_destroy(desc);
+}
+
+/**
+ * devm_spi_mem_dirmap_create() - Create a direct mapping descriptor and attach
+ *				  it to a device
+ * @dev: device the dirmap desc will be attached to
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * devm_ variant of the spi_mem_dirmap_create() function. See
+ * spi_mem_dirmap_create() for more details.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+devm_spi_mem_dirmap_create(struct udevice *dev, struct spi_slave *slave,
+			   const struct spi_mem_dirmap_info *info)
+{
+	struct spi_mem_dirmap_desc **ptr, *desc;
+
+	ptr = devres_alloc(devm_spi_mem_dirmap_release, sizeof(*ptr),
+			   GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	desc = spi_mem_dirmap_create(slave, info);
+	if (IS_ERR(desc)) {
+		devres_free(ptr);
+	} else {
+		*ptr = desc;
+		devres_add(dev, ptr);
+	}
+
+	return desc;
+}
+EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_create);
+
+static int devm_spi_mem_dirmap_match(struct udevice *dev, void *res, void *data)
+{
+	struct spi_mem_dirmap_desc **ptr = res;
+
+	if (WARN_ON(!ptr || !*ptr))
+		return 0;
+
+	return *ptr == data;
+}
+
+/**
+ * devm_spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor attached
+ *				   to a device
+ * @dev: device the dirmap desc is attached to
+ * @desc: the direct mapping descriptor to destroy
+ *
+ * devm_ variant of the spi_mem_dirmap_destroy() function. See
+ * spi_mem_dirmap_destroy() for more details.
+ */
+void devm_spi_mem_dirmap_destroy(struct udevice *dev,
+				 struct spi_mem_dirmap_desc *desc)
+{
+	devres_release(dev, devm_spi_mem_dirmap_release,
+		       devm_spi_mem_dirmap_match, desc);
+}
+EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_destroy);
+#endif /* __UBOOT__ */
+
+/**
+ * spi_mem_dirmap_read() - Read data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start reading from. Note that this is not an absolute
+ *	  offset, but the offset within the direct mapping which already has
+ *	  its own offset
+ * @len: length in bytes
+ * @buf: destination buffer. This buffer must be DMA-able
+ *
+ * This function reads data from a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data read from the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_read() again when that happens.
+ */
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+			    u64 offs, size_t len, void *buf)
+{
+	struct udevice *bus = desc->slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+	ssize_t ret;
+
+	if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+		return -EINVAL;
+
+	if (!len)
+		return 0;
+
+	if (desc->nodirmap)
+		ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
+	else if (ops->mem_ops && ops->mem_ops->dirmap_read)
+		ret = ops->mem_ops->dirmap_read(desc, offs, len, buf);
+	else
+		ret = -EOPNOTSUPP;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
+
+/**
+ * spi_mem_dirmap_write() - Write data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start writing from. Note that this is not an absolute
+ *	  offset, but the offset within the direct mapping which already has
+ *	  its own offset
+ * @len: length in bytes
+ * @buf: source buffer. This buffer must be DMA-able
+ *
+ * This function writes data to a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data written to the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_write() again when that happens.
+ */
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+			     u64 offs, size_t len, const void *buf)
+{
+	struct udevice *bus = desc->slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+	ssize_t ret;
+
+	if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
+		return -EINVAL;
+
+	if (!len)
+		return 0;
+
+	if (desc->nodirmap)
+		ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
+	else if (ops->mem_ops && ops->mem_ops->dirmap_write)
+		ret = ops->mem_ops->dirmap_write(desc, offs, len, buf);
+	else
+		ret = -EOPNOTSUPP;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
+
 #ifndef __UBOOT__
 static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
 {
diff --git a/include/spi-mem.h b/include/spi-mem.h
index 32ffdc2e0f..b07cf2ed83 100644
--- a/include/spi-mem.h
+++ b/include/spi-mem.h
@@ -134,6 +134,48 @@ struct spi_mem_op {
 		.dummy = __dummy,				\
 		.data = __data,					\
 	}
+/**
+ * struct spi_mem_dirmap_info - Direct mapping information
+ * @op_tmpl: operation template that should be used by the direct mapping when
+ *	     the memory device is accessed
+ * @offset: absolute offset this direct mapping is pointing to
+ * @length: length in byte of this direct mapping
+ *
+ * This information is used by the controller specific implementation to know
+ * the portion of memory that is directly mapped and the spi_mem_op that should
+ * be used to access the device.
+ * A direct mapping is only valid for one direction (read or write) and this
+ * direction is directly encoded in the ->op_tmpl.data.dir field.
+ */
+struct spi_mem_dirmap_info {
+	struct spi_mem_op op_tmpl;
+	u64 offset;
+	u64 length;
+};
+
+/**
+ * struct spi_mem_dirmap_desc - Direct mapping descriptor
+ * @mem: the SPI memory device this direct mapping is attached to
+ * @info: information passed at direct mapping creation time
+ * @nodirmap: set to 1 if the SPI controller does not implement
+ *            ->mem_ops->dirmap_create() or when this function returned an
+ *            error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
+ *            calls will use spi_mem_exec_op() to access the memory. This is a
+ *            degraded mode that allows spi_mem drivers to use the same code
+ *            no matter whether the controller supports direct mapping or not
+ * @priv: field pointing to controller specific data
+ *
+ * Common part of a direct mapping descriptor. This object is created by
+ * spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
+ * can create/attach direct mapping resources to the descriptor in the ->priv
+ * field.
+ */
+struct spi_mem_dirmap_desc {
+	struct spi_slave *slave;
+	struct spi_mem_dirmap_info info;
+	unsigned int nodirmap;
+	void *priv;
+};
 
 #ifndef __UBOOT__
 /**
@@ -183,10 +225,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
  *		    limitations)
  * @supports_op: check if an operation is supported by the controller
  * @exec_op: execute a SPI memory operation
+ * @dirmap_create: create a direct mapping descriptor that can later be used to
+ *		   access the memory device. This method is optional
+ * @dirmap_destroy: destroy a memory descriptor previous created by
+ *		    ->dirmap_create()
+ * @dirmap_read: read data from the memory device using the direct mapping
+ *		 created by ->dirmap_create(). The function can return less
+ *		 data than requested (for example when the request is crossing
+ *		 the currently mapped area), and the caller of
+ *		 spi_mem_dirmap_read() is responsible for calling it again in
+ *		 this case.
+ * @dirmap_write: write data to the memory device using the direct mapping
+ *		  created by ->dirmap_create(). The function can return less
+ *		  data than requested (for example when the request is crossing
+ *		  the currently mapped area), and the caller of
+ *		  spi_mem_dirmap_write() is responsible for calling it again in
+ *		  this case.
  *
  * This interface should be implemented by SPI controllers providing an
  * high-level interface to execute SPI memory operation, which is usually the
  * case for QSPI controllers.
+ *
+ * Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
+ * mapping from the CPU because doing that can stall the CPU waiting for the
+ * SPI mem transaction to finish, and this will make real-time maintainers
+ * unhappy and might make your system less reactive. Instead, drivers should
+ * use DMA to access this direct mapping.
  */
 struct spi_controller_mem_ops {
 	int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op);
@@ -194,6 +258,12 @@ struct spi_controller_mem_ops {
 			    const struct spi_mem_op *op);
 	int (*exec_op)(struct spi_slave *slave,
 		       const struct spi_mem_op *op);
+	int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
+	void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
+	ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
+			       u64 offs, size_t len, void *buf);
+	ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
+				u64 offs, size_t len, const void *buf);
 };
 
 #ifndef __UBOOT__
@@ -260,6 +330,15 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op);
 bool spi_mem_default_supports_op(struct spi_slave *mem,
 				 const struct spi_mem_op *op);
 
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_slave *mem,
+		      const struct spi_mem_dirmap_info *info);
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+			    u64 offs, size_t len, void *buf);
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+			     u64 offs, size_t len, const void *buf);
+
 #ifndef __UBOOT__
 int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
 				       struct module *owner);

From 463cdf66632a0d67bf824cb43b6c1b41782d0765 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:09 +0800
Subject: [PATCH 08/14] mtd: spi-nor: Use spi-mem dirmap API

This adds support for the dirmap API to the spi-nor subsystem, as
introduced in Linux commit df5c21002cf4  ("mtd: spi-nor: use
spi-mem dirmap API").

This patch is synchronize from the following patch
https://patchwork.ozlabs.org/project/uboot/patch/20210205043924.149504-4-seanga2@gmail.com/
The corresponding Linux kernel SHA1 is df5c21002cf4.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Signed-off-by: Sean Anderson <seanga2@gmail.com>
Acked-by: Pratyush Yadav <p.yadav@ti.com>
---
 drivers/mtd/spi/sf_probe.c     | 76 ++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/spi-nor-core.c | 55 +++++++++++++++++-------
 include/linux/mtd/spi-nor.h    | 18 ++++++++
 3 files changed, 133 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index f461082e03..e192f97efd 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -10,13 +10,69 @@
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
+#include <linux/mtd/spi-nor.h>
 #include <log.h>
 #include <malloc.h>
 #include <spi.h>
 #include <spi_flash.h>
+#include <spi-mem.h>
 
 #include "sf_internal.h"
 
+static int spi_nor_create_read_dirmap(struct spi_nor *nor)
+{
+	struct spi_mem_dirmap_info info = {
+		.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
+				      SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
+				      SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
+				      SPI_MEM_OP_DATA_IN(0, NULL, 0)),
+		.offset = 0,
+		.length = nor->mtd.size,
+	};
+	struct spi_mem_op *op = &info.op_tmpl;
+
+	/* get transfer protocols. */
+	spi_nor_setup_op(nor, op, nor->read_proto);
+	op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+	/* convert the dummy cycles to the number of bytes */
+	op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
+	if (spi_nor_protocol_is_dtr(nor->read_proto))
+		op->dummy.nbytes *= 2;
+
+	nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info);
+	if (IS_ERR(nor->dirmap.rdesc))
+		return PTR_ERR(nor->dirmap.rdesc);
+
+	return 0;
+}
+
+static int spi_nor_create_write_dirmap(struct spi_nor *nor)
+{
+	struct spi_mem_dirmap_info info = {
+		.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
+				      SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
+				      SPI_MEM_OP_NO_DUMMY,
+				      SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
+		.offset = 0,
+		.length = nor->mtd.size,
+	};
+	struct spi_mem_op *op = &info.op_tmpl;
+
+	/* get transfer protocols. */
+	spi_nor_setup_op(nor, op, nor->write_proto);
+	op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
+	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+		op->addr.nbytes = 0;
+
+	nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info);
+	if (IS_ERR(nor->dirmap.wdesc))
+		return PTR_ERR(nor->dirmap.wdesc);
+
+	return 0;
+}
+
 /**
  * spi_flash_probe_slave() - Probe for a SPI flash device on a bus
  *
@@ -45,6 +101,16 @@ static int spi_flash_probe_slave(struct spi_flash *flash)
 	if (ret)
 		goto err_read_id;
 
+	if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
+		ret = spi_nor_create_read_dirmap(flash);
+		if (ret)
+			return ret;
+
+		ret = spi_nor_create_write_dirmap(flash);
+		if (ret)
+			return ret;
+	}
+
 	if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
 		ret = spi_flash_mtd_register(flash);
 
@@ -83,6 +149,11 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
 
 void spi_flash_free(struct spi_flash *flash)
 {
+	if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
+		spi_mem_dirmap_destroy(flash->dirmap.wdesc);
+		spi_mem_dirmap_destroy(flash->dirmap.rdesc);
+	}
+
 	if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
 		spi_flash_mtd_unregister(flash);
 
@@ -153,6 +224,11 @@ static int spi_flash_std_remove(struct udevice *dev)
 	struct spi_flash *flash = dev_get_uclass_priv(dev);
 	int ret;
 
+	if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
+		spi_mem_dirmap_destroy(flash->dirmap.wdesc);
+		spi_mem_dirmap_destroy(flash->dirmap.rdesc);
+	}
+
 	ret = spi_nor_remove(flash);
 	if (ret)
 		return ret;
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index e3c86e080a..f236e87510 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -246,9 +246,9 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor,
  *			need to be initialized.
  * @proto:		the protocol from which the properties need to be set.
  */
-static void spi_nor_setup_op(const struct spi_nor *nor,
-			     struct spi_mem_op *op,
-			     const enum spi_nor_protocol proto)
+void spi_nor_setup_op(const struct spi_nor *nor,
+		      struct spi_mem_op *op,
+		      const enum spi_nor_protocol proto)
 {
 	u8 ext;
 
@@ -369,13 +369,29 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
 
 	while (remaining) {
 		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
-		ret = spi_mem_adjust_op_size(nor->spi, &op);
-		if (ret)
-			return ret;
 
-		ret = spi_mem_exec_op(nor->spi, &op);
-		if (ret)
-			return ret;
+		if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) {
+			/*
+			 * Record current operation information which may be used
+			 * when the address or data length exceeds address mapping.
+			 */
+			memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op,
+			       sizeof(struct spi_mem_op));
+			ret = spi_mem_dirmap_read(nor->dirmap.rdesc,
+						  op.addr.val, op.data.nbytes,
+						  op.data.buf.in);
+			if (ret < 0)
+				return ret;
+			op.data.nbytes = ret;
+		} else {
+			ret = spi_mem_adjust_op_size(nor->spi, &op);
+			if (ret)
+				return ret;
+
+			ret = spi_mem_exec_op(nor->spi, &op);
+			if (ret)
+				return ret;
+		}
 
 		op.addr.val += op.data.nbytes;
 		remaining -= op.data.nbytes;
@@ -400,14 +416,21 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
 
 	spi_nor_setup_op(nor, &op, nor->write_proto);
 
-	ret = spi_mem_adjust_op_size(nor->spi, &op);
-	if (ret)
-		return ret;
-	op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
+	if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) {
+		memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op,
+		       sizeof(struct spi_mem_op));
+		op.data.nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val,
+						      op.data.nbytes, op.data.buf.out);
+	} else {
+		ret = spi_mem_adjust_op_size(nor->spi, &op);
+		if (ret)
+			return ret;
+		op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
 
-	ret = spi_mem_exec_op(nor->spi, &op);
-	if (ret)
-		return ret;
+		ret = spi_mem_exec_op(nor->spi, &op);
+		if (ret)
+			return ret;
+	}
 
 	return op.data.nbytes;
 }
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 2595bad9df..638d807ee5 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -11,6 +11,7 @@
 #include <linux/bitops.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/mtd.h>
+#include <spi-mem.h>
 
 /*
  * Manufacturer IDs
@@ -522,6 +523,7 @@ struct spi_flash {
  * @quad_enable:	[FLASH-SPECIFIC] enables SPI NOR quad mode
  * @octal_dtr_enable:	[FLASH-SPECIFIC] enables SPI NOR octal DTR mode.
  * @ready:		[FLASH-SPECIFIC] check if the flash is ready
+ * @dirmap:		pointers to struct spi_mem_dirmap_desc for reads/writes.
  * @priv:		the private data
  */
 struct spi_nor {
@@ -572,6 +574,11 @@ struct spi_nor {
 	int (*octal_dtr_enable)(struct spi_nor *nor);
 	int (*ready)(struct spi_nor *nor);
 
+	struct {
+		struct spi_mem_dirmap_desc *rdesc;
+		struct spi_mem_dirmap_desc *wdesc;
+	} dirmap;
+
 	void *priv;
 	char mtd_name[MTD_NAME_SIZE(MTD_DEV_TYPE_NOR)];
 /* Compatibility for spi_flash, remove once sf layer is merged with mtd */
@@ -595,6 +602,17 @@ device_node *spi_nor_get_flash_node(struct spi_nor *nor)
 }
 #endif /* __UBOOT__ */
 
+/**
+ * spi_nor_setup_op() - Set up common properties of a spi-mem op.
+ * @nor:		pointer to a 'struct spi_nor'
+ * @op:			pointer to the 'struct spi_mem_op' whose properties
+ *			need to be initialized.
+ * @proto:		the protocol from which the properties need to be set.
+ */
+void spi_nor_setup_op(const struct spi_nor *nor,
+		      struct spi_mem_op *op,
+		      const enum spi_nor_protocol proto);
+
 /**
  * spi_nor_scan() - scan the SPI NOR
  * @nor:	the spi_nor structure

From 992d02ea737895dc67246bbf7065084bb2721d6b Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:10 +0800
Subject: [PATCH 09/14] spi: aspeed: SPI dirmap read support

From the HW point of view, the performance of
command read mode is greater than user mode slightly.
Thus, dirmap read framework is introduced to achieve
this goal. In dirmap_create, command read mode is
configured. Usually, the decoded address area with flash
size is assigned to each CS. CPU can thus access the
SPI flash as normal memory in dirmap_read function.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 88 ++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index f858d36023..6099b85255 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -87,6 +87,7 @@ struct aspeed_spi_info {
 };
 
 static const struct aspeed_spi_info ast2400_spi_info;
+static int aspeed_spi_decoded_range_config(struct udevice *bus);
 
 static u32 aspeed_spi_get_io_mode(u32 bus_width)
 {
@@ -381,6 +382,91 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
 	return 0;
 }
 
+static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+	int ret = 0;
+	struct udevice *dev = desc->slave->dev;
+	struct udevice *bus = dev->parent;
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	const struct aspeed_spi_info *info = priv->info;
+	struct spi_mem_op op_tmpl = desc->info.op_tmpl;
+	u32 i;
+	u32 cs = slave_plat->cs;
+	u32 reg_val;
+	u32 ce_ctrl_reg;
+
+	if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
+		/*
+		 * dirmap_write is not supported currently due to a HW
+		 * limitation for command write mode: The written data
+		 * length should be multiple of 4-byte.
+		 */
+		return -EOPNOTSUPP;
+	}
+
+	ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+	if (info == &ast2400_spi_info)
+		ce_ctrl_reg = (u32)&priv->regs->ctrl;
+
+	if (desc->info.length > 0x1000000)
+		priv->info->set_4byte(bus, cs);
+
+	/* AST2400 SPI1 doesn't have decoded address segment register. */
+	if (info != &ast2400_spi_info) {
+		priv->flashes[cs].ahb_decoded_sz = desc->info.length;
+
+		for (i = 0; i < priv->num_cs; i++) {
+			dev_dbg(dev, "cs: %d, sz: 0x%x\n", i,
+				priv->flashes[cs].ahb_decoded_sz);
+		}
+
+		ret = aspeed_spi_decoded_range_config(bus);
+		if (ret)
+			return ret;
+	}
+
+	reg_val = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
+		  op_tmpl.cmd.opcode << 16 |
+		  ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
+		  ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
+		  CTRL_IO_MODE_CMD_READ;
+
+	writel(reg_val, ce_ctrl_reg);
+
+	priv->flashes[cs].ce_ctrl_read = reg_val;
+
+	dev_dbg(dev, "read bus width: %d ce_ctrl_val: 0x%08x\n",
+		op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
+
+	return ret;
+}
+
+static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
+				      u64 offs, size_t len, void *buf)
+{
+	struct udevice *dev = desc->slave->dev;
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 cs = slave_plat->cs;
+	int ret;
+
+	dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%x\n",
+		desc->info.op_tmpl.cmd.opcode, offs, len);
+
+	if (priv->flashes[cs].ahb_decoded_sz < offs + len ||
+	    (offs % 4) != 0) {
+		ret = aspeed_spi_exec_op_user_mode(desc->slave,
+						   &desc->info.op_tmpl);
+		if (ret != 0)
+			return 0;
+	} else {
+		memcpy_fromio(buf, priv->flashes[cs].ahb_base + offs, len);
+	}
+
+	return len;
+}
+
 static struct aspeed_spi_flash *aspeed_spi_get_flash(struct udevice *dev)
 {
 	struct udevice *bus = dev->parent;
@@ -662,6 +748,8 @@ static int aspeed_spi_probe(struct udevice *bus)
 static const struct spi_controller_mem_ops aspeed_spi_mem_ops = {
 	.supports_op = aspeed_spi_supports_op,
 	.exec_op = aspeed_spi_exec_op_user_mode,
+	.dirmap_create = aspeed_spi_dirmap_create,
+	.dirmap_read = aspeed_spi_dirmap_read,
 };
 
 static const struct dm_spi_ops aspeed_spi_ops = {

From 99325ee0568aa12f79b044ffcfc80c8ff348841d Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:11 +0800
Subject: [PATCH 10/14] configs: aspeed: Enable CONFIG_SPI_DIRMAP

Enable CONFIG_SPI_DIRMAP on ASPEED platforms.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 configs/evb-ast2500_defconfig | 1 +
 configs/evb-ast2600_defconfig | 1 +
 2 files changed, 2 insertions(+)

diff --git a/configs/evb-ast2500_defconfig b/configs/evb-ast2500_defconfig
index dfcf74ae01..3061cbec41 100644
--- a/configs/evb-ast2500_defconfig
+++ b/configs/evb-ast2500_defconfig
@@ -59,6 +59,7 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
+CONFIG_SPI_DIRMAP=y
 CONFIG_SPI_ASPEED_SMC=y
 CONFIG_SYSRESET=y
 CONFIG_TIMER=y
diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig
index ebe58d7abd..1981f895d6 100644
--- a/configs/evb-ast2600_defconfig
+++ b/configs/evb-ast2600_defconfig
@@ -110,6 +110,7 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
+CONFIG_SPI_DIRMAP=y
 CONFIG_SPI_ASPEED_SMC=y
 CONFIG_SYSRESET=y
 CONFIG_SPL_SYSRESET=y

From 15a5c806a31522ab940f4ed252c917d7a77f8200 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:12 +0800
Subject: [PATCH 11/14] spi: aspeed: Adjust decoded range size support

There are some known HW problems about decoded
range register configurations on existing AST2500 and
AST2600 platforms. Additional callback function,
adjust_decoded_sz, is added to solve these problems
on each platform. Besides, aspeed_spi_trim_decoded_size
function is added to modify overall decoded address
size for fitting the maximum AHB decoded size.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 144 ++++++++++++++++++++++++++++++++++-
 1 file changed, 143 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 6099b85255..4287f5d8c0 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -84,10 +84,12 @@ struct aspeed_spi_info {
 	u32 (*segment_start)(struct udevice *bus, u32 reg);
 	u32 (*segment_end)(struct udevice *bus, u32 reg);
 	u32 (*segment_reg)(u32 start, u32 end);
+	int (*adjust_decoded_sz)(struct udevice *bus);
 };
 
 static const struct aspeed_spi_info ast2400_spi_info;
 static int aspeed_spi_decoded_range_config(struct udevice *bus);
+static int aspeed_spi_trim_decoded_size(struct udevice *bus);
 
 static u32 aspeed_spi_get_io_mode(u32 bus_width)
 {
@@ -195,6 +197,53 @@ static void ast2500_spi_chip_set_4byte(struct udevice *bus, u32 cs)
 	writel(reg_val, &priv->regs->ctrl);
 }
 
+/*
+ * For AST2500, the minimum address decoded size for each CS
+ * is 8MB instead of zero. This address decoded size is
+ * mandatory for each CS no matter whether it will be used.
+ * This is a HW limitation.
+ */
+static int ast2500_adjust_decoded_size(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct aspeed_spi_flash *flashes = &priv->flashes[0];
+	int ret;
+	int i;
+	int cs;
+	u32 pre_sz;
+	u32 lack_sz;
+
+	/* Assign min_decoded_sz to unused CS. */
+	for (cs = priv->num_cs; cs < plat->max_cs; cs++)
+		flashes[cs].ahb_decoded_sz = priv->info->min_decoded_sz;
+
+	/*
+	 * If commnad mode or normal mode is used, the start address of a
+	 * decoded range should be multiple of its related flash size.
+	 * Namely, the total decoded size from flash 0 to flash N should
+	 * be multiple of the size of flash (N + 1).
+	 */
+	for (cs = priv->num_cs - 1; cs >= 0; cs--) {
+		pre_sz = 0;
+		for (i = 0; i < cs; i++)
+			pre_sz += flashes[i].ahb_decoded_sz;
+
+		if (flashes[cs].ahb_decoded_sz != 0 &&
+		    (pre_sz % flashes[cs].ahb_decoded_sz) != 0) {
+			lack_sz = flashes[cs].ahb_decoded_sz -
+				  (pre_sz % flashes[cs].ahb_decoded_sz);
+			flashes[0].ahb_decoded_sz += lack_sz;
+		}
+	}
+
+	ret = aspeed_spi_trim_decoded_size(bus);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
 static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
@@ -236,6 +285,86 @@ static void ast2600_spi_chip_set_4byte(struct udevice *bus, u32 cs)
 	writel(reg_val, &priv->regs->ctrl);
 }
 
+static int ast2600_adjust_decoded_size(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct aspeed_spi_flash *flashes = &priv->flashes[0];
+	int ret;
+	int i;
+	int cs;
+	u32 pre_sz;
+	u32 lack_sz;
+
+	/* Close unused CS. */
+	for (cs = priv->num_cs; cs < plat->max_cs; cs++)
+		flashes[cs].ahb_decoded_sz = 0;
+
+	/*
+	 * If commnad mode or normal mode is used, the start address of a
+	 * decoded range should be multiple of its related flash size.
+	 * Namely, the total decoded size from flash 0 to flash N should
+	 * be multiple of the size of flash (N + 1).
+	 */
+	for (cs = priv->num_cs - 1; cs >= 0; cs--) {
+		pre_sz = 0;
+		for (i = 0; i < cs; i++)
+			pre_sz += flashes[i].ahb_decoded_sz;
+
+		if (flashes[cs].ahb_decoded_sz != 0 &&
+		    (pre_sz % flashes[cs].ahb_decoded_sz) != 0) {
+			lack_sz = flashes[cs].ahb_decoded_sz -
+				  (pre_sz % flashes[cs].ahb_decoded_sz);
+			flashes[0].ahb_decoded_sz += lack_sz;
+		}
+	}
+
+	ret = aspeed_spi_trim_decoded_size(bus);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * As the flash size grows up, we need to trim some decoded
+ * size if needed for the sake of conforming the maximum
+ * decoded size. We trim the decoded size from the largest
+ * CS in order to avoid affecting the default boot up sequence
+ * from CS0 where command mode or normal mode is used.
+ * Notice, if a CS decoded size is trimmed, command mode may
+ * not work perfectly on that CS.
+ */
+static int aspeed_spi_trim_decoded_size(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct aspeed_spi_flash *flashes = &priv->flashes[0];
+	u32 total_sz;
+	int cs = plat->max_cs - 1;
+	u32 i;
+
+	do {
+		total_sz = 0;
+		for (i = 0; i < plat->max_cs; i++)
+			total_sz += flashes[i].ahb_decoded_sz;
+
+		if (flashes[cs].ahb_decoded_sz <= priv->info->min_decoded_sz)
+			cs--;
+
+		if (cs < 0)
+			return -ENOMEM;
+
+		if (total_sz > plat->ahb_sz) {
+			flashes[cs].ahb_decoded_sz -=
+					priv->info->min_decoded_sz;
+			total_sz -= priv->info->min_decoded_sz;
+		}
+	} while (total_sz > plat->ahb_sz);
+
+	return 0;
+}
+
 static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf,
 				    size_t len)
 {
@@ -522,10 +651,19 @@ static void aspeed_spi_decoded_range_set(struct udevice *bus)
 
 static int aspeed_spi_decoded_range_config(struct udevice *bus)
 {
+	int ret = 0;
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+
+	if (priv->info->adjust_decoded_sz) {
+		ret = priv->info->adjust_decoded_sz(bus);
+		if (ret != 0)
+			return ret;
+	}
+
 	aspeed_spi_decoded_base_calculate(bus);
 	aspeed_spi_decoded_range_set(bus);
 
-	return 0;
+	return ret;
 }
 
 /*
@@ -616,6 +754,7 @@ static const struct aspeed_spi_info ast2500_fmc_info = {
 	.segment_start = ast2500_spi_segment_start,
 	.segment_end = ast2500_spi_segment_end,
 	.segment_reg = ast2500_spi_segment_reg,
+	.adjust_decoded_sz = ast2500_adjust_decoded_size,
 };
 
 /*
@@ -630,6 +769,7 @@ static const struct aspeed_spi_info ast2500_spi_info = {
 	.segment_start = ast2500_spi_segment_start,
 	.segment_end = ast2500_spi_segment_end,
 	.segment_reg = ast2500_spi_segment_reg,
+	.adjust_decoded_sz = ast2500_adjust_decoded_size,
 };
 
 static const struct aspeed_spi_info ast2600_fmc_info = {
@@ -640,6 +780,7 @@ static const struct aspeed_spi_info ast2600_fmc_info = {
 	.segment_start = ast2600_spi_segment_start,
 	.segment_end = ast2600_spi_segment_end,
 	.segment_reg = ast2600_spi_segment_reg,
+	.adjust_decoded_sz = ast2600_adjust_decoded_size,
 };
 
 static const struct aspeed_spi_info ast2600_spi_info = {
@@ -650,6 +791,7 @@ static const struct aspeed_spi_info ast2600_spi_info = {
 	.segment_start = ast2600_spi_segment_start,
 	.segment_end = ast2600_spi_segment_end,
 	.segment_reg = ast2600_spi_segment_reg,
+	.adjust_decoded_sz = ast2600_adjust_decoded_size,
 };
 
 static int aspeed_spi_claim_bus(struct udevice *dev)

From dd29cee8d83588e631bbd87f1d6cbf9b2fb960b8 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:13 +0800
Subject: [PATCH 12/14] spi: aspeed: Support customized decoded address ranges

If "decoded-ranges" is defined in the device tree, the
driver will apply the decoded address ranges from this
property to the controller during probe stage.

This patch refers to the following OpenBMC u-boot patch.
https://patchwork.ozlabs.org/project/openbmc/list/?series=306969

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 136 ++++++++++++++++++++++++++++++++---
 1 file changed, 127 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 4287f5d8c0..2b6c4b48bd 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -74,6 +74,7 @@ struct aspeed_spi_priv {
 	struct aspeed_spi_regs *regs;
 	struct aspeed_spi_info *info;
 	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
+	bool fixed_decoded_range;
 };
 
 struct aspeed_spi_info {
@@ -87,7 +88,15 @@ struct aspeed_spi_info {
 	int (*adjust_decoded_sz)(struct udevice *bus);
 };
 
+struct aspeed_spi_decoded_range {
+	u32 cs;
+	u32 ahb_base;
+	u32 sz;
+};
+
 static const struct aspeed_spi_info ast2400_spi_info;
+static const struct aspeed_spi_info ast2500_fmc_info;
+static const struct aspeed_spi_info ast2500_spi_info;
 static int aspeed_spi_decoded_range_config(struct udevice *bus);
 static int aspeed_spi_trim_decoded_size(struct udevice *bus);
 
@@ -618,6 +627,9 @@ static void aspeed_spi_decoded_base_calculate(struct udevice *bus)
 	struct aspeed_spi_priv *priv = dev_get_priv(bus);
 	u32 cs;
 
+	if (priv->fixed_decoded_range)
+		return;
+
 	priv->flashes[0].ahb_base = plat->ahb_base;
 
 	for (cs = 1; cs < plat->max_cs; cs++) {
@@ -654,7 +666,8 @@ static int aspeed_spi_decoded_range_config(struct udevice *bus)
 	int ret = 0;
 	struct aspeed_spi_priv *priv = dev_get_priv(bus);
 
-	if (priv->info->adjust_decoded_sz) {
+	if (priv->info->adjust_decoded_sz &&
+	    !priv->fixed_decoded_range) {
 		ret = priv->info->adjust_decoded_sz(bus);
 		if (ret != 0)
 			return ret;
@@ -666,6 +679,104 @@ static int aspeed_spi_decoded_range_config(struct udevice *bus)
 	return ret;
 }
 
+static int aspeed_spi_decoded_ranges_sanity(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 cs;
+	u32 total_sz = 0;
+
+	/* Check overall size. */
+	for (cs = 0; cs < plat->max_cs; cs++)
+		total_sz += priv->flashes[cs].ahb_decoded_sz;
+
+	if (total_sz > plat->ahb_sz) {
+		dev_err(bus, "invalid total size 0x%08x\n", total_sz);
+		return -EINVAL;
+	}
+
+	/* Check each decoded range size for AST2500. */
+	if (priv->info == &ast2500_fmc_info ||
+	    priv->info == &ast2500_spi_info) {
+		for (cs = 0; cs < plat->max_cs; cs++) {
+			if (priv->flashes[cs].ahb_decoded_sz <
+			    priv->info->min_decoded_sz) {
+				dev_err(bus, "insufficient decoded range.\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	/*
+	 * Check overlay. Here, we assume the deccded ranges and
+	 * address base	are monotonic increasing with CE#.
+	 */
+	for (cs = plat->max_cs - 1; cs > 0; cs--) {
+		if ((u32)priv->flashes[cs].ahb_base != 0 &&
+		    (u32)priv->flashes[cs].ahb_base <
+		    (u32)priv->flashes[cs - 1].ahb_base +
+		    priv->flashes[cs - 1].ahb_decoded_sz) {
+			dev_err(bus, "decoded range overlay 0x%08x 0x%08x\n",
+				(u32)priv->flashes[cs].ahb_base,
+				(u32)priv->flashes[cs - 1].ahb_base);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int aspeed_spi_read_fixed_decoded_ranges(struct udevice *bus)
+{
+	int ret = 0;
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	const char *range_prop = "decoded-ranges";
+	struct aspeed_spi_decoded_range ranges[ASPEED_SPI_MAX_CS];
+	const struct property *prop;
+	u32 prop_sz;
+	u32 count;
+	u32 i;
+
+	priv->fixed_decoded_range = false;
+
+	prop = dev_read_prop(bus, range_prop, &prop_sz);
+	if (!prop)
+		return 0;
+
+	count = prop_sz / sizeof(struct aspeed_spi_decoded_range);
+	if (count > plat->max_cs || count < priv->num_cs) {
+		dev_err(bus, "invalid '%s' property %d %d\n",
+			range_prop, count, priv->num_cs);
+		return -EINVAL;
+	}
+
+	ret = dev_read_u32_array(bus, range_prop, (u32 *)ranges, count * 3);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < count; i++) {
+		priv->flashes[ranges[i].cs].ahb_base =
+				(void __iomem *)ranges[i].ahb_base;
+		priv->flashes[ranges[i].cs].ahb_decoded_sz =
+				ranges[i].sz;
+	}
+
+	for (i = 0; i < plat->max_cs; i++) {
+		dev_dbg(bus, "ahb_base: 0x%p, size: 0x%08x\n",
+			priv->flashes[i].ahb_base,
+			priv->flashes[i].ahb_decoded_sz);
+	}
+
+	ret = aspeed_spi_decoded_ranges_sanity(bus);
+	if (ret != 0)
+		return ret;
+
+	priv->fixed_decoded_range = true;
+
+	return 0;
+}
+
 /*
  * Initialize SPI controller for each chip select.
  * Here, only the minimum decode range is configured
@@ -709,16 +820,23 @@ static int aspeed_spi_ctrl_init(struct udevice *bus)
 		return 0;
 	}
 
-	/* Assign basic AHB decoded size for each CS. */
-	for (cs = 0; cs < plat->max_cs; cs++) {
-		reg_val = readl(&priv->regs->segment_addr[cs]);
-		decoded_sz = priv->info->segment_end(bus, reg_val) -
-			     priv->info->segment_start(bus, reg_val);
 
-		if (decoded_sz < priv->info->min_decoded_sz)
-			decoded_sz = priv->info->min_decoded_sz;
+	ret = aspeed_spi_read_fixed_decoded_ranges(bus);
+	if (ret != 0)
+		return ret;
 
-		priv->flashes[cs].ahb_decoded_sz = decoded_sz;
+	if (!priv->fixed_decoded_range) {
+		/* Assign basic AHB decoded size for each CS. */
+		for (cs = 0; cs < plat->max_cs; cs++) {
+			reg_val = readl(&priv->regs->segment_addr[cs]);
+			decoded_sz = priv->info->segment_end(bus, reg_val) -
+				     priv->info->segment_start(bus, reg_val);
+
+			if (decoded_sz < priv->info->min_decoded_sz)
+				decoded_sz = priv->info->min_decoded_sz;
+
+			priv->flashes[cs].ahb_decoded_sz = decoded_sz;
+		}
 	}
 
 	ret = aspeed_spi_decoded_range_config(bus);

From 9a163720237e5e0d9989e4411f70b61414f3711e Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:14 +0800
Subject: [PATCH 13/14] spi: aspeed: Clock frequency adjustment support

Driver can configure the SPI clock frequnecy to the
target value of "spi-max-frequency" property in
the device tree. The frequency is divided from HCLK,
200MHz. Usually, the ASPEED SPI clock frequency range
is between 12.5MHz and 100MHz. On AST2600, the lowest
SPI clock frequency can be about 780kHz.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 196 +++++++++++++++++++++++++++++++++--
 1 file changed, 186 insertions(+), 10 deletions(-)

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 2b6c4b48bd..a3c9633382 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -60,6 +60,7 @@ struct aspeed_spi_plat {
 	u8 max_cs;
 	void __iomem *ahb_base; /* AHB address base for all flash devices. */
 	fdt_size_t ahb_sz; /* Overall AHB window size for all flash device. */
+	u32 hclk_rate; /* AHB clock rate */
 };
 
 struct aspeed_spi_flash {
@@ -67,6 +68,7 @@ struct aspeed_spi_flash {
 	u32 ahb_decoded_sz;
 	u32 ce_ctrl_user;
 	u32 ce_ctrl_read;
+	u32 max_freq;
 };
 
 struct aspeed_spi_priv {
@@ -81,11 +83,13 @@ struct aspeed_spi_info {
 	u32 io_mode_mask;
 	u32 max_bus_width;
 	u32 min_decoded_sz;
+	u32 clk_ctrl_mask;
 	void (*set_4byte)(struct udevice *bus, u32 cs);
 	u32 (*segment_start)(struct udevice *bus, u32 reg);
 	u32 (*segment_end)(struct udevice *bus, u32 reg);
 	u32 (*segment_reg)(u32 start, u32 end);
 	int (*adjust_decoded_sz)(struct udevice *bus);
+	u32 (*get_clk_setting)(struct udevice *dev, uint hz);
 };
 
 struct aspeed_spi_decoded_range {
@@ -165,6 +169,44 @@ static void ast2400_spi_chip_set_4byte(struct udevice *bus, u32 cs)
 	writel(flash->ce_ctrl_read, &priv->regs->ctrl);
 }
 
+/* Transfer maximum clock frequency to register setting */
+static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 hclk_clk = plat->hclk_rate;
+	u32 hclk_div = 0x0000; /* default value */
+	u32 i;
+	bool found = false;
+	/* HCLK/1 ..	HCLK/16 */
+	u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+			    11, 3, 10, 2, 9,  1, 8,  0};
+
+	/* FMC/SPIR10[11:8] */
+	for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+		if (hclk_clk / (i + 1) <= max_hz) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		hclk_div = hclk_masks[i] << 8;
+		priv->flashes[slave_plat->cs].max_freq = hclk_clk / (i + 1);
+	}
+
+	dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+		hclk_clk, max_hz);
+
+	if (found) {
+		dev_dbg(dev, "h_div: %d (mask %x), speed: %d\n",
+			i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+	}
+
+	return hclk_div;
+}
+
 static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
@@ -253,6 +295,58 @@ static int ast2500_adjust_decoded_size(struct udevice *bus)
 	return 0;
 }
 
+static u32 ast2500_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 hclk_clk = plat->hclk_rate;
+	u32 hclk_div = 0x0000; /* default value */
+	u32 i;
+	bool found = false;
+	/* HCLK/1 ..	HCLK/16 */
+	u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+			    11, 3, 10, 2, 9,  1, 8,  0};
+
+	/* FMC/SPIR10[11:8] */
+	for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+		if (hclk_clk / (i + 1) <= max_hz) {
+			found = true;
+			priv->flashes[slave_plat->cs].max_freq =
+							hclk_clk / (i + 1);
+			break;
+		}
+	}
+
+	if (found) {
+		hclk_div = hclk_masks[i] << 8;
+		goto end;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+		if (hclk_clk / ((i + 1) * 4) <= max_hz) {
+			found = true;
+			priv->flashes[slave_plat->cs].max_freq =
+						hclk_clk / ((i + 1) * 4);
+			break;
+		}
+	}
+
+	if (found)
+		hclk_div = BIT(13) | (hclk_masks[i] << 8);
+
+end:
+	dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+		hclk_clk, max_hz);
+
+	if (found) {
+		dev_dbg(dev, "h_div: %d (mask %x), speed: %d\n",
+			i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+	}
+
+	return hclk_div;
+}
+
 static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
@@ -335,6 +429,51 @@ static int ast2600_adjust_decoded_size(struct udevice *bus)
 	return 0;
 }
 
+static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 hclk_clk = plat->hclk_rate;
+	u32 hclk_div = 0x0400; /* default value */
+	u32 i, j;
+	bool found = false;
+	/* HCLK/1 ..	HCLK/16 */
+	u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+			    11, 3, 10, 2, 9,  1, 8,  0};
+
+	/* FMC/SPIR10[27:24] */
+	for (j = 0; j < 0xf; j++) {
+		/* FMC/SPIR10[11:8] */
+		for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+			if (i == 0 && j == 0)
+				continue;
+
+			if (hclk_clk / (i + 1 + (j * 16)) <= max_hz) {
+				found = true;
+				break;
+			}
+		}
+
+		if (found) {
+			hclk_div = ((j << 24) | hclk_masks[i] << 8);
+			priv->flashes[slave_plat->cs].max_freq =
+						hclk_clk / (i + 1 + j * 16);
+			break;
+		}
+	}
+
+	dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+		hclk_clk, max_hz);
+
+	if (found) {
+		dev_dbg(dev, "base_clk: %d, h_div: %d (mask %x), speed: %d\n",
+			j, i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+	}
+
+	return hclk_div;
+}
+
 /*
  * As the flash size grows up, we need to trim some decoded
  * size if needed for the sake of conforming the maximum
@@ -531,7 +670,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
 	struct spi_mem_op op_tmpl = desc->info.op_tmpl;
 	u32 i;
 	u32 cs = slave_plat->cs;
-	u32 reg_val;
+	u32 cmd_io_conf;
 	u32 ce_ctrl_reg;
 
 	if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
@@ -564,15 +703,16 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
 			return ret;
 	}
 
-	reg_val = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
-		  op_tmpl.cmd.opcode << 16 |
-		  ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
-		  ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
-		  CTRL_IO_MODE_CMD_READ;
+	cmd_io_conf = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
+		      op_tmpl.cmd.opcode << 16 |
+		      ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
+		      ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
+		      CTRL_IO_MODE_CMD_READ;
 
-	writel(reg_val, ce_ctrl_reg);
+	priv->flashes[cs].ce_ctrl_read &= priv->info->clk_ctrl_mask;
+	priv->flashes[cs].ce_ctrl_read |= cmd_io_conf;
 
-	priv->flashes[cs].ce_ctrl_read = reg_val;
+	writel(priv->flashes[cs].ce_ctrl_read, ce_ctrl_reg);
 
 	dev_dbg(dev, "read bus width: %d ce_ctrl_val: 0x%08x\n",
 		op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
@@ -806,7 +946,8 @@ static int aspeed_spi_ctrl_init(struct udevice *bus)
 
 	/* Initial user mode. */
 	for (cs = 0; cs < priv->num_cs; cs++) {
-		priv->flashes[cs].ce_ctrl_user =
+		priv->flashes[cs].ce_ctrl_user &= priv->info->clk_ctrl_mask;
+		priv->flashes[cs].ce_ctrl_user |=
 				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
 	}
 
@@ -848,31 +989,37 @@ static const struct aspeed_spi_info ast2400_fmc_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00002f00,
 	.set_4byte = ast2400_fmc_chip_set_4byte,
 	.segment_start = ast2400_spi_segment_start,
 	.segment_end = ast2400_spi_segment_end,
 	.segment_reg = ast2400_spi_segment_reg,
+	.get_clk_setting = ast2400_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2400_spi_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00000f00,
 	.set_4byte = ast2400_spi_chip_set_4byte,
 	.segment_start = ast2400_spi_segment_start,
 	.segment_end = ast2400_spi_segment_end,
 	.segment_reg = ast2400_spi_segment_reg,
+	.get_clk_setting = ast2400_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2500_fmc_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00002f00,
 	.set_4byte = ast2500_spi_chip_set_4byte,
 	.segment_start = ast2500_spi_segment_start,
 	.segment_end = ast2500_spi_segment_end,
 	.segment_reg = ast2500_spi_segment_reg,
 	.adjust_decoded_sz = ast2500_adjust_decoded_size,
+	.get_clk_setting = ast2500_get_clk_setting,
 };
 
 /*
@@ -883,42 +1030,59 @@ static const struct aspeed_spi_info ast2500_spi_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00002f00,
 	.set_4byte = ast2500_spi_chip_set_4byte,
 	.segment_start = ast2500_spi_segment_start,
 	.segment_end = ast2500_spi_segment_end,
 	.segment_reg = ast2500_spi_segment_reg,
 	.adjust_decoded_sz = ast2500_adjust_decoded_size,
+	.get_clk_setting = ast2500_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2600_fmc_info = {
 	.io_mode_mask = 0xf0000000,
 	.max_bus_width = 4,
 	.min_decoded_sz = 0x200000,
+	.clk_ctrl_mask = 0x0f000f00,
 	.set_4byte = ast2600_spi_chip_set_4byte,
 	.segment_start = ast2600_spi_segment_start,
 	.segment_end = ast2600_spi_segment_end,
 	.segment_reg = ast2600_spi_segment_reg,
 	.adjust_decoded_sz = ast2600_adjust_decoded_size,
+	.get_clk_setting = ast2600_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2600_spi_info = {
 	.io_mode_mask = 0xf0000000,
 	.max_bus_width = 4,
 	.min_decoded_sz = 0x200000,
+	.clk_ctrl_mask = 0x0f000f00,
 	.set_4byte = ast2600_spi_chip_set_4byte,
 	.segment_start = ast2600_spi_segment_start,
 	.segment_end = ast2600_spi_segment_end,
 	.segment_reg = ast2600_spi_segment_reg,
 	.adjust_decoded_sz = ast2600_adjust_decoded_size,
+	.get_clk_setting = ast2600_get_clk_setting,
 };
 
 static int aspeed_spi_claim_bus(struct udevice *dev)
 {
 	struct udevice *bus = dev->parent;
 	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct aspeed_spi_flash *flash = &priv->flashes[slave_plat->cs];
+	u32 clk_setting;
 
 	dev_dbg(bus, "%s: claim bus CS%u\n", bus->name, slave_plat->cs);
 
+	if (flash->max_freq == 0) {
+		clk_setting = priv->info->get_clk_setting(dev, slave_plat->max_hz);
+		flash->ce_ctrl_user &= ~(priv->info->clk_ctrl_mask);
+		flash->ce_ctrl_user |= clk_setting;
+		flash->ce_ctrl_read &= ~(priv->info->clk_ctrl_mask);
+		flash->ce_ctrl_read |= clk_setting;
+	}
+
 	return 0;
 }
 
@@ -958,6 +1122,8 @@ static int apseed_spi_of_to_plat(struct udevice *bus)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
 	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	int ret;
+	struct clk hclk;
 
 	priv->regs = (void __iomem *)devfdt_get_addr_index(bus, 0);
 	if ((u32)priv->regs == FDT_ADDR_T_NONE) {
@@ -976,9 +1142,19 @@ static int apseed_spi_of_to_plat(struct udevice *bus)
 	if (plat->max_cs > ASPEED_SPI_MAX_CS)
 		return -EINVAL;
 
+	ret = clk_get_by_index(bus, 0, &hclk);
+	if (ret < 0) {
+		dev_err(bus, "%s could not get clock: %d\n", bus->name, ret);
+		return ret;
+	}
+
+	plat->hclk_rate = clk_get_rate(&hclk);
+	clk_free(&hclk);
+
 	dev_dbg(bus, "ctrl_base = 0x%x, ahb_base = 0x%p, size = 0x%lx\n",
 		(u32)priv->regs, plat->ahb_base, plat->ahb_sz);
-	dev_dbg(bus, "max_cs = %d\n", plat->max_cs);
+	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
+		plat->hclk_rate / 1000000, plat->max_cs);
 
 	return 0;
 }

From c184aca7b061f81681fc10d777c936f9a787a317 Mon Sep 17 00:00:00 2001
From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Date: Fri, 19 Aug 2022 17:01:15 +0800
Subject: [PATCH 14/14] mtd: spi-nor-ids: Add Winbond W25Q512JVQ ID
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add ID for Winbond W25Q512JVQ device which is supported
on AST2600 EVB by default.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/mtd/spi/spi-nor-ids.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 4fe8b0d92c..65eb35a918 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -424,6 +424,11 @@ const struct flash_info spi_nor_ids[] = {
 			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
 			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
 	},
+	{
+		INFO("w25q512jvq", 0xef4020, 0, 64 * 1024, 1024,
+		     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+		     SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+	},
 	{
 		INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048,
 			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |