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