All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pavel Machek <pavel@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCHv4 2/3] driver/ddr/altera: Add the sdram calibration portion
Date: Tue, 9 Jun 2015 14:21:45 +0200	[thread overview]
Message-ID: <20150609122145.GB29408@amd> (raw)
In-Reply-To: <1433303570-10004-3-git-send-email-dinguyen@opensource.altera.com>

In title, you can convert sdram->SDRAM

On Tue 2015-06-02 22:52:49, dinguyen at opensource.altera.com wrote:
> From: Dinh Nguyen <dinguyen@opensource.altera.com>
> 
> This patch adds the DDR calibration portion of the Altera SDRAM driver.
> 
> Signed-off-by: Dinh Nguyen <dinguyen@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

  reply	other threads:[~2015-06-09 12:21 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-03  3:52 [U-Boot] [PATCHv4 0/3] drivers/ddr/altera: Add the DDR controller driver for SoCFPGA dinguyen at opensource.altera.com
2015-06-03  3:52 ` [U-Boot] [PATCHv4 1/3] driver/ddr/altera: Add DDR driver for Altera's SDRAM controller dinguyen at opensource.altera.com
2015-06-09 11:55   ` Pavel Machek
2015-06-09 12:58     ` Wolfgang Denk
2015-06-09 15:51     ` Dinh Nguyen
2015-06-22  9:38       ` Chin Liang See
2015-06-22 10:56         ` Pavel Machek
2015-06-03  3:52 ` [U-Boot] [PATCHv4 2/3] driver/ddr/altera: Add the sdram calibration portion dinguyen at opensource.altera.com
2015-06-09 12:21   ` Pavel Machek [this message]
2015-06-03  3:52 ` [U-Boot] [PATCHv4 3/3] arm: socfpga: enable the Altera SDRAM controller driver dinguyen at opensource.altera.com
2015-06-09 12:25   ` Pavel Machek
2015-06-26 16:43 ` [U-Boot] [PATCHv4 0/3] drivers/ddr/altera: Add the DDR controller driver for SoCFPGA Marek Vasut
2015-06-26 20:01   ` Marek Vasut
2015-07-12 19:50     ` Marek Vasut
2015-07-17 19:58       ` Dinh Nguyen
2015-07-17 20:22         ` Marek Vasut
2015-07-18 23:51           ` Marek Vasut
2015-07-20 13:40           ` Dinh Nguyen
2015-07-20 18:36             ` Marek Vasut
2015-07-20 19:31               ` Dinh Nguyen
2015-07-20 19:40                 ` Marek Vasut
2015-07-21 22:46                   ` Dinh Nguyen
2015-07-22  3:24                     ` Marek Vasut
2015-07-23 18:29                       ` Dinh Nguyen
2015-07-24  3:57                         ` Marek Vasut
2015-07-22  8:27                   ` Dinh Nguyen
2015-07-22  9:00                     ` Marek Vasut
2015-07-22 12:57                       ` Dinh Nguyen
2015-07-22 13:01                         ` Marek Vasut
2015-07-23  4:03                           ` Dinh Nguyen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150609122145.GB29408@amd \
    --to=pavel@denx.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.