All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads
@ 2018-06-22 12:14 Cédric Le Goater
  2018-06-22 12:14 ` [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads Cédric Le Goater
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Cédric Le Goater @ 2018-06-22 12:14 UTC (permalink / raw)
  To: linux-mtd
  Cc: Marek Vasut, Boris Brezillon, David Woodhouse, Brian Norris,
	Richard Weinberger, linux-aspeed, Joel Stanley, Andrew Jeffery,
	Cédric Le Goater

Hello,

This series adds support for faster reads on the Aspeed SoC SMC (FMC
and SPI) controllers using a SPI timing calibration sequence to select
the best settings.
 
Thanks,

C.

 Changes since last time this was discussed (06/2017)

 - the "spi-max-frequency" property is now used to cap the results
 - chips on FMC controllers are also covered

Cédric Le Goater (4):
  mtd: spi-nor: aspeed: use command mode for reads
  mtd: spi-nor: aspeed: add support for SPI dual IO read mode
  mtd: spi-nor: aspeed: retrieve the ABH clock frequency
  mtd: spi-nor: aspeed: introduce optimized settings for fast reads

 drivers/mtd/spi-nor/aspeed-smc.c | 294 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 281 insertions(+), 13 deletions(-)

-- 
2.13.6

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

* [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads
  2018-06-22 12:14 [PATCH 0/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
@ 2018-06-22 12:14 ` Cédric Le Goater
  2018-07-23 12:12   ` Joel Stanley
  2018-06-22 12:14 ` [PATCH 2/4] mtd: spi-nor: aspeed: add support for SPI dual IO read mode Cédric Le Goater
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Cédric Le Goater @ 2018-06-22 12:14 UTC (permalink / raw)
  To: linux-mtd
  Cc: Marek Vasut, Boris Brezillon, David Woodhouse, Brian Norris,
	Richard Weinberger, linux-aspeed, Joel Stanley, Andrew Jeffery,
	Cédric Le Goater

When reading flash contents, try to use the "command mode" if the AHB
window configured for the flash module is big enough. Else, just fall
back to the "user mode" to perform the read.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/mtd/spi-nor/aspeed-smc.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 95e54468cf7d..af84a6fa2360 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -402,6 +402,31 @@ static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to,
 	return len;
 }
 
+static ssize_t aspeed_smc_read(struct spi_nor *nor, loff_t from, size_t len,
+			       u_char *read_buf)
+{
+	struct aspeed_smc_chip *chip = nor->priv;
+
+	/*
+	 * The AHB window configured for the chip is too small for the
+	 * read offset. Use the "User mode" of the controller to
+	 * perform the read.
+	 */
+	if (from >= chip->ahb_window_size) {
+		aspeed_smc_read_user(nor, from, len, read_buf);
+		goto out;
+	}
+
+	/*
+	 * Use the "Command mode" to do a direct read from the AHB
+	 * window configured for the chip. This should be the default.
+	 */
+	memcpy_fromio(read_buf, chip->ahb_base + from, len);
+
+out:
+	return len;
+}
+
 static int aspeed_smc_unregister(struct aspeed_smc_controller *controller)
 {
 	struct aspeed_smc_chip *chip;
@@ -743,6 +768,7 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 	}
 
 	chip->ctl_val[smc_read] |= cmd |
