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