All of lore.kernel.org
 help / color / mirror / Atom feed
* [v4 00/12] Add ASPEED SPI controller driver
@ 2022-05-24  5:56 Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 01/12] clk: aspeed: Get HCLK frequency support Chin-Ting Kuo
                   ` (13 more replies)
  0 siblings, 14 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

This patch series aims to porting ASPEED FMC/SPI memory controller
driver with spi-mem interface. spi-mem dirmap framework is also
synchronized from Linux. These patches have been verified on both
AST2600 and AST2500 EVBs.

Changes in v2:
  - Separate defconfig files from the SPI driver patch.
  - Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
    spi_dirmap related functions.
  - Add Winbond w25q512jv flash ID.

Changes in v3:
  - Get AHB bus clock frequency from the function parameter.
  - Fix a grammatical error in spi-mem.h.

Changes in v4:
  - Fix bug when SPI_NOR_4B_OPCODES flag is set.

Chin-Ting Kuo (12):
  clk: aspeed: Get HCLK frequency support
  pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
  spi: aspeed: Add ASPEED SPI controller driver
  configs: aspeed: Enable SPI flash features
  MAINTAINERS: Add ASPEED SPI driver file
  arm: dts: aspeed: Update SPI flash node settings
  spi-mem: Add dirmap API from Linux
  mtd: spi-nor: Use spi-mem dirmap API
  spi: aspeed: SPI dirmap read support
  configs: aspeed: Enable CONFIG_SPI_DIRMAP
  mtd: spi-nor-ids: Add Winbond W25Q512JV ID
  spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set

 MAINTAINERS                              |   7 +
 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 +-
 configs/evb-ast2500_defconfig            |  14 +
 configs/evb-ast2600_defconfig            |  14 +
 drivers/clk/aspeed/clk_ast2500.c         |  23 +
 drivers/mtd/spi/sf_probe.c               |  76 ++
 drivers/mtd/spi/spi-nor-core.c           |  55 +-
 drivers/mtd/spi/spi-nor-ids.c            |   5 +
 drivers/pinctrl/aspeed/pinctrl_ast2500.c |   2 +
 drivers/spi/Kconfig                      |  18 +
 drivers/spi/Makefile                     |   1 +
 drivers/spi/spi-aspeed.c                 | 914 +++++++++++++++++++++++
 drivers/spi/spi-mem.c                    | 268 +++++++
 include/linux/mtd/spi-nor.h              |  18 +
 include/spi-mem.h                        |  79 ++
 18 files changed, 1546 insertions(+), 46 deletions(-)
 create mode 100644 drivers/spi/spi-aspeed.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [v4 01/12] clk: aspeed: Get HCLK frequency support
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 02/12] pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support Chin-Ting Kuo
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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>
---
v3: Get AHB bus clock frequency from the function parameter.

 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 a1b4496ca2..d99964fcd7 100644
--- a/drivers/clk/aspeed/clk_ast2500.c
+++ b/drivers/clk/aspeed/clk_ast2500.c
@@ -29,6 +29,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;
 
 /*
@@ -85,6 +91,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
  *
@@ -146,6 +166,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)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 02/12] pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 01/12] clk: aspeed: Get HCLK frequency support Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 01/12] clk: aspeed: Get HCLK frequency support Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 02/12] pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01  9:28   ` Cédric Le Goater
  2022-07-07  5:36   ` Joel Stanley
  2022-05-24  5:56 ` [v4 04/12] configs: aspeed: Enable SPI flash features Chin-Ting Kuo
                   ` (10 subsequent siblings)
  13 siblings, 2 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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.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.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
v2: Remove defconfig files from this patch.

 drivers/spi/Kconfig      |   8 +
 drivers/spi/Makefile     |   1 +
 drivers/spi/spi-aspeed.c | 822 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 831 insertions(+)
 create mode 100644 drivers/spi/spi-aspeed.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a1e515cb2b..a616294910 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -387,6 +387,14 @@ config SANDBOX_SPI
 		};
 	  };
 
+config SPI_ASPEED
+	bool "ASPEED SPI controller driver"
+	depends on DM_SPI && SPI_MEM
+	default n
+	help
+	  Enable ASPEED SPI 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 06e81b465b..36a4bd5dce 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -9,6 +9,7 @@ obj-y += spi-uclass.o
 obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
 obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
 obj-$(CONFIG_SOFT_SPI) += soft_spi.o
+obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c
new file mode 100644
index 0000000000..9574aff793
--- /dev/null
+++ b/drivers/spi/spi-aspeed.c
@@ -0,0 +1,822 @@
+// 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>
+
+/* ASPEED FMC/SPI memory control register related */
+#define REG_CE_TYPE_SETTING          0x00
+#define REG_CE_ADDR_MODE_CTRL        0x04
+#define REG_INTR_CTRL_STATUS         0x08
+#define REG_CE0_CTRL_REG             0x10
+#define REG_CE0_DECODED_ADDR_REG     0x30
+
+#define ASPEED_SPI_MAX_CS       3
+#define FLASH_CALIBRATION_LEN   0x400
+
+#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_plat {
+	fdt_addr_t ctrl_base;
+	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 */
+	u8 max_cs;
+};
+
+struct aspeed_spi_flash {
+	u8 cs;
+	void __iomem *ahb_base;
+	u32 ahb_win_sz;
+	u32 ce_ctrl_user;
+	u32 ce_ctrl_read;
+	u32 max_freq;
+	bool trimmed_decoded_sz;
+};
+
+struct aspeed_spi_priv {
+	u32 num_cs;
+	struct aspeed_spi_info *info;
+	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
+	u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
+};
+
+struct aspeed_spi_info {
+	u32 cmd_io_ctrl_mask;
+	u32 clk_ctrl_mask;
+	u32 max_data_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);
+	int (*adjust_decoded_sz)(struct udevice *bus, u32 decoded_sz_arr[]);
+	u32 (*get_clk_setting)(struct udevice *dev, uint hz);
+};
+
+static int aspeed_spi_trim_decoded_size(struct udevice *bus,
+					u32 decoded_sz_arr[]);
+
+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:
+		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 + 0x100000;
+}
+
+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_plat *plat = dev_get_plat(bus);
+	u32 reg_val;
+
+	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
+	reg_val |= 0x1 << cs;
+	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
+				       u32 decoded_sz_arr[])
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	int ret;
+	int cs;
+
+	/* Assign min_decoded_sz to unused CS. */
+	for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
+		if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
+			decoded_sz_arr[cs] = priv->info->min_decoded_sz;
+	}
+
+	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
+/* Transfer maximum clock frequency to register setting */
+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 = 0x000; /* 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);
+	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_plat *plat = dev_get_plat(bus);
+	u32 reg_val;
+
+	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
+	reg_val |= 0x11 << cs;
+	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
+}
+
+static int ast2600_adjust_decoded_size(struct udevice *bus,
+				       u32 decoded_sz_arr[])
+{
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	int ret;
+	int i;
+	int cs;
+	u32 pre_sz;
+	u32 lack_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 += decoded_sz_arr[i];
+
+		if (decoded_sz_arr[cs] != 0 && (pre_sz % decoded_sz_arr[cs]) != 0) {
+			lack_sz = decoded_sz_arr[cs] - (pre_sz % decoded_sz_arr[cs]);
+			decoded_sz_arr[0] += lack_sz;
+		}
+	}
+
+	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
+/* Transfer maximum clock frequency to register setting */
+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 = 0x400; /* 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
+ * 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,
+					u32 decoded_sz_arr[])
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	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 += decoded_sz_arr[i];
+
+		if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
+			cs--;
+
+		if (cs < 0)
+			return -ENOMEM;
+
+		if (total_sz > plat->ahb_sz) {
+			decoded_sz_arr[cs] -= priv->info->min_decoded_sz;
+			total_sz -= priv->info->min_decoded_sz;
+			priv->flashes[cs].trimmed_decoded_sz = true;
+		}
+	} while (total_sz > plat->ahb_sz);
+
+	return 0;
+}
+
+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.buswidth > 1 || op->addr.nbytes > 4)
+		return false;
+
+	if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
+		return false;
+
+	if (op->data.buswidth > priv->info->max_data_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_plat *plat = dev_get_plat(bus);
+	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;
+	fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4;
+	struct aspeed_spi_flash *flash = &priv->flashes[cs];
+	u32 ctrl_val;
+	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);
+
+	/* Start user mode */
+	ctrl_val = flash->ce_ctrl_user;
+	writel(ctrl_val, ctrl_reg);
+	ctrl_val &= (~CTRL_STOP_ACTIVE);
+	writel(ctrl_val, 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;
+	}
+	aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
+
+	/* Send dummy cycle */
+	aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data, op->dummy.nbytes);
+
+	/* Change io_mode */
+	ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
+	writel(ctrl_val, 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);
+	}
+
+	ctrl_val |= CTRL_STOP_ACTIVE;
+	writel(ctrl_val, ctrl_reg);
+
+	/* Restore controller setting. */
+	writel(flash->ce_ctrl_read, ctrl_reg);
+
+	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
+	if (op->cmd.opcode == SPINOR_OP_EN4B)
+		priv->info->set_4byte(bus, cs);
+
+	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 int aspeed_spi_decoded_range_config(struct udevice *bus,
+					   u32 decoded_sz_arr[])
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	int ret;
+	u32 cs;
+	u32 decoded_reg_val;
+	u32 start_addr;
+	u32 end_addr = 0;
+
+	ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
+	if (ret != 0)
+		return ret;
+
+	/* Configure each CS decoded range */
+	for (cs = 0; cs < plat->max_cs; cs++) {
+		if (cs == 0)
+			start_addr = (u32)plat->ahb_base;
+		else
+			start_addr = end_addr;
+
+		priv->flashes[cs].ahb_base = (void __iomem *)start_addr;
+		priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
+
+		end_addr = start_addr + decoded_sz_arr[cs];
+		decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
+
+		writel(decoded_reg_val,
+		       plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
+
+		dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
+			cs, decoded_reg_val, start_addr, end_addr);
+	}
+
+	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(plat->ctrl_base + REG_CE_TYPE_SETTING);
+	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
+	       plat->ctrl_base + REG_CE_TYPE_SETTING);
+
+	memset(priv->flashes, 0x0,
+	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
+
+	/* Initial each CS controller register */
+	for (cs = 0; cs < priv->num_cs; cs++) {
+		priv->flashes[cs].ce_ctrl_user &=
+				~(priv->info->cmd_io_ctrl_mask);
+		priv->flashes[cs].ce_ctrl_user |=
+				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
+		writel(priv->flashes[cs].ce_ctrl_user,
+		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
+	}
+
+	memset(priv->decoded_sz_arr, 0x0, sizeof(u32) * ASPEED_SPI_MAX_CS);
+
+	for (cs = 0; cs < priv->num_cs; cs++) {
+		reg_val = readl(plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
+		decoded_sz = priv->info->segment_end(bus, reg_val) -
+			     priv->info->segment_start(bus, reg_val);
+
+		/*
+		 * For CS0, if the default address decoded area exists,
+		 * keep its value in order to make sure that the whole boot
+		 * image can be accessed with normal read mode.
+		 */
+		if (cs == 0 && decoded_sz != 0)
+			priv->decoded_sz_arr[cs] = decoded_sz;
+		else
+			priv->decoded_sz_arr[cs] = priv->info->min_decoded_sz;
+	}
+
+	ret = aspeed_spi_decoded_range_config(bus, priv->decoded_sz_arr);
+
+	return ret;
+}
+
+static const struct aspeed_spi_info ast2500_fmc_info = {
+	.max_data_bus_width = 2,
+	.cmd_io_ctrl_mask = 0x70ff40c3,
+	.clk_ctrl_mask = 0x00002f00,
+	.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,
+	.adjust_decoded_sz = ast2500_adjust_decoded_size,
+	.get_clk_setting = ast2500_get_clk_setting,
+};
+
+/*
+ * 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 = {
+	.max_data_bus_width = 2,
+	.cmd_io_ctrl_mask = 0x70ff40c3,
+	.clk_ctrl_mask = 0x00002f00,
+	.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,
+	.adjust_decoded_sz = ast2500_adjust_decoded_size,
+	.get_clk_setting = ast2500_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2600_fmc_info = {
+	.max_data_bus_width = 4,
+	.cmd_io_ctrl_mask = 0xf0ff40c3,
+	.clk_ctrl_mask = 0x0f000f00,
+	.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,
+	.adjust_decoded_sz = ast2600_adjust_decoded_size,
+	.get_clk_setting = ast2600_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2600_spi_info = {
+	.max_data_bus_width = 4,
+	.cmd_io_ctrl_mask = 0xf0ff40c3,
+	.clk_ctrl_mask = 0x0f000f00,
+	.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,
+	.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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	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;
+}
+
+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 clk hclk;
+	int ret;
+
+	plat->ctrl_base = devfdt_get_addr_index(bus, 0);
+	if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
+		dev_err(bus, "wrong AHB 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;
+	}
+
+	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);
+
+	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%lx, ahb_base = 0x%p, size = 0x%lx\n",
+		plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
+	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
+		plat->hclk_rate / 1000000, 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",
+	.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,
+};
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 04/12] configs: aspeed: Enable SPI flash features
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (2 preceding siblings ...)
  2022-05-24  5:56 ` [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01  9:28   ` Cédric Le Goater
  2022-05-24  5:56 ` [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file Chin-Ting Kuo
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

- 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 2371cc2742..fd04fe5a60 100644
--- a/configs/evb-ast2500_defconfig
+++ b/configs/evb-ast2500_defconfig
@@ -35,6 +35,16 @@ CONFIG_DM_I2C=y
 CONFIG_SYS_I2C_ASPEED=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_ETH=y
 CONFIG_FTGMAC100=y
@@ -44,6 +54,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=y
 CONFIG_SYSRESET=y
 CONFIG_TIMER=y
 CONFIG_WDT=y
diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig
index f84b723bbb..ccf63ec6d0 100644
--- a/configs/evb-ast2600_defconfig
+++ b/configs/evb-ast2600_defconfig
@@ -65,6 +65,16 @@ CONFIG_DM_I2C=y
 CONFIG_MISC=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_ETH=y
 CONFIG_DM_MDIO=y
@@ -77,6 +87,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=y
 CONFIG_SYSRESET=y
 CONFIG_SPL_SYSRESET=y
 CONFIG_WDT=y
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (3 preceding siblings ...)
  2022-05-24  5:56 ` [v4 04/12] configs: aspeed: Enable SPI flash features Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01 11:52   ` Jagan Teki
  2022-05-24  5:56 ` [v4 06/12] arm: dts: aspeed: Update SPI flash node settings Chin-Ting Kuo
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

Add spi-aspeed.c file for ARM ASPEED.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 56be0bfad0..f2cd707eda 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -688,6 +688,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.c
+
 BINMAN
 M:	Simon Glass <sjg@chromium.org>
 M:	Alper Nebi Yasak <alpernebiyasak@gmail.com>
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 06/12] arm: dts: aspeed: Update SPI flash node settings
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (4 preceding siblings ...)
  2022-05-24  5:56 ` [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01  9:42   ` Cédric Le Goater
  2022-05-24  5:56 ` [v4 07/12] spi-mem: Add dirmap API from Linux Chin-Ting Kuo
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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 4796ed445f..c6b7675902 100644
--- a/arch/arm/dts/ast2500-evb.dts
+++ b/arch/arm/dts/ast2500-evb.dts
@@ -73,3 +73,36 @@
 	pinctrl-names = "default";
 	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>;
+	};
+};
diff --git a/arch/arm/dts/ast2500.dtsi b/arch/arm/dts/ast2500.dtsi
index ee66ef6704..d78a53aeb7 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 0d65054313..c1965e9093 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 64074309b7..dd36f55f20 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";
 			};
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 07/12] spi-mem: Add dirmap API from Linux
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (5 preceding siblings ...)
  2022-05-24  5:56 ` [v4 06/12] arm: dts: aspeed: Update SPI flash node settings Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01  9:36   ` Cédric Le Goater
  2022-07-01 12:04   ` Jagan Teki
  2022-05-24  5:56 ` [v4 08/12] mtd: spi-nor: Use spi-mem dirmap API Chin-Ting Kuo
                   ` (6 subsequent siblings)
  13 siblings, 2 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

This adds the dirmap API originally introduced in Linux commit aa167f3
("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/

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>
---
v2: Remove "#if CONFIG_SPI_DIRMAP" compile wrapper.
v3: Fix a grammatical error in spi-mem.h.

 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 a616294910..297253714a 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);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 08/12] mtd: spi-nor: Use spi-mem dirmap API
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (6 preceding siblings ...)
  2022-05-24  5:56 ` [v4 07/12] spi-mem: Add dirmap API from Linux Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 09/12] spi: aspeed: SPI dirmap read support Chin-Ting Kuo
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

This adds support for the dirmap API to the spi-nor subsystem, as
introduced in Linux commit df5c210 ("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/

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>
---
v2: Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
    spi_dirmap related functions.

 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 3b7c817c02..0c6262b7fd 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -239,9 +239,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;
 
@@ -362,13 +362,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;
@@ -393,14 +409,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 4ceeae623d..2a5ad09625 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
@@ -511,6 +512,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 {
@@ -561,6 +563,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 */
@@ -584,6 +591,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
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 09/12] spi: aspeed: SPI dirmap read support
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (7 preceding siblings ...)
  2022-05-24  5:56 ` [v4 08/12] mtd: spi-nor: Use spi-mem dirmap API Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 10/12] configs: aspeed: Enable CONFIG_SPI_DIRMAP Chin-Ting Kuo
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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, a specific 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.c | 93 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/drivers/spi/spi-aspeed.c b/drivers/spi/spi-aspeed.c
index 9574aff793..e5e348eb7b 100644
--- a/drivers/spi/spi-aspeed.c
+++ b/drivers/spi/spi-aspeed.c
@@ -85,6 +85,8 @@ struct aspeed_spi_info {
 
 static int aspeed_spi_trim_decoded_size(struct udevice *bus,
 					u32 decoded_sz_arr[]);
+static int aspeed_spi_decoded_range_config(struct udevice *bus,
+					   u32 decoded_sz_arr[]);
 
 static u32 aspeed_spi_get_io_mode(u32 bus_width)
 {
@@ -509,6 +511,95 @@ 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_plat *plat = dev_get_plat(bus);
+	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 decoded_sz_arr[ASPEED_SPI_MAX_CS];
+	u32 reg_val;
+
+	if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) {
+		for (i = 0; i < priv->num_cs; i++) {
+			reg_val = readl(plat->ctrl_base +
+					REG_CE0_DECODED_ADDR_REG + i * 4);
+			decoded_sz_arr[i] =
+				info->segment_end(bus, reg_val) -
+				info->segment_start(bus, reg_val);
+		}
+
+		decoded_sz_arr[cs] = desc->info.length;
+
+		if (info->adjust_decoded_sz)
+			info->adjust_decoded_sz(bus, decoded_sz_arr);
+
+		for (i = 0; i < priv->num_cs; i++) {
+			dev_dbg(dev, "cs: %d, sz: 0x%x\n", i,
+				decoded_sz_arr[i]);
+		}
+
+		ret = aspeed_spi_decoded_range_config(bus, decoded_sz_arr);
+		if (ret)
+			return ret;
+
+		reg_val = readl(plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4) &
+			  (~info->cmd_io_ctrl_mask);
+		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,
+		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
+		priv->flashes[cs].ce_ctrl_read = reg_val;
+
+		dev_dbg(dev, "read bus width: %d [0x%08x]\n",
+			op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
+	} else {
+		/*
+		 * 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;
+	}
+
+	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_win_sz < offs + len ||
+	    (offs + len) % 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;
@@ -792,6 +883,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 = {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 10/12] configs: aspeed: Enable CONFIG_SPI_DIRMAP
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (8 preceding siblings ...)
  2022-05-24  5:56 ` [v4 09/12] spi: aspeed: SPI dirmap read support Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-05-24  5:56 ` [v4 11/12] mtd: spi-nor-ids: Add Winbond W25Q512JV ID Chin-Ting Kuo
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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 fd04fe5a60..097b991a1b 100644
--- a/configs/evb-ast2500_defconfig
+++ b/configs/evb-ast2500_defconfig
@@ -56,6 +56,7 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
+CONFIG_SPI_DIRMAP=y
 CONFIG_SPI_ASPEED=y
 CONFIG_SYSRESET=y
 CONFIG_TIMER=y
diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig
index ccf63ec6d0..7caa04d5b7 100644
--- a/configs/evb-ast2600_defconfig
+++ b/configs/evb-ast2600_defconfig
@@ -89,6 +89,7 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_SPI=y
 CONFIG_DM_SPI=y
+CONFIG_SPI_DIRMAP=y
 CONFIG_SPI_ASPEED=y
 CONFIG_SYSRESET=y
 CONFIG_SPL_SYSRESET=y
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 11/12] mtd: spi-nor-ids: Add Winbond W25Q512JV ID
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (9 preceding siblings ...)
  2022-05-24  5:56 ` [v4 10/12] configs: aspeed: Enable CONFIG_SPI_DIRMAP Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01  9:43   ` Cédric Le Goater
  2022-05-24  5:56 ` [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set Chin-Ting Kuo
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

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

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 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 7050ddc397..444affb5af 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -362,6 +362,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("w25q512jv", 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 |
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (10 preceding siblings ...)
  2022-05-24  5:56 ` [v4 11/12] mtd: spi-nor-ids: Add Winbond W25Q512JV ID Chin-Ting Kuo
@ 2022-05-24  5:56 ` Chin-Ting Kuo
  2022-07-01  9:44   ` Cédric Le Goater
  2022-06-26  4:56 ` [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
  2022-07-01 11:57 ` Jagan Teki
  13 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-05-24  5:56 UTC (permalink / raw)
  To: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav

When SPI_NOR_4B_OPCODES flag is set for a specific flash
in spi_nor_ids[] table, EN4B command will not be issued and
4-byte dedicated commands will be used to access SPI flash.
Thus, instead of EN4B command, address length is more suitable
for deciding whether the controller should be set to 4-byte mode.

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

diff --git a/drivers/spi/spi-aspeed.c b/drivers/spi/spi-aspeed.c
index e5e348eb7b..687ddc370b 100644
--- a/drivers/spi/spi-aspeed.c
+++ b/drivers/spi/spi-aspeed.c
@@ -504,10 +504,6 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
 	/* Restore controller setting. */
 	writel(flash->ce_ctrl_read, ctrl_reg);
 
-	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
-	if (op->cmd.opcode == SPINOR_OP_EN4B)
-		priv->info->set_4byte(bus, cs);
-
 	return 0;
 }
 
@@ -561,6 +557,9 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
 		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
 		priv->flashes[cs].ce_ctrl_read = reg_val;
 
+		if (op_tmpl.addr.nbytes == 4)
+			priv->info->set_4byte(bus, cs);
+
 		dev_dbg(dev, "read bus width: %d [0x%08x]\n",
 			op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
 	} else {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* RE: [v4 00/12] Add ASPEED SPI controller driver
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (11 preceding siblings ...)
  2022-05-24  5:56 ` [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set Chin-Ting Kuo
@ 2022-06-26  4:56 ` Chin-Ting Kuo
  2022-06-26 16:15   ` Cédric Le Goater
  2022-07-01 11:57 ` Jagan Teki
  13 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-06-26  4:56 UTC (permalink / raw)
  To: ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW, jagan, vigneshr,
	clg, u-boot, p.yadav
  Cc: trini

Hi All,

Are there any comments about this patch series?


Thanks.

Best Wishes,
Chin-Ting

> -----Original Message-----
> From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> Sent: Tuesday, May 24, 2022 1:57 PM
> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>; lukma@denx.de;
> seanga2@gmail.com; Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com; vigneshr@ti.com;
> clg@kaod.org; u-boot@lists.denx.de; p.yadav@ti.com
> Subject: [v4 00/12] Add ASPEED SPI controller driver
> 
> This patch series aims to porting ASPEED FMC/SPI memory controller driver
> with spi-mem interface. spi-mem dirmap framework is also synchronized from
> Linux. These patches have been verified on both
> AST2600 and AST2500 EVBs.
> 
> Changes in v2:
>   - Separate defconfig files from the SPI driver patch.
>   - Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
>     spi_dirmap related functions.
>   - Add Winbond w25q512jv flash ID.
> 
> Changes in v3:
>   - Get AHB bus clock frequency from the function parameter.
>   - Fix a grammatical error in spi-mem.h.
> 
> Changes in v4:
>   - Fix bug when SPI_NOR_4B_OPCODES flag is set.
> 
> Chin-Ting Kuo (12):
>   clk: aspeed: Get HCLK frequency support
>   pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
>   spi: aspeed: Add ASPEED SPI controller driver
>   configs: aspeed: Enable SPI flash features
>   MAINTAINERS: Add ASPEED SPI driver file
>   arm: dts: aspeed: Update SPI flash node settings
>   spi-mem: Add dirmap API from Linux
>   mtd: spi-nor: Use spi-mem dirmap API
>   spi: aspeed: SPI dirmap read support
>   configs: aspeed: Enable CONFIG_SPI_DIRMAP
>   mtd: spi-nor-ids: Add Winbond W25Q512JV ID
>   spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
> 
>  MAINTAINERS                              |   7 +
>  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 +-
>  configs/evb-ast2500_defconfig            |  14 +
>  configs/evb-ast2600_defconfig            |  14 +
>  drivers/clk/aspeed/clk_ast2500.c         |  23 +
>  drivers/mtd/spi/sf_probe.c               |  76 ++
>  drivers/mtd/spi/spi-nor-core.c           |  55 +-
>  drivers/mtd/spi/spi-nor-ids.c            |   5 +
>  drivers/pinctrl/aspeed/pinctrl_ast2500.c |   2 +
>  drivers/spi/Kconfig                      |  18 +
>  drivers/spi/Makefile                     |   1 +
>  drivers/spi/spi-aspeed.c                 | 914
> +++++++++++++++++++++++
>  drivers/spi/spi-mem.c                    | 268 +++++++
>  include/linux/mtd/spi-nor.h              |  18 +
>  include/spi-mem.h                        |  79 ++
>  18 files changed, 1546 insertions(+), 46 deletions(-)  create mode 100644
> drivers/spi/spi-aspeed.c
> 
> --
> 2.25.1


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 00/12] Add ASPEED SPI controller driver
  2022-06-26  4:56 ` [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
@ 2022-06-26 16:15   ` Cédric Le Goater
  2022-06-27  1:41     ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-06-26 16:15 UTC (permalink / raw)
  To: Chin-Ting Kuo, ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav
  Cc: trini

On 6/26/22 06:56, Chin-Ting Kuo wrote:
> Hi All,
> 
> Are there any comments about this patch series?*

Sorry, I haven't had time to take a look at the driver. I will try this week.

Thanks,

C.

> 
> 
> Thanks.
> 
> Best Wishes,
> Chin-Ting
> 
>> -----Original Message-----
>> From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
>> Sent: Tuesday, May 24, 2022 1:57 PM
>> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>; lukma@denx.de;
>> seanga2@gmail.com; Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
>> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com; vigneshr@ti.com;
>> clg@kaod.org; u-boot@lists.denx.de; p.yadav@ti.com
>> Subject: [v4 00/12] Add ASPEED SPI controller driver
>>
>> This patch series aims to porting ASPEED FMC/SPI memory controller driver
>> with spi-mem interface. spi-mem dirmap framework is also synchronized from
>> Linux. These patches have been verified on both
>> AST2600 and AST2500 EVBs.
>>
>> Changes in v2:
>>    - Separate defconfig files from the SPI driver patch.
>>    - Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
>>      spi_dirmap related functions.
>>    - Add Winbond w25q512jv flash ID.
>>
>> Changes in v3:
>>    - Get AHB bus clock frequency from the function parameter.
>>    - Fix a grammatical error in spi-mem.h.
>>
>> Changes in v4:
>>    - Fix bug when SPI_NOR_4B_OPCODES flag is set.
>>
>> Chin-Ting Kuo (12):
>>    clk: aspeed: Get HCLK frequency support
>>    pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
>>    spi: aspeed: Add ASPEED SPI controller driver
>>    configs: aspeed: Enable SPI flash features
>>    MAINTAINERS: Add ASPEED SPI driver file
>>    arm: dts: aspeed: Update SPI flash node settings
>>    spi-mem: Add dirmap API from Linux
>>    mtd: spi-nor: Use spi-mem dirmap API
>>    spi: aspeed: SPI dirmap read support
>>    configs: aspeed: Enable CONFIG_SPI_DIRMAP
>>    mtd: spi-nor-ids: Add Winbond W25Q512JV ID
>>    spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
>>
>>   MAINTAINERS                              |   7 +
>>   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 +-
>>   configs/evb-ast2500_defconfig            |  14 +
>>   configs/evb-ast2600_defconfig            |  14 +
>>   drivers/clk/aspeed/clk_ast2500.c         |  23 +
>>   drivers/mtd/spi/sf_probe.c               |  76 ++
>>   drivers/mtd/spi/spi-nor-core.c           |  55 +-
>>   drivers/mtd/spi/spi-nor-ids.c            |   5 +
>>   drivers/pinctrl/aspeed/pinctrl_ast2500.c |   2 +
>>   drivers/spi/Kconfig                      |  18 +
>>   drivers/spi/Makefile                     |   1 +
>>   drivers/spi/spi-aspeed.c                 | 914
>> +++++++++++++++++++++++
>>   drivers/spi/spi-mem.c                    | 268 +++++++
>>   include/linux/mtd/spi-nor.h              |  18 +
>>   include/spi-mem.h                        |  79 ++
>>   18 files changed, 1546 insertions(+), 46 deletions(-)  create mode 100644
>> drivers/spi/spi-aspeed.c
>>
>> --
>> 2.25.1
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 00/12] Add ASPEED SPI controller driver
  2022-06-26 16:15   ` Cédric Le Goater
@ 2022-06-27  1:41     ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-06-27  1:41 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav
  Cc: trini

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Monday, June 27, 2022 12:16 AM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>; ChiaWei Wang
> <chiawei_wang@aspeedtech.com>; lukma@denx.de; seanga2@gmail.com;
> Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com; vigneshr@ti.com;
> u-boot@lists.denx.de; p.yadav@ti.com
> Cc: trini@konsulko.com
> Subject: Re: [v4 00/12] Add ASPEED SPI controller driver
> 
> On 6/26/22 06:56, Chin-Ting Kuo wrote:
> > Hi All,
> >
> > Are there any comments about this patch series?*
> 
> Sorry, I haven't had time to take a look at the driver. I will try this week.

Okay and thanks for the reply.


Chin-Ting

> 
> Thanks,
> 
> C.
> 
> >
> >
> > Thanks.
> >
> > Best Wishes,
> > Chin-Ting
> >
> >> -----Original Message-----
> >> From: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> >> Sent: Tuesday, May 24, 2022 1:57 PM
> >> To: ChiaWei Wang <chiawei_wang@aspeedtech.com>; lukma@denx.de;
> >> seanga2@gmail.com; Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
> >> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com;
> vigneshr@ti.com;
> >> clg@kaod.org; u-boot@lists.denx.de; p.yadav@ti.com
> >> Subject: [v4 00/12] Add ASPEED SPI controller driver
> >>
> >> This patch series aims to porting ASPEED FMC/SPI memory controller
> >> driver with spi-mem interface. spi-mem dirmap framework is also
> >> synchronized from Linux. These patches have been verified on both
> >> AST2600 and AST2500 EVBs.
> >>
> >> Changes in v2:
> >>    - Separate defconfig files from the SPI driver patch.
> >>    - Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
> >>      spi_dirmap related functions.
> >>    - Add Winbond w25q512jv flash ID.
> >>
> >> Changes in v3:
> >>    - Get AHB bus clock frequency from the function parameter.
> >>    - Fix a grammatical error in spi-mem.h.
> >>
> >> Changes in v4:
> >>    - Fix bug when SPI_NOR_4B_OPCODES flag is set.
> >>
> >> Chin-Ting Kuo (12):
> >>    clk: aspeed: Get HCLK frequency support
> >>    pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
> >>    spi: aspeed: Add ASPEED SPI controller driver
> >>    configs: aspeed: Enable SPI flash features
> >>    MAINTAINERS: Add ASPEED SPI driver file
> >>    arm: dts: aspeed: Update SPI flash node settings
> >>    spi-mem: Add dirmap API from Linux
> >>    mtd: spi-nor: Use spi-mem dirmap API
> >>    spi: aspeed: SPI dirmap read support
> >>    configs: aspeed: Enable CONFIG_SPI_DIRMAP
> >>    mtd: spi-nor-ids: Add Winbond W25Q512JV ID
> >>    spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
> >>
> >>   MAINTAINERS                              |   7 +
> >>   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 +-
> >>   configs/evb-ast2500_defconfig            |  14 +
> >>   configs/evb-ast2600_defconfig            |  14 +
> >>   drivers/clk/aspeed/clk_ast2500.c         |  23 +
> >>   drivers/mtd/spi/sf_probe.c               |  76 ++
> >>   drivers/mtd/spi/spi-nor-core.c           |  55 +-
> >>   drivers/mtd/spi/spi-nor-ids.c            |   5 +
> >>   drivers/pinctrl/aspeed/pinctrl_ast2500.c |   2 +
> >>   drivers/spi/Kconfig                      |  18 +
> >>   drivers/spi/Makefile                     |   1 +
> >>   drivers/spi/spi-aspeed.c                 | 914
> >> +++++++++++++++++++++++
> >>   drivers/spi/spi-mem.c                    | 268 +++++++
> >>   include/linux/mtd/spi-nor.h              |  18 +
> >>   include/spi-mem.h                        |  79 ++
> >>   18 files changed, 1546 insertions(+), 46 deletions(-)  create mode
> >> 100644 drivers/spi/spi-aspeed.c
> >>
> >> --
> >> 2.25.1
> >


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-05-24  5:56 ` [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver Chin-Ting Kuo
@ 2022-07-01  9:28   ` Cédric Le Goater
  2022-07-03  8:47     ` Chin-Ting Kuo
  2022-07-07  5:36   ` Joel Stanley
  1 sibling, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01  9:28 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

Hello Chin-Ting,

On 5/24/22 07:56, Chin-Ting Kuo wrote:
> 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.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.
> 
> Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>

I would split the patch furthermore to ease reading.

  1 - Add basic support

      with default decoding ranges set for all possible CS, even
      without a device.

      WE only have USER mode for now. So it's not important to
      correctly set the ranges since we won't use them before
      direct mapping is introduced. They should not overlap,
      that's all.

  2 - decoding range adjustments
      
      On that topic, we might want to take the simple DT approach
      with a "ranges" property defining the mapping windows of each
      CE. I think it is safer than trying to compute perfect ranges
      like on Linux.
    
  3 - clock settings

      That should simply be the property defined in the DT


> ---
> v2: Remove defconfig files from this patch.
> 
>   drivers/spi/Kconfig      |   8 +
>   drivers/spi/Makefile     |   1 +
>   drivers/spi/spi-aspeed.c | 822 +++++++++++++++++++++++++++++++++++++++
>   3 files changed, 831 insertions(+)
>   create mode 100644 drivers/spi/spi-aspeed.c
> 
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index a1e515cb2b..a616294910 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -387,6 +387,14 @@ config SANDBOX_SPI
>   		};
>   	  };
>   
> +config SPI_ASPEED
> +	bool "ASPEED SPI controller driver"
> +	depends on DM_SPI && SPI_MEM
> +	default n
> +	help
> +	  Enable ASPEED SPI 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 06e81b465b..36a4bd5dce 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -9,6 +9,7 @@ obj-y += spi-uclass.o
>   obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
>   obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
>   obj-$(CONFIG_SOFT_SPI) += soft_spi.o
> +obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c
> new file mode 100644
> index 0000000000..9574aff793
> --- /dev/null
> +++ b/drivers/spi/spi-aspeed.c
> @@ -0,0 +1,822 @@
> +// 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>
> +
> +/* ASPEED FMC/SPI memory control register related */
> +#define REG_CE_TYPE_SETTING          0x00
> +#define REG_CE_ADDR_MODE_CTRL        0x04
> +#define REG_INTR_CTRL_STATUS         0x08
> +#define REG_CE0_CTRL_REG             0x10
> +#define REG_CE0_DECODED_ADDR_REG     0x30
> +
> +#define ASPEED_SPI_MAX_CS       3
> +#define FLASH_CALIBRATION_LEN   0x400
> +
> +#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_plat {
> +	fdt_addr_t ctrl_base;

are these the registers ?

> +	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 */
> +	u8 max_cs;


I don't think we need a "max_cs" in the controller struct and a
"num-cs" property in the DT. We could simply use a HW maxmimum
defined in aspeed_spi_info.
  

> +};
> +
> +struct aspeed_spi_flash {
> +	u8 cs;
> +	void __iomem *ahb_base;
> +	u32 ahb_win_sz;
> +	u32 ce_ctrl_user;
> +	u32 ce_ctrl_read;
> +	u32 max_freq;
> +	bool trimmed_decoded_sz;

I wonder what this is for. We need to split the patches :)

> +};
> +
> +struct aspeed_spi_priv {
> +	u32 num_cs;

See above.

> +	struct aspeed_spi_info *info;
> +	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
> +	u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
> +};


Couldn't we have a 'struct aspeed_spi_regs' defining the layout of
the registers ?

> +struct aspeed_spi_info {
> +	u32 cmd_io_ctrl_mask;
> +	u32 clk_ctrl_mask;
> +	u32 max_data_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);
> +	int (*adjust_decoded_sz)(struct udevice *bus, u32 decoded_sz_arr[]);
> +	u32 (*get_clk_setting)(struct udevice *dev, uint hz);
> +};
> +
> +static int aspeed_spi_trim_decoded_size(struct udevice *bus,
> +					u32 decoded_sz_arr[]);
> +
> +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:
> +		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 + 0x100000;
> +}
> +
> +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_plat *plat = dev_get_plat(bus);
> +	u32 reg_val;
> +
> +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> +	reg_val |= 0x1 << cs;
> +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
> +				       u32 decoded_sz_arr[])
> +{
> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +	int ret;
> +	int cs;
> +
> +	/* Assign min_decoded_sz to unused CS. */
> +	for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
> +		if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
> +			decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> +	}
> +
> +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> +	if (ret != 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/* Transfer maximum clock frequency to register setting */
> +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 = 0x000; /* 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);
> +	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_plat *plat = dev_get_plat(bus);
> +	u32 reg_val;
> +
> +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> +	reg_val |= 0x11 << cs;
> +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> +}
> +
> +static int ast2600_adjust_decoded_size(struct udevice *bus,
> +				       u32 decoded_sz_arr[])
> +{
> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +	int ret;
> +	int i;
> +	int cs;
> +	u32 pre_sz;
> +	u32 lack_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 += decoded_sz_arr[i];
> +
> +		if (decoded_sz_arr[cs] != 0 && (pre_sz % decoded_sz_arr[cs]) != 0) {
> +			lack_sz = decoded_sz_arr[cs] - (pre_sz % decoded_sz_arr[cs]);
> +			decoded_sz_arr[0] += lack_sz;
> +		}
> +	}
> +
> +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> +	if (ret != 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/* Transfer maximum clock frequency to register setting */
> +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 = 0x400; /* 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
> + * 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,
> +					u32 decoded_sz_arr[])
> +{
> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +	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 += decoded_sz_arr[i];
> +
> +		if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
> +			cs--;
> +
> +		if (cs < 0)
> +			return -ENOMEM;
> +
> +		if (total_sz > plat->ahb_sz) {
> +			decoded_sz_arr[cs] -= priv->info->min_decoded_sz;
> +			total_sz -= priv->info->min_decoded_sz;
> +			priv->flashes[cs].trimmed_decoded_sz = true;
> +		}
> +	} while (total_sz > plat->ahb_sz);
> +
> +	return 0;
> +}
> +
> +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.buswidth > 1 || op->addr.nbytes > 4)
> +		return false;
> +
> +	if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
> +		return false;
> +
> +	if (op->data.buswidth > priv->info->max_data_bus_width)
> +		return false;
> +
> +	if (!spi_mem_default_supports_op(slave, op))
> +		return false;
> +
> +	return true;
> +}

You could copy the Linux aspeed_spi_supports_op()

> +
> +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_plat *plat = dev_get_plat(bus);
> +	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;
> +	fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4;
> +	struct aspeed_spi_flash *flash = &priv->flashes[cs];
> +	u32 ctrl_val;
> +	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);
> +
> +	/* Start user mode */
> +	ctrl_val = flash->ce_ctrl_user;
> +	writel(ctrl_val, ctrl_reg);
> +	ctrl_val &= (~CTRL_STOP_ACTIVE);
> +	writel(ctrl_val, 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;
> +	}
> +	aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);

