In title, you can convert sdram->SDRAM On Tue 2015-06-02 22:52:49, dingu...@opensource.altera.com wrote: > From: Dinh Nguyen <dingu...@opensource.altera.com> > > This patch adds the DDR calibration portion of the Altera SDRAM driver. > > Signed-off-by: Dinh Nguyen <dingu...@opensource.altera.com>
> +/* > + * In order to reduce ROM size, most of the selectable calibration steps are > + * decided at compile time based on the user's calibration mode selection, > + * as captured by the STATIC_CALIB_STEPS selection below. > + * > + * However, to support simulation-time selection of fast simulation mode, > where > + * we skip everything except the bare minimum, we need a few of the steps to > + * be dynamic. In those cases, we either use the DYNAMIC_CALIB_STEPS for the > + * check, which is based on the rtl-supplied value, or we dynamically compute > + * the value to use based on the dynamically-chosen calibration mode "." at the end of sentence. > + > +#define DLEVEL 0 > +#define STATIC_IN_RTL_SIM 0 > +#define STATIC_SKIP_DELAY_LOOPS 0 > + > +#define STATIC_CALIB_STEPS (STATIC_IN_RTL_SIM | CALIB_SKIP_FULL_TEST | \ > + STATIC_SKIP_DELAY_LOOPS) Would it make sense to drop simulation-time support for initial merge? > +/* calibration steps requested by the rtl */ > +uint16_t dyn_calib_steps; rtl? > +/* > + * To make CALIB_SKIP_DELAY_LOOPS a dynamic conditional option > + * instead of static, we use boolean logic to select between > + * non-skip and skip values > + * > + * The mask is set to include all bits when not-skipping, but is > + * zero when skipping > + */ "." at the end of sentence. Twice here :-). > + > +uint16_t skip_delay_mask; /* mask off bits when skipping/not-skipping */ > + > +#define SKIP_DELAY_LOOP_VALUE_OR_ZERO(non_skip_value) \ > + ((non_skip_value) & skip_delay_mask) > + > +struct gbl_type *gbl; > +struct param_type *param; > +uint32_t curr_shadow_reg; > + > +static uint32_t rw_mgr_mem_calibrate_write_test(uint32_t rank_bgn, > + uint32_t write_group, uint32_t use_dm, > + uint32_t all_correct, uint32_t *bit_chk, uint32_t all_ranks); > + > +static u32 sdr_get_addr(u32 *base) > +{ > + u32 addr = (u32)base & MGR_SELECT_MASK; You sometimes use uint32_t, and sometimes u32, as seen here. Would it be more readable to just use u32 everywhere? And this function would be really helped by taking "u32 base", not "u32 *", as youd reduce number of typecasts below... > + switch (addr) { > + case BASE_PHY_MGR: > + addr = (((u32)base >> 8) & (1 << 6)) | ((u32)base & 0x3f) | > + SDR_PHYGRP_PHYMGRGRP_ADDRESS; > + break; > + case BASE_RW_MGR: > + addr = ((u32)base & 0x1fff) | SDR_PHYGRP_RWMGRGRP_ADDRESS; > + break; > + case BASE_DATA_MGR: > + addr = ((u32)base & 0x7ff) | SDR_PHYGRP_DATAMGRGRP_ADDRESS; > + break; > + case BASE_SCC_MGR: > + addr = ((u32)base & 0xfff) | SDR_PHYGRP_SCCGRP_ADDRESS; > + break; > + case BASE_REG_FILE: > + addr = ((u32)base & 0x7ff) | SDR_PHYGRP_REGFILEGRP_ADDRESS; > + break; > + case BASE_MMR: > + addr = ((u32)base & 0xfff) | SDR_CTRLGRP_ADDRESS; > + break; Or at least introduce temporary variable... > +static void initialize(void) > +{ > + u32 addr = sdr_get_addr(&phy_mgr_cfg->mux_sel); > + > + debug("%s:%d\n", __func__, __LINE__); Is this debugging neccessary? It will change when you for example change comment in here... > + /* USER calibration has control over path to memory */ > + /* > + * In Hard PHY this is a 2-bit control: > + * 0: AFI Mux Select > + * 1: DDIO Mux Select > + */ > + writel(0x3, SOCFPGA_SDR_ADDRESS + addr); > + > + /* USER memory clock is not stable we begin initialization */ " */" -> " */" > + for (i = 0; i < 16; i++) { > + debug_cond(DLEVEL == 1, "%s:%d: Clearing SCC RFILE index %u", > + __func__, __LINE__, i); Ok, custom debugging system. Neccessary? > +static void scc_mgr_set_dqs_en_delay(uint32_t read_group, uint32_t delay) > +{ > + uint32_t addr = sdr_get_addr((u32 *)SCC_MGR_DQS_EN_DELAY); Now uint32_t vs. u32 on single line... > + for (r = 0; r < RW_MGR_MEM_NUMBER_OF_RANKS; > + r += NUM_RANKS_PER_SHADOW_REG) { > + scc_mgr_set_dqs_en_delay(read_group, delay); > + > + addr = sdr_get_addr(&sdr_scc_mgr->dqs_ena); > + writel(read_group, SOCFPGA_SDR_ADDRESS + addr); > + /* > + * In shadow register mode, the T11 settings are stored in > + * registers in the core, which are updated by the DQS_ENA > + * signals. Not issuing the SCC_MGR_UPD command allows us to > + * save lots of rank switching overhead, by calling > + * select_shadow_regs_for_update with update_scan_chains > + * set to 0. > + */ > + addr = sdr_get_addr(&sdr_scc_mgr->update); > + writel(0, SOCFPGA_SDR_ADDRESS + addr); > + } > + /* > + * In shadow register mode, the T11 settings are stored in > + * registers in the core, which are updated by the DQS_ENA > + * signals. Not issuing the SCC_MGR_UPD command allows us to > + * save lots of rank switching overhead, by calling > + * select_shadow_regs_for_update with update_scan_chains > + * set to 0. > + */ > + addr = sdr_get_addr(&sdr_scc_mgr->update); > + writel(0, SOCFPGA_SDR_ADDRESS + addr); Umm. Two completely same comments, two same pieces of code. Make it function? Does it make sense to run this twice in fact? > +static void scc_set_bypass_mode(uint32_t write_group, uint32_t mode) > +{ > + uint32_t addr; > + /* mode = 0 : Do NOT bypass - Half Rate Mode */ > + /* mode = 1 : Bypass - Full Rate Mode */ Then perhaps the parameter should be named "do_bypass" or "full_rate"? > + if ((RW_MGR_MEM_ADDRESS_MIRRORING >> r) & 0x1) { > + set_jump_as_return(); > + addr = sdr_get_addr((u32 *)RW_MGR_RUN_SINGLE_GROUP); > + writel(RW_MGR_MRS2_MIRR, SOCFPGA_SDR_ADDRESS + addr); > + delay_for_n_mem_clocks(4); > + set_jump_as_return(); > + writel(RW_MGR_MRS3_MIRR, SOCFPGA_SDR_ADDRESS + addr); > + delay_for_n_mem_clocks(4); What is this, some kind of bytecode interpretter? Or does it create a bytecode for someone? > +static int find_working_phase(uint32_t *grp, uint32_t *bit_chk, > + uint32_t dtaps_per_ptap, uint32_t *work_bgn, > + uint32_t *v, uint32_t *d, uint32_t *p, > + uint32_t *i, uint32_t *max_working_cnt) > +{ Undocumented, single-letter parameters do not really make reading the code easy... Should we have structure for work_bgn, v, d, p, i ... instead of separate parameters? > + uint32_t found_begin = 0; > + uint32_t tmp_delay = 0; > + uint32_t test_status; > + > + for (*d = 0; *d <= dtaps_per_ptap; (*d)++, tmp_delay += > + IO_DELAY_PER_DQS_EN_DCHAIN_TAP) { > + *work_bgn = tmp_delay; > + scc_mgr_set_dqs_en_delay_all_ranks(*grp, *d); > + > + for (*i = 0; *i < VFIFO_SIZE; (*i)++) { > + for (*p = 0; *p <= IO_DQS_EN_PHASE_MAX; (*p)++, > *work_bgn += > + IO_DELAY_PER_OPA_TAP) { Does this look like c-code to you? > + } else { > + /* ******************************************************* */ > + /* * step 3-5b: Find the right edge of the window using > + delay taps * */ > + debug_cond(DLEVEL == 2, "%s:%d find_dqs_en_phase:vfifo=%u \ > + ptap=%u dtap=%u bgn=%u\n", __func__, __LINE__, > + v, p, d, work_bgn); > + > + work_end = work_bgn; > + > + /* * The actual increment of dtaps is done outside of the > + if/else loop to share code */ > + > + /* Only here to counterbalance a subtract later on which is > + not needed if this branch of the algorithm is taken */ > + max_working_cnt++; Comment style in this block is even stranger than in other pieces of this patch. > + } else { > + for (i = 0; i < RW_MGR_MEM_DQ_PER_READ_DQS; i++) { > + if (bit_chk & 1) { > + /* Remember a passing test as > + the right_edge */ > + right_edge[i] = d; > + } else { > + if (d != 0) { > + /* If a right edge has not been > + seen yet, then a future passing > + test will mark this edge as the > + left edge */ > + if (right_edge[i] == > + IO_IO_IN_DELAY_MAX + 1) { > + left_edge[i] = -(d + 1); > + } > + } else { > + /* d = 0 failed, but it passed > + when testing the left edge, > + so it must be marginal, > + set it to -1 */ > + if (right_edge[i] == > + IO_IO_IN_DELAY_MAX + 1 > && > + left_edge[i] != > + IO_IO_IN_DELAY_MAX > + + 1) { > + right_edge[i] = -1; > + } When this happens to your code, you know it is time to split your function into sub-functions. > + if > (rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase_sweep_dq_in_delay > + (write_group, read_group, test_bgn)) { > + /* > + * USER Read per-bit deskew can be done on a > + * per shadow register basis. > + */ > + for (rank_bgn = 0, sr = 0; > + rank_bgn < RW_MGR_MEM_NUMBER_OF_RANKS; > + rank_bgn += NUM_RANKS_PER_SHADOW_REG, > + ++sr) { > + /* > + * Determine if this set of ranks > + * should be skipped entirely. > + */ > + if (!param->skip_shadow_regs[sr]) { > + /* > + * If doing read after write > + * calibration, do not update > + * FOM, now - do it then. > + */ > + if (!rw_mgr_mem_calibrate_vfifo_center > + (rank_bgn, write_group, > + read_group, test_bgn, 1, 0)) { > + grp_calibrated = 0; > + failed_substage = > + CAL_SUBSTAGE_VFIFO_CENTER; > + } > + } Here too. Notice that indentation is actually wrong/confusing here, the ifs are nested. > +/* VFIFO Calibration -- Read Deskew Calibration after write deskew */ > +static uint32_t rw_mgr_mem_calibrate_vfifo_end(uint32_t read_group, > + uint32_t test_bgn) > +{ > + uint32_t rank_bgn, sr; > + uint32_t grp_calibrated; > + uint32_t write_group; > + > + debug("%s:%d %u %u", __func__, __LINE__, read_group, test_bgn); > + > + /* update info for sims */ > + > + reg_file_set_stage(CAL_STAGE_VFIFO_AFTER_WRITES); > + reg_file_set_sub_stage(CAL_SUBSTAGE_VFIFO_CENTER); > + > + write_group = read_group; > + > + /* update info for sims */ > + reg_file_set_group(read_group); > + > + grp_calibrated = 1; > + /* Read per-bit deskew can be done on a per shadow register basis */ > + for (rank_bgn = 0, sr = 0; rank_bgn < RW_MGR_MEM_NUMBER_OF_RANKS; > + rank_bgn += NUM_RANKS_PER_SHADOW_REG, ++sr) { > + /* Determine if this set of ranks should be skipped entirely */ > + if (!param->skip_shadow_regs[sr]) { if (param...) continue; Then you get one less level of identation. > + /* This is the last calibration round, update FOM here */ > + if (!rw_mgr_mem_calibrate_vfifo_center(rank_bgn, > + write_group, > + read_group, > + test_bgn, 0, > + 1)) { > + grp_calibrated = 0; > + } ..and you'll be able to do something with this. > + writel(gbl->curr_read_lat, SOCFPGA_SDR_ADDRESS + addr); > + debug_cond(DLEVEL == 2, "%s:%d lfifo: read_lat=%u", > + __func__, __LINE__, gbl->curr_read_lat); > + > + if (!rw_mgr_mem_calibrate_read_test_all_ranks(0, > + NUM_READ_TESTS, > + PASS_ALL_BITS, > + &bit_chk, 1)) { > + break; > + } If you can't fix it any other way, if (!rw_mgr_mem_calibrate_read_test_all_ranks(0, NUM_READ_TESTS, PASS_ALL_BITS, &bit_chk, 1)) { is preferable. And you don't need {}'s around break; > + if (stop == 1) { > + if (d == 0) { > + for (i = 0; i < RW_MGR_MEM_DQ_PER_WRITE_DQS; > + i++) { > + /* d = 0 failed, but it passed when > + testing the left edge, so it must be > + marginal, set it to -1 */ > + if (right_edge[i] == > + IO_IO_OUT1_DELAY_MAX + 1 && > + left_edge[i] != > + IO_IO_OUT1_DELAY_MAX + 1) { > + right_edge[i] = -1; > + } > + } > + } > + break; > + } else { > + for (i = 0; i < RW_MGR_MEM_DQ_PER_WRITE_DQS; i++) { > + if (bit_chk & 1) { > + /* > + * Remember a passing test as > + * the right_edge. > + */ > + right_edge[i] = d; > + } else { > + if (d != 0) { > + /* > + * If a right edge has not > + * been seen yet, then a future > + * passing test will mark this > + * edge as the left edge. > + */ > + if (right_edge[i] == > + IO_IO_OUT1_DELAY_MAX + 1) > + left_edge[i] = -(d + 1); > + } else { > + /* > + * d = 0 failed, but it passed > + * when testing the left edge, > + * so it must be marginal, set > + * it to -1. > + */ > + if (right_edge[i] == > + IO_IO_OUT1_DELAY_MAX + 1 && > + left_edge[i] != > + IO_IO_OUT1_DELAY_MAX + 1) > + right_edge[i] = -1; > + /* > + * If a right edge has not been > + * seen yet, then a future > + * passing test will mark this > + * edge as the left edge. > + */ > + else if (right_edge[i] == > + IO_IO_OUT1_DELAY_MAX + > + 1) > + left_edge[i] = -(d + 1); > + } > + } Something needs to be done here, too... > + for (read_group = write_group * > + RW_MGR_MEM_IF_READ_DQS_WIDTH / > + RW_MGR_MEM_IF_WRITE_DQS_WIDTH, > + read_test_bgn = 0; > + read_group < (write_group + 1) * > + RW_MGR_MEM_IF_READ_DQS_WIDTH / > + RW_MGR_MEM_IF_WRITE_DQS_WIDTH && > + group_failed == 0; > + read_group++, read_test_bgn += > + RW_MGR_MEM_DQ_PER_READ_DQS) { > + /* Calibrate the VFIFO */ > + if (!((STATIC_CALIB_STEPS) & > + CALIB_SKIP_VFIFO)) { > + if (!rw_mgr_mem_calibrate_vfifo > + (read_group, > + read_test_bgn)) { > + group_failed = 1; > + > + if (!(gbl-> > + phy_debug_mode_flags & > + PHY_DEBUG_SWEEP_ALL_GROUPS)) { > + return 0; > + } > + } > + } > + } > + > + /* Calibrate the output side */ > + if (group_failed == 0) { > + for (rank_bgn = 0, sr = 0; rank_bgn > + < RW_MGR_MEM_NUMBER_OF_RANKS; > + rank_bgn += > + NUM_RANKS_PER_SHADOW_REG, > + ++sr) { > + sr_failed = 0; > + if (!((STATIC_CALIB_STEPS) & > + CALIB_SKIP_WRITES)) { > + if ((STATIC_CALIB_STEPS) > + & CALIB_SKIP_DELAY_SWEEPS) { > + /* not needed in quick mode! */ > + } else { > + /* > + * Determine if this set of > + * ranks should be skipped > + * entirely. > + */ > + if (!param->skip_shadow_regs[sr]) { > + if (!rw_mgr_mem_calibrate_writes > + (rank_bgn, write_group, > + write_test_bgn)) { > + sr_failed = 1; > + if (!(gbl-> > + phy_debug_mode_flags & > + PHY_DEBUG_SWEEP_ALL_GROUPS)) { > + return 0; > + } > + } > + } > + } > + } > + if (sr_failed != 0) > + group_failed = 1; > + } > + } > + > + if (group_failed == 0) { > + for (read_group = write_group * > + RW_MGR_MEM_IF_READ_DQS_WIDTH / > + RW_MGR_MEM_IF_WRITE_DQS_WIDTH, > + read_test_bgn = 0; > + read_group < (write_group + 1) > + * RW_MGR_MEM_IF_READ_DQS_WIDTH > + / RW_MGR_MEM_IF_WRITE_DQS_WIDTH > && > + group_failed == 0; > + read_group++, read_test_bgn += > + RW_MGR_MEM_DQ_PER_READ_DQS) { > + if (!((STATIC_CALIB_STEPS) & > + CALIB_SKIP_WRITES)) { > + if (!rw_mgr_mem_calibrate_vfifo_end > + (read_group, read_test_bgn)) { > + group_failed = 1; > + > + if (!(gbl->phy_debug_mode_flags > + & PHY_DEBUG_SWEEP_ALL_GROUPS)) { > + return 0; > + } > + } > + } > + } > + } And here. Note how indentation is so deep ifs are actually indented the other way around here. I guess it would be better to ignore 80-columns rule than _this_. But splitting it into smaller functions is of course preffered. Thanks, Pavel -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot