Home | History | Annotate | Download | only in dram
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (C) 2011-2014 Panasonic Corporation
      4  * Copyright (C) 2015-2016 Socionext Inc.
      5  */
      6 
      7 #include <linux/bitops.h>
      8 #include <linux/delay.h>
      9 #include <linux/errno.h>
     10 #include <linux/io.h>
     11 #include <linux/kernel.h>
     12 #include <linux/printk.h>
     13 #include <time.h>
     14 
     15 #include "ddrphy-init.h"
     16 #include "ddrphy-regs.h"
     17 
     18 /* for LD4, Pro4, sLD8 */
     19 #define NR_DATX8_PER_DDRPHY	2
     20 
     21 void ddrphy_prepare_training(void __iomem *phy_base, int rank)
     22 {
     23 	void __iomem *dx_base = phy_base + PHY_DX_BASE;
     24 	int dx;
     25 	u32 tmp;
     26 
     27 	for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
     28 		tmp = readl(dx_base + PHY_DX_GCR);
     29 		/* Specify the rank that should be write leveled */
     30 		tmp &= ~PHY_DX_GCR_WLRKEN_MASK;
     31 		tmp |= (1 << (PHY_DX_GCR_WLRKEN_SHIFT + rank)) &
     32 			PHY_DX_GCR_WLRKEN_MASK;
     33 		writel(tmp, dx_base + PHY_DX_GCR);
     34 		dx_base += PHY_DX_STRIDE;
     35 	}
     36 
     37 	tmp = readl(phy_base + PHY_DTCR);
     38 	/* Specify the rank used during data bit deskew and eye centering */
     39 	tmp &= ~PHY_DTCR_DTRANK_MASK;
     40 	tmp |= (rank << PHY_DTCR_DTRANK_SHIFT) & PHY_DTCR_DTRANK_MASK;
     41 	/* Use Multi-Purpose Register for DQS gate training */
     42 	tmp |= PHY_DTCR_DTMPR;
     43 	/* Specify the rank enabled for data-training */
     44 	tmp &= ~PHY_DTCR_RANKEN_MASK;
     45 	tmp |= (1 << (PHY_DTCR_RANKEN_SHIFT + rank)) & PHY_DTCR_RANKEN_MASK;
     46 	writel(tmp, phy_base + PHY_DTCR);
     47 }
     48 
     49 struct ddrphy_init_sequence {
     50 	char *description;
     51 	u32 init_flag;
     52 	u32 done_flag;
     53 	u32 err_flag;
     54 };
     55 
     56 static const struct ddrphy_init_sequence init_sequence[] = {
     57 	{
     58 		"DRAM Initialization",
     59 		PHY_PIR_DRAMRST | PHY_PIR_DRAMINIT,
     60 		PHY_PGSR0_DIDONE,
     61 		PHY_PGSR0_DIERR
     62 	},
     63 	{
     64 		"Write Leveling",
     65 		PHY_PIR_WL,
     66 		PHY_PGSR0_WLDONE,
     67 		PHY_PGSR0_WLERR
     68 	},
     69 	{
     70 		"Read DQS Gate Training",
     71 		PHY_PIR_QSGATE,
     72 		PHY_PGSR0_QSGDONE,
     73 		PHY_PGSR0_QSGERR
     74 	},
     75 	{
     76 		"Write Leveling Adjustment",
     77 		PHY_PIR_WLADJ,
     78 		PHY_PGSR0_WLADONE,
     79 		PHY_PGSR0_WLAERR
     80 	},
     81 	{
     82 		"Read Bit Deskew",
     83 		PHY_PIR_RDDSKW,
     84 		PHY_PGSR0_RDDONE,
     85 		PHY_PGSR0_RDERR
     86 	},
     87 	{
     88 		"Write Bit Deskew",
     89 		PHY_PIR_WRDSKW,
     90 		PHY_PGSR0_WDDONE,
     91 		PHY_PGSR0_WDERR
     92 	},
     93 	{
     94 		"Read Eye Training",
     95 		PHY_PIR_RDEYE,
     96 		PHY_PGSR0_REDONE,
     97 		PHY_PGSR0_REERR
     98 	},
     99 	{
    100 		"Write Eye Training",
    101 		PHY_PIR_WREYE,
    102 		PHY_PGSR0_WEDONE,
    103 		PHY_PGSR0_WEERR
    104 	}
    105 };
    106 
    107 int ddrphy_training(void __iomem *phy_base)
    108 {
    109 	int i;
    110 	u32 pgsr0;
    111 	u32 init_flag = PHY_PIR_INIT;
    112 	u32 done_flag = PHY_PGSR0_IDONE;
    113 	int timeout = 50000; /* 50 msec is long enough */
    114 #ifdef DEBUG
    115 	ulong start = get_timer(0);
    116 #endif
    117 
    118 	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
    119 		init_flag |= init_sequence[i].init_flag;
    120 		done_flag |= init_sequence[i].done_flag;
    121 	}
    122 
    123 	writel(init_flag, phy_base + PHY_PIR);
    124 
    125 	do {
    126 		if (--timeout < 0) {
    127 			pr_err("timeout during DDR training\n");
    128 			return -ETIMEDOUT;
    129 		}
    130 		udelay(1);
    131 		pgsr0 = readl(phy_base + PHY_PGSR0);
    132 	} while ((pgsr0 & done_flag) != done_flag);
    133 
    134 	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
    135 		if (pgsr0 & init_sequence[i].err_flag) {
    136 			pr_err("%s failed\n", init_sequence[i].description);
    137 			return -EIO;
    138 		}
    139 	}
    140 
    141 #ifdef DEBUG
    142 	pr_debug("DDR training: elapsed time %ld msec\n", get_timer(start));
    143 #endif
    144 
    145 	return 0;
    146 }
    147