This could be writing 3 bytes. Not optimal.

> +
> +	/* Send dummy cycle */
> +	aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data, op->dummy.nbytes);
> +
> +	/* Change io_mode */
> +	ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
> +	writel(ctrl_val, 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);
> +	}
> +
> +	ctrl_val |= CTRL_STOP_ACTIVE;
> +	writel(ctrl_val, ctrl_reg);
> +
> +	/* Restore controller setting. */
> +	writel(flash->ce_ctrl_read, ctrl_reg);
> +
> +	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
> +	if (op->cmd.opcode == SPINOR_OP_EN4B)
> +		priv->info->set_4byte(bus, cs);

We don't need to set 4B earlier ? I trust you there.

> +
> +	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 int aspeed_spi_decoded_range_config(struct udevice *bus,
> +					   u32 decoded_sz_arr[])
> +{
> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +	int ret;
> +	u32 cs;
> +	u32 decoded_reg_val;
> +	u32 start_addr;
> +	u32 end_addr = 0;
> +
> +	ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
> +	if (ret != 0)
> +		return ret;
> +
> +	/* Configure each CS decoded range */
> +	for (cs = 0; cs < plat->max_cs; cs++) {
> +		if (cs == 0)
> +			start_addr = (u32)plat->ahb_base;
> +		else
> +			start_addr = end_addr;
> +		priv->flashes[cs].ahb_base = (void __iomem *)start_addr;
> +		priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
> +
> +		end_addr = start_addr + decoded_sz_arr[cs];
> +		decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
> +
> +		writel(decoded_reg_val,
> +		       plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
> +
> +		dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
> +			cs, decoded_reg_val, start_addr, end_addr);
> +	}
> +
> +	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(plat->ctrl_base + REG_CE_TYPE_SETTING);
> +	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
> +	       plat->ctrl_base + REG_CE_TYPE_SETTING);
> +
> +	memset(priv->flashes, 0x0,
> +	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
> +
> +	/* Initial each CS controller register */
> +	for (cs = 0; cs < priv->num_cs; cs++) {
> +		priv->flashes[cs].ce_ctrl_user &=
> +				~(priv->info->cmd_io_ctrl_mask);
> +		priv->flashes[cs].ce_ctrl_user |=
> +				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
> +		writel(priv->flashes[cs].ce_ctrl_user,
> +		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
> +	}
>

and we should start by setting sane defaults for the ranges.

It's too early to add the decoding ranges calculation.

Thanks,

C.

> +	memset(priv->decoded_sz_arr, 0x0, sizeof(u32) * ASPEED_SPI_MAX_CS);
> +
> +	for (cs = 0; cs < priv->num_cs; cs++) {
> +		reg_val = readl(plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
> +		decoded_sz = priv->info->segment_end(bus, reg_val) -
> +			     priv->info->segment_start(bus, reg_val);
> +
> +		/*
> +		 * For CS0, if the default address decoded area exists,
> +		 * keep its value in order to make sure that the whole boot
> +		 * image can be accessed with normal read mode.
> +		 */
> +		if (cs == 0 && decoded_sz != 0)
> +			priv->decoded_sz_arr[cs] = decoded_sz;
> +		else
> +			priv->decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> +	}
> +
> +	ret = aspeed_spi_decoded_range_config(bus, priv->decoded_sz_arr);
> +
> +	return ret;
> +}
> +
> +static const struct aspeed_spi_info ast2500_fmc_info = {
> +	.max_data_bus_width = 2,
> +	.cmd_io_ctrl_mask = 0x70ff40c3,
> +	.clk_ctrl_mask = 0x00002f00,
> +	.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,
> +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
> +	.get_clk_setting = ast2500_get_clk_setting,
> +};
> +
> +/*
> + * 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 = {
> +	.max_data_bus_width = 2,
> +	.cmd_io_ctrl_mask = 0x70ff40c3,
> +	.clk_ctrl_mask = 0x00002f00,
> +	.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,
> +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
> +	.get_clk_setting = ast2500_get_clk_setting,
> +};
> +
> +static const struct aspeed_spi_info ast2600_fmc_info = {
> +	.max_data_bus_width = 4,
> +	.cmd_io_ctrl_mask = 0xf0ff40c3,
> +	.clk_ctrl_mask = 0x0f000f00,
> +	.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,
> +	.adjust_decoded_sz = ast2600_adjust_decoded_size,
> +	.get_clk_setting = ast2600_get_clk_setting,
> +};
> +
> +static const struct aspeed_spi_info ast2600_spi_info = {
> +	.max_data_bus_width = 4,
> +	.cmd_io_ctrl_mask = 0xf0ff40c3,
> +	.clk_ctrl_mask = 0x0f000f00,
> +	.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,
> +	.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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
> +	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
> +	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;
> +}
> +
> +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 clk hclk;
> +	int ret;
> +
> +	plat->ctrl_base = devfdt_get_addr_index(bus, 0);
> +	if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
> +		dev_err(bus, "wrong AHB 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;
> +	}
> +
> +	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);
> +
> +	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%lx, ahb_base = 0x%p, size = 0x%lx\n",
> +		plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
> +	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
> +		plat->hclk_rate / 1000000, 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",
> +	.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,
> +};


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 04/12] configs: aspeed: Enable SPI flash features
  2022-05-24  5:56 ` [v4 04/12] configs: aspeed: Enable SPI flash features Chin-Ting Kuo
@ 2022-07-01  9:28   ` Cédric Le Goater
  2022-07-01 11:50     ` Cédric Le Goater
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01  9:28 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

On 5/24/22 07:56, Chin-Ting Kuo wrote:
> - 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 2371cc2742..fd04fe5a60 100644
> --- a/configs/evb-ast2500_defconfig
> +++ b/configs/evb-ast2500_defconfig
> @@ -35,6 +35,16 @@ CONFIG_DM_I2C=y
>   CONFIG_SYS_I2C_ASPEED=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_ETH=y
>   CONFIG_FTGMAC100=y
> @@ -44,6 +54,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=y
>   CONFIG_SYSRESET=y
>   CONFIG_TIMER=y
>   CONFIG_WDT=y

This is breaking boot on the AST2500 evb (QEMU) . I haven't found why
yet. You might want to take a look.

Thanks,

C.


> diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig
> index f84b723bbb..ccf63ec6d0 100644
> --- a/configs/evb-ast2600_defconfig
> +++ b/configs/evb-ast2600_defconfig
> @@ -65,6 +65,16 @@ CONFIG_DM_I2C=y
>   CONFIG_MISC=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_ETH=y
>   CONFIG_DM_MDIO=y
> @@ -77,6 +87,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=y
>   CONFIG_SYSRESET=y
>   CONFIG_SPL_SYSRESET=y
>   CONFIG_WDT=y


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 07/12] spi-mem: Add dirmap API from Linux
  2022-05-24  5:56 ` [v4 07/12] spi-mem: Add dirmap API from Linux Chin-Ting Kuo
@ 2022-07-01  9:36   ` Cédric Le Goater
  2022-07-03  8:49     ` Chin-Ting Kuo
  2022-07-01 12:04   ` Jagan Teki
  1 sibling, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01  9:36 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

On 5/24/22 07:56, Chin-Ting Kuo wrote:
> This adds the dirmap API originally introduced in Linux commit aa167f3
> ("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/


It has been sent long ago. Is there an issue with the backport from Linux ?
Is it the lack of drivers using it ?

Thanks,

C.

> 
> 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>
> ---
> v2: Remove "#if CONFIG_SPI_DIRMAP" compile wrapper.
> v3: Fix a grammatical error in spi-mem.h.
> 
>   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 a616294910..297253714a 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);


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 06/12] arm: dts: aspeed: Update SPI flash node settings
  2022-05-24  5:56 ` [v4 06/12] arm: dts: aspeed: Update SPI flash node settings Chin-Ting Kuo
@ 2022-07-01  9:42   ` Cédric Le Goater
  2022-07-03  8:54     ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01  9:42 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav, Joel Stanley

On 5/24/22 07:56, Chin-Ting Kuo wrote:
> 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>

I might be wrong for the comment I did on 'num-cs' in the patch adding
the driver. Joel, what's your opinion ? Hard coded in the driver or
a property in the DT ?

Thanks,

C.

> ---
>   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 4796ed445f..c6b7675902 100644
> --- a/arch/arm/dts/ast2500-evb.dts
> +++ b/arch/arm/dts/ast2500-evb.dts
> @@ -73,3 +73,36 @@
>   	pinctrl-names = "default";
>   	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>;
> +	};
> +};
> diff --git a/arch/arm/dts/ast2500.dtsi b/arch/arm/dts/ast2500.dtsi
> index ee66ef6704..d78a53aeb7 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 0d65054313..c1965e9093 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 64074309b7..dd36f55f20 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";
>   			};


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 11/12] mtd: spi-nor-ids: Add Winbond W25Q512JV ID
  2022-05-24  5:56 ` [v4 11/12] mtd: spi-nor-ids: Add Winbond W25Q512JV ID Chin-Ting Kuo
@ 2022-07-01  9:43   ` Cédric Le Goater
  0 siblings, 0 replies; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01  9:43 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

On 5/24/22 07:56, Chin-Ting Kuo wrote:
> Add ID for Winbond W25Q512JV 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>

Thanks,

C.

> ---
>   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 7050ddc397..444affb5af 100644
> --- a/drivers/mtd/spi/spi-nor-ids.c
> +++ b/drivers/mtd/spi/spi-nor-ids.c
> @@ -362,6 +362,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("w25q512jv", 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 |


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
  2022-05-24  5:56 ` [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set Chin-Ting Kuo
@ 2022-07-01  9:44   ` Cédric Le Goater
  2022-07-03  8:56     ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01  9:44 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

On 5/24/22 07:56, Chin-Ting Kuo wrote:
> When SPI_NOR_4B_OPCODES flag is set for a specific flash
> in spi_nor_ids[] table, EN4B command will not be issued and
> 4-byte dedicated commands will be used to access SPI flash.
> Thus, instead of EN4B command, address length is more suitable
> for deciding whether the controller should be set to 4-byte mode.
> 
> Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> ---
>   drivers/spi/spi-aspeed.c | 7 +++----
>   1 file changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/spi/spi-aspeed.c b/drivers/spi/spi-aspeed.c
> index e5e348eb7b..687ddc370b 100644
> --- a/drivers/spi/spi-aspeed.c
> +++ b/drivers/spi/spi-aspeed.c
> @@ -504,10 +504,6 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
>   	/* Restore controller setting. */
>   	writel(flash->ce_ctrl_read, ctrl_reg);
>   
> -	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
> -	if (op->cmd.opcode == SPINOR_OP_EN4B)
> -		priv->info->set_4byte(bus, cs);
> -
>   	return 0;
>   }


This should be part of the patch adding the driver.

Thanks,

C.


> @@ -561,6 +557,9 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
>   		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
>   		priv->flashes[cs].ce_ctrl_read = reg_val;
>   
> +		if (op_tmpl.addr.nbytes == 4)
> +			priv->info->set_4byte(bus, cs);
> +
>   		dev_dbg(dev, "read bus width: %d [0x%08x]\n",
>   			op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
>   	} else {


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 04/12] configs: aspeed: Enable SPI flash features
  2022-07-01  9:28   ` Cédric Le Goater
@ 2022-07-01 11:50     ` Cédric Le Goater
  2022-07-03  9:00       ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-01 11:50 UTC (permalink / raw)
  To: Chin-Ting Kuo, chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

On 7/1/22 11:28, Cédric Le Goater wrote:
> On 5/24/22 07:56, Chin-Ting Kuo wrote:
>> - 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 2371cc2742..fd04fe5a60 100644
>> --- a/configs/evb-ast2500_defconfig
>> +++ b/configs/evb-ast2500_defconfig
>> @@ -35,6 +35,16 @@ CONFIG_DM_I2C=y
>>   CONFIG_SYS_I2C_ASPEED=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_ETH=y
>>   CONFIG_FTGMAC100=y
>> @@ -44,6 +54,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=y
>>   CONFIG_SYSRESET=y
>>   CONFIG_TIMER=y
>>   CONFIG_WDT=y
> 
> This is breaking boot on the AST2500 evb (QEMU) . I haven't found why
> yet. You might want to take a look.

The resulting binary is too big for the flash layout we use on OpenBMC.

C.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file
  2022-05-24  5:56 ` [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file Chin-Ting Kuo
@ 2022-07-01 11:52   ` Jagan Teki
  2022-08-11  5:20     ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Jagan Teki @ 2022-07-01 11:52 UTC (permalink / raw)
  To: Chin-Ting Kuo
  Cc: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, vigneshr, clg,
	u-boot, p.yadav

On Tue, May 24, 2022 at 11:28 AM Chin-Ting Kuo
<chin-ting_kuo@aspeedtech.com> wrote:
>
> Add spi-aspeed.c file for ARM ASPEED.
>
> Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> ---
>  MAINTAINERS | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 56be0bfad0..f2cd707eda 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -688,6 +688,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.c

Squash this part of spi-aspeed.c driver patch

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 00/12] Add ASPEED SPI controller driver
  2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
                   ` (12 preceding siblings ...)
  2022-06-26  4:56 ` [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
@ 2022-07-01 11:57 ` Jagan Teki
  2022-08-11  5:25   ` Chin-Ting Kuo
  13 siblings, 1 reply; 41+ messages in thread
From: Jagan Teki @ 2022-07-01 11:57 UTC (permalink / raw)
  To: Chin-Ting Kuo
  Cc: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, vigneshr, clg,
	u-boot, p.yadav

On Tue, May 24, 2022 at 11:27 AM Chin-Ting Kuo
<chin-ting_kuo@aspeedtech.com> wrote:
>
> This patch series aims to porting ASPEED FMC/SPI memory controller
> driver with spi-mem interface. spi-mem dirmap framework is also
> synchronized from Linux. These patches have been verified on both
> AST2600 and AST2500 EVBs.
>
> Changes in v2:
>   - Separate defconfig files from the SPI driver patch.
>   - Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
>     spi_dirmap related functions.
>   - Add Winbond w25q512jv flash ID.
>
> Changes in v3:
>   - Get AHB bus clock frequency from the function parameter.
>   - Fix a grammatical error in spi-mem.h.
>
> Changes in v4:
>   - Fix bug when SPI_NOR_4B_OPCODES flag is set.
>
> Chin-Ting Kuo (12):
>   clk: aspeed: Get HCLK frequency support
>   pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
>   spi: aspeed: Add ASPEED SPI controller driver
>   configs: aspeed: Enable SPI flash features
>   MAINTAINERS: Add ASPEED SPI driver file
>   arm: dts: aspeed: Update SPI flash node settings
>   spi-mem: Add dirmap API from Linux
>   mtd: spi-nor: Use spi-mem dirmap API
>   spi: aspeed: SPI dirmap read support
>   configs: aspeed: Enable CONFIG_SPI_DIRMAP
>   mtd: spi-nor-ids: Add Winbond W25Q512JV ID
>   spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set

Sperate series for spi changes would really make it easier for review.
please send it.

Jagan.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 07/12] spi-mem: Add dirmap API from Linux
  2022-05-24  5:56 ` [v4 07/12] spi-mem: Add dirmap API from Linux Chin-Ting Kuo
  2022-07-01  9:36   ` Cédric Le Goater
@ 2022-07-01 12:04   ` Jagan Teki
  2022-08-11  5:19     ` Chin-Ting Kuo
  1 sibling, 1 reply; 41+ messages in thread
From: Jagan Teki @ 2022-07-01 12:04 UTC (permalink / raw)
  To: Chin-Ting Kuo
  Cc: chiawei_wang, lukma, seanga2, ryan_chen, BMC-SW, vigneshr, clg,
	u-boot, p.yadav

