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