// SPDX-License-Identifier: GPL-2.0+ /* * ddrmc DDR3 calibration code for NXP's VF610 * * Copyright (C) 2018 DENX Software Engineering * Lukasz Majewski, DENX Software Engineering, lukma@denx.de * */ /* #define DEBUG */ #include <common.h> #include <log.h> #include <asm/io.h> #include <asm/arch/imx-regs.h> #include <linux/bitmap.h> #include "ddrmc-vf610-calibration.h" /* * Documents: * * [1] "Vybrid: About DDR leveling feature on DDRMC." * https://community.nxp.com/thread/395323 * * [2] VFxxx Controller Reference Manual, Rev. 0, 10/2016 * * * NOTE * ==== * * NXP recommends setting 'fixed' parameters instead of performing the * training at each boot. * * Use those functions to determine those values on new HW, read the * calculated value from registers and add them to the board specific * struct ddrmc_cr_setting. * * SW leveling supported operations - CR93[SW_LVL_MODE]: * * - 0x0 (b'00) - No leveling * * - 0x1 (b'01) - WRLVL_DL_X - It is not recommended to perform this tuning * on HW designs utilizing non-flyback topology * (Single DDR3 with x16). * Instead the WRLVL_DL_0/1 fields shall be set * based on trace length differences from their * layout. * Mismatches up to 25% or tCK (clock period) are * allowed, so the value in the filed doesn’t have * to be very accurate. * * - 0x2 (b'10) - RDLVL_DL_0/1 - refers to adjusting the DQS strobe in relation * to the DQ signals so that the strobe edge is * centered in the window of valid read data. * * - 0x3 (b'11) - RDLVL_GTDL_0/1 - refers to the delay the PHY uses to un-gate * the Read DQS strobe pad from the time that the * PHY enables the pad to input the strobe signal. * */ static int ddr_cal_get_first_edge_index(unsigned long *bmap, enum edge e, int samples, int start, int max) { int i, ret = -1; /* * We look only for the first value (and filter out * some wrong data) */ switch (e) { case RISING_EDGE: for (i = start; i <= max - samples; i++) { if (test_bit(i, bmap)) { if (!test_bit(i - 1, bmap) && test_bit(i + 1, bmap) && test_bit(i + 2, bmap) && test_bit(i + 3, bmap)) { return i; } } } break; case FALLING_EDGE: for (i = start; i <= max - samples; i++) { if (!test_bit(i, bmap)) { if (test_bit(i - 1, bmap) && test_bit(i - 2, bmap) && test_bit(i - 3, bmap)) { return i; } } } } return ret; } static void bitmap_print(unsigned long *bmap, int max) { int i; debug("BITMAP [0x%p]:\n", bmap); for (i = 0; i <= max; i++) { debug("%d ", test_bit(i, bmap) ? 1 : 0); if (i && (i % 32) == (32 - 1)) debug("\n"); } debug("\n"); } #define sw_leveling_op_done \ while (!(readl(&ddrmr->cr[94]) & DDRMC_CR94_SWLVL_OP_DONE)) #define sw_leveling_load_value \ do { clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SWLVL_LOAD, \ DDRMC_CR93_SWLVL_LOAD); } while (0) #define sw_leveling_start \ do { clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SWLVL_START, \ DDRMC_CR93_SWLVL_START); } while (0) #define sw_leveling_exit \ do { clrsetbits_le32(&ddrmr->cr[94], DDRMC_CR94_SWLVL_EXIT, \ DDRMC_CR94_SWLVL_EXIT); } while (0) /* * RDLVL_DL calibration: * * NXP is _NOT_ recommending performing the leveling at each * boot. Instead - one shall run this procedure on new boards * and then use hardcoded values. * */ static int ddrmc_cal_dqs_to_dq(struct ddrmr_regs *ddrmr) { DECLARE_BITMAP(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); int rdlvl_dl_0_min = -1, rdlvl_dl_0_max = -1; int rdlvl_dl_1_min = -1, rdlvl_dl_1_max = -1; int rdlvl_dl_0, rdlvl_dl_1; u8 swlvl_rsp; u32 tmp; int i; /* Read defaults */ u16 rdlvl_dl_0_def = (readl(&ddrmr->cr[105]) >> DDRMC_CR105_RDLVL_DL_0_OFF) & 0xFFFF; u16 rdlvl_dl_1_def = readl(&ddrmr->cr[110]) & 0xFFFF; debug("\nRDLVL: ======================\n"); debug("RDLVL: DQS to DQ (RDLVL)\n"); debug("RDLVL: RDLVL_DL_0_DFL:\t 0x%x\n", rdlvl_dl_0_def); debug("RDLVL: RDLVL_DL_1_DFL:\t 0x%x\n", rdlvl_dl_1_def); /* * Set/Read setup for calibration * * Values necessary for leveling from Vybrid RM [2] - page 1600 */ writel(0x40703030, &ddrmr->cr[144]); writel(0x40, &ddrmr->cr[145]); writel(0x40, &ddrmr->cr[146]); tmp = readl(&ddrmr->cr[144]); debug("RDLVL: PHY_RDLVL_RES:\t 0x%x\n", (tmp >> 24) & 0xFF);// set 0x40 debug("RDLVL: PHY_RDLV_LOAD:\t 0x%x\n", (tmp >> 16) & 0xFF);// set 0x70 debug("RDLVL: PHY_RDLV_DLL:\t 0x%x\n", (tmp >> 8) & 0xFF); // set 0x30 debug("RDLVL: PHY_RDLV_EN:\t 0x%x\n", tmp & 0xFF); //set 0x30 tmp = readl(&ddrmr->cr[145]); debug("RDLVL: PHY_RDLV_RR:\t 0x%x\n", tmp & 0x3FF); //set 0x40 tmp = readl(&ddrmr->cr[146]); debug("RDLVL: PHY_RDLV_RESP:\t 0x%x\n", tmp); //set 0x40 /* * Program/read the leveling edge RDLVL_EDGE = 0 * * 0x00 is the correct output on SWLVL_RSP_X * If by any chance 1s are visible -> wrong number read */ clrbits_le32(&ddrmr->cr[101], DDRMC_CR101_PHY_RDLVL_EDGE); tmp = readl(&ddrmr->cr[101]); debug("RDLVL: PHY_RDLVL_EDGE:\t 0x%x\n", (tmp >> DDRMC_CR101_PHY_RDLVL_EDGE_OFF) & 0x1); //set 0 /* Program Leveling mode - CR93[SW_LVL_MODE] to ’b10 */ clrsetbits_le32(&ddrmr->cr[93], DDRMC_CR93_SW_LVL_MODE(0x3), DDRMC_CR93_SW_LVL_MODE(0x2)); tmp = readl(&ddrmr->cr[93]); debug("RDLVL: SW_LVL_MODE:\t 0x%x\n", (tmp >> DDRMC_CR93_SW_LVL_MODE_OFF) & 0x3); /* Start procedure - CR93[SWLVL_START] to ’b1 */ sw_leveling_start; /* Poll CR94[SWLVL_OP_DONE] */ sw_leveling_op_done; /* * Program delays for RDLVL_DL_0 * * The procedure is to increase the delay values from 0 to 0xFF * and read the response from the DDRMC */ debug("\nRDLVL: ---> RDLVL_DL_0\n"); bitmap_zero(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); for (i = 0; i <= DDRMC_DQS_DQ_MAX_DELAY; i++) { clrsetbits_le32(&ddrmr->cr[105], 0xFFFF << DDRMC_CR105_RDLVL_DL_0_OFF, i << DDRMC_CR105_RDLVL_DL_0_OFF); /* Load values CR93[SWLVL_LOAD] to ’b1 */ sw_leveling_load_value; /* Poll CR94[SWLVL_OP_DONE] */ sw_leveling_op_done; /* * Read Responses - SWLVL_RESP_0 * * The 0x00 (correct response when PHY_RDLVL_EDGE = 0) * -> 1 in the bit vector */ swlvl_rsp = (readl(&ddrmr->cr[94]) >> DDRMC_CR94_SWLVL_RESP_0_OFF) & 0xF; if (swlvl_rsp == 0) generic_set_bit(i, rdlvl_rsp); } bitmap_print(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY); /* * First test for rising edge 0x0 -> 0x1 in bitmap */ rdlvl_dl_0_min = ddr_cal_get_first_edge_index(rdlvl_rsp, RISING_EDGE, N_SAMPLES, N_SAMPLES, DDRMC_DQS_DQ_MAX_DELAY); /* * Secondly test for falling edge 0x1 -> 0x0 in bitmap */ rdlvl_dl_0_max = ddr_cal_get_first_edge_index(rdlvl_rsp, FALLING_EDGE, N_SAMPLES, rdlvl_dl_0_min, DDRMC_DQS_DQ_MAX_DELAY); debug("RDLVL: DL_0 min: %d [0x%x] DL_0 max: %d [0x%x]\n", rdlvl_dl_0_min, rdlvl_dl_0_min, rdlvl_dl_0_max, rdlvl_dl_0_max); rdlvl_dl_0 = (rdlvl_dl_0_max - rdlvl_dl_0_min) / 2; if (rdlvl_dl_0_max == -1 || rdlvl_dl_0_min == -1 || rdlvl_dl_0 <= 0) { debug("RDLVL: The DQS to DQ delay cannot be found!\n"); debug("RDLVL: Using default - slice 0: %d!\n", rdlvl_dl_0_def); rdlvl_dl_0 = rdlvl_dl_0_def; } debug("\nRDLVL: ---> RDLVL_DL_1\n"); bitmap_zero(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY + 1); for (i = 0; i <= DDRMC_DQS_DQ_MAX_DELAY; i++) { clrsetbits_le32(&ddrmr->cr[110], 0xFFFF << DDRMC_CR110_RDLVL_DL_1_OFF, i << DDRMC_CR110_RDLVL_DL_1_OFF); /* Load values CR93[SWLVL_LOAD] to ’b1 */ sw_leveling_load_value; /* Poll CR94[SWLVL_OP_DONE] */ sw_leveling_op_done; /* * Read Responses - SWLVL_RESP_1 * * The 0x00 (correct response when PHY_RDLVL_EDGE = 0) * -> 1 in the bit vector */ swlvl_rsp = (readl(&ddrmr->cr[95]) >> DDRMC_CR95_SWLVL_RESP_1_OFF) & 0xF; if (swlvl_rsp == 0) generic_set_bit(i, rdlvl_rsp); } bitmap_print(rdlvl_rsp, DDRMC_DQS_DQ_MAX_DELAY); /* * First test for rising edge 0x0 -> 0x1 in bitmap */ rdlvl_dl_1_min = ddr_cal_get_first_edge_index(rdlvl_rsp, RISING_EDGE, N_SAMPLES, N_SAMPLES, DDRMC_DQS_DQ_MAX_DELAY); /* * Secondly test for falling edge 0x1 -> 0x0 in bitmap */ rdlvl_dl_1_max = ddr_cal_get_first_edge_index(rdlvl_rsp, FALLING_EDGE, N_SAMPLES, rdlvl_dl_1_min, DDRMC_DQS_DQ_MAX_DELAY); debug("RDLVL: DL_1 min: %d [0x%x] DL_1 max: %d [0x%x]\n", rdlvl_dl_1_min, rdlvl_dl_1_min, rdlvl_dl_1_max, rdlvl_dl_1_max); rdlvl_dl_1 = (rdlvl_dl_1_max - rdlvl_dl_1_min) / 2; if (rdlvl_dl_1_max == -1 || rdlvl_dl_1_min == -1 || rdlvl_dl_1 <= 0) { debug("RDLVL: The DQS to DQ delay cannot be found!\n"); debug("RDLVL: Using default - slice 1: %d!\n", rdlvl_dl_1_def); rdlvl_dl_1 = rdlvl_dl_1_def; } debug("RDLVL: CALIBRATED: rdlvl_dl_0: 0x%x\t rdlvl_dl_1: 0x%x\n", rdlvl_dl_0, rdlvl_dl_1); /* Write new delay values */ writel(DDRMC_CR105_RDLVL_DL_0(rdlvl_dl_0), &ddrmr->cr[105]); writel(DDRMC_CR110_RDLVL_DL_1(rdlvl_dl_1), &ddrmr->cr[110]); sw_leveling_load_value; sw_leveling_op_done; /* Exit procedure - CR94[SWLVL_EXIT] to ’b1 */ sw_leveling_exit; /* Poll CR94[SWLVL_OP_DONE] */ sw_leveling_op_done; return 0; } /* * WRLVL_DL calibration: * * For non-flyback memory architecture - where one have a single DDR3 x16 * memory - it is NOT necessary to perform "Write Leveling" * [3] 'Vybrid DDR3 write leveling' https://community.nxp.com/thread/429362 * */ int ddrmc_calibration(struct ddrmr_regs *ddrmr) { ddrmc_cal_dqs_to_dq(ddrmr); return 0; }