On Tue, May 24, 2022 at 11:28 AM Chin-Ting Kuo
<chin-ting_kuo@aspeedtech.com> wrote:
>
> This adds the dirmap API originally introduced in Linux commit aa167f3
> ("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/
>
> 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>
> ---
> v2: Remove "#if CONFIG_SPI_DIRMAP" compile wrapper.
> v3: Fix a grammatical error in spi-mem.h.
>
>  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 a616294910..297253714a 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

Look like the following code is not part of this if construct, we need
that to build only when SPI_DIRMAP is defined otherwise it footprint
increase for non-DIRMAPs. Also please take care of unnecessary code
while copying from Linux and add SHA1 in the commit message.

Jagan.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-07-01  9:28   ` Cédric Le Goater
@ 2022-07-03  8:47     ` Chin-Ting Kuo
  2022-07-04 15:24       ` Cédric Le Goater
  0 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-03  8:47 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav

Hi Cédric,

Thanks for the review.

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Friday, July 1, 2022 5:28 PM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>; ChiaWei Wang
> <chiawei_wang@aspeedtech.com>; lukma@denx.de; seanga2@gmail.com;
> Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com; vigneshr@ti.com;
> u-boot@lists.denx.de; p.yadav@ti.com
> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
> 
> Hello Chin-Ting,
> 
> On 5/24/22 07:56, Chin-Ting Kuo wrote:
> > 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.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.
> >
> > Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> 
> I would split the patch furthermore to ease reading.

Okay, this will be update in the next version.

>   1 - Add basic support
> 
>       with default decoding ranges set for all possible CS, even
>       without a device.
> 
>       WE only have USER mode for now. So it's not important to
>       correctly set the ranges since we won't use them before
>       direct mapping is introduced. They should not overlap,
>       that's all.
> 
>   2 - decoding range adjustments
> 
>       On that topic, we might want to take the simple DT approach
>       with a "ranges" property defining the mapping windows of each
>       CE. I think it is safer than trying to compute perfect ranges
>       like on Linux.
> 
>   3 - clock settings
> 
>       That should simply be the property defined in the DT
> 
> 
> > ---
> > v2: Remove defconfig files from this patch.
> >
> >   drivers/spi/Kconfig      |   8 +
> >   drivers/spi/Makefile     |   1 +
> >   drivers/spi/spi-aspeed.c | 822
> +++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 831 insertions(+)
> >   create mode 100644 drivers/spi/spi-aspeed.c
> >
> > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index
> > a1e515cb2b..a616294910 100644
> > --- a/drivers/spi/Kconfig
> > +++ b/drivers/spi/Kconfig
> > @@ -387,6 +387,14 @@ config SANDBOX_SPI
> >   		};
> >   	  };
> >
> > +config SPI_ASPEED
> > +	bool "ASPEED SPI controller driver"
> > +	depends on DM_SPI && SPI_MEM
> > +	default n
> > +	help
> > +	  Enable ASPEED SPI 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
> > 06e81b465b..36a4bd5dce 100644
> > --- a/drivers/spi/Makefile
> > +++ b/drivers/spi/Makefile
> > @@ -9,6 +9,7 @@ obj-y += spi-uclass.o
> >   obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
> >   obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
> >   obj-$(CONFIG_SOFT_SPI) += soft_spi.o
> > +obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c new file mode
> > 100644 index 0000000000..9574aff793
> > --- /dev/null
> > +++ b/drivers/spi/spi-aspeed.c
> > @@ -0,0 +1,822 @@
> > +// 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>
> > +
> > +/* ASPEED FMC/SPI memory control register related */
> > +#define REG_CE_TYPE_SETTING          0x00
> > +#define REG_CE_ADDR_MODE_CTRL        0x04
> > +#define REG_INTR_CTRL_STATUS         0x08
> > +#define REG_CE0_CTRL_REG             0x10
> > +#define REG_CE0_DECODED_ADDR_REG     0x30
> > +
> > +#define ASPEED_SPI_MAX_CS       3
> > +#define FLASH_CALIBRATION_LEN   0x400
> > +
> > +#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_plat {
> > +	fdt_addr_t ctrl_base;
> 
> are these the registers ?

No, "struct aspeed_spi_plat" is used to record some basic information of this platform.

> > +	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 */
> > +	u8 max_cs;
> 
> 
> I don't think we need a "max_cs" in the controller struct and a "num-cs"
> property in the DT. We could simply use a HW maxmimum defined in
> aspeed_spi_info.
> 

"num-cs" is used to detect the number of active flash node.
This property is mainly used to maintain the address decoded range.

"max-cs" is used for controlling register access.
We need to know the maximum CS number supported by the current controller.

> 
> > +};
> > +
> > +struct aspeed_spi_flash {
> > +	u8 cs;
> > +	void __iomem *ahb_base;
> > +	u32 ahb_win_sz;
> > +	u32 ce_ctrl_user;
> > +	u32 ce_ctrl_read;
> > +	u32 max_freq;
> > +	bool trimmed_decoded_sz;
> 
> I wonder what this is for. We need to split the patches :)

Oh, it is the redundant one and it will be removed in the next patch version.

> 
> > +};
> > +
> > +struct aspeed_spi_priv {
> > +	u32 num_cs;
> 
> See above.
> 
> > +	struct aspeed_spi_info *info;
> > +	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
> > +	u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
> > +};
> 
> 
> Couldn't we have a 'struct aspeed_spi_regs' defining the layout of the
> registers ?
> 

Why? The register offset has been defined by macro previously.

> > +struct aspeed_spi_info {
> > +	u32 cmd_io_ctrl_mask;
> > +	u32 clk_ctrl_mask;
> > +	u32 max_data_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);
> > +	int (*adjust_decoded_sz)(struct udevice *bus, u32 decoded_sz_arr[]);
> > +	u32 (*get_clk_setting)(struct udevice *dev, uint hz); };
> > +
> > +static int aspeed_spi_trim_decoded_size(struct udevice *bus,
> > +					u32 decoded_sz_arr[]);
> > +
> > +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:
> > +		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 + 0x100000; }
> > +
> > +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_plat *plat = dev_get_plat(bus);
> > +	u32 reg_val;
> > +
> > +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> > +	reg_val |= 0x1 << cs;
> > +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
> > +				       u32 decoded_sz_arr[])
> > +{
> > +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> > +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +	int ret;
> > +	int cs;
> > +
> > +	/* Assign min_decoded_sz to unused CS. */
> > +	for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
> > +		if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
> > +			decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> > +	}
> > +
> > +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> > +	if (ret != 0)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +/* Transfer maximum clock frequency to register setting */ 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 = 0x000; /* 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);
> > +	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_plat *plat = dev_get_plat(bus);
> > +	u32 reg_val;
> > +
> > +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> > +	reg_val |= 0x11 << cs;
> > +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL); }
> > +
> > +static int ast2600_adjust_decoded_size(struct udevice *bus,
> > +				       u32 decoded_sz_arr[])
> > +{
> > +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +	int ret;
> > +	int i;
> > +	int cs;
> > +	u32 pre_sz;
> > +	u32 lack_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 += decoded_sz_arr[i];
> > +
> > +		if (decoded_sz_arr[cs] != 0 && (pre_sz % decoded_sz_arr[cs]) != 0) {
> > +			lack_sz = decoded_sz_arr[cs] - (pre_sz % decoded_sz_arr[cs]);
> > +			decoded_sz_arr[0] += lack_sz;
> > +		}
> > +	}
> > +
> > +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> > +	if (ret != 0)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +/* Transfer maximum clock frequency to register setting */ 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 = 0x400; /* 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
> > + * 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,
> > +					u32 decoded_sz_arr[])
> > +{
> > +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> > +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +	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 += decoded_sz_arr[i];
> > +
> > +		if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
> > +			cs--;
> > +
> > +		if (cs < 0)
> > +			return -ENOMEM;
> > +
> > +		if (total_sz > plat->ahb_sz) {
> > +			decoded_sz_arr[cs] -= priv->info->min_decoded_sz;
> > +			total_sz -= priv->info->min_decoded_sz;
> > +			priv->flashes[cs].trimmed_decoded_sz = true;
> > +		}
> > +	} while (total_sz > plat->ahb_sz);
> > +
> > +	return 0;
> > +}
> > +
> > +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.buswidth > 1 || op->addr.nbytes > 4)
> > +		return false;
> > +
> > +	if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
> > +		return false;
> > +
> > +	if (op->data.buswidth > priv->info->max_data_bus_width)
> > +		return false;
> > +
> > +	if (!spi_mem_default_supports_op(slave, op))
> > +		return false;
> > +
> > +	return true;
> > +}
> 
> You could copy the Linux aspeed_spi_supports_op()
>

Okay, this patch series may be too old.

> > +
> > +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_plat *plat = dev_get_plat(bus);
> > +	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;
> > +	fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4;
> > +	struct aspeed_spi_flash *flash = &priv->flashes[cs];
> > +	u32 ctrl_val;
> > +	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);
> > +
> > +	/* Start user mode */
> > +	ctrl_val = flash->ce_ctrl_user;
> > +	writel(ctrl_val, ctrl_reg);
> > +	ctrl_val &= (~CTRL_STOP_ACTIVE);
> > +	writel(ctrl_val, 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;
> > +	}
> > +	aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
> 
> This could be writing 3 bytes. Not optimal.

Why? Doesn't it depend on the value of "op->addr.nbytes"? This function has been verified with different flash parts.

> 
> > +
> > +	/* Send dummy cycle */
> > +	aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data,
> > +op->dummy.nbytes);
> > +
> > +	/* Change io_mode */
> > +	ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
> > +	writel(ctrl_val, 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);
> > +	}
> > +
> > +	ctrl_val |= CTRL_STOP_ACTIVE;
> > +	writel(ctrl_val, ctrl_reg);
> > +
> > +	/* Restore controller setting. */
> > +	writel(flash->ce_ctrl_read, ctrl_reg);
> > +
> > +	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
> > +	if (op->cmd.opcode == SPINOR_OP_EN4B)
> > +		priv->info->set_4byte(bus, cs);
> 
> We don't need to set 4B earlier ? I trust you there.

Do you mean early in this function? It may be okay.

> 
> > +
> > +	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 int aspeed_spi_decoded_range_config(struct udevice *bus,
> > +					   u32 decoded_sz_arr[])
> > +{
> > +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> > +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +	int ret;
> > +	u32 cs;
> > +	u32 decoded_reg_val;
> > +	u32 start_addr;
> > +	u32 end_addr = 0;
> > +
> > +	ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
> > +	if (ret != 0)
> > +		return ret;
> > +
> > +	/* Configure each CS decoded range */
> > +	for (cs = 0; cs < plat->max_cs; cs++) {
> > +		if (cs == 0)
> > +			start_addr = (u32)plat->ahb_base;
> > +		else
> > +			start_addr = end_addr;
> > +		priv->flashes[cs].ahb_base = (void __iomem *)start_addr;
> > +		priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
> > +
> > +		end_addr = start_addr + decoded_sz_arr[cs];
> > +		decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
> > +
> > +		writel(decoded_reg_val,
> > +		       plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
> > +
> > +		dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
> > +			cs, decoded_reg_val, start_addr, end_addr);
> > +	}
> > +
> > +	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(plat->ctrl_base + REG_CE_TYPE_SETTING);
> > +	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
> > +	       plat->ctrl_base + REG_CE_TYPE_SETTING);
> > +
> > +	memset(priv->flashes, 0x0,
> > +	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
> > +
> > +	/* Initial each CS controller register */
> > +	for (cs = 0; cs < priv->num_cs; cs++) {
> > +		priv->flashes[cs].ce_ctrl_user &=
> > +				~(priv->info->cmd_io_ctrl_mask);
> > +		priv->flashes[cs].ce_ctrl_user |=
> > +				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
> > +		writel(priv->flashes[cs].ce_ctrl_user,
> > +		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
> > +	}
> >
> 
> and we should start by setting sane defaults for the ranges.
> 
> It's too early to add the decoding ranges calculation.

Okay.

> 
> Thanks,
> 
> C.
> 
> > +	memset(priv->decoded_sz_arr, 0x0, sizeof(u32) * ASPEED_SPI_MAX_CS);
> > +
> > +	for (cs = 0; cs < priv->num_cs; cs++) {
> > +		reg_val = readl(plat->ctrl_base + REG_CE0_DECODED_ADDR_REG +
> cs * 4);
> > +		decoded_sz = priv->info->segment_end(bus, reg_val) -
> > +			     priv->info->segment_start(bus, reg_val);
> > +
> > +		/*
> > +		 * For CS0, if the default address decoded area exists,
> > +		 * keep its value in order to make sure that the whole boot
> > +		 * image can be accessed with normal read mode.
> > +		 */
> > +		if (cs == 0 && decoded_sz != 0)
> > +			priv->decoded_sz_arr[cs] = decoded_sz;
> > +		else
> > +			priv->decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> > +	}
> > +
> > +	ret = aspeed_spi_decoded_range_config(bus, priv->decoded_sz_arr);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct aspeed_spi_info ast2500_fmc_info = {
> > +	.max_data_bus_width = 2,
> > +	.cmd_io_ctrl_mask = 0x70ff40c3,
> > +	.clk_ctrl_mask = 0x00002f00,
> > +	.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,
> > +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
> > +	.get_clk_setting = ast2500_get_clk_setting, };
> > +
> > +/*
> > + * 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 = {
> > +	.max_data_bus_width = 2,
> > +	.cmd_io_ctrl_mask = 0x70ff40c3,
> > +	.clk_ctrl_mask = 0x00002f00,
> > +	.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,
> > +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
> > +	.get_clk_setting = ast2500_get_clk_setting, };
> > +
> > +static const struct aspeed_spi_info ast2600_fmc_info = {
> > +	.max_data_bus_width = 4,
> > +	.cmd_io_ctrl_mask = 0xf0ff40c3,
> > +	.clk_ctrl_mask = 0x0f000f00,
> > +	.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,
> > +	.adjust_decoded_sz = ast2600_adjust_decoded_size,
> > +	.get_clk_setting = ast2600_get_clk_setting, };
> > +
> > +static const struct aspeed_spi_info ast2600_spi_info = {
> > +	.max_data_bus_width = 4,
> > +	.cmd_io_ctrl_mask = 0xf0ff40c3,
> > +	.clk_ctrl_mask = 0x0f000f00,
> > +	.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,
> > +	.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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
> > +	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
> > +	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;
> > +}
> > +
> > +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 clk hclk;
> > +	int ret;
> > +
> > +	plat->ctrl_base = devfdt_get_addr_index(bus, 0);
> > +	if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
> > +		dev_err(bus, "wrong AHB 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;
> > +	}
> > +
> > +	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);
> > +
> > +	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%lx, ahb_base = 0x%p, size = 0x%lx\n",
> > +		plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
> > +	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
> > +		plat->hclk_rate / 1000000, 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",
> > +	.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,
> > +};


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 07/12] spi-mem: Add dirmap API from Linux
  2022-07-01  9:36   ` Cédric Le Goater
@ 2022-07-03  8:49     ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-03  8:49 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Friday, July 1, 2022 5:37 PM
> Subject: Re: [v4 07/12] spi-mem: Add dirmap API from Linux
> 
> On 5/24/22 07:56, Chin-Ting Kuo wrote:
> > This adds the dirmap API originally introduced in Linux commit aa167f3
> > ("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/
> 
> 
> It has been sent long ago. Is there an issue with the backport from Linux ?

No.

> Is it the lack of drivers using it ?

Yes and thus, it is postponed at that time.

> 
> Thanks,
> 
> C.
> 
> >
> > 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>
> > ---
> > v2: Remove "#if CONFIG_SPI_DIRMAP" compile wrapper.
> > v3: Fix a grammatical error in spi-mem.h.
> >
> >   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
> > a616294910..297253714a 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);


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 06/12] arm: dts: aspeed: Update SPI flash node settings
  2022-07-01  9:42   ` Cédric Le Goater
@ 2022-07-03  8:54     ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-03  8:54 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav, Joel Stanley

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Friday, July 1, 2022 5:42 PM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>; ChiaWei Wang
> <chiawei_wang@aspeedtech.com>; lukma@denx.de; seanga2@gmail.com;
> Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com; vigneshr@ti.com;
> u-boot@lists.denx.de; p.yadav@ti.com; Joel Stanley <joel@jms.id.au>
> Subject: Re: [v4 06/12] arm: dts: aspeed: Update SPI flash node settings
> 
> On 5/24/22 07:56, Chin-Ting Kuo wrote:
> > 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>
> 
> I might be wrong for the comment I did on 'num-cs' in the patch adding the
> driver. Joel, what's your opinion ? Hard coded in the driver or a property in the
> DT ?
> 

Do you mean " num_cs" or "max-cs" here?
If "num_cs", I think that it should be detected during runtime, as the original implementation.
If "max-cs", I think that both hard coded or a property in the DT are okay.


Thanks.

Chin-Ting

> Thanks,
> 
> C.
> 
> > ---
> >   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 4796ed445f..c6b7675902 100644
> > --- a/arch/arm/dts/ast2500-evb.dts
> > +++ b/arch/arm/dts/ast2500-evb.dts
> > @@ -73,3 +73,36 @@
> >   	pinctrl-names = "default";
> >   	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>;
> > +	};
> > +};
> > diff --git a/arch/arm/dts/ast2500.dtsi b/arch/arm/dts/ast2500.dtsi
> > index ee66ef6704..d78a53aeb7 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 0d65054313..c1965e9093 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 64074309b7..dd36f55f20 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";
> >   			};


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
  2022-07-01  9:44   ` Cédric Le Goater
