[PATCH linux dev-5.3 13/13] mtd: spi-nor: aspeed: add support for AST2600 training
Cédric Le Goater
clg at kaod.org
Wed Sep 25 22:42:39 AEST 2019
Signed-off-by: Cédric Le Goater <clg at kaod.org>
---
drivers/mtd/spi-nor/aspeed-smc.c | 68 ++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 48164d819a37..e9bc89755912 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -142,6 +142,9 @@ static u32 aspeed_smc_segment_end_ast2600(
static u32 aspeed_smc_segment_reg_ast2600(
struct aspeed_smc_controller *controller, u32 start, u32 end);
+static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip *chip,
+ u32 hdiv, const u8 *golden_buf, u8 *test_buf);
+
static const struct aspeed_smc_info fmc_2600_info = {
.maxsize = 256 * 1024 * 1024,
.nce = 3,
@@ -149,7 +152,11 @@ static const struct aspeed_smc_info fmc_2600_info = {
.we0 = 16,
.ctl0 = 0x10,
.timing = 0x94,
+ .hclk_mask = 0xf0fff0ff,
+ .hdiv_max = 2,
.set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads_ast2600,
.segment_start = aspeed_smc_segment_start_ast2600,
.segment_end = aspeed_smc_segment_end_ast2600,
.segment_reg = aspeed_smc_segment_reg_ast2600,
@@ -162,7 +169,11 @@ static const struct aspeed_smc_info spi_2600_info = {
.we0 = 16,
.ctl0 = 0x10,
.timing = 0x94,
+ .hclk_mask = 0xf0fff0ff,
+ .hdiv_max = 2,
.set_4b = aspeed_smc_chip_set_4b,
+ .optimize_read = aspeed_smc_optimize_read,
+ .calibrate = aspeed_smc_calibrate_reads_ast2600,
.segment_start = aspeed_smc_segment_start_ast2600,
.segment_end = aspeed_smc_segment_end_ast2600,
.segment_reg = aspeed_smc_segment_reg_ast2600,
@@ -1101,6 +1112,63 @@ static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip,
return 0;
}
+#define TIMING_DELAY_DI BIT(3)
+#define TIMING_DELAY_HCYCLE_MAX 5
+
+static int aspeed_smc_calibrate_reads_ast2600(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 hcycle;
+ u32 shift = (hdiv - 2) << 3;
+ u32 mask = ~(0xfu << shift);
+ u32 fread_timing_val = 0;
+
+ for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
+ int delay_ns;
+ bool pass = false;
+
+ fread_timing_val &= mask;
+ fread_timing_val |= hcycle << shift;
+
+ /* no DI input delay first */
+ 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, DI delay none : %s",
+ fread_timing_val, hcycle, pass ? "PASS" : "FAIL");
+ if (pass)
+ return 0;
+
+ /* Add DI input delays */
+ fread_timing_val &= mask;
+ fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift;
+
+ for (delay_ns = 0; delay_ns < 0x10; delay_ns++) {
+ fread_timing_val &= ~(0xf << (4 + shift));
+ fread_timing_val |= delay_ns << (4 + 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, DI delay %d.%dns : %s",
+ fread_timing_val, hcycle, (delay_ns + 1)/2,
+ (delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL");
+ /*
+ * TODO: This is optimistic. We should look
+ * for a working interval and save the middle
+ * value in the read timing register.
+ */
+ if (pass)
+ return 0;
+ }
+ }
+
+ /* No good setting for this frequency */
+ return -1;
+}
+
static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
--
2.21.0
More information about the openbmc
mailing list