+		chip->nor.read_opcode << CONTROL_COMMAND_SHIFT |
 		CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8);
 
 	dev_dbg(controller->dev, "base control register: %08x\n",
@@ -809,7 +835,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 		nor->dev = dev;
 		nor->priv = chip;
 		spi_nor_set_flash_node(nor, child);
-		nor->read = aspeed_smc_read_user;
+		nor->read = aspeed_smc_read;
 		nor->write = aspeed_smc_write_user;
 		nor->read_reg = aspeed_smc_read_reg;
 		nor->write_reg = aspeed_smc_write_reg;
-- 
2.13.6

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

* [PATCH 2/4] mtd: spi-nor: aspeed: add support for SPI dual IO read mode
  2018-06-22 12:14 [PATCH 0/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
  2018-06-22 12:14 ` [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads Cédric Le Goater
@ 2018-06-22 12:14 ` Cédric Le Goater
  2018-07-23 12:12   ` Joel Stanley
  2018-06-22 12:14 ` [PATCH 3/4] mtd: spi-nor: aspeed: retrieve the ABH clock frequency Cédric Le Goater
  2018-06-22 12:14 ` [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
  3 siblings, 1 reply; 12+ messages in thread
From: Cédric Le Goater @ 2018-06-22 12:14 UTC (permalink / raw)
  To: linux-mtd
  Cc: Marek Vasut, Boris Brezillon, David Woodhouse, Brian Norris,
	Richard Weinberger, linux-aspeed, Joel Stanley, Andrew Jeffery,
	Cédric Le Goater

Implements support for the dual IO read mode on aspeed SMC/FMC
controllers which uses both MISO and MOSI lines for data during a read
to double the read bandwidth.

Still to be done SNOR_PROTO_1_2_2

Based on work from Robert Lippert <roblip@gmail.com>

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/mtd/spi-nor/aspeed-smc.c | 56 +++++++++++++++++++++++++++++++---------
 1 file changed, 44 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index af84a6fa2360..054614f34698 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -373,18 +373,49 @@ static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr)
 	}
 }
 
+static int aspeed_smc_get_io_mode(struct aspeed_smc_chip *chip)
+{
+	switch (chip->nor.read_proto) {
+	case SNOR_PROTO_1_1_1:
+		return 0;
+	case SNOR_PROTO_1_1_2:
+		return CONTROL_IO_DUAL_DATA;
+	case SNOR_PROTO_1_2_2:
+		return CONTROL_IO_DUAL_ADDR_DATA;
+	default:
+		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
+		return -EINVAL;
+	}
+}
+
+static void aspeed_smc_set_io_mode(struct aspeed_smc_chip *chip, u32 io_mode)
+{
+	u32 ctl;
+
+	if (io_mode > 0) {
+		ctl = readl(chip->ctl) & ~CONTROL_IO_MODE_MASK;
+		ctl |= io_mode;
+		writel(ctl, chip->ctl);
+	}
+}
+
 static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from,
 				    size_t len, u_char *read_buf)
 {
 	struct aspeed_smc_chip *chip = nor->priv;
 	int i;
 	u8 dummy = 0xFF;
+	int io_mode = aspeed_smc_get_io_mode(chip);
 
 	aspeed_smc_start_user(nor);
 	aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
 	for (i = 0; i < chip->nor.read_dummy / 8; i++)
 		aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy));
 
+	/* Set IO mode only for data */
+	if (io_mode == CONTROL_IO_DUAL_DATA)
+		aspeed_smc_set_io_mode(chip, io_mode);
+
 	aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len);
 	aspeed_smc_stop_user(nor);
 	return len;
@@ -735,6 +766,7 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 {
 	struct aspeed_smc_controller *controller = chip->controller;
 	const struct aspeed_smc_info *info = controller->info;
+	int io_mode;
 	u32 cmd;
 
 	if (chip->nor.addr_width == 4 && info->set_4b)
@@ -757,22 +789,21 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 	 * TODO: Adjust clocks if fast read is supported and interpret
 	 * SPI-NOR flags to adjust controller settings.
 	 */
-	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
-		if (chip->nor.read_dummy == 0)
-			cmd = CONTROL_COMMAND_MODE_NORMAL;
-		else
-			cmd = CONTROL_COMMAND_MODE_FREAD;
-	} else {
-		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
-		return -EINVAL;
-	}
+	io_mode = aspeed_smc_get_io_mode(chip);
+	if (io_mode < 0)
+		return io_mode;
 
-	chip->ctl_val[smc_read] |= cmd |
+	if (chip->nor.read_dummy == 0)
+		cmd = CONTROL_COMMAND_MODE_NORMAL;
+	else
+		cmd = CONTROL_COMMAND_MODE_FREAD;
+
+	chip->ctl_val[smc_read] |= cmd | io_mode |
 		chip->nor.read_opcode << CONTROL_COMMAND_SHIFT |
 		CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8);
 
-	dev_dbg(controller->dev, "base control register: %08x\n",
-		chip->ctl_val[smc_read]);
+	dev_info(controller->dev, "read control register: %08x\n",
+		 chip->ctl_val[smc_read]);
 	return 0;
 }
 
@@ -782,6 +813,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 	const struct spi_nor_hwcaps hwcaps = {
 		.mask = SNOR_HWCAPS_READ |
 			SNOR_HWCAPS_READ_FAST |
+			SNOR_HWCAPS_READ_1_1_2 |
 			SNOR_HWCAPS_PP,
 	};
 	const struct aspeed_smc_info *info = controller->info;
-- 
2.13.6

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

* [PATCH 3/4] mtd: spi-nor: aspeed: retrieve the ABH clock frequency
  2018-06-22 12:14 [PATCH 0/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
  2018-06-22 12:14 ` [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads Cédric Le Goater
  2018-06-22 12:14 ` [PATCH 2/4] mtd: spi-nor: aspeed: add support for SPI dual IO read mode Cédric Le Goater
@ 2018-06-22 12:14 ` Cédric Le Goater
  2018-07-23 12:13   ` Joel Stanley
  2018-06-22 12:14 ` [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
  3 siblings, 1 reply; 12+ messages in thread
From: Cédric Le Goater @ 2018-06-22 12:14 UTC (permalink / raw)
  To: linux-mtd
  Cc: Marek Vasut, Boris Brezillon, David Woodhouse, Brian Norris,
	Richard Weinberger, linux-aspeed, Joel Stanley, Andrew Jeffery,
	Cédric Le Goater

We will need the AHB frequency to set the SPI clock frequency to
perform the SPI calibration sequence and optimize the controller
settings for the fast reads.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/mtd/spi-nor/aspeed-smc.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 054614f34698..0251724eeecb 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/bug.h>
+#include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/module.h>
@@ -113,6 +114,8 @@ struct aspeed_smc_controller {
 	void __iomem *ahb_base;			/* per-chip windows resource */
 	u32 ahb_window_size;			/* full mapping window size */
 
+	unsigned long	clk_frequency;
+
 	struct aspeed_smc_chip *chips[0];	/* pointers to attached chips */
 };
 
@@ -911,6 +914,7 @@ static int aspeed_smc_probe(struct platform_device *pdev)
 	struct aspeed_smc_controller *controller;
 	const struct of_device_id *match;
 	const struct aspeed_smc_info *info;
+	struct clk *clk;
 	struct resource *res;
 	int ret;
 
@@ -942,6 +946,12 @@ static int aspeed_smc_probe(struct platform_device *pdev)
 
 	controller->ahb_window_size = resource_size(res);
 
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	controller->clk_frequency = clk_get_rate(clk);
+	devm_clk_put(&pdev->dev, clk);
+
 	ret = aspeed_smc_setup_flash(controller, np, res);
 	if (ret)
 		dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
-- 
2.13.6

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

* [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads
  2018-06-22 12:14 [PATCH 0/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
                   ` (2 preceding siblings ...)
  2018-06-22 12:14 ` [PATCH 3/4] mtd: spi-nor: aspeed: retrieve the ABH clock frequency Cédric Le Goater
@ 2018-06-22 12:14 ` Cédric Le Goater
  2018-07-23 12:16   ` Joel Stanley
  3 siblings, 1 reply; 12+ messages in thread
From: Cédric Le Goater @ 2018-06-22 12:14 UTC (permalink / raw)
  To: linux-mtd
  Cc: Marek Vasut, Boris Brezillon, David Woodhouse, Brian Norris,
	Richard Weinberger, linux-aspeed, Joel Stanley, Andrew Jeffery,
	Cédric Le Goater

Better settings for fast reads are looked for by implementing a SPI
timing calibration sequence described in the Aspeed SoC specification
document. The code is based on the OpenPOWER pflash tool and a similar
sequence using DMAs can be found in the SDK U-Boot.

The SPI calibration performs a loop on different SPI clock rates
(dividers of the AHB clock rates) and on different input delay cycles
for each SPI clock rates. The successive read results are compared to
a golden buffer, read at low speed, to select the safest and fastest
read settings for the chip.

The "spi-max-frequency" property is used to cap the optimize read
algorithm on some devices or controllers for which we want a "really"
safe setting, on the FMC controller chips for instance.

It can also be deactivated at boot time with a kernel parameter
'optimize_read', but that was never used on the field.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/mtd/spi-nor/aspeed-smc.c | 200 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 200 insertions(+)

diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 0251724eeecb..02387b0eb9bb 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/sizes.h>
+#include <linux/slab.h>
 #include <linux/sysfs.h>
 
 #define DEVICE_NAME	"aspeed-smc"
@@ -42,12 +43,16 @@ struct aspeed_smc_info {
 	bool hastype;		/* flash type field exists in config reg */
 	u8 we0;			/* shift for write enable bit for CE0 */
 	u8 ctl0;		/* offset in regs of ctl for CE0 */
+	u8 timing;		/* offset in regs of timing */
 
 	void (*set_4b)(struct aspeed_smc_chip *chip);
+	int (*optimize_read)(struct aspeed_smc_chip *chip, u32 max_freq);
 };
 
 static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip);
 static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip);
+static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip,
+				    u32 max_freq);
 
 static const struct aspeed_smc_info fmc_2400_info = {
 	.maxsize = 64 * 1024 * 1024,
@@ -55,7 +60,9 @@ static const struct aspeed_smc_info fmc_2400_info = {
 	.hastype = true,
 	.we0 = 16,
 	.ctl0 = 0x10,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 static const struct aspeed_smc_info spi_2400_info = {
@@ -64,7 +71,9 @@ static const struct aspeed_smc_info spi_2400_info = {
 	.hastype = false,
 	.we0 = 0,
 	.ctl0 = 0x04,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b_spi_2400,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 static const struct aspeed_smc_info fmc_2500_info = {
@@ -73,7 +82,9 @@ static const struct aspeed_smc_info fmc_2500_info = {
 	.hastype = true,
 	.we0 = 16,
 	.ctl0 = 0x10,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 static const struct aspeed_smc_info spi_2500_info = {
@@ -82,7 +93,9 @@ static const struct aspeed_smc_info spi_2500_info = {
 	.hastype = false,
 	.we0 = 16,
 	.ctl0 = 0x10,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 enum aspeed_smc_ctl_reg_value {
@@ -103,6 +116,7 @@ struct aspeed_smc_chip {
 	u32 ctl_val[smc_max];			/* control settings */
 	enum aspeed_smc_flash_type type;	/* what type of flash */
 	struct spi_nor nor;
+	u32 clk_rate;
 };
 
 struct aspeed_smc_controller {
@@ -119,6 +133,8 @@ struct aspeed_smc_controller {
 	struct aspeed_smc_chip *chips[0];	/* pointers to attached chips */
 };
 
+#define ASPEED_SPI_DEFAULT_FREQ		50000000
+
 /*
  * SPI Flash Configuration Register (AST2500 SPI)
  *     or
@@ -205,6 +221,12 @@ struct aspeed_smc_controller {
 	((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4)
 
 /*
+ * Switch to turn off read optimisation if needed
+ */
+static bool optimize_read = true;
+module_param(optimize_read, bool, 0644);
+
+/*
  * In user mode all data bytes read or written to the chip decode address
  * range are transferred to or from the SPI bus. The range is treated as a
  * fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned
@@ -765,6 +787,174 @@ static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
 	return 0;
 }
 
+#define CALIBRATE_BUF_SIZE 16384
+
+static bool aspeed_smc_check_reads(struct aspeed_smc_chip *chip,
+				   const u8 *golden_buf, u8 *test_buf)
+{
+	int i;
+
+	for (i = 0; i < 10; i++) {
+		aspeed_smc_read_from_ahb(test_buf, chip->ahb_base,
+					 CALIBRATE_BUF_SIZE);
+		if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0)
+			return false;
+	}
+	return true;
+}
+
+static int aspeed_smc_calibrate_reads(struct aspeed_smc_chip *chip, u32 hdiv,
+				      const u8 *golden_buf, u8 *test_buf)
+{
+	struct aspeed_smc_controller *controller = chip->controller;
+	const struct aspeed_smc_info *info = controller->info;
+	int i;
+	int good_pass = -1, pass_count = 0;
+	u32 shift = (hdiv - 1) << 2;
+	u32 mask = ~(0xfu << shift);
+	u32 fread_timing_val = 0;
+
+#define FREAD_TPASS(i)	(((i) / 2) | (((i) & 1) ? 0 : 8))
+
+	/* Try HCLK delay 0..5, each one with/without delay and look for a
+	 * good pair.
+	 */
+	for (i = 0; i < 12; i++) {
+		bool pass;
+
+		fread_timing_val &= mask;
+		fread_timing_val |= FREAD_TPASS(i) << shift;
+
+		writel(fread_timing_val, controller->regs + info->timing);
+		pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
+		dev_dbg(chip->nor.dev,
+			"  * [%08x] %d HCLK delay, %dns DI delay : %s",
+			fread_timing_val, i / 2, (i & 1) ? 0 : 4,
+			pass ? "PASS" : "FAIL");
+		if (pass) {
+			pass_count++;
+			if (pass_count == 3) {
+				good_pass = i - 1;
+				break;
+			}
+		} else {
+			pass_count = 0;
+		}
+	}
+
+	/* No good setting for this frequency */
+	if (good_pass < 0)
+		return -1;
+
+	/* We have at least one pass of margin, let's use first pass */
+	fread_timing_val &= mask;
+	fread_timing_val |= FREAD_TPASS(good_pass) << shift;
+	writel(fread_timing_val, controller->regs + info->timing);
+	dev_dbg(chip->nor.dev, " * -> good is pass %d [0x%08x]",
+		good_pass, fread_timing_val);
+	return 0;
+}
+
+static bool aspeed_smc_check_calib_data(const u8 *test_buf, u32 size)
+{
+	const u32 *tb32 = (const u32 *) test_buf;
+	u32 i, cnt = 0;
+
+	/* We check if we have enough words that are neither all 0
+	 * nor all 1's so the calibration can be considered valid.
+	 *
+	 * I use an arbitrary threshold for now of 64
+	 */
+	size >>= 2;
+	for (i = 0; i < size; i++) {
+		if (tb32[i] != 0 && tb32[i] != 0xffffffff)
+			cnt++;
+	}
+	return cnt >= 64;
+}
+
+static const u32 aspeed_smc_hclk_divs[] = {
+	0xf, /* HCLK */
+	0x7, /* HCLK/2 */
+	0xe, /* HCLK/3 */
+	0x6, /* HCLK/4 */
+	0xd, /* HCLK/5 */
+};
+
+#define ASPEED_SMC_HCLK_DIV(i) (aspeed_smc_hclk_divs[(i) - 1] << 8)
+
+static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip, u32 max_freq)
+{
+	u8 *golden_buf, *test_buf;
+	int i, rc, best_div = -1;
+	u32 save_read_val = chip->ctl_val[smc_read];
+	u32 ahb_freq = chip->controller->clk_frequency;
+
+	dev_dbg(chip->nor.dev, "AHB frequency: %d MHz", ahb_freq / 1000000);
+
+	test_buf = kmalloc(CALIBRATE_BUF_SIZE * 2, GFP_KERNEL);
+	golden_buf = test_buf + CALIBRATE_BUF_SIZE;
+
+	/* We start with the dumbest setting (keep 4Byte bit) and read
+	 * some data
+	 */
+	chip->ctl_val[smc_read] = (chip->ctl_val[smc_read] & 0x2000) |
+		(0x00 << 28) | /* Single bit */
+		(0x00 << 24) | /* CE# max */
+		(0x03 << 16) | /* use normal reads */
+		(0x00 <<  8) | /* HCLK/16 */
+		(0x00 <<  6) | /* no dummy cycle */
+		(0x00);        /* normal read */
+
+	writel(chip->ctl_val[smc_read], chip->ctl);
+
+	aspeed_smc_read_from_ahb(golden_buf, chip->ahb_base,
+				 CALIBRATE_BUF_SIZE);
+
+	/* Establish our read mode with freq field set to 0 (HCLK/16) */
+	chip->ctl_val[smc_read] = save_read_val & 0xfffff0ff;
+
+	/* Check if calibration data is suitable */
+	if (!aspeed_smc_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) {
+		dev_info(chip->nor.dev,
+			 "Calibration area too uniform, using low speed");
+		writel(chip->ctl_val[smc_read], chip->ctl);
+		kfree(test_buf);
+		return 0;
+	}
+
+	/* Now we iterate the HCLK dividers until we find our breaking point */
+	for (i = ARRAY_SIZE(aspeed_smc_hclk_divs); i > 0; i--) {
+		u32 tv, freq;
+
+		/* Compare timing to max */
+		freq = ahb_freq / i;
+		if (freq >= max_freq)
+			continue;
+
+		/* Set the timing */
+		tv = chip->ctl_val[smc_read] | ASPEED_SMC_HCLK_DIV(i);
+		writel(tv, chip->ctl);
+		dev_dbg(chip->nor.dev, "Trying HCLK/%d...", i);
+		rc = aspeed_smc_calibrate_reads(chip, i, golden_buf, test_buf);
+		if (rc == 0)
+			best_div = i;
+	}
+	kfree(test_buf);
+
+	/* Nothing found ? */
+	if (best_div < 0) {
+		dev_warn(chip->nor.dev, "No good frequency, using dumb slow");
+	} else {
+		dev_dbg(chip->nor.dev, "Found good read timings at HCLK/%d",
+			best_div);
+		chip->ctl_val[smc_read] |= ASPEED_SMC_HCLK_DIV(best_div);
+	}
+
+	writel(chip->ctl_val[smc_read], chip->ctl);
+	return 0;
+}
+
 static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 {
 	struct aspeed_smc_controller *controller = chip->controller;
@@ -807,6 +997,9 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 
 	dev_info(controller->dev, "read control register: %08x\n",
 		 chip->ctl_val[smc_read]);
+
+	if (optimize_read && info->optimize_read)
+		info->optimize_read(chip, chip->clk_rate);
 	return 0;
 }
 
@@ -860,6 +1053,13 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 			break;
 		}
 
+		if (of_property_read_u32(child, "spi-max-frequency",
+					 &chip->clk_rate)) {
+			chip->clk_rate = ASPEED_SPI_DEFAULT_FREQ;
+		}
+		dev_info(dev, "Using %d MHz SPI frequency\n",
+			 chip->clk_rate / 1000000);
+
 		chip->controller = controller;
 		chip->ctl = controller->regs + info->ctl0 + cs * 4;
 		chip->cs = cs;
-- 
2.13.6

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

* Re: [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads
  2018-06-22 12:14 ` [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads Cédric Le Goater
@ 2018-07-23 12:12   ` Joel Stanley
  2018-09-21  2:39     ` Joel Stanley
  0 siblings, 1 reply; 12+ messages in thread
From: Joel Stanley @ 2018-07-23 12:12 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: linux-mtd, Marek Vasut, Boris Brezillon, David Woodhouse,
	Brian Norris, Richard Weinberger, linux-aspeed, Andrew Jeffery

On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
> When reading flash contents, try to use the "command mode" if the AHB
> window configured for the flash module is big enough. Else, just fall
> back to the "user mode" to perform the read.
>
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: Joel Stanley <joel@jms.id.au>

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

* Re: [PATCH 2/4] mtd: spi-nor: aspeed: add support for SPI dual IO read mode
  2018-06-22 12:14 ` [PATCH 2/4] mtd: spi-nor: aspeed: add support for SPI dual IO read mode Cédric Le Goater
@ 2018-07-23 12:12   ` Joel Stanley
  0 siblings, 0 replies; 12+ messages in thread
From: Joel Stanley @ 2018-07-23 12:12 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: linux-mtd, Marek Vasut, Boris Brezillon, David Woodhouse,
	Brian Norris, Richard Weinberger, linux-aspeed, Andrew Jeffery

On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
> Implements support for the dual IO read mode on aspeed SMC/FMC
> controllers which uses both MISO and MOSI lines for data during a read
> to double the read bandwidth.
>
> Still to be done SNOR_PROTO_1_2_2
>
> Based on work from Robert Lippert <roblip@gmail.com>
>
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: Joel Stanley <joel@jms.id.au>

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

* Re: [PATCH 3/4] mtd: spi-nor: aspeed: retrieve the ABH clock frequency
  2018-06-22 12:14 ` [PATCH 3/4] mtd: spi-nor: aspeed: retrieve the ABH clock frequency Cédric Le Goater
@ 2018-07-23 12:13   ` Joel Stanley
  0 siblings, 0 replies; 12+ messages in thread
From: Joel Stanley @ 2018-07-23 12:13 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: linux-mtd, Marek Vasut, Boris Brezillon, David Woodhouse,
	Brian Norris, Richard Weinberger, linux-aspeed, Andrew Jeffery

On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
> We will need the AHB frequency to set the SPI clock frequency to
> perform the SPI calibration sequence and optimize the controller
> settings for the fast reads.
>
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: Joel Stanley <joel@jms.id.au>

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

* Re: [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads
  2018-06-22 12:14 ` [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
@ 2018-07-23 12:16   ` Joel Stanley
  2018-08-01  7:43     ` Cédric Le Goater
  0 siblings, 1 reply; 12+ messages in thread
From: Joel Stanley @ 2018-07-23 12:16 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: linux-mtd, Marek Vasut, Boris Brezillon, David Woodhouse,
	Brian Norris, Richard Weinberger, linux-aspeed, Andrew Jeffery

On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
> Better settings for fast reads are looked for by implementing a SPI
> timing calibration sequence described in the Aspeed SoC specification
> document. The code is based on the OpenPOWER pflash tool and a similar
> sequence using DMAs can be found in the SDK U-Boot.
>
> The SPI calibration performs a loop on different SPI clock rates
> (dividers of the AHB clock rates) and on different input delay cycles
> for each SPI clock rates. The successive read results are compared to
> a golden buffer, read at low speed, to select the safest and fastest
> read settings for the chip.
>
> The "spi-max-frequency" property is used to cap the optimize read
> algorithm on some devices or controllers for which we want a "really"
> safe setting, on the FMC controller chips for instance.
>
> It can also be deactivated at boot time with a kernel parameter
> 'optimize_read', but that was never used on the field.
>
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: Joel Stanley <joel@jms.id.au>

I have also been running these applied to a 4.17 base on ast2400 and
ast2500 systems for the past few months. This week I gave them a spin
on top of linux-next too.

They have looked good so far, so I would encourage the series to me
merged for 4.19 so we can reduce the number of out of tree we use in
OpenBMC systems.

For the series:

Tested-by: Joel Stanley <joel@jms.id.au>

Cheers,

Joel

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

* Re: [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads
  2018-07-23 12:16   ` Joel Stanley
@ 2018-08-01  7:43     ` Cédric Le Goater
  0 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2018-08-01  7:43 UTC (permalink / raw)
  To: Joel Stanley
  Cc: linux-mtd, Marek Vasut, Boris Brezillon, David Woodhouse,
	Brian Norris, Richard Weinberger, linux-aspeed, Andrew Jeffery

On 07/23/2018 02:16 PM, Joel Stanley wrote:
> On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
>> Better settings for fast reads are looked for by implementing a SPI
>> timing calibration sequence described in the Aspeed SoC specification
>> document. The code is based on the OpenPOWER pflash tool and a similar
>> sequence using DMAs can be found in the SDK U-Boot.
>>
>> The SPI calibration performs a loop on different SPI clock rates
>> (dividers of the AHB clock rates) and on different input delay cycles
>> for each SPI clock rates. The successive read results are compared to
>> a golden buffer, read at low speed, to select the safest and fastest
>> read settings for the chip.
>>
>> The "spi-max-frequency" property is used to cap the optimize read
>> algorithm on some devices or controllers for which we want a "really"
>> safe setting, on the FMC controller chips for instance.
>>
>> It can also be deactivated at boot time with a kernel parameter
>> 'optimize_read', but that was never used on the field.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> 
> I have also been running these applied to a 4.17 base on ast2400 and
> ast2500 systems for the past few months. This week I gave them a spin
> on top of linux-next too.
> 
> They have looked good so far, so I would encourage the series to me
> merged for 4.19 so we can reduce the number of out of tree we use in
> OpenBMC systems.
> 
> For the series:
> 
> Tested-by: Joel Stanley <joel@jms.id.au>

The first 3 patches should not be too much of a problem. What about 
patch 4/4 ? It is not the usual way of setting the freq but the Aspeed
controller has its own mean for tuning it. 

Thanks,

C.

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

* Re: [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads
  2018-07-23 12:12   ` Joel Stanley
@ 2018-09-21  2:39     ` Joel Stanley
  2018-09-21  6:09       ` Cédric Le Goater
  0 siblings, 1 reply; 12+ messages in thread
From: Joel Stanley @ 2018-09-21  2:39 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-mtd, Cédric Le Goater, Boris Brezillon,
	David Woodhouse, Brian Norris, Richard Weinberger, linux-aspeed,
	Andrew Jeffery

Hello Mark,

On Mon, 23 Jul 2018 at 21:42, Joel Stanley <joel@jms.id.au> wrote:
>
> On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
> > When reading flash contents, try to use the "command mode" if the AHB
> > window configured for the flash module is big enough. Else, just fall
> > back to the "user mode" to perform the read.
> >
> > Signed-off-by: Cédric Le Goater <clg@kaod.org>
>
> Reviewed-by: Joel Stanley <joel@jms.id.au>

Are you waiting on anything from Cedric or myself for this series. If
not, can we please get them queued for 4.20?

Cheers,

Joel

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

* Re: [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads
  2018-09-21  2:39     ` Joel Stanley
@ 2018-09-21  6:09       ` Cédric Le Goater
  0 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2018-09-21  6:09 UTC (permalink / raw)
  To: Joel Stanley, Marek Vasut
  Cc: linux-mtd, Boris Brezillon, David Woodhouse, Brian Norris,
	Richard Weinberger, linux-aspeed, Andrew Jeffery

On 09/21/2018 04:39 AM, Joel Stanley wrote:
> Hello Mark,
> 
> On Mon, 23 Jul 2018 at 21:42, Joel Stanley <joel@jms.id.au> wrote:
>>
>> On 22 June 2018 at 21:44, Cédric Le Goater <clg@kaod.org> wrote:
>>> When reading flash contents, try to use the "command mode" if the AHB
>>> window configured for the flash module is big enough. Else, just fall
>>> back to the "user mode" to perform the read.
>>>
>>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>>
>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> 
> Are you waiting on anything from Cedric or myself for this series. If
> not, can we please get them queued for 4.20?

There is one typo on the definition of the timing register of the AST2400
SPI controller we should fix. It can come later. Apart from that, I don't 
see any changes I would like to do. May be a couple of variable renames to 
clarify the loop on the hclk cycles and delays to fit the U-Boot driver
which was proposed recently. Please tell me :)

Thanks,

C. 

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

end of thread, other threads:[~2018-09-21  6:09 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-22 12:14 [PATCH 0/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
2018-06-22 12:14 ` [PATCH 1/4] mtd: spi-nor: aspeed: use command mode for reads Cédric Le Goater
2018-07-23 12:12   ` Joel Stanley
2018-09-21  2:39     ` Joel Stanley
2018-09-21  6:09       ` Cédric Le Goater
2018-06-22 12:14 ` [PATCH 2/4] mtd: spi-nor: aspeed: add support for SPI dual IO read mode Cédric Le Goater
2018-07-23 12:12   ` Joel Stanley
2018-06-22 12:14 ` [PATCH 3/4] mtd: spi-nor: aspeed: retrieve the ABH clock frequency Cédric Le Goater
2018-07-23 12:13   ` Joel Stanley
2018-06-22 12:14 ` [PATCH 4/4] mtd: spi-nor: aspeed: introduce optimized settings for fast reads Cédric Le Goater
2018-07-23 12:16   ` Joel Stanley
2018-08-01  7:43     ` Cédric Le Goater

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.