@ 2022-07-03  8:56     ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-03  8:56 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Friday, July 1, 2022 5:45 PM
> Subject: Re: [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag
> is set
> 
> On 5/24/22 07:56, Chin-Ting Kuo wrote:
> > When SPI_NOR_4B_OPCODES flag is set for a specific flash in
> > spi_nor_ids[] table, EN4B command will not be issued and 4-byte
> > dedicated commands will be used to access SPI flash.
> > Thus, instead of EN4B command, address length is more suitable for
> > deciding whether the controller should be set to 4-byte mode.
> >
> > Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> > ---
> >   drivers/spi/spi-aspeed.c | 7 +++----
> >   1 file changed, 3 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/spi/spi-aspeed.c b/drivers/spi/spi-aspeed.c index
> > e5e348eb7b..687ddc370b 100644
> > --- a/drivers/spi/spi-aspeed.c
> > +++ b/drivers/spi/spi-aspeed.c
> > @@ -504,10 +504,6 @@ static int aspeed_spi_exec_op_user_mode(struct
> spi_slave *slave,
> >   	/* Restore controller setting. */
> >   	writel(flash->ce_ctrl_read, ctrl_reg);
> >
> > -	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
> > -	if (op->cmd.opcode == SPINOR_OP_EN4B)
> > -		priv->info->set_4byte(bus, cs);
> > -
> >   	return 0;
> >   }
> 
> 
> This should be part of the patch adding the driver.
> 

Okay.

Thanks.

Chin-Ting

> Thanks,
> 
> C.
> 
> 
> > @@ -561,6 +557,9 @@ static int aspeed_spi_dirmap_create(struct
> spi_mem_dirmap_desc *desc)
> >   		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
> >   		priv->flashes[cs].ce_ctrl_read = reg_val;
> >
> > +		if (op_tmpl.addr.nbytes == 4)
> > +			priv->info->set_4byte(bus, cs);
> > +
> >   		dev_dbg(dev, "read bus width: %d [0x%08x]\n",
> >   			op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
> >   	} else {


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 04/12] configs: aspeed: Enable SPI flash features
  2022-07-01 11:50     ` Cédric Le Goater
@ 2022-07-03  9:00       ` Chin-Ting Kuo
  2022-07-04  8:01         ` Cédric Le Goater
  0 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-03  9:00 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Friday, July 1, 2022 7:51 PM
> Subject: Re: [v4 04/12] configs: aspeed: Enable SPI flash features
> 
> On 7/1/22 11:28, Cédric Le Goater wrote:
> > On 5/24/22 07:56, Chin-Ting Kuo wrote:
> >> - 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 2371cc2742..fd04fe5a60 100644
> >> --- a/configs/evb-ast2500_defconfig
> >> +++ b/configs/evb-ast2500_defconfig
> >> @@ -35,6 +35,16 @@ CONFIG_DM_I2C=y
> >>   CONFIG_SYS_I2C_ASPEED=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_ETH=y
> >>   CONFIG_FTGMAC100=y
> >> @@ -44,6 +54,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=y
> >>   CONFIG_SYSRESET=y
> >>   CONFIG_TIMER=y
> >>   CONFIG_WDT=y
> >
> > This is breaking boot on the AST2500 evb (QEMU) . I haven't found why
> > yet. You might want to take a look.
> 
> The resulting binary is too big for the flash layout we use on OpenBMC.

What’s the flash layout used by you? I tested this patch series on AST2500 EVB, and it was okay before.


Chin-Ting

> 
> C.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 04/12] configs: aspeed: Enable SPI flash features
  2022-07-03  9:00       ` Chin-Ting Kuo
@ 2022-07-04  8:01         ` Cédric Le Goater
  0 siblings, 0 replies; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-04  8:01 UTC (permalink / raw)
  To: Chin-Ting Kuo, ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav, Joel Stanley

[ Adding Joel ]

On 7/3/22 11:00, Chin-Ting Kuo wrote:
> Hi Cédric,
> 
>> -----Original Message-----
>> From: Cédric Le Goater <clg@kaod.org>
>> Sent: Friday, July 1, 2022 7:51 PM
>> Subject: Re: [v4 04/12] configs: aspeed: Enable SPI flash features
>>
>> On 7/1/22 11:28, Cédric Le Goater wrote:
>>> On 5/24/22 07:56, Chin-Ting Kuo wrote:
>>>> - 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 2371cc2742..fd04fe5a60 100644
>>>> --- a/configs/evb-ast2500_defconfig
>>>> +++ b/configs/evb-ast2500_defconfig
>>>> @@ -35,6 +35,16 @@ CONFIG_DM_I2C=y
>>>>    CONFIG_SYS_I2C_ASPEED=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_ETH=y
>>>>    CONFIG_FTGMAC100=y
>>>> @@ -44,6 +54,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=y
>>>>    CONFIG_SYSRESET=y
>>>>    CONFIG_TIMER=y
>>>>    CONFIG_WDT=y
>>>
>>> This is breaking boot on the AST2500 evb (QEMU) . I haven't found why
>>> yet. You might want to take a look.
>>
>> The resulting binary is too big for the flash layout we use on OpenBMC.
> 
> What’s the flash layout used by you? I tested this patch series on AST2500 EVB, and it was okay before.

This is the OpenBMC 32M flash layout  :

[    1.389291] Creating 5 MTD partitions on "bmc":
[    1.389710] 0x000000000000-0x000000060000 : "u-boot"
[    1.391281] 0x000000060000-0x000000080000 : "u-boot-env"
[    1.392391] 0x000000080000-0x0000004c0000 : "kernel"
[    1.393434] 0x0000004c0000-0x000001c00000 : "rofs"
[    1.394467] 0x000001c00000-0x000002000000 : "rwfs"

When using mainline u-boot, these extra configs are needed to boot
the OpenBMC image :
  
  CONFIG_FIT=y
  CONFIG_BOOTCOMMAND="bootm 20080000"

The SPI driver is adding quite a lot of .text so I removed MMC support to
keep uboot.bin below 384KB


Thanks,

C.




^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-07-03  8:47     ` Chin-Ting Kuo
@ 2022-07-04 15:24       ` Cédric Le Goater
  2022-07-06 11:06         ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-04 15:24 UTC (permalink / raw)
  To: Chin-Ting Kuo, ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW,
	jagan, vigneshr, u-boot, p.yadav

Hello Chin-Ting,

On 7/3/22 10:47, Chin-Ting Kuo wrote:
> Hi Cédric,
> 
> Thanks for the review.
> 
>> -----Original Message-----
>> From: Cédric Le Goater <clg@kaod.org>
>> Sent: Friday, July 1, 2022 5:28 PM
>> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>; ChiaWei Wang
>> <chiawei_wang@aspeedtech.com>; lukma@denx.de; seanga2@gmail.com;
>> Ryan Chen <ryan_chen@aspeedtech.com>; BMC-SW
>> <BMC-SW@aspeedtech.com>; jagan@amarulasolutions.com; vigneshr@ti.com;
>> u-boot@lists.denx.de; p.yadav@ti.com
>> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
>>
>> Hello Chin-Ting,
>>
>> On 5/24/22 07:56, Chin-Ting Kuo wrote:
>>> 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.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.
>>>
>>> Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
>>
>> I would split the patch furthermore to ease reading.
> 
> Okay, this will be update in the next version.
> 
>>    1 - Add basic support
>>
>>        with default decoding ranges set for all possible CS, even
>>        without a device.
>>
>>        WE only have USER mode for now. So it's not important to
>>        correctly set the ranges since we won't use them before
>>        direct mapping is introduced. They should not overlap,
>>        that's all.
>>
>>    2 - decoding range adjustments
>>
>>        On that topic, we might want to take the simple DT approach
>>        with a "ranges" property defining the mapping windows of each
>>        CE. I think it is safer than trying to compute perfect ranges
>>        like on Linux.
>>
>>    3 - clock settings
>>
>>        That should simply be the property defined in the DT
>>
>>
>>> ---
>>> v2: Remove defconfig files from this patch.
>>>
>>>    drivers/spi/Kconfig      |   8 +
>>>    drivers/spi/Makefile     |   1 +
>>>    drivers/spi/spi-aspeed.c | 822
>> +++++++++++++++++++++++++++++++++++++++
>>>    3 files changed, 831 insertions(+)
>>>    create mode 100644 drivers/spi/spi-aspeed.c
>>>
>>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index
>>> a1e515cb2b..a616294910 100644
>>> --- a/drivers/spi/Kconfig
>>> +++ b/drivers/spi/Kconfig
>>> @@ -387,6 +387,14 @@ config SANDBOX_SPI
>>>    		};
>>>    	  };
>>>
>>> +config SPI_ASPEED
>>> +	bool "ASPEED SPI controller driver"
>>> +	depends on DM_SPI && SPI_MEM
>>> +	default n
>>> +	help
>>> +	  Enable ASPEED SPI 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
>>> 06e81b465b..36a4bd5dce 100644
>>> --- a/drivers/spi/Makefile
>>> +++ b/drivers/spi/Makefile
>>> @@ -9,6 +9,7 @@ obj-y += spi-uclass.o
>>>    obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
>>>    obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
>>>    obj-$(CONFIG_SOFT_SPI) += soft_spi.o
>>> +obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c new file mode
>>> 100644 index 0000000000..9574aff793
>>> --- /dev/null
>>> +++ b/drivers/spi/spi-aspeed.c
>>> @@ -0,0 +1,822 @@
>>> +// 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>
>>> +
>>> +/* ASPEED FMC/SPI memory control register related */
>>> +#define REG_CE_TYPE_SETTING          0x00
>>> +#define REG_CE_ADDR_MODE_CTRL        0x04
>>> +#define REG_INTR_CTRL_STATUS         0x08
>>> +#define REG_CE0_CTRL_REG             0x10
>>> +#define REG_CE0_DECODED_ADDR_REG     0x30
>>> +
>>> +#define ASPEED_SPI_MAX_CS       3
>>> +#define FLASH_CALIBRATION_LEN   0x400
>>> +
>>> +#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_plat {
>>> +	fdt_addr_t ctrl_base;
>>
>> are these the registers ?
> 
> No, "struct aspeed_spi_plat" is used to record some basic information of this platform.
> 
>>> +	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 */
>>> +	u8 max_cs;
>>
>>
>> I don't think we need a "max_cs" in the controller struct and a "num-cs"
>> property in the DT. We could simply use a HW maxmimum defined in
>> aspeed_spi_info.
>>
> 
> "num-cs" is used to detect the number of active flash node.
> This property is mainly used to maintain the address decoded range.

I am not sure we need "num-cs" since we should configure all decoding
range registers to avoid overlaps. At least on some of the aST2500
controllers.

Does the AST2600 FMC and SPI1/2 allow to configure a single CE to cover
the whole mapping window of the controller while the other CE windows
are closed ?
  
> "max-cs" is used for controlling register access.
> We need to know the maximum CS number supported by the current controller.

Yes. That's fine. It is an HW limit per controller and we need to keep
this property in the DT or in the driver.

I have been defining this HW property in the driver. That might have
been a wrong choice. Something to fix in Linux may be.

>>
>>> +};
>>> +
>>> +struct aspeed_spi_flash {
>>> +	u8 cs;
>>> +	void __iomem *ahb_base;
>>> +	u32 ahb_win_sz;
>>> +	u32 ce_ctrl_user;
>>> +	u32 ce_ctrl_read;
>>> +	u32 max_freq;
>>> +	bool trimmed_decoded_sz;
>>
>> I wonder what this is for. We need to split the patches :)
> 
> Oh, it is the redundant one and it will be removed in the next patch version.
> 
>>
>>> +};
>>> +
>>> +struct aspeed_spi_priv {
>>> +	u32 num_cs;
>>
>> See above.
>>
>>> +	struct aspeed_spi_info *info;
>>> +	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
>>> +	u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
>>> +};
>>
>>
>> Couldn't we have a 'struct aspeed_spi_regs' defining the layout of the
>> registers ?
>>
> 
> Why? The register offset has been defined by macro previously.

Not always. Look at the *_regs struct in the different drivers and :

https://github.com/legoater/u-boot/blob/aspeed-v2022.04/arch/arm/include/asm/arch-aspeed/spi.h#L72

I am not sure what is the preferred practice in U-Boot. But a few
Aspeed drivers have been folowing this scheme.

> 
>>> +struct aspeed_spi_info {
>>> +	u32 cmd_io_ctrl_mask;
>>> +	u32 clk_ctrl_mask;
>>> +	u32 max_data_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);
>>> +	int (*adjust_decoded_sz)(struct udevice *bus, u32 decoded_sz_arr[]);
>>> +	u32 (*get_clk_setting)(struct udevice *dev, uint hz); };
>>> +
>>> +static int aspeed_spi_trim_decoded_size(struct udevice *bus,
>>> +					u32 decoded_sz_arr[]);
>>> +
>>> +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:
>>> +		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 + 0x100000; }
>>> +
>>> +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_plat *plat = dev_get_plat(bus);
>>> +	u32 reg_val;
>>> +
>>> +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
>>> +	reg_val |= 0x1 << cs;
>>> +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
>>> +				       u32 decoded_sz_arr[])
>>> +{
>>> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
>>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
>>> +	int ret;
>>> +	int cs;
>>> +
>>> +	/* Assign min_decoded_sz to unused CS. */
>>> +	for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
>>> +		if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
>>> +			decoded_sz_arr[cs] = priv->info->min_decoded_sz;
>>> +	}
>>> +
>>> +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
>>> +	if (ret != 0)
>>> +		return ret;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/* Transfer maximum clock frequency to register setting */ 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 = 0x000; /* 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);
>>> +	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_plat *plat = dev_get_plat(bus);
>>> +	u32 reg_val;
>>> +
>>> +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
>>> +	reg_val |= 0x11 << cs;
>>> +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL); }
>>> +
>>> +static int ast2600_adjust_decoded_size(struct udevice *bus,
>>> +				       u32 decoded_sz_arr[])
>>> +{
>>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
>>> +	int ret;
>>> +	int i;
>>> +	int cs;
>>> +	u32 pre_sz;
>>> +	u32 lack_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 += decoded_sz_arr[i];
>>> +
>>> +		if (decoded_sz_arr[cs] != 0 && (pre_sz % decoded_sz_arr[cs]) != 0) {
>>> +			lack_sz = decoded_sz_arr[cs] - (pre_sz % decoded_sz_arr[cs]);
>>> +			decoded_sz_arr[0] += lack_sz;
>>> +		}
>>> +	}
>>> +
>>> +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
>>> +	if (ret != 0)
>>> +		return ret;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/* Transfer maximum clock frequency to register setting */ 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 = 0x400; /* 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
>>> + * 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,
>>> +					u32 decoded_sz_arr[])
>>> +{
>>> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
>>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
>>> +	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 += decoded_sz_arr[i];
>>> +
>>> +		if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
>>> +			cs--;
>>> +
>>> +		if (cs < 0)
>>> +			return -ENOMEM;
>>> +
>>> +		if (total_sz > plat->ahb_sz) {
>>> +			decoded_sz_arr[cs] -= priv->info->min_decoded_sz;
>>> +			total_sz -= priv->info->min_decoded_sz;
>>> +			priv->flashes[cs].trimmed_decoded_sz = true;
>>> +		}
>>> +	} while (total_sz > plat->ahb_sz);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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.buswidth > 1 || op->addr.nbytes > 4)
>>> +		return false;
>>> +
>>> +	if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
>>> +		return false;
>>> +
>>> +	if (op->data.buswidth > priv->info->max_data_bus_width)
>>> +		return false;
>>> +
>>> +	if (!spi_mem_default_supports_op(slave, op))
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>
>> You could copy the Linux aspeed_spi_supports_op()
>>
> 
> Okay, this patch series may be too old.
> 
>>> +
>>> +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_plat *plat = dev_get_plat(bus);
>>> +	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;
>>> +	fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4;
>>> +	struct aspeed_spi_flash *flash = &priv->flashes[cs];
>>> +	u32 ctrl_val;
>>> +	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);
>>> +
>>> +	/* Start user mode */
>>> +	ctrl_val = flash->ce_ctrl_user;
>>> +	writel(ctrl_val, ctrl_reg);
>>> +	ctrl_val &= (~CTRL_STOP_ACTIVE);
>>> +	writel(ctrl_val, 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;
>>> +	}
>>> +	aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
>>
>> This could be writing 3 bytes. Not optimal.
> 
> Why? Doesn't it depend on the value of "op->addr.nbytes"? This function has been verified with different flash parts.

It works but you could issue a single 32-bit transaction if the OP was
included in the write, as Linux does. This is an optimisation to avoid
4 writes of one byte. This is minor.

>>
>>> +
>>> +	/* Send dummy cycle */
>>> +	aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data,
>>> +op->dummy.nbytes);
>>> +
>>> +	/* Change io_mode */
>>> +	ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
>>> +	writel(ctrl_val, 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);
>>> +	}
>>> +
>>> +	ctrl_val |= CTRL_STOP_ACTIVE;
>>> +	writel(ctrl_val, ctrl_reg);
>>> +
>>> +	/* Restore controller setting. */
>>> +	writel(flash->ce_ctrl_read, ctrl_reg);
>>> +
>>> +	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
>>> +	if (op->cmd.opcode == SPINOR_OP_EN4B)
>>> +		priv->info->set_4byte(bus, cs);
>>
>> We don't need to set 4B earlier ? I trust you there.
> 
> Do you mean early in this function? It may be okay.
> 
>>
>>> +
>>> +	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 int aspeed_spi_decoded_range_config(struct udevice *bus,
>>> +					   u32 decoded_sz_arr[])
>>> +{
>>> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
>>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
>>> +	int ret;
>>> +	u32 cs;
>>> +	u32 decoded_reg_val;
>>> +	u32 start_addr;
>>> +	u32 end_addr = 0;
>>> +
>>> +	ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
>>> +	if (ret != 0)
>>> +		return ret;
>>> +
>>> +	/* Configure each CS decoded range */
>>> +	for (cs = 0; cs < plat->max_cs; cs++) {
>>> +		if (cs == 0)
>>> +			start_addr = (u32)plat->ahb_base;
>>> +		else
>>> +			start_addr = end_addr;
>>> +		priv->flashes[cs].ahb_base = (void __iomem *)start_addr;
>>> +		priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
>>> +
>>> +		end_addr = start_addr + decoded_sz_arr[cs];
>>> +		decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
>>> +
>>> +		writel(decoded_reg_val,
>>> +		       plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
>>> +
>>> +		dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
>>> +			cs, decoded_reg_val, start_addr, end_addr);
>>> +	}
>>> +
>>> +	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(plat->ctrl_base + REG_CE_TYPE_SETTING);
>>> +	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
>>> +	       plat->ctrl_base + REG_CE_TYPE_SETTING);
>>> +
>>> +	memset(priv->flashes, 0x0,
>>> +	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
>>> +
>>> +	/* Initial each CS controller register */
>>> +	for (cs = 0; cs < priv->num_cs; cs++) {
>>> +		priv->flashes[cs].ce_ctrl_user &=
>>> +				~(priv->info->cmd_io_ctrl_mask);
>>> +		priv->flashes[cs].ce_ctrl_user |=
>>> +				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
>>> +		writel(priv->flashes[cs].ce_ctrl_user,
>>> +		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
>>> +	}
>>>
>>
>> and we should start by setting sane defaults for the ranges.
>>
>> It's too early to add the decoding ranges calculation.
> 
> Okay.
> 
>>
>> Thanks,
>>
>> C.
>>
>>> +	memset(priv->decoded_sz_arr, 0x0, sizeof(u32) * ASPEED_SPI_MAX_CS);
>>> +
>>> +	for (cs = 0; cs < priv->num_cs; cs++) {
>>> +		reg_val = readl(plat->ctrl_base + REG_CE0_DECODED_ADDR_REG +
>> cs * 4);
>>> +		decoded_sz = priv->info->segment_end(bus, reg_val) -
>>> +			     priv->info->segment_start(bus, reg_val);
>>> +
>>> +		/*
>>> +		 * For CS0, if the default address decoded area exists,
>>> +		 * keep its value in order to make sure that the whole boot
>>> +		 * image can be accessed with normal read mode.
>>> +		 */
>>> +		if (cs == 0 && decoded_sz != 0)
>>> +			priv->decoded_sz_arr[cs] = decoded_sz;
>>> +		else
>>> +			priv->decoded_sz_arr[cs] = priv->info->min_decoded_sz;
>>> +	}
>>> +
>>> +	ret = aspeed_spi_decoded_range_config(bus, priv->decoded_sz_arr);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct aspeed_spi_info ast2500_fmc_info = {
>>> +	.max_data_bus_width = 2,
>>> +	.cmd_io_ctrl_mask = 0x70ff40c3,
>>> +	.clk_ctrl_mask = 0x00002f00,
>>> +	.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,
>>> +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
>>> +	.get_clk_setting = ast2500_get_clk_setting, };
>>> +
>>> +/*
>>> + * 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 = {
>>> +	.max_data_bus_width = 2,
>>> +	.cmd_io_ctrl_mask = 0x70ff40c3,
>>> +	.clk_ctrl_mask = 0x00002f00,
>>> +	.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,
>>> +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
>>> +	.get_clk_setting = ast2500_get_clk_setting, };
>>> +
>>> +static const struct aspeed_spi_info ast2600_fmc_info = {
>>> +	.max_data_bus_width = 4,
>>> +	.cmd_io_ctrl_mask = 0xf0ff40c3,
>>> +	.clk_ctrl_mask = 0x0f000f00,
>>> +	.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,
>>> +	.adjust_decoded_sz = ast2600_adjust_decoded_size,
>>> +	.get_clk_setting = ast2600_get_clk_setting, };
>>> +
>>> +static const struct aspeed_spi_info ast2600_spi_info = {
>>> +	.max_data_bus_width = 4,
>>> +	.cmd_io_ctrl_mask = 0xf0ff40c3,
>>> +	.clk_ctrl_mask = 0x0f000f00,
>>> +	.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,
>>> +	.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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
>>> +	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
>>> +	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;
>>> +}
>>> +
>>> +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 clk hclk;
>>> +	int ret;
>>> +
>>> +	plat->ctrl_base = devfdt_get_addr_index(bus, 0);
>>> +	if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
>>> +		dev_err(bus, "wrong AHB 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;
>>> +	}
>>> +
>>> +	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);
>>> +
>>> +	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%lx, ahb_base = 0x%p, size = 0x%lx\n",
>>> +		plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
>>> +	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
>>> +		plat->hclk_rate / 1000000, 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",
>>> +	.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,
>>> +};
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-07-04 15:24       ` Cédric Le Goater
@ 2022-07-06 11:06         ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-06 11:06 UTC (permalink / raw)
  To: Cédric Le Goater, ChiaWei Wang, lukma, seanga2, Ryan Chen,
	BMC-SW, jagan, vigneshr, u-boot, p.yadav

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Monday, July 4, 2022 11:25 PM
> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
> 
> Hello Chin-Ting,
> 
> On 7/3/22 10:47, Chin-Ting Kuo wrote:
> > Hi Cédric,
> >
> > Thanks for the review.
> >
> >> -----Original Message-----
> >> From: Cédric Le Goater <clg@kaod.org>
> >> Sent: Friday, July 1, 2022 5:28 PM
> >> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>; ChiaWei Wang
> >> <chiawei_wang@aspeedtech.com>; lukma@denx.de; seanga2@gmail.com;
> Ryan
> >> Chen <ryan_chen@aspeedtech.com>; BMC-SW
> <BMC-SW@aspeedtech.com>;
> >> jagan@amarulasolutions.com; vigneshr@ti.com; u-boot@lists.denx.de;
> >> p.yadav@ti.com
> >> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
> >>
> >> Hello Chin-Ting,
> >>
> >> On 5/24/22 07:56, Chin-Ting Kuo wrote:
> >>> 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.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.
> >>>
> >>> Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> >>
> >> I would split the patch furthermore to ease reading.
> >
> > Okay, this will be update in the next version.
> >
> >>    1 - Add basic support
> >>
> >>        with default decoding ranges set for all possible CS, even
> >>        without a device.
> >>
> >>        WE only have USER mode for now. So it's not important to
> >>        correctly set the ranges since we won't use them before
> >>        direct mapping is introduced. They should not overlap,
> >>        that's all.
> >>
> >>    2 - decoding range adjustments
> >>
> >>        On that topic, we might want to take the simple DT approach
> >>        with a "ranges" property defining the mapping windows of each
> >>        CE. I think it is safer than trying to compute perfect ranges
> >>        like on Linux.
> >>
> >>    3 - clock settings
> >>
> >>        That should simply be the property defined in the DT
> >>
> >>
> >>> ---
> >>> v2: Remove defconfig files from this patch.
> >>>
> >>>    drivers/spi/Kconfig      |   8 +
> >>>    drivers/spi/Makefile     |   1 +
> >>>    drivers/spi/spi-aspeed.c | 822
> >> +++++++++++++++++++++++++++++++++++++++
> >>>    3 files changed, 831 insertions(+)
> >>>    create mode 100644 drivers/spi/spi-aspeed.c
> >>>
> >>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index
> >>> a1e515cb2b..a616294910 100644
> >>> --- a/drivers/spi/Kconfig
> >>> +++ b/drivers/spi/Kconfig
> >>> @@ -387,6 +387,14 @@ config SANDBOX_SPI
> >>>    		};
> >>>    	  };
> >>>
> >>> +config SPI_ASPEED
> >>> +	bool "ASPEED SPI controller driver"
> >>> +	depends on DM_SPI && SPI_MEM
> >>> +	default n
> >>> +	help
> >>> +	  Enable ASPEED SPI 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
> >>> 06e81b465b..36a4bd5dce 100644
> >>> --- a/drivers/spi/Makefile
> >>> +++ b/drivers/spi/Makefile
> >>> @@ -9,6 +9,7 @@ obj-y += spi-uclass.o
> >>>    obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o
> cadence_qspi_apb.o
> >>>    obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
> >>>    obj-$(CONFIG_SOFT_SPI) += soft_spi.o
> >>> +obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c new file mode
> >>> 100644 index 0000000000..9574aff793
> >>> --- /dev/null
> >>> +++ b/drivers/spi/spi-aspeed.c
> >>> @@ -0,0 +1,822 @@
> >>> +// 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>
> >>> +
> >>> +/* ASPEED FMC/SPI memory control register related */
> >>> +#define REG_CE_TYPE_SETTING          0x00
> >>> +#define REG_CE_ADDR_MODE_CTRL        0x04
> >>> +#define REG_INTR_CTRL_STATUS         0x08
> >>> +#define REG_CE0_CTRL_REG             0x10
> >>> +#define REG_CE0_DECODED_ADDR_REG     0x30
> >>> +
> >>> +#define ASPEED_SPI_MAX_CS       3
> >>> +#define FLASH_CALIBRATION_LEN   0x400
> >>> +
> >>> +#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_plat {
> >>> +	fdt_addr_t ctrl_base;
> >>
> >> are these the registers ?
> >
> > No, "struct aspeed_spi_plat" is used to record some basic information of this
> platform.
> >
> >>> +	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 */
> >>> +	u8 max_cs;
> >>
> >>
> >> I don't think we need a "max_cs" in the controller struct and a "num-cs"
> >> property in the DT. We could simply use a HW maxmimum defined in
> >> aspeed_spi_info.
> >>
> >
> > "num-cs" is used to detect the number of active flash node.
> > This property is mainly used to maintain the address decoded range.
> 
> I am not sure we need "num-cs" since we should configure all decoding range
> registers to avoid overlaps. At least on some of the aST2500 controllers.
> 
> Does the AST2600 FMC and SPI1/2 allow to configure a single CE to cover the
> whole mapping window of the controller while the other CE windows are
> closed ?

