Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * ddrmc DDR3 calibration code for NXP's VF610 |
| 4 | * |
| 5 | * Copyright (C) 2018 DENX Software Engineering |
| 6 | * Lukasz Majewski, DENX Software Engineering, lukma@denx.de |
| 7 | * |
| 8 | */ |
| 9 | /* #define DEBUG */ |
| 10 | #include <common.h> |
Simon Glass | 0f2af88 | 2020-05-10 11:40:05 -0600 | [diff] [blame] | 11 | #include <log.h> |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 12 | #include <asm/io.h> |
| 13 | #include <asm/arch/imx-regs.h> |
| 14 | #include <linux/bitmap.h> |
| 15 | |
| 16 | #include "ddrmc-vf610-calibration.h" |
| 17 | |
| 18 | /* |
| 19 | * Documents: |
| 20 | * |
| 21 | * [1] "Vybrid: About DDR leveling feature on DDRMC." |
| 22 | * https://community.nxp.com/thread/395323 |
| 23 | * |
| 24 | * [2] VFxxx Controller Reference Manual, Rev. 0, 10/2016 |
| 25 | * |
| 26 | * |
| 27 | * NOTE |
| 28 | * ==== |
| 29 | * |
| 30 | * NXP recommends setting 'fixed' parameters instead of performing the |
| 31 | * training at each boot. |
| 32 | * |
| 33 | * Use those functions to determine those values on new HW, read the |
| 34 | * calculated value from registers and add them to the board specific |
| 35 | * struct ddrmc_cr_setting. |
| 36 | * |
| 37 | * SW leveling supported operations - CR93[SW_LVL_MODE]: |
| 38 | * |
| 39 | * - 0x0 (b'00) - No leveling |
| 40 | * |
| 41 | * - 0x1 (b'01) - WRLVL_DL_X - It is not recommended to perform this tuning |
| 42 | * on HW designs utilizing non-flyback topology |
| 43 | * (Single DDR3 with x16). |
| 44 | * Instead the WRLVL_DL_0/1 fields shall be set |
| 45 | * based on trace length differences from their |
| 46 | * layout. |
| 47 | * Mismatches up to 25% or tCK (clock period) are |
Michal Simek | cc046dc | 2024-04-16 08:55:19 +0200 | [diff] [blame] | 48 | * allowed, so the value in the filed doesn't have |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 49 | * to be very accurate. |
| 50 | * |
| 51 | * - 0x2 (b'10) - RDLVL_DL_0/1 - refers to adjusting the DQS strobe in relation |
| 52 | * to the DQ signals so that the strobe edge is |
| 53 | * centered in the window of valid read data. |
| 54 | * |
| 55 | * - 0x3 (b'11) - RDLVL_GTDL_0/1 - refers to the delay the PHY uses to un-gate |
| 56 | * the Read DQS strobe pad from the time that the |
| 57 | * PHY enables the pad to input the strobe signal. |
| 58 | * |
| 59 | */ |
| 60 | static int ddr_cal_get_first_edge_index(unsigned long *bmap, enum edge e, |
| 61 | int samples, int start, int max) |
| 62 | { |
| 63 | int i, ret = -1; |
| 64 | |
| 65 | /* |
| 66 | * We look only for the first value (and filter out |
| 67 | * some wrong data) |
| 68 | */ |
| 69 | switch (e) { |
| 70 | case RISING_EDGE: |
| 71 | for (i = start; i <= max - samples; i++) { |
| 72 | if (test_bit(i, bmap)) { |
| 73 | if (!test_bit(i - 1, bmap) && |
| 74 | test_bit(i + 1, bmap) && |
| 75 | test_bit(i + 2, bmap) && |
| 76 | test_bit(i + 3, bmap)) { |
| 77 | return i; |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | break; |
| 82 | case FALLING_EDGE: |
| 83 | for (i = start; i <= max - samples; i++) { |
| 84 | if (!test_bit(i, bmap)) { |
| 85 | if (test_bit(i - 1, bmap) && |
| 86 | test_bit(i - 2, bmap) && |
| 87 | test_bit(i - 3, bmap)) { |
| 88 | return i; |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | return ret; |
| 95 | } |
| 96 | |
| 97 | static void bitmap_print(unsigned long *bmap, int max) |
| 98 | { |
| 99 | int i; |
| 100 | |
| 101 | debug("BITMAP [0x%p]:\n", bmap); |
| 102 | for (i = 0; i <= max; i++) { |
| 103 | debug("%d ", test_bit(i, bmap) ? 1 : 0); |
| 104 | if (i && (i % 32) == (32 - 1)) |
| 105 | debug("\n"); |
| 106 | } |
| 107 | debug("\n"); |
| 108 | } |
| 109 | |
| 110 | #define sw_leveling_op_done \ |
| 111 | while (!(readl(&ddrmr->cr[94]) & DDRMC_CR94_SWLVL_OP_DONE)) |
| 112 | |
| 113 | #define sw_leveling_load_value \ |
| 114 | do { clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SWLVL_LOAD, \ |
| 115 | DDRMC_CR93_SWLVL_LOAD); } while (0) |
| 116 | |
| 117 | #define sw_leveling_start \ |
| 118 | do { clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SWLVL_START, \ |
| 119 | DDRMC_CR93_SWLVL_START); } while (0) |
| 120 | |
| 121 | #define sw_leveling_exit \ |
| 122 | do { clrsetbits_le32(&ddrmr->cr[94], DDRMC_CR94_SWLVL_EXIT, \ |
| 123 | DDRMC_CR94_SWLVL_EXIT); } while (0) |
| 124 | |
| 125 | /* |
| 126 | * RDLVL_DL calibration: |
| 127 | * |
| 128 | * NXP is _NOT_ recommending performing the leveling at each |
| 129 | * boot. Instead - one shall run this procedure on new boards |
| 130 | * and then use hardcoded values. |
| 131 | * |
| 132 | */ |
| 133 | static int ddrmc_cal_dqs_to_dq(struct ddrmr_regs *ddrmr) |
| 134 | { |
| 135 | DECLARE_BITMAP(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); |
| 136 | int rdlvl_dl_0_min = -1, rdlvl_dl_0_max = -1; |
| 137 | int rdlvl_dl_1_min = -1, rdlvl_dl_1_max = -1; |
| 138 | int rdlvl_dl_0, rdlvl_dl_1; |
| 139 | u8 swlvl_rsp; |
| 140 | u32 tmp; |
| 141 | int i; |
| 142 | |
| 143 | /* Read defaults */ |
| 144 | u16 rdlvl_dl_0_def = |
| 145 | (readl(&ddrmr->cr[105]) >> DDRMC_CR105_RDLVL_DL_0_OFF) & 0xFFFF; |
| 146 | u16 rdlvl_dl_1_def = readl(&ddrmr->cr[110]) & 0xFFFF; |
| 147 | |
| 148 | debug("\nRDLVL: ======================\n"); |
| 149 | debug("RDLVL: DQS to DQ (RDLVL)\n"); |
| 150 | |
| 151 | debug("RDLVL: RDLVL_DL_0_DFL:\t 0x%x\n", rdlvl_dl_0_def); |
| 152 | debug("RDLVL: RDLVL_DL_1_DFL:\t 0x%x\n", rdlvl_dl_1_def); |
| 153 | |
| 154 | /* |
| 155 | * Set/Read setup for calibration |
| 156 | * |
| 157 | * Values necessary for leveling from Vybrid RM [2] - page 1600 |
| 158 | */ |
| 159 | writel(0x40703030, &ddrmr->cr[144]); |
| 160 | writel(0x40, &ddrmr->cr[145]); |
| 161 | writel(0x40, &ddrmr->cr[146]); |
| 162 | |
| 163 | tmp = readl(&ddrmr->cr[144]); |
| 164 | debug("RDLVL: PHY_RDLVL_RES:\t 0x%x\n", (tmp >> 24) & 0xFF);// set 0x40 |
| 165 | debug("RDLVL: PHY_RDLV_LOAD:\t 0x%x\n", (tmp >> 16) & 0xFF);// set 0x70 |
| 166 | debug("RDLVL: PHY_RDLV_DLL:\t 0x%x\n", (tmp >> 8) & 0xFF); // set 0x30 |
| 167 | debug("RDLVL: PHY_RDLV_EN:\t 0x%x\n", tmp & 0xFF); //set 0x30 |
| 168 | |
| 169 | tmp = readl(&ddrmr->cr[145]); |
| 170 | debug("RDLVL: PHY_RDLV_RR:\t 0x%x\n", tmp & 0x3FF); //set 0x40 |
| 171 | |
| 172 | tmp = readl(&ddrmr->cr[146]); |
| 173 | debug("RDLVL: PHY_RDLV_RESP:\t 0x%x\n", tmp); //set 0x40 |
| 174 | |
| 175 | /* |
| 176 | * Program/read the leveling edge RDLVL_EDGE = 0 |
| 177 | * |
| 178 | * 0x00 is the correct output on SWLVL_RSP_X |
| 179 | * If by any chance 1s are visible -> wrong number read |
| 180 | */ |
| 181 | clrbits_le32(&ddrmr->cr[101], DDRMC_CR101_PHY_RDLVL_EDGE); |
| 182 | |
| 183 | tmp = readl(&ddrmr->cr[101]); |
| 184 | debug("RDLVL: PHY_RDLVL_EDGE:\t 0x%x\n", |
| 185 | (tmp >> DDRMC_CR101_PHY_RDLVL_EDGE_OFF) & 0x1); //set 0 |
| 186 | |
Michal Simek | cc046dc | 2024-04-16 08:55:19 +0200 | [diff] [blame] | 187 | /* Program Leveling mode - CR93[SW_LVL_MODE] to 'b10 */ |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 188 | clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SW_LVL_MODE(0x3), |
| 189 | DDRMC_CR93_SW_LVL_MODE(0x2)); |
| 190 | tmp = readl(&ddrmr->cr[93]); |
| 191 | debug("RDLVL: SW_LVL_MODE:\t 0x%x\n", |
| 192 | (tmp >> DDRMC_CR93_SW_LVL_MODE_OFF) & 0x3); |
| 193 | |
Michal Simek | cc046dc | 2024-04-16 08:55:19 +0200 | [diff] [blame] | 194 | /* Start procedure - CR93[SWLVL_START] to 'b1 */ |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 195 | sw_leveling_start; |
| 196 | |
| 197 | /* Poll CR94[SWLVL_OP_DONE] */ |
| 198 | sw_leveling_op_done; |
| 199 | |
| 200 | /* |
| 201 | * Program delays for RDLVL_DL_0 |
| 202 | * |
| 203 | * The procedure is to increase the delay values from 0 to 0xFF |
| 204 | * and read the response from the DDRMC |
| 205 | */ |
| 206 | debug("\nRDLVL: ---> RDLVL_DL_0\n"); |
| 207 | bitmap_zero(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); |
| 208 | |
| 209 | for (i = 0; i <= DDRMC_DQS_DQ_MAX_DELAY; i++) { |
| 210 | clrsetbits_le32(&ddrmr->cr[105], |
| 211 | 0xFFFF << DDRMC_CR105_RDLVL_DL_0_OFF, |
| 212 | i << DDRMC_CR105_RDLVL_DL_0_OFF); |
| 213 | |
Michal Simek | cc046dc | 2024-04-16 08:55:19 +0200 | [diff] [blame] | 214 | /* Load values CR93[SWLVL_LOAD] to 'b1 */ |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 215 | sw_leveling_load_value; |
| 216 | |
| 217 | /* Poll CR94[SWLVL_OP_DONE] */ |
| 218 | sw_leveling_op_done; |
| 219 | |
| 220 | /* |
| 221 | * Read Responses - SWLVL_RESP_0 |
| 222 | * |
| 223 | * The 0x00 (correct response when PHY_RDLVL_EDGE = 0) |
| 224 | * -> 1 in the bit vector |
| 225 | */ |
| 226 | swlvl_rsp = (readl(&ddrmr->cr[94]) >> |
| 227 | DDRMC_CR94_SWLVL_RESP_0_OFF) & 0xF; |
| 228 | if (swlvl_rsp == 0) |
| 229 | generic_set_bit(i, rdlvl_rsp); |
| 230 | } |
| 231 | |
| 232 | bitmap_print(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY); |
| 233 | |
| 234 | /* |
| 235 | * First test for rising edge 0x0 -> 0x1 in bitmap |
| 236 | */ |
| 237 | rdlvl_dl_0_min = ddr_cal_get_first_edge_index(rdlvl_rsp, RISING_EDGE, |
| 238 | N_SAMPLES, N_SAMPLES, |
| 239 | DDRMC_DQS_DQ_MAX_DELAY); |
| 240 | |
| 241 | /* |
| 242 | * Secondly test for falling edge 0x1 -> 0x0 in bitmap |
| 243 | */ |
| 244 | rdlvl_dl_0_max = ddr_cal_get_first_edge_index(rdlvl_rsp, FALLING_EDGE, |
| 245 | N_SAMPLES, rdlvl_dl_0_min, |
| 246 | DDRMC_DQS_DQ_MAX_DELAY); |
| 247 | |
| 248 | debug("RDLVL: DL_0 min: %d [0x%x] DL_0 max: %d [0x%x]\n", |
| 249 | rdlvl_dl_0_min, rdlvl_dl_0_min, rdlvl_dl_0_max, rdlvl_dl_0_max); |
| 250 | rdlvl_dl_0 = (rdlvl_dl_0_max - rdlvl_dl_0_min) / 2; |
| 251 | |
| 252 | if (rdlvl_dl_0_max == -1 || rdlvl_dl_0_min == -1 || rdlvl_dl_0 <= 0) { |
| 253 | debug("RDLVL: The DQS to DQ delay cannot be found!\n"); |
| 254 | debug("RDLVL: Using default - slice 0: %d!\n", rdlvl_dl_0_def); |
| 255 | rdlvl_dl_0 = rdlvl_dl_0_def; |
| 256 | } |
| 257 | |
| 258 | debug("\nRDLVL: ---> RDLVL_DL_1\n"); |
| 259 | bitmap_zero(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); |
| 260 | |
| 261 | for (i = 0; i <= DDRMC_DQS_DQ_MAX_DELAY; i++) { |
| 262 | clrsetbits_le32(&ddrmr->cr[110], |
| 263 | 0xFFFF << DDRMC_CR110_RDLVL_DL_1_OFF, |
| 264 | i << DDRMC_CR110_RDLVL_DL_1_OFF); |
| 265 | |
Michal Simek | cc046dc | 2024-04-16 08:55:19 +0200 | [diff] [blame] | 266 | /* Load values CR93[SWLVL_LOAD] to 'b1 */ |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 267 | sw_leveling_load_value; |
| 268 | |
| 269 | /* Poll CR94[SWLVL_OP_DONE] */ |
| 270 | sw_leveling_op_done; |
| 271 | |
| 272 | /* |
| 273 | * Read Responses - SWLVL_RESP_1 |
| 274 | * |
| 275 | * The 0x00 (correct response when PHY_RDLVL_EDGE = 0) |
| 276 | * -> 1 in the bit vector |
| 277 | */ |
| 278 | swlvl_rsp = (readl(&ddrmr->cr[95]) >> |
| 279 | DDRMC_CR95_SWLVL_RESP_1_OFF) & 0xF; |
| 280 | if (swlvl_rsp == 0) |
| 281 | generic_set_bit(i, rdlvl_rsp); |
| 282 | } |
| 283 | |
| 284 | bitmap_print(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY); |
| 285 | |
| 286 | /* |
| 287 | * First test for rising edge 0x0 -> 0x1 in bitmap |
| 288 | */ |
| 289 | rdlvl_dl_1_min = ddr_cal_get_first_edge_index(rdlvl_rsp, RISING_EDGE, |
| 290 | N_SAMPLES, N_SAMPLES, |
| 291 | DDRMC_DQS_DQ_MAX_DELAY); |
| 292 | |
| 293 | /* |
| 294 | * Secondly test for falling edge 0x1 -> 0x0 in bitmap |
| 295 | */ |
| 296 | rdlvl_dl_1_max = ddr_cal_get_first_edge_index(rdlvl_rsp, FALLING_EDGE, |
| 297 | N_SAMPLES, rdlvl_dl_1_min, |
| 298 | DDRMC_DQS_DQ_MAX_DELAY); |
| 299 | |
| 300 | debug("RDLVL: DL_1 min: %d [0x%x] DL_1 max: %d [0x%x]\n", |
| 301 | rdlvl_dl_1_min, rdlvl_dl_1_min, rdlvl_dl_1_max, rdlvl_dl_1_max); |
| 302 | rdlvl_dl_1 = (rdlvl_dl_1_max - rdlvl_dl_1_min) / 2; |
| 303 | |
| 304 | if (rdlvl_dl_1_max == -1 || rdlvl_dl_1_min == -1 || rdlvl_dl_1 <= 0) { |
| 305 | debug("RDLVL: The DQS to DQ delay cannot be found!\n"); |
| 306 | debug("RDLVL: Using default - slice 1: %d!\n", rdlvl_dl_1_def); |
| 307 | rdlvl_dl_1 = rdlvl_dl_1_def; |
| 308 | } |
| 309 | |
| 310 | debug("RDLVL: CALIBRATED: rdlvl_dl_0: 0x%x\t rdlvl_dl_1: 0x%x\n", |
| 311 | rdlvl_dl_0, rdlvl_dl_1); |
| 312 | |
| 313 | /* Write new delay values */ |
| 314 | writel(DDRMC_CR105_RDLVL_DL_0(rdlvl_dl_0), &ddrmr->cr[105]); |
| 315 | writel(DDRMC_CR110_RDLVL_DL_1(rdlvl_dl_1), &ddrmr->cr[110]); |
| 316 | |
| 317 | sw_leveling_load_value; |
| 318 | sw_leveling_op_done; |
| 319 | |
Michal Simek | cc046dc | 2024-04-16 08:55:19 +0200 | [diff] [blame] | 320 | /* Exit procedure - CR94[SWLVL_EXIT] to 'b1 */ |
Lukasz Majewski | 71d42b3 | 2018-12-05 17:04:02 +0100 | [diff] [blame] | 321 | sw_leveling_exit; |
| 322 | |
| 323 | /* Poll CR94[SWLVL_OP_DONE] */ |
| 324 | sw_leveling_op_done; |
| 325 | |
| 326 | return 0; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | * WRLVL_DL calibration: |
| 331 | * |
| 332 | * For non-flyback memory architecture - where one have a single DDR3 x16 |
| 333 | * memory - it is NOT necessary to perform "Write Leveling" |
| 334 | * [3] 'Vybrid DDR3 write leveling' https://community.nxp.com/thread/429362 |
| 335 | * |
| 336 | */ |
| 337 | |
| 338 | int ddrmc_calibration(struct ddrmr_regs *ddrmr) |
| 339 | { |
| 340 | ddrmc_cal_dqs_to_dq(ddrmr); |
| 341 | |
| 342 | return 0; |
| 343 | } |