/** * This function performs SW read leveling to compensate DQ-DQS skew at * receiver it first finds the optimal read offset value on each DQS * then applies the value to PHY. * * Read offset value has its min margin and max margin. If read offset * value exceeds its min or max margin, read data will have corruption. * To avoid this we are doing sw read leveling. * * SW read leveling is: * 1> Finding offset value's left_limit and right_limit * 2> and calculate its center value * 3> finally programs that center value to PHY * 4> then PHY gets its optimal offset value. * * @param phy_ctrl pointer to the current phy controller * @param ch channel number * @param coarse_lock_val The coarse lock value read from PHY_CON13. * (0 - 0x7f) */ static void software_find_read_offset(struct exynos5420_phy_control *phy_ctrl, int ch, unsigned int coarse_lock_val) { unsigned int offsetr_cent; int byte_lane; int left_limit; int right_limit; int left[NUM_BYTE_LANES]; int right[NUM_BYTE_LANES]; int i; /* Fill the memory with test patterns */ for (i = 0; i < ARRAY_SIZE(test_pattern); i++) writel(test_pattern[i], test_addr + i * 4 + ch * 0x80); /* Figure out the limits we'll test with; keep -127 < limit < 127 */ left_limit = DEFAULT_DQS - coarse_lock_val; right_limit = DEFAULT_DQS + coarse_lock_val; if (right_limit > 127) right_limit = 127; /* Fill in the location where reads were OK from left and right */ test_shifts(phy_ctrl, ch, left_limit, right_limit, left); test_shifts(phy_ctrl, ch, right_limit, left_limit, right); /* Make a final value by taking the center between the left and right */ offsetr_cent = 0; for (byte_lane = 0; byte_lane < NUM_BYTE_LANES; byte_lane++) { int temp_center; unsigned int vmwc; temp_center = (left[byte_lane] + right[byte_lane]) / 2; vmwc = make_signed_byte(temp_center); offsetr_cent |= vmwc << (8 * byte_lane); } dmc_set_read_offset_value(phy_ctrl, offsetr_cent); }
int main() { test_shifts(); }