Yes, AST2600 allows a single CE to occupy the whole decoded windows while
the other CEs are closed.

> 
> > "max-cs" is used for controlling register access.
> > We need to know the maximum CS number supported by the current
> controller.
> 
> Yes. That's fine. It is an HW limit per controller and we need to keep this
> property in the DT or in the driver.
> 

Okay.

> I have been defining this HW property in the driver. That might have been a
> wrong choice. Something to fix in Linux may be.
> 
> >>
> >>> +};
> >>> +
> >>> +struct aspeed_spi_flash {
> >>> +	u8 cs;
> >>> +	void __iomem *ahb_base;
> >>> +	u32 ahb_win_sz;
> >>> +	u32 ce_ctrl_user;
> >>> +	u32 ce_ctrl_read;
> >>> +	u32 max_freq;
> >>> +	bool trimmed_decoded_sz;
> >>
> >> I wonder what this is for. We need to split the patches :)
> >
> > Oh, it is the redundant one and it will be removed in the next patch version.
> >
> >>
> >>> +};
> >>> +
> >>> +struct aspeed_spi_priv {
> >>> +	u32 num_cs;
> >>
> >> See above.
> >>
> >>> +	struct aspeed_spi_info *info;
> >>> +	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
> >>> +	u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
> >>> +};
> >>
> >>
> >> Couldn't we have a 'struct aspeed_spi_regs' defining the layout of
> >> the registers ?
> >>
> >
> > Why? The register offset has been defined by macro previously.
> 
> Not always. Look at the *_regs struct in the different drivers and :
> 
> https://github.com/legoater/u-boot/blob/aspeed-v2022.04/arch/arm/include/a
> sm/arch-aspeed/spi.h#L72
> 
> I am not sure what is the preferred practice in U-Boot. But a few Aspeed drivers
> have been folowing this scheme.
> 

Okay, but we need to take some time to modify the whole driver.

> >
> >>> +struct aspeed_spi_info {
> >>> +	u32 cmd_io_ctrl_mask;
> >>> +	u32 clk_ctrl_mask;
> >>> +	u32 max_data_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);
> >>> +	int (*adjust_decoded_sz)(struct udevice *bus, u32 decoded_sz_arr[]);
> >>> +	u32 (*get_clk_setting)(struct udevice *dev, uint hz); };
> >>> +
> >>> +static int aspeed_spi_trim_decoded_size(struct udevice *bus,
> >>> +					u32 decoded_sz_arr[]);
> >>> +
> >>> +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:
> >>> +		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 + 0x100000; }
> >>> +
> >>> +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_plat *plat = dev_get_plat(bus);
> >>> +	u32 reg_val;
> >>> +
> >>> +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> >>> +	reg_val |= 0x1 << cs;
> >>> +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
> >>> +				       u32 decoded_sz_arr[])
> >>> +{
> >>> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> >>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> >>> +	int ret;
> >>> +	int cs;
> >>> +
> >>> +	/* Assign min_decoded_sz to unused CS. */
> >>> +	for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
> >>> +		if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
> >>> +			decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> >>> +	}
> >>> +
> >>> +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> >>> +	if (ret != 0)
> >>> +		return ret;
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +/* Transfer maximum clock frequency to register setting */ 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 = 0x000; /* 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);
> >>> +	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_plat *plat = dev_get_plat(bus);
> >>> +	u32 reg_val;
> >>> +
> >>> +	reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> >>> +	reg_val |= 0x11 << cs;
> >>> +	writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL); }
> >>> +
> >>> +static int ast2600_adjust_decoded_size(struct udevice *bus,
> >>> +				       u32 decoded_sz_arr[])
> >>> +{
> >>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> >>> +	int ret;
> >>> +	int i;
> >>> +	int cs;
> >>> +	u32 pre_sz;
> >>> +	u32 lack_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 += decoded_sz_arr[i];
> >>> +
> >>> +		if (decoded_sz_arr[cs] != 0 && (pre_sz % decoded_sz_arr[cs]) !=
> 0) {
> >>> +			lack_sz = decoded_sz_arr[cs] - (pre_sz %
> decoded_sz_arr[cs]);
> >>> +			decoded_sz_arr[0] += lack_sz;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> >>> +	if (ret != 0)
> >>> +		return ret;
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +/* Transfer maximum clock frequency to register setting */ 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 = 0x400; /* 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
> >>> + * 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,
> >>> +					u32 decoded_sz_arr[])
> >>> +{
> >>> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> >>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> >>> +	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 += decoded_sz_arr[i];
> >>> +
> >>> +		if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
> >>> +			cs--;
> >>> +
> >>> +		if (cs < 0)
> >>> +			return -ENOMEM;
> >>> +
> >>> +		if (total_sz > plat->ahb_sz) {
> >>> +			decoded_sz_arr[cs] -= priv->info->min_decoded_sz;
> >>> +			total_sz -= priv->info->min_decoded_sz;
> >>> +			priv->flashes[cs].trimmed_decoded_sz = true;
> >>> +		}
> >>> +	} while (total_sz > plat->ahb_sz);
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +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.buswidth > 1 || op->addr.nbytes > 4)
> >>> +		return false;
> >>> +
> >>> +	if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
> >>> +		return false;
> >>> +
> >>> +	if (op->data.buswidth > priv->info->max_data_bus_width)
> >>> +		return false;
> >>> +
> >>> +	if (!spi_mem_default_supports_op(slave, op))
> >>> +		return false;
> >>> +
> >>> +	return true;
> >>> +}
> >>
> >> You could copy the Linux aspeed_spi_supports_op()
> >>
> >
> > Okay, this patch series may be too old.
> >
> >>> +
> >>> +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_plat *plat = dev_get_plat(bus);
> >>> +	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;
> >>> +	fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4;
> >>> +	struct aspeed_spi_flash *flash = &priv->flashes[cs];
> >>> +	u32 ctrl_val;
> >>> +	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);
> >>> +
> >>> +	/* Start user mode */
> >>> +	ctrl_val = flash->ce_ctrl_user;
> >>> +	writel(ctrl_val, ctrl_reg);
> >>> +	ctrl_val &= (~CTRL_STOP_ACTIVE);
> >>> +	writel(ctrl_val, 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;
> >>> +	}
> >>> +	aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
> >>
> >> This could be writing 3 bytes. Not optimal.
> >
> > Why? Doesn't it depend on the value of "op->addr.nbytes"? This function has
> been verified with different flash parts.
> 
> It works but you could issue a single 32-bit transaction if the OP was included
> in the write, as Linux does. This is an optimisation to avoid
> 4 writes of one byte. This is minor.
> 

"aspeed_spi_write_to_ahb" function here is almost the same as the one in Linux.
It can write a 32-bit transaction once if needed.

