1 /* 2 * Copyright (C) 2011-2015 Masahiro Yamada <yamada.masahiro@socionext.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <linux/err.h> 9 #include <linux/io.h> 10 #include <mach/ddrphy-regs.h> 11 12 void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank) 13 { 14 int dx; 15 u32 __iomem tmp, *p; 16 17 for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) { 18 p = &phy->dx[dx].gcr; 19 20 tmp = readl(p); 21 /* Specify the rank that should be write leveled */ 22 tmp &= ~DXGCR_WLRKEN_MASK; 23 tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK; 24 writel(tmp, p); 25 } 26 27 p = &phy->dtcr; 28 29 tmp = readl(p); 30 /* Specify the rank used during data bit deskew and eye centering */ 31 tmp &= ~DTCR_DTRANK_MASK; 32 tmp |= (rank << DTCR_DTRANK_SHIFT) & DTCR_DTRANK_MASK; 33 /* Use Multi-Purpose Register for DQS gate training */ 34 tmp |= DTCR_DTMPR; 35 /* Specify the rank enabled for data-training */ 36 tmp &= ~DTCR_RANKEN_MASK; 37 tmp |= (1 << (DTCR_RANKEN_SHIFT + rank)) & DTCR_RANKEN_MASK; 38 writel(tmp, p); 39 } 40 41 struct ddrphy_init_sequence { 42 char *description; 43 u32 init_flag; 44 u32 done_flag; 45 u32 err_flag; 46 }; 47 48 static const struct ddrphy_init_sequence init_sequence[] = { 49 { 50 "DRAM Initialization", 51 PIR_DRAMRST | PIR_DRAMINIT, 52 PGSR0_DIDONE, 53 PGSR0_DIERR 54 }, 55 { 56 "Write Leveling", 57 PIR_WL, 58 PGSR0_WLDONE, 59 PGSR0_WLERR 60 }, 61 { 62 "Read DQS Gate Training", 63 PIR_QSGATE, 64 PGSR0_QSGDONE, 65 PGSR0_QSGERR 66 }, 67 { 68 "Write Leveling Adjustment", 69 PIR_WLADJ, 70 PGSR0_WLADONE, 71 PGSR0_WLAERR 72 }, 73 { 74 "Read Bit Deskew", 75 PIR_RDDSKW, 76 PGSR0_RDDONE, 77 PGSR0_RDERR 78 }, 79 { 80 "Write Bit Deskew", 81 PIR_WRDSKW, 82 PGSR0_WDDONE, 83 PGSR0_WDERR 84 }, 85 { 86 "Read Eye Training", 87 PIR_RDEYE, 88 PGSR0_REDONE, 89 PGSR0_REERR 90 }, 91 { 92 "Write Eye Training", 93 PIR_WREYE, 94 PGSR0_WEDONE, 95 PGSR0_WEERR 96 } 97 }; 98 99 int ddrphy_training(struct ddrphy __iomem *phy) 100 { 101 int i; 102 u32 pgsr0; 103 u32 init_flag = PIR_INIT; 104 u32 done_flag = PGSR0_IDONE; 105 int timeout = 50000; /* 50 msec is long enough */ 106 #ifdef DISPLAY_ELAPSED_TIME 107 ulong start = get_timer(0); 108 #endif 109 110 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) { 111 init_flag |= init_sequence[i].init_flag; 112 done_flag |= init_sequence[i].done_flag; 113 } 114 115 writel(init_flag, &phy->pir); 116 117 do { 118 if (--timeout < 0) { 119 printf("%s: error: timeout during DDR training\n", 120 __func__); 121 return -ETIMEDOUT; 122 } 123 udelay(1); 124 pgsr0 = readl(&phy->pgsr[0]); 125 } while ((pgsr0 & done_flag) != done_flag); 126 127 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) { 128 if (pgsr0 & init_sequence[i].err_flag) { 129 printf("%s: error: %s failed\n", __func__, 130 init_sequence[i].description); 131 return -EIO; 132 } 133 } 134 135 #ifdef DISPLAY_ELAPSED_TIME 136 printf("%s: info: elapsed time %ld msec\n", get_timer(start)); 137 #endif 138 139 return 0; 140 } 141