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