> >>
> >>> +
> >>> +	/* Send dummy cycle */
> >>> +	aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data,
> >>> +op->dummy.nbytes);
> >>> +
> >>> +	/* Change io_mode */
> >>> +	ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
> >>> +	writel(ctrl_val, 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);
> >>> +	}
> >>> +
> >>> +	ctrl_val |= CTRL_STOP_ACTIVE;
> >>> +	writel(ctrl_val, ctrl_reg);
> >>> +
> >>> +	/* Restore controller setting. */
> >>> +	writel(flash->ce_ctrl_read, ctrl_reg);
> >>> +
> >>> +	/* Set controller to 4-byte mode when flash is in 4-byte mode. */
> >>> +	if (op->cmd.opcode == SPINOR_OP_EN4B)
> >>> +		priv->info->set_4byte(bus, cs);
> >>
> >> We don't need to set 4B earlier ? I trust you there.
> >
> > Do you mean early in this function? It may be okay.
> >
> >>
> >>> +
> >>> +	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 int aspeed_spi_decoded_range_config(struct udevice *bus,
> >>> +					   u32 decoded_sz_arr[])
> >>> +{
> >>> +	struct aspeed_spi_plat *plat = dev_get_plat(bus);
> >>> +	struct aspeed_spi_priv *priv = dev_get_priv(bus);
> >>> +	int ret;
> >>> +	u32 cs;
> >>> +	u32 decoded_reg_val;
> >>> +	u32 start_addr;
> >>> +	u32 end_addr = 0;
> >>> +
> >>> +	ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
> >>> +	if (ret != 0)
> >>> +		return ret;
> >>> +
> >>> +	/* Configure each CS decoded range */
> >>> +	for (cs = 0; cs < plat->max_cs; cs++) {
> >>> +		if (cs == 0)
> >>> +			start_addr = (u32)plat->ahb_base;
> >>> +		else
> >>> +			start_addr = end_addr;
> >>> +		priv->flashes[cs].ahb_base = (void __iomem *)start_addr;
> >>> +		priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
> >>> +
> >>> +		end_addr = start_addr + decoded_sz_arr[cs];
> >>> +		decoded_reg_val = priv->info->segment_reg(start_addr,
> end_addr);
> >>> +
> >>> +		writel(decoded_reg_val,
> >>> +		       plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs
> * 4);
> >>> +
> >>> +		dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end:
> 0x%x\n",
> >>> +			cs, decoded_reg_val, start_addr, end_addr);
> >>> +	}
> >>> +
> >>> +	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(plat->ctrl_base + REG_CE_TYPE_SETTING);
> >>> +	writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
> >>> +	       plat->ctrl_base + REG_CE_TYPE_SETTING);
> >>> +
> >>> +	memset(priv->flashes, 0x0,
> >>> +	       sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
> >>> +
> >>> +	/* Initial each CS controller register */
> >>> +	for (cs = 0; cs < priv->num_cs; cs++) {
> >>> +		priv->flashes[cs].ce_ctrl_user &=
> >>> +				~(priv->info->cmd_io_ctrl_mask);
> >>> +		priv->flashes[cs].ce_ctrl_user |=
> >>> +				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
> >>> +		writel(priv->flashes[cs].ce_ctrl_user,
> >>> +		       plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
> >>> +	}
> >>>
> >>
> >> and we should start by setting sane defaults for the ranges.
> >>
> >> It's too early to add the decoding ranges calculation.
> >
> > Okay.
> >
> >>
> >> Thanks,
> >>
> >> C.
> >>
> >>> +	memset(priv->decoded_sz_arr, 0x0, sizeof(u32) *
> >>> +ASPEED_SPI_MAX_CS);
> >>> +
> >>> +	for (cs = 0; cs < priv->num_cs; cs++) {
> >>> +		reg_val = readl(plat->ctrl_base +
> REG_CE0_DECODED_ADDR_REG +
> >> cs * 4);
> >>> +		decoded_sz = priv->info->segment_end(bus, reg_val) -
> >>> +			     priv->info->segment_start(bus, reg_val);
> >>> +
> >>> +		/*
> >>> +		 * For CS0, if the default address decoded area exists,
> >>> +		 * keep its value in order to make sure that the whole boot
> >>> +		 * image can be accessed with normal read mode.
> >>> +		 */
> >>> +		if (cs == 0 && decoded_sz != 0)
> >>> +			priv->decoded_sz_arr[cs] = decoded_sz;
> >>> +		else
> >>> +			priv->decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> >>> +	}
> >>> +
> >>> +	ret = aspeed_spi_decoded_range_config(bus, priv->decoded_sz_arr);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static const struct aspeed_spi_info ast2500_fmc_info = {
> >>> +	.max_data_bus_width = 2,
> >>> +	.cmd_io_ctrl_mask = 0x70ff40c3,
> >>> +	.clk_ctrl_mask = 0x00002f00,
> >>> +	.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,
> >>> +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
> >>> +	.get_clk_setting = ast2500_get_clk_setting, };
> >>> +
> >>> +/*
> >>> + * 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 = {
> >>> +	.max_data_bus_width = 2,
> >>> +	.cmd_io_ctrl_mask = 0x70ff40c3,
> >>> +	.clk_ctrl_mask = 0x00002f00,
> >>> +	.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,
> >>> +	.adjust_decoded_sz = ast2500_adjust_decoded_size,
> >>> +	.get_clk_setting = ast2500_get_clk_setting, };
> >>> +
> >>> +static const struct aspeed_spi_info ast2600_fmc_info = {
> >>> +	.max_data_bus_width = 4,
> >>> +	.cmd_io_ctrl_mask = 0xf0ff40c3,
> >>> +	.clk_ctrl_mask = 0x0f000f00,
> >>> +	.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,
> >>> +	.adjust_decoded_sz = ast2600_adjust_decoded_size,
> >>> +	.get_clk_setting = ast2600_get_clk_setting, };
> >>> +
> >>> +static const struct aspeed_spi_info ast2600_spi_info = {
> >>> +	.max_data_bus_width = 4,
> >>> +	.cmd_io_ctrl_mask = 0xf0ff40c3,
> >>> +	.clk_ctrl_mask = 0x0f000f00,
> >>> +	.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,
> >>> +	.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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
> >>> +	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
> >>> +	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;
> >>> +}
> >>> +
> >>> +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 clk hclk;
> >>> +	int ret;
> >>> +
> >>> +	plat->ctrl_base = devfdt_get_addr_index(bus, 0);
> >>> +	if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
> >>> +		dev_err(bus, "wrong AHB 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;
> >>> +	}
> >>> +
> >>> +	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);
> >>> +
> >>> +	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%lx, ahb_base = 0x%p, size = 0x%lx\n",
> >>> +		plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
> >>> +	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
> >>> +		plat->hclk_rate / 1000000, 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",
> >>> +	.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,
> >>> +};
> >


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-05-24  5:56 ` [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver Chin-Ting Kuo
  2022-07-01  9:28   ` Cédric Le Goater
@ 2022-07-07  5:36   ` Joel Stanley
  2022-07-08  5:42     ` Chin-Ting Kuo
  1 sibling, 1 reply; 41+ messages in thread
From: Joel Stanley @ 2022-07-07  5:36 UTC (permalink / raw)
  To: Chin-Ting Kuo
  Cc: Chia-Wei, Wang, Lukasz Majewski, Sean Anderson, Ryan Chen,
	BMC-SW, jagan, Vignesh Raghavendra, Cédric Le Goater,
	U-Boot Mailing List, Pratyush Yadav

Hi Chin-Ting,

On Tue, 24 May 2022 at 05:58, Chin-Ting Kuo
<chin-ting_kuo@aspeedtech.com> wrote:
>
> Add ASPEED BMC FMC/SPI memory controller driver with
> spi-mem interface for AST2500 and AST2600 platform.

Have you considered including 2400 support in your patch set?

I have prepared a series that adds upstream support for the 2400. We
already have support in other drivers (gpio, i2c, sdhci, timer, wdt)
so there's no blocker in including support in your patch.

Cheers,

Joel

>
> 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.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.
>
> Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> ---
> v2: Remove defconfig files from this patch.
>
>  drivers/spi/Kconfig      |   8 +
>  drivers/spi/Makefile     |   1 +
>  drivers/spi/spi-aspeed.c | 822 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 831 insertions(+)
>  create mode 100644 drivers/spi/spi-aspeed.c
>
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index a1e515cb2b..a616294910 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -387,6 +387,14 @@ config SANDBOX_SPI
>                 };
>           };
>
> +config SPI_ASPEED
> +       bool "ASPEED SPI controller driver"
> +       depends on DM_SPI && SPI_MEM
> +       default n
> +       help
> +         Enable ASPEED SPI 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 06e81b465b..36a4bd5dce 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -9,6 +9,7 @@ obj-y += spi-uclass.o
>  obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
>  obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
>  obj-$(CONFIG_SOFT_SPI) += soft_spi.o
> +obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c
> new file mode 100644
> index 0000000000..9574aff793
> --- /dev/null
> +++ b/drivers/spi/spi-aspeed.c
> @@ -0,0 +1,822 @@
> +// 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>
> +
> +/* ASPEED FMC/SPI memory control register related */
> +#define REG_CE_TYPE_SETTING          0x00
> +#define REG_CE_ADDR_MODE_CTRL        0x04
> +#define REG_INTR_CTRL_STATUS         0x08
> +#define REG_CE0_CTRL_REG             0x10
> +#define REG_CE0_DECODED_ADDR_REG     0x30
> +
> +#define ASPEED_SPI_MAX_CS       3
> +#define FLASH_CALIBRATION_LEN   0x400
> +
> +#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_plat {
> +       fdt_addr_t ctrl_base;
> +       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 */
> +       u8 max_cs;
> +};
> +
> +struct aspeed_spi_flash {
> +       u8 cs;
> +       void __iomem *ahb_base;
> +       u32 ahb_win_sz;
> +       u32 ce_ctrl_user;
> +       u32 ce_ctrl_read;
> +       u32 max_freq;
> +       bool trimmed_decoded_sz;
> +};
> +
> +struct aspeed_spi_priv {
> +       u32 num_cs;
> +       struct aspeed_spi_info *info;
> +       struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
> +       u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
> +};
> +
> +struct aspeed_spi_info {
> +       u32 cmd_io_ctrl_mask;
> +       u32 clk_ctrl_mask;
> +       u32 max_data_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);
> +       int (*adjust_decoded_sz)(struct udevice *bus, u32 decoded_sz_arr[]);
> +       u32 (*get_clk_setting)(struct udevice *dev, uint hz);
> +};
> +
> +static int aspeed_spi_trim_decoded_size(struct udevice *bus,
> +                                       u32 decoded_sz_arr[]);
> +
> +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:
> +               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 + 0x100000;
> +}
> +
> +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_plat *plat = dev_get_plat(bus);
> +       u32 reg_val;
> +
> +       reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> +       reg_val |= 0x1 << cs;
> +       writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
> +                                      u32 decoded_sz_arr[])
> +{
> +       struct aspeed_spi_plat *plat = dev_get_plat(bus);
> +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +       int ret;
> +       int cs;
> +
> +       /* Assign min_decoded_sz to unused CS. */
> +       for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
> +               if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
> +                       decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> +       }
> +
> +       ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> +       if (ret != 0)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +/* Transfer maximum clock frequency to register setting */
> +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 = 0x000; /* 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);
> +       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_plat *plat = dev_get_plat(bus);
> +       u32 reg_val;
> +
> +       reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> +       reg_val |= 0x11 << cs;
> +       writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> +}
> +
> +static int ast2600_adjust_decoded_size(struct udevice *bus,
> +                                      u32 decoded_sz_arr[])
> +{
> +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +       int ret;
> +       int i;
> +       int cs;
> +       u32 pre_sz;
> +       u32 lack_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 += decoded_sz_arr[i];
> +
> +               if (decoded_sz_arr[cs] != 0 && (pre_sz % decoded_sz_arr[cs]) != 0) {
> +                       lack_sz = decoded_sz_arr[cs] - (pre_sz % decoded_sz_arr[cs]);
> +                       decoded_sz_arr[0] += lack_sz;
> +               }
> +       }
> +
> +       ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> +       if (ret != 0)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +/* Transfer maximum clock frequency to register setting */
> +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 = 0x400; /* 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
> + * 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,
> +                                       u32 decoded_sz_arr[])
> +{
> +       struct aspeed_spi_plat *plat = dev_get_plat(bus);
> +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +       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 += decoded_sz_arr[i];
> +
> +               if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
> +                       cs--;
> +
> +               if (cs < 0)
> +                       return -ENOMEM;
> +
> +               if (total_sz > plat->ahb_sz) {
> +                       decoded_sz_arr[cs] -= priv->info->min_decoded_sz;
> +                       total_sz -= priv->info->min_decoded_sz;
> +                       priv->flashes[cs].trimmed_decoded_sz = true;
> +               }
> +       } while (total_sz > plat->ahb_sz);
> +
> +       return 0;
> +}
> +
> +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.buswidth > 1 || op->addr.nbytes > 4)
> +               return false;
> +
> +       if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
> +               return false;
> +
> +       if (op->data.buswidth > priv->info->max_data_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_plat *plat = dev_get_plat(bus);
> +       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;
> +       fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4;
> +       struct aspeed_spi_flash *flash = &priv->flashes[cs];
> +       u32 ctrl_val;
> +       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);
> +
> +       /* Start user mode */
> +       ctrl_val = flash->ce_ctrl_user;
> +       writel(ctrl_val, ctrl_reg);
> +       ctrl_val &= (~CTRL_STOP_ACTIVE);
> +       writel(ctrl_val, 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;
> +       }
> +       aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
> +
> +       /* Send dummy cycle */
> +       aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data, op->dummy.nbytes);
> +
> +       /* Change io_mode */
> +       ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
> +       writel(ctrl_val, 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);
> +       }
> +
> +       ctrl_val |= CTRL_STOP_ACTIVE;
> +       writel(ctrl_val, ctrl_reg);
> +
> +       /* Restore controller setting. */
> +       writel(flash->ce_ctrl_read, ctrl_reg);
> +
> +       /* Set controller to 4-byte mode when flash is in 4-byte mode. */
> +       if (op->cmd.opcode == SPINOR_OP_EN4B)
> +               priv->info->set_4byte(bus, cs);
> +
> +       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 int aspeed_spi_decoded_range_config(struct udevice *bus,
> +                                          u32 decoded_sz_arr[])
> +{
> +       struct aspeed_spi_plat *plat = dev_get_plat(bus);
> +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> +       int ret;
> +       u32 cs;
> +       u32 decoded_reg_val;
> +       u32 start_addr;
> +       u32 end_addr = 0;
> +
> +       ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
> +       if (ret != 0)
> +               return ret;
> +
> +       /* Configure each CS decoded range */
> +       for (cs = 0; cs < plat->max_cs; cs++) {
> +               if (cs == 0)
> +                       start_addr = (u32)plat->ahb_base;
> +               else
> +                       start_addr = end_addr;
> +
> +               priv->flashes[cs].ahb_base = (void __iomem *)start_addr;
> +               priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
> +
> +               end_addr = start_addr + decoded_sz_arr[cs];
> +               decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
> +
> +               writel(decoded_reg_val,
> +                      plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
> +
> +               dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
> +                       cs, decoded_reg_val, start_addr, end_addr);
> +       }
> +
> +       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(plat->ctrl_base + REG_CE_TYPE_SETTING);
> +       writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
> +              plat->ctrl_base + REG_CE_TYPE_SETTING);
> +
> +       memset(priv->flashes, 0x0,
> +              sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
> +
> +       /* Initial each CS controller register */
> +       for (cs = 0; cs < priv->num_cs; cs++) {
> +               priv->flashes[cs].ce_ctrl_user &=
> +                               ~(priv->info->cmd_io_ctrl_mask);
> +               priv->flashes[cs].ce_ctrl_user |=
> +                               (CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
> +               writel(priv->flashes[cs].ce_ctrl_user,
> +                      plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
> +       }
> +
> +       memset(priv->decoded_sz_arr, 0x0, sizeof(u32) * ASPEED_SPI_MAX_CS);
> +
> +       for (cs = 0; cs < priv->num_cs; cs++) {
> +               reg_val = readl(plat->ctrl_base + REG_CE0_DECODED_ADDR_REG + cs * 4);
> +               decoded_sz = priv->info->segment_end(bus, reg_val) -
> +                            priv->info->segment_start(bus, reg_val);
> +
> +               /*
> +                * For CS0, if the default address decoded area exists,
> +                * keep its value in order to make sure that the whole boot
> +                * image can be accessed with normal read mode.
> +                */
> +               if (cs == 0 && decoded_sz != 0)
> +                       priv->decoded_sz_arr[cs] = decoded_sz;
> +               else
> +                       priv->decoded_sz_arr[cs] = priv->info->min_decoded_sz;
> +       }
> +
> +       ret = aspeed_spi_decoded_range_config(bus, priv->decoded_sz_arr);
> +
> +       return ret;
> +}
> +
> +static const struct aspeed_spi_info ast2500_fmc_info = {
> +       .max_data_bus_width = 2,
> +       .cmd_io_ctrl_mask = 0x70ff40c3,
> +       .clk_ctrl_mask = 0x00002f00,
> +       .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,
> +       .adjust_decoded_sz = ast2500_adjust_decoded_size,
> +       .get_clk_setting = ast2500_get_clk_setting,
> +};
> +
> +/*
> + * 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 = {
> +       .max_data_bus_width = 2,
> +       .cmd_io_ctrl_mask = 0x70ff40c3,
> +       .clk_ctrl_mask = 0x00002f00,
> +       .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,
> +       .adjust_decoded_sz = ast2500_adjust_decoded_size,
> +       .get_clk_setting = ast2500_get_clk_setting,
> +};
> +
> +static const struct aspeed_spi_info ast2600_fmc_info = {
> +       .max_data_bus_width = 4,
> +       .cmd_io_ctrl_mask = 0xf0ff40c3,
> +       .clk_ctrl_mask = 0x0f000f00,
> +       .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,
> +       .adjust_decoded_sz = ast2600_adjust_decoded_size,
> +       .get_clk_setting = ast2600_get_clk_setting,
> +};
> +
> +static const struct aspeed_spi_info ast2600_spi_info = {
> +       .max_data_bus_width = 4,
> +       .cmd_io_ctrl_mask = 0xf0ff40c3,
> +       .clk_ctrl_mask = 0x0f000f00,
> +       .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,
> +       .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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
> +       struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
> +       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;
> +}
> +
> +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 clk hclk;
> +       int ret;
> +
> +       plat->ctrl_base = devfdt_get_addr_index(bus, 0);
> +       if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
> +               dev_err(bus, "wrong AHB 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;
> +       }
> +
> +       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);
> +
> +       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%lx, ahb_base = 0x%p, size = 0x%lx\n",
> +               plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
> +       dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
> +               plat->hclk_rate / 1000000, 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",
> +       .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,
> +};
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-07-07  5:36   ` Joel Stanley
@ 2022-07-08  5:42     ` Chin-Ting Kuo
  2022-07-08  8:52       ` Cédric Le Goater
  0 siblings, 1 reply; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-08  5:42 UTC (permalink / raw)
  To: Joel Stanley
  Cc: ChiaWei Wang, Lukasz Majewski, Sean Anderson, Ryan Chen, BMC-SW,
	jagan, Vignesh Raghavendra, Cédric Le Goater,
	U-Boot Mailing List, Pratyush Yadav

Hi Joel,

> -----Original Message-----
> From: Joel Stanley <joel@jms.id.au>
> Sent: Thursday, July 7, 2022 1:37 PM
> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
> 
> Hi Chin-Ting,
> 
> On Tue, 24 May 2022 at 05:58, Chin-Ting Kuo
> <chin-ting_kuo@aspeedtech.com> wrote:
> >
> > Add ASPEED BMC FMC/SPI memory controller driver with spi-mem interface
> > for AST2500 and AST2600 platform.
> 
> Have you considered including 2400 support in your patch set?
> 

No. But in order to achieve sustainability and completeness, we will add AST2400 part in the next patch version.


Best Wishes,
Chin-Ting

> I have prepared a series that adds upstream support for the 2400. We already
> have support in other drivers (gpio, i2c, sdhci, timer, wdt) so there's no blocker
> in including support in your patch.
> 
> Cheers,
> 
> Joel
> 
> >
> > 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.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.
> >
> > Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> > ---
> > v2: Remove defconfig files from this patch.
> >
> >  drivers/spi/Kconfig      |   8 +
> >  drivers/spi/Makefile     |   1 +
> >  drivers/spi/spi-aspeed.c | 822
> > +++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 831 insertions(+)
> >  create mode 100644 drivers/spi/spi-aspeed.c
> >
> > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index
> > a1e515cb2b..a616294910 100644
> > --- a/drivers/spi/Kconfig
> > +++ b/drivers/spi/Kconfig
> > @@ -387,6 +387,14 @@ config SANDBOX_SPI
> >                 };
> >           };
> >
> > +config SPI_ASPEED
> > +       bool "ASPEED SPI controller driver"
> > +       depends on DM_SPI && SPI_MEM
> > +       default n
> > +       help
> > +         Enable ASPEED SPI 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
> > 06e81b465b..36a4bd5dce 100644
> > --- a/drivers/spi/Makefile
> > +++ b/drivers/spi/Makefile
> > @@ -9,6 +9,7 @@ obj-y += spi-uclass.o
> >  obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
> >  obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
> >  obj-$(CONFIG_SOFT_SPI) += soft_spi.o
> > +obj-$(CONFIG_SPI_ASPEED) += spi-aspeed.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.c b/drivers/spi/spi-aspeed.c new
> > file mode 100644 index 0000000000..9574aff793
> > --- /dev/null
> > +++ b/drivers/spi/spi-aspeed.c
> > @@ -0,0 +1,822 @@
> > +// 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>
> > +
> > +/* ASPEED FMC/SPI memory control register related */
> > +#define REG_CE_TYPE_SETTING          0x00
> > +#define REG_CE_ADDR_MODE_CTRL        0x04
> > +#define REG_INTR_CTRL_STATUS         0x08
> > +#define REG_CE0_CTRL_REG             0x10
> > +#define REG_CE0_DECODED_ADDR_REG     0x30
> > +
> > +#define ASPEED_SPI_MAX_CS       3
> > +#define FLASH_CALIBRATION_LEN   0x400
> > +
> > +#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_plat {
> > +       fdt_addr_t ctrl_base;
> > +       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 */
> > +       u8 max_cs;
> > +};
> > +
> > +struct aspeed_spi_flash {
> > +       u8 cs;
> > +       void __iomem *ahb_base;
> > +       u32 ahb_win_sz;
> > +       u32 ce_ctrl_user;
> > +       u32 ce_ctrl_read;
> > +       u32 max_freq;
> > +       bool trimmed_decoded_sz;
> > +};
> > +
> > +struct aspeed_spi_priv {
> > +       u32 num_cs;
> > +       struct aspeed_spi_info *info;
> > +       struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
> > +       u32 decoded_sz_arr[ASPEED_SPI_MAX_CS];
> > +};
> > +
> > +struct aspeed_spi_info {
> > +       u32 cmd_io_ctrl_mask;
> > +       u32 clk_ctrl_mask;
> > +       u32 max_data_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);
> > +       int (*adjust_decoded_sz)(struct udevice *bus, u32
> decoded_sz_arr[]);
> > +       u32 (*get_clk_setting)(struct udevice *dev, uint hz); };
> > +
> > +static int aspeed_spi_trim_decoded_size(struct udevice *bus,
> > +                                       u32 decoded_sz_arr[]);
> > +
> > +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:
> > +               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 + 0x100000; }
> > +
> > +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_plat *plat = dev_get_plat(bus);
> > +       u32 reg_val;
> > +
> > +       reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> > +       reg_val |= 0x1 << cs;
> > +       writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_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,
> > +                                      u32 decoded_sz_arr[]) {
> > +       struct aspeed_spi_plat *plat = dev_get_plat(bus);
> > +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +       int ret;
> > +       int cs;
> > +
> > +       /* Assign min_decoded_sz to unused CS. */
> > +       for (cs = priv->num_cs; cs < plat->max_cs; cs++) {
> > +               if (decoded_sz_arr[cs] < priv->info->min_decoded_sz)
> > +                       decoded_sz_arr[cs] =
> priv->info->min_decoded_sz;
> > +       }
> > +
> > +       ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> > +       if (ret != 0)
> > +               return ret;
> > +
> > +       return 0;
> > +}
> > +
> > +/* Transfer maximum clock frequency to register setting */ 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 = 0x000; /* 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);
> > +       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_plat *plat = dev_get_plat(bus);
> > +       u32 reg_val;
> > +
> > +       reg_val = readl(plat->ctrl_base + REG_CE_ADDR_MODE_CTRL);
> > +       reg_val |= 0x11 << cs;
> > +       writel(reg_val, plat->ctrl_base + REG_CE_ADDR_MODE_CTRL); }
> > +
> > +static int ast2600_adjust_decoded_size(struct udevice *bus,
> > +                                      u32 decoded_sz_arr[]) {
> > +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +       int ret;
> > +       int i;
> > +       int cs;
> > +       u32 pre_sz;
> > +       u32 lack_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 += decoded_sz_arr[i];
> > +
> > +               if (decoded_sz_arr[cs] != 0 && (pre_sz %
> decoded_sz_arr[cs]) != 0) {
> > +                       lack_sz = decoded_sz_arr[cs] - (pre_sz %
> decoded_sz_arr[cs]);
> > +                       decoded_sz_arr[0] += lack_sz;
> > +               }
> > +       }
> > +
> > +       ret = aspeed_spi_trim_decoded_size(bus, decoded_sz_arr);
> > +       if (ret != 0)
> > +               return ret;
> > +
> > +       return 0;
> > +}
> > +
> > +/* Transfer maximum clock frequency to register setting */ 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 = 0x400; /* 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
> > + * 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,
> > +                                       u32 decoded_sz_arr[]) {
> > +       struct aspeed_spi_plat *plat = dev_get_plat(bus);
> > +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +       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 += decoded_sz_arr[i];
> > +
> > +               if (decoded_sz_arr[cs] <= priv->info->min_decoded_sz)
> > +                       cs--;
> > +
> > +               if (cs < 0)
> > +                       return -ENOMEM;
> > +
> > +               if (total_sz > plat->ahb_sz) {
> > +                       decoded_sz_arr[cs] -=
> priv->info->min_decoded_sz;
> > +                       total_sz -= priv->info->min_decoded_sz;
> > +                       priv->flashes[cs].trimmed_decoded_sz = true;
> > +               }
> > +       } while (total_sz > plat->ahb_sz);
> > +
> > +       return 0;
> > +}
> > +
> > +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.buswidth > 1 || op->addr.nbytes > 4)
> > +               return false;
> > +
> > +       if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
> > +               return false;
> > +
> > +       if (op->data.buswidth > priv->info->max_data_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_plat *plat = dev_get_plat(bus);
> > +       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;
> > +       fdt_addr_t ctrl_reg = plat->ctrl_base + REG_CE0_CTRL_REG + cs *
> 4;
> > +       struct aspeed_spi_flash *flash = &priv->flashes[cs];
> > +       u32 ctrl_val;
> > +       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);
> > +
> > +       /* Start user mode */
> > +       ctrl_val = flash->ce_ctrl_user;
> > +       writel(ctrl_val, ctrl_reg);
> > +       ctrl_val &= (~CTRL_STOP_ACTIVE);
> > +       writel(ctrl_val, 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;
> > +       }
> > +       aspeed_spi_write_to_ahb(flash->ahb_base, addr,
> > + op->addr.nbytes);
> > +
> > +       /* Send dummy cycle */
> > +       aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data,
> > + op->dummy.nbytes);
> > +
> > +       /* Change io_mode */
> > +       ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
> > +       writel(ctrl_val, 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);
> > +       }
> > +
> > +       ctrl_val |= CTRL_STOP_ACTIVE;
> > +       writel(ctrl_val, ctrl_reg);
> > +
> > +       /* Restore controller setting. */
> > +       writel(flash->ce_ctrl_read, ctrl_reg);
> > +
> > +       /* Set controller to 4-byte mode when flash is in 4-byte mode. */
> > +       if (op->cmd.opcode == SPINOR_OP_EN4B)
> > +               priv->info->set_4byte(bus, cs);
> > +
> > +       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 int aspeed_spi_decoded_range_config(struct udevice *bus,
> > +                                          u32 decoded_sz_arr[]) {
> > +       struct aspeed_spi_plat *plat = dev_get_plat(bus);
> > +       struct aspeed_spi_priv *priv = dev_get_priv(bus);
> > +       int ret;
> > +       u32 cs;
> > +       u32 decoded_reg_val;
> > +       u32 start_addr;
> > +       u32 end_addr = 0;
> > +
> > +       ret = priv->info->adjust_decoded_sz(bus, decoded_sz_arr);
> > +       if (ret != 0)
> > +               return ret;
> > +
> > +       /* Configure each CS decoded range */
> > +       for (cs = 0; cs < plat->max_cs; cs++) {
> > +               if (cs == 0)
> > +                       start_addr = (u32)plat->ahb_base;
> > +               else
> > +                       start_addr = end_addr;
> > +
> > +               priv->flashes[cs].ahb_base = (void __iomem
> *)start_addr;
> > +               priv->flashes[cs].ahb_win_sz = decoded_sz_arr[cs];
> > +
> > +               end_addr = start_addr + decoded_sz_arr[cs];
> > +               decoded_reg_val = priv->info->segment_reg(start_addr,
> > + end_addr);
> > +
> > +               writel(decoded_reg_val,
> > +                      plat->ctrl_base +
> REG_CE0_DECODED_ADDR_REG + cs
> > + * 4);
> > +
> > +               dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x,
> end: 0x%x\n",
> > +                       cs, decoded_reg_val, start_addr, end_addr);
> > +       }
> > +
> > +       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(plat->ctrl_base + REG_CE_TYPE_SETTING);
> > +       writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
> > +              plat->ctrl_base + REG_CE_TYPE_SETTING);
> > +
> > +       memset(priv->flashes, 0x0,
> > +              sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
> > +
> > +       /* Initial each CS controller register */
> > +       for (cs = 0; cs < priv->num_cs; cs++) {
> > +               priv->flashes[cs].ce_ctrl_user &=
> > +                               ~(priv->info->cmd_io_ctrl_mask);
> > +               priv->flashes[cs].ce_ctrl_user |=
> > +                               (CTRL_STOP_ACTIVE |
> CTRL_IO_MODE_USER);
> > +               writel(priv->flashes[cs].ce_ctrl_user,
> > +                      plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
> > +       }
> > +
> > +       memset(priv->decoded_sz_arr, 0x0, sizeof(u32) *
> > + ASPEED_SPI_MAX_CS);
> > +
> > +       for (cs = 0; cs < priv->num_cs; cs++) {
> > +               reg_val = readl(plat->ctrl_base +
> REG_CE0_DECODED_ADDR_REG + cs * 4);
> > +               decoded_sz = priv->info->segment_end(bus, reg_val) -
> > +                            priv->info->segment_start(bus, reg_val);
> > +
> > +               /*
> > +                * For CS0, if the default address decoded area exists,
> > +                * keep its value in order to make sure that the whole
> boot
> > +                * image can be accessed with normal read mode.
> > +                */
> > +               if (cs == 0 && decoded_sz != 0)
> > +                       priv->decoded_sz_arr[cs] = decoded_sz;
> > +               else
> > +                       priv->decoded_sz_arr[cs] =
> priv->info->min_decoded_sz;
> > +       }
> > +
> > +       ret = aspeed_spi_decoded_range_config(bus,
> > + priv->decoded_sz_arr);
> > +
> > +       return ret;
> > +}
> > +
> > +static const struct aspeed_spi_info ast2500_fmc_info = {
> > +       .max_data_bus_width = 2,
> > +       .cmd_io_ctrl_mask = 0x70ff40c3,
> > +       .clk_ctrl_mask = 0x00002f00,
> > +       .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,
> > +       .adjust_decoded_sz = ast2500_adjust_decoded_size,
> > +       .get_clk_setting = ast2500_get_clk_setting, };
> > +
> > +/*
> > + * 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 = {
> > +       .max_data_bus_width = 2,
> > +       .cmd_io_ctrl_mask = 0x70ff40c3,
> > +       .clk_ctrl_mask = 0x00002f00,
> > +       .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,
> > +       .adjust_decoded_sz = ast2500_adjust_decoded_size,
> > +       .get_clk_setting = ast2500_get_clk_setting, };
> > +
> > +static const struct aspeed_spi_info ast2600_fmc_info = {
> > +       .max_data_bus_width = 4,
> > +       .cmd_io_ctrl_mask = 0xf0ff40c3,
> > +       .clk_ctrl_mask = 0x0f000f00,
> > +       .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,
> > +       .adjust_decoded_sz = ast2600_adjust_decoded_size,
> > +       .get_clk_setting = ast2600_get_clk_setting, };
> > +
> > +static const struct aspeed_spi_info ast2600_spi_info = {
> > +       .max_data_bus_width = 4,
> > +       .cmd_io_ctrl_mask = 0xf0ff40c3,
> > +       .clk_ctrl_mask = 0x0f000f00,
> > +       .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,
> > +       .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 aspeed_spi_priv *priv = dev_get_priv(dev->parent);
> > +       struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
> > +       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;
> > +}
> > +
> > +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 clk hclk;
> > +       int ret;
> > +
> > +       plat->ctrl_base = devfdt_get_addr_index(bus, 0);
> > +       if ((u32)plat->ctrl_base == FDT_ADDR_T_NONE) {
> > +               dev_err(bus, "wrong AHB 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;
> > +       }
> > +
> > +       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);
> > +
> > +       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%lx, ahb_base = 0x%p, size =
> 0x%lx\n",
> > +               plat->ctrl_base, plat->ahb_base, plat->ahb_sz);
> > +       dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
> > +               plat->hclk_rate / 1000000, 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",
> > +       .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,
> > +};
> > --
> > 2.25.1
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-07-08  5:42     ` Chin-Ting Kuo
@ 2022-07-08  8:52       ` Cédric Le Goater
  2022-07-11  6:51         ` Chin-Ting Kuo
  0 siblings, 1 reply; 41+ messages in thread
From: Cédric Le Goater @ 2022-07-08  8:52 UTC (permalink / raw)
  To: Chin-Ting Kuo, Joel Stanley
  Cc: ChiaWei Wang, Lukasz Majewski, Sean Anderson, Ryan Chen, BMC-SW,
	jagan, Vignesh Raghavendra, U-Boot Mailing List, Pratyush Yadav

On 7/8/22 07:42, Chin-Ting Kuo wrote:
> Hi Joel,
> 
>> -----Original Message-----
>> From: Joel Stanley <joel@jms.id.au>
>> Sent: Thursday, July 7, 2022 1:37 PM
>> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
>>
>> Hi Chin-Ting,
>>
>> On Tue, 24 May 2022 at 05:58, Chin-Ting Kuo
>> <chin-ting_kuo@aspeedtech.com> wrote:
>>>
>>> Add ASPEED BMC FMC/SPI memory controller driver with spi-mem interface
>>> for AST2500 and AST2600 platform.
>>
>> Have you considered including 2400 support in your patch set?
>>
> 
> No. But in order to achieve sustainability and completeness, we will add AST2400 part in the next patch version.

Could you please provide the AST2400 support as a separate patch on top
of the AST2500/AST2600 ? It helps to understand the differences.

Thanks,

C.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
  2022-07-08  8:52       ` Cédric Le Goater
@ 2022-07-11  6:51         ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-07-11  6:51 UTC (permalink / raw)
  To: Cédric Le Goater, Joel Stanley
  Cc: ChiaWei Wang, Lukasz Majewski, Sean Anderson, Ryan Chen, BMC-SW,
	jagan, Vignesh Raghavendra, U-Boot Mailing List, Pratyush Yadav

Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Friday, July 8, 2022 4:52 PM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>; Joel Stanley
> <joel@jms.id.au>
> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
> 
> On 7/8/22 07:42, Chin-Ting Kuo wrote:
> > Hi Joel,
> >
> >> -----Original Message-----
> >> From: Joel Stanley <joel@jms.id.au>
> >> Sent: Thursday, July 7, 2022 1:37 PM
> >> Subject: Re: [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver
> >>
> >> Hi Chin-Ting,
> >>
> >> On Tue, 24 May 2022 at 05:58, Chin-Ting Kuo
> >> <chin-ting_kuo@aspeedtech.com> wrote:
> >>>
> >>> Add ASPEED BMC FMC/SPI memory controller driver with spi-mem
> >>> interface for AST2500 and AST2600 platform.
> >>
> >> Have you considered including 2400 support in your patch set?
> >>
> >
> > No. But in order to achieve sustainability and completeness, we will add
> AST2400 part in the next patch version.
> 
> Could you please provide the AST2400 support as a separate patch on top of
> the AST2500/AST2600 ? It helps to understand the differences.
> 

Okay, it will be more clear.


Best Wishes,
Chin-Ting

> Thanks,
> 
> C.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 07/12] spi-mem: Add dirmap API from Linux
  2022-07-01 12:04   ` Jagan Teki
@ 2022-08-11  5:19     ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-08-11  5:19 UTC (permalink / raw)
  To: Jagan Teki
  Cc: ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW, vigneshr, clg,
	u-boot, p.yadav

Hi Jagan,

> -----Original Message-----
> From: Jagan Teki <jagan@amarulasolutions.com>
> Sent: Friday, July 1, 2022 8:05 PM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> Subject: Re: [v4 07/12] spi-mem: Add dirmap API from Linux
> 
> On Tue, May 24, 2022 at 11:28 AM Chin-Ting Kuo
> <chin-ting_kuo@aspeedtech.com> wrote:
> >
> > This adds the dirmap API originally introduced in Linux commit aa167f3
> > ("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/
> >
> > 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>
> > ---
> > v2: Remove "#if CONFIG_SPI_DIRMAP" compile wrapper.
> > v3: Fix a grammatical error in spi-mem.h.
> >
> >  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
> > a616294910..297253714a 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
> 
> Look like the following code is not part of this if construct, we need that to
> build only when SPI_DIRMAP is defined otherwise it footprint increase for
> non-DIRMAPs. Also please take care of unnecessary code while copying from
> Linux and add SHA1 in the commit message.
> 

Okay and I will take care the footprint for non-DITMAPs.


Chin-Ting

> Jagan.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file
  2022-07-01 11:52   ` Jagan Teki
@ 2022-08-11  5:20     ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-08-11  5:20 UTC (permalink / raw)
  To: Jagan Teki
  Cc: ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW, vigneshr, clg,
	u-boot, p.yadav

Hi Jagan,

> -----Original Message-----
> From: Jagan Teki <jagan@amarulasolutions.com>
> Sent: Friday, July 1, 2022 7:53 PM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> Subject: Re: [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file
> 
> On Tue, May 24, 2022 at 11:28 AM Chin-Ting Kuo
> <chin-ting_kuo@aspeedtech.com> wrote:
> >
> > Add spi-aspeed.c file for ARM ASPEED.
> >
> > Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> > ---
> >  MAINTAINERS | 7 +++++++
> >  1 file changed, 7 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index 56be0bfad0..f2cd707eda
> > 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -688,6 +688,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.c
> 
> Squash this part of spi-aspeed.c driver patch

Okay and will be updated in the next patch version.


Chin-Ting

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [v4 00/12] Add ASPEED SPI controller driver
  2022-07-01 11:57 ` Jagan Teki
@ 2022-08-11  5:25   ` Chin-Ting Kuo
  0 siblings, 0 replies; 41+ messages in thread
From: Chin-Ting Kuo @ 2022-08-11  5:25 UTC (permalink / raw)
  To: Jagan Teki
  Cc: ChiaWei Wang, lukma, seanga2, Ryan Chen, BMC-SW, vigneshr, clg,
	u-boot, p.yadav

Hi Jagan,

> -----Original Message-----
> From: Jagan Teki <jagan@amarulasolutions.com>
> Sent: Friday, July 1, 2022 7:57 PM
> To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
> Subject: Re: [v4 00/12] Add ASPEED SPI controller driver
> 
> On Tue, May 24, 2022 at 11:27 AM Chin-Ting Kuo
> <chin-ting_kuo@aspeedtech.com> wrote:
> >
> > This patch series aims to porting ASPEED FMC/SPI memory controller
> > driver with spi-mem interface. spi-mem dirmap framework is also
> > synchronized from Linux. These patches have been verified on both
> > AST2600 and AST2500 EVBs.
> >
> > Changes in v2:
> >   - Separate defconfig files from the SPI driver patch.
> >   - Use "if (CONFIG_IS_ENABLED(SPI_DIRMAP))" to wrap
> >     spi_dirmap related functions.
> >   - Add Winbond w25q512jv flash ID.
> >
> > Changes in v3:
> >   - Get AHB bus clock frequency from the function parameter.
> >   - Fix a grammatical error in spi-mem.h.
> >
> > Changes in v4:
> >   - Fix bug when SPI_NOR_4B_OPCODES flag is set.
> >
> > Chin-Ting Kuo (12):
> >   clk: aspeed: Get HCLK frequency support
> >   pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support
> >   spi: aspeed: Add ASPEED SPI controller driver
> >   configs: aspeed: Enable SPI flash features
> >   MAINTAINERS: Add ASPEED SPI driver file
> >   arm: dts: aspeed: Update SPI flash node settings
> >   spi-mem: Add dirmap API from Linux
> >   mtd: spi-nor: Use spi-mem dirmap API
> >   spi: aspeed: SPI dirmap read support
> >   configs: aspeed: Enable CONFIG_SPI_DIRMAP
> >   mtd: spi-nor-ids: Add Winbond W25Q512JV ID
> >   spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set
> 
> Sperate series for spi changes would really make it easier for review.
> please send it.
> 

The patches in this series depend on each other.
Patch with higher number relies on the one with lower number.
Thus, I think they cannot be separated into different series.

Chin-Ting

> Jagan.

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2022-08-11  5:26 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-24  5:56 [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
2022-05-24  5:56 ` [v4 01/12] clk: aspeed: Get HCLK frequency support Chin-Ting Kuo
2022-05-24  5:56 ` [v4 02/12] pinctrl: aspeed: FWSPICS1 and SPI1CS1 pin support Chin-Ting Kuo
2022-05-24  5:56 ` [v4 03/12] spi: aspeed: Add ASPEED SPI controller driver Chin-Ting Kuo
2022-07-01  9:28   ` Cédric Le Goater
2022-07-03  8:47     ` Chin-Ting Kuo
2022-07-04 15:24       ` Cédric Le Goater
2022-07-06 11:06         ` Chin-Ting Kuo
2022-07-07  5:36   ` Joel Stanley
2022-07-08  5:42     ` Chin-Ting Kuo
2022-07-08  8:52       ` Cédric Le Goater
2022-07-11  6:51         ` Chin-Ting Kuo
2022-05-24  5:56 ` [v4 04/12] configs: aspeed: Enable SPI flash features Chin-Ting Kuo
2022-07-01  9:28   ` Cédric Le Goater
2022-07-01 11:50     ` Cédric Le Goater
2022-07-03  9:00       ` Chin-Ting Kuo
2022-07-04  8:01         ` Cédric Le Goater
2022-05-24  5:56 ` [v4 05/12] MAINTAINERS: Add ASPEED SPI driver file Chin-Ting Kuo
2022-07-01 11:52   ` Jagan Teki
2022-08-11  5:20     ` Chin-Ting Kuo
2022-05-24  5:56 ` [v4 06/12] arm: dts: aspeed: Update SPI flash node settings Chin-Ting Kuo
2022-07-01  9:42   ` Cédric Le Goater
2022-07-03  8:54     ` Chin-Ting Kuo
2022-05-24  5:56 ` [v4 07/12] spi-mem: Add dirmap API from Linux Chin-Ting Kuo
2022-07-01  9:36   ` Cédric Le Goater
2022-07-03  8:49     ` Chin-Ting Kuo
2022-07-01 12:04   ` Jagan Teki
2022-08-11  5:19     ` Chin-Ting Kuo
2022-05-24  5:56 ` [v4 08/12] mtd: spi-nor: Use spi-mem dirmap API Chin-Ting Kuo
2022-05-24  5:56 ` [v4 09/12] spi: aspeed: SPI dirmap read support Chin-Ting Kuo
2022-05-24  5:56 ` [v4 10/12] configs: aspeed: Enable CONFIG_SPI_DIRMAP Chin-Ting Kuo
2022-05-24  5:56 ` [v4 11/12] mtd: spi-nor-ids: Add Winbond W25Q512JV ID Chin-Ting Kuo
2022-07-01  9:43   ` Cédric Le Goater
2022-05-24  5:56 ` [v4 12/12] spi: aspeed: Fix bug when SPI_NOR_4B_OPCODES flag is set Chin-Ting Kuo
2022-07-01  9:44   ` Cédric Le Goater
2022-07-03  8:56     ` Chin-Ting Kuo
2022-06-26  4:56 ` [v4 00/12] Add ASPEED SPI controller driver Chin-Ting Kuo
2022-06-26 16:15   ` Cédric Le Goater
2022-06-27  1:41     ` Chin-Ting Kuo
2022-07-01 11:57 ` Jagan Teki
2022-08-11  5:25   ` Chin-Ting Kuo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.