]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - drivers/ddr/altera/sequencer.c
ddr: altera: Clean up rw_mgr_mem_calibrate_lfifo()
[karo-tx-uboot.git] / drivers / ddr / altera / sequencer.c
index b81fc8c358f3a19e7e692ef86710e66bb0af5bc8..eb0e8cf2593e24b00046326badd5e8b38efe4981 100644 (file)
@@ -80,10 +80,6 @@ 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 void set_failing_group_stage(uint32_t group, uint32_t stage,
        uint32_t substage)
 {
@@ -1036,6 +1032,219 @@ static void rw_mgr_mem_handoff(void)
         */
 }
 
+
+/**
+ * rw_mgr_mem_calibrate_write_test_issue() - Issue write test command
+ * @group:     Write Group
+ * @use_dm:    Use DM
+ *
+ * Issue write test command. Two variants are provided, one that just tests
+ * a write pattern and another that tests datamask functionality.
+ */
+static void rw_mgr_mem_calibrate_write_test_issue(u32 group,
+                                                 u32 test_dm)
+{
+       const u32 quick_write_mode =
+               (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES) &&
+               ENABLE_SUPER_QUICK_CALIBRATION;
+       u32 mcc_instruction;
+       u32 rw_wl_nop_cycles;
+
+       /*
+        * Set counter and jump addresses for the right
+        * number of NOP cycles.
+        * The number of supported NOP cycles can range from -1 to infinity
+        * Three different cases are handled:
+        *
+        * 1. For a number of NOP cycles greater than 0, the RW Mgr looping
+        *    mechanism will be used to insert the right number of NOPs
+        *
+        * 2. For a number of NOP cycles equals to 0, the micro-instruction
+        *    issuing the write command will jump straight to the
+        *    micro-instruction that turns on DQS (for DDRx), or outputs write
+        *    data (for RLD), skipping
+        *    the NOP micro-instruction all together
+        *
+        * 3. A number of NOP cycles equal to -1 indicates that DQS must be
+        *    turned on in the same micro-instruction that issues the write
+        *    command. Then we need
+        *    to directly jump to the micro-instruction that sends out the data
+        *
+        * NOTE: Implementing this mechanism uses 2 RW Mgr jump-counters
+        *       (2 and 3). One jump-counter (0) is used to perform multiple
+        *       write-read operations.
+        *       one counter left to issue this command in "multiple-group" mode
+        */
+
+       rw_wl_nop_cycles = gbl->rw_wl_nop_cycles;
+
+       if (rw_wl_nop_cycles == -1) {
+               /*
+                * CNTR 2 - We want to execute the special write operation that
+                * turns on DQS right away and then skip directly to the
+                * instruction that sends out the data. We set the counter to a
+                * large number so that the jump is always taken.
+                */
+               writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2);
+
+               /* CNTR 3 - Not used */
+               if (test_dm) {
+                       mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0_WL_1;
+                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DATA,
+                              &sdr_rw_load_jump_mgr_regs->load_jump_add2);
+                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP,
+                              &sdr_rw_load_jump_mgr_regs->load_jump_add3);
+               } else {
+                       mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0_WL_1;
+                       writel(RW_MGR_LFSR_WR_RD_BANK_0_DATA,
+                               &sdr_rw_load_jump_mgr_regs->load_jump_add2);
+                       writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP,
+                               &sdr_rw_load_jump_mgr_regs->load_jump_add3);
+               }
+       } else if (rw_wl_nop_cycles == 0) {
+               /*
+                * CNTR 2 - We want to skip the NOP operation and go straight
+                * to the DQS enable instruction. We set the counter to a large
+                * number so that the jump is always taken.
+                */
+               writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2);
+
+               /* CNTR 3 - Not used */
+               if (test_dm) {
+                       mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0;
+                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DQS,
+                              &sdr_rw_load_jump_mgr_regs->load_jump_add2);
+               } else {
+                       mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0;
+                       writel(RW_MGR_LFSR_WR_RD_BANK_0_DQS,
+                               &sdr_rw_load_jump_mgr_regs->load_jump_add2);
+               }
+       } else {
+               /*
+                * CNTR 2 - In this case we want to execute the next instruction
+                * and NOT take the jump. So we set the counter to 0. The jump
+                * address doesn't count.
+                */
+               writel(0x0, &sdr_rw_load_mgr_regs->load_cntr2);
+               writel(0x0, &sdr_rw_load_jump_mgr_regs->load_jump_add2);
+
+               /*
+                * CNTR 3 - Set the nop counter to the number of cycles we
+                * need to loop for, minus 1.
+                */
+               writel(rw_wl_nop_cycles - 1, &sdr_rw_load_mgr_regs->load_cntr3);
+               if (test_dm) {
+                       mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0;
+                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP,
+                               &sdr_rw_load_jump_mgr_regs->load_jump_add3);
+               } else {
+                       mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0;
+                       writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP,
+                               &sdr_rw_load_jump_mgr_regs->load_jump_add3);
+               }
+       }
+
+       writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS |
+                 RW_MGR_RESET_READ_DATAPATH_OFFSET);
+
+       if (quick_write_mode)
+               writel(0x08, &sdr_rw_load_mgr_regs->load_cntr0);
+       else
+               writel(0x40, &sdr_rw_load_mgr_regs->load_cntr0);
+
+       writel(mcc_instruction, &sdr_rw_load_jump_mgr_regs->load_jump_add0);
+
+       /*
+        * CNTR 1 - This is used to ensure enough time elapses
+        * for read data to come back.
+        */
+       writel(0x30, &sdr_rw_load_mgr_regs->load_cntr1);
+
+       if (test_dm) {
+               writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_WAIT,
+                       &sdr_rw_load_jump_mgr_regs->load_jump_add1);
+       } else {
+               writel(RW_MGR_LFSR_WR_RD_BANK_0_WAIT,
+                       &sdr_rw_load_jump_mgr_regs->load_jump_add1);
+       }
+
+       writel(mcc_instruction, (SDR_PHYGRP_RWMGRGRP_ADDRESS |
+                               RW_MGR_RUN_SINGLE_GROUP_OFFSET) +
+                               (group << 2));
+}
+
+/**
+ * rw_mgr_mem_calibrate_write_test() - Test writes, check for single/multiple pass
+ * @rank_bgn:          Rank number
+ * @write_group:       Write Group
+ * @use_dm:            Use DM
+ * @all_correct:       All bits must be correct in the mask
+ * @bit_chk:           Resulting bit mask after the test
+ * @all_ranks:         Test all ranks
+ *
+ * Test writes, can check for a single bit pass or multiple bit pass.
+ */
+static int
+rw_mgr_mem_calibrate_write_test(const u32 rank_bgn, const u32 write_group,
+                               const u32 use_dm, const u32 all_correct,
+                               u32 *bit_chk, const u32 all_ranks)
+{
+       const u32 rank_end = all_ranks ?
+                               RW_MGR_MEM_NUMBER_OF_RANKS :
+                               (rank_bgn + NUM_RANKS_PER_SHADOW_REG);
+       const u32 shift_ratio = RW_MGR_MEM_DQ_PER_WRITE_DQS /
+                               RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS;
+       const u32 correct_mask_vg = param->write_correct_mask_vg;
+
+       u32 tmp_bit_chk, base_rw_mgr;
+       int vg, r;
+
+       *bit_chk = param->write_correct_mask;
+
+       for (r = rank_bgn; r < rank_end; r++) {
+               /* Request to skip the rank */
+               if (param->skip_ranks[r])
+                       continue;
+
+               /* Set rank */
+               set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_READ_WRITE);
+
+               tmp_bit_chk = 0;
+               for (vg = RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS - 1;
+                    vg >= 0; vg--) {
+                       /* Reset the FIFOs to get pointers to known state. */
+                       writel(0, &phy_mgr_cmd->fifo_reset);
+
+                       rw_mgr_mem_calibrate_write_test_issue(
+                               write_group *
+                               RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS + vg,
+                               use_dm);
+
+                       base_rw_mgr = readl(SDR_PHYGRP_RWMGRGRP_ADDRESS);
+                       tmp_bit_chk <<= shift_ratio;
+                       tmp_bit_chk |= (correct_mask_vg & ~(base_rw_mgr));
+               }
+
+               *bit_chk &= tmp_bit_chk;
+       }
+
+       set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF);
+       if (all_correct) {
+               debug_cond(DLEVEL == 2,
+                          "write_test(%u,%u,ALL) : %u == %u => %i\n",
+                          write_group, use_dm, *bit_chk,
+                          param->write_correct_mask,
+                          *bit_chk == param->write_correct_mask);
+               return *bit_chk == param->write_correct_mask;
+       } else {
+               set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF);
+               debug_cond(DLEVEL == 2,
+                          "write_test(%u,%u,ONE) : %u != %i => %i\n",
+                          write_group, use_dm, *bit_chk, 0, *bit_chk != 0);
+               return *bit_chk != 0x00;
+       }
+}
+
 /**
  * rw_mgr_mem_calibrate_read_test_patterns() - Read back test patterns
  * @rank_bgn:  Rank number
@@ -2579,332 +2788,207 @@ cal_done_ok:
        return 1;
 }
 
-/* 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)
+/**
+ * rw_mgr_mem_calibrate_vfifo_end() - DQ/DQS Centering.
+ * @rw_group:          Read/Write Group
+ * @test_bgn:          Rank at which the test begins
+ *
+ * Stage 3: DQ/DQS Centering.
+ *
+ * This function implements UniPHY calibration Stage 3, as explained in
+ * detail in Altera EMI_RM 2015.05.04 , "UniPHY Calibration Stages".
+ */
+static int rw_mgr_mem_calibrate_vfifo_end(const u32 rw_group,
+                                         const u32 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);
+       int ret;
 
-       /* update info for sims */
+       debug("%s:%d %u %u", __func__, __LINE__, rw_group, test_bgn);
 
+       /* Update info for sims. */
+       reg_file_set_group(rw_group);
        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]) {
-               /* This is the last calibration round, update FOM here */
-                       if (rw_mgr_mem_calibrate_vfifo_center(rank_bgn,
-                                                               read_group,
-                                                               test_bgn, 0,
-                                                               1)) {
-                               grp_calibrated = 0;
-                       }
-               }
-       }
-
-
-       if (grp_calibrated == 0) {
-               set_failing_group_stage(write_group,
+       ret = rw_mgr_mem_calibrate_dq_dqs_centering(rw_group, test_bgn, 0, 1);
+       if (ret)
+               set_failing_group_stage(rw_group,
                                        CAL_STAGE_VFIFO_AFTER_WRITES,
                                        CAL_SUBSTAGE_VFIFO_CENTER);
-               return 0;
-       }
-
-       return 1;
+       return ret;
 }
 
-/* Calibrate LFIFO to find smallest read latency */
+/**
+ * rw_mgr_mem_calibrate_lfifo() - Minimize latency
+ *
+ * Stage 4: Minimize latency.
+ *
+ * This function implements UniPHY calibration Stage 4, as explained in
+ * detail in Altera EMI_RM 2015.05.04 , "UniPHY Calibration Stages".
+ * Calibrate LFIFO to find smallest read latency.
+ */
 static uint32_t rw_mgr_mem_calibrate_lfifo(void)
 {
-       uint32_t found_one;
+       int found_one = 0;
 
        debug("%s:%d\n", __func__, __LINE__);
 
-       /* update info for sims */
+       /* Update info for sims. */
        reg_file_set_stage(CAL_STAGE_LFIFO);
        reg_file_set_sub_stage(CAL_SUBSTAGE_READ_LATENCY);
 
        /* Load up the patterns used by read calibration for all ranks */
        rw_mgr_mem_calibrate_read_load_patterns(0, 1);
-       found_one = 0;
 
        do {
                writel(gbl->curr_read_lat, &phy_mgr_cfg->phy_rlat);
                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,
-                                                             1)) {
+               if (!rw_mgr_mem_calibrate_read_test_all_ranks(0, NUM_READ_TESTS,
+                                                             PASS_ALL_BITS, 1))
                        break;
-               }
 
                found_one = 1;
-               /* reduce read latency and see if things are working */
-               /* correctly */
+               /*
+                * Reduce read latency and see if things are
+                * working correctly.
+                */
                gbl->curr_read_lat--;
        } while (gbl->curr_read_lat > 0);
 
-       /* reset the fifos to get pointers to known state */
-
+       /* Reset the fifos to get pointers to known state. */
        writel(0, &phy_mgr_cmd->fifo_reset);
 
        if (found_one) {
-               /* add a fudge factor to the read latency that was determined */
+               /* Add a fudge factor to the read latency that was determined */
                gbl->curr_read_lat += 2;
                writel(gbl->curr_read_lat, &phy_mgr_cfg->phy_rlat);
-               debug_cond(DLEVEL == 2, "%s:%d lfifo: success: using \
-                          read_lat=%u\n", __func__, __LINE__,
-                          gbl->curr_read_lat);
-               return 1;
+               debug_cond(DLEVEL == 2,
+                          "%s:%d lfifo: success: using read_lat=%u\n",
+                          __func__, __LINE__, gbl->curr_read_lat);
        } else {
                set_failing_group_stage(0xff, CAL_STAGE_LFIFO,
                                        CAL_SUBSTAGE_READ_LATENCY);
 
-               debug_cond(DLEVEL == 2, "%s:%d lfifo: failed at initial \
-                          read_lat=%u\n", __func__, __LINE__,
-                          gbl->curr_read_lat);
-               return 0;
+               debug_cond(DLEVEL == 2,
+                          "%s:%d lfifo: failed at initial read_lat=%u\n",
+                          __func__, __LINE__, gbl->curr_read_lat);
        }
+
+       return found_one;
 }
 
-/*
- * issue write test command.
- * two variants are provided. one that just tests a write pattern and
- * another that tests datamask functionality.
+/**
+ * search_window() - Search for the/part of the window with DM/DQS shift
+ * @search_dm:         If 1, search for the DM shift, if 0, search for DQS shift
+ * @rank_bgn:          Rank number
+ * @write_group:       Write Group
+ * @bgn_curr:          Current window begin
+ * @end_curr:          Current window end
+ * @bgn_best:          Current best window begin
+ * @end_best:          Current best window end
+ * @win_best:          Size of the best window
+ * @new_dqs:           New DQS value (only applicable if search_dm = 0).
+ *
+ * Search for the/part of the window with DM/DQS shift.
  */
-static void rw_mgr_mem_calibrate_write_test_issue(uint32_t group,
-                                                 uint32_t test_dm)
+static void search_window(const int search_dm,
+                         const u32 rank_bgn, const u32 write_group,
+                         int *bgn_curr, int *end_curr, int *bgn_best,
+                         int *end_best, int *win_best, int new_dqs)
 {
-       uint32_t mcc_instruction;
-       uint32_t quick_write_mode = (((STATIC_CALIB_STEPS) & CALIB_SKIP_WRITES) &&
-               ENABLE_SUPER_QUICK_CALIBRATION);
-       uint32_t rw_wl_nop_cycles;
-       uint32_t addr;
-
-       /*
-        * Set counter and jump addresses for the right
-        * number of NOP cycles.
-        * The number of supported NOP cycles can range from -1 to infinity
-        * Three different cases are handled:
-        *
-        * 1. For a number of NOP cycles greater than 0, the RW Mgr looping
-        *    mechanism will be used to insert the right number of NOPs
-        *
-        * 2. For a number of NOP cycles equals to 0, the micro-instruction
-        *    issuing the write command will jump straight to the
-        *    micro-instruction that turns on DQS (for DDRx), or outputs write
-        *    data (for RLD), skipping
-        *    the NOP micro-instruction all together
-        *
-        * 3. A number of NOP cycles equal to -1 indicates that DQS must be
-        *    turned on in the same micro-instruction that issues the write
-        *    command. Then we need
-        *    to directly jump to the micro-instruction that sends out the data
-        *
-        * NOTE: Implementing this mechanism uses 2 RW Mgr jump-counters
-        *       (2 and 3). One jump-counter (0) is used to perform multiple
-        *       write-read operations.
-        *       one counter left to issue this command in "multiple-group" mode
-        */
-
-       rw_wl_nop_cycles = gbl->rw_wl_nop_cycles;
-
-       if (rw_wl_nop_cycles == -1) {
-               /*
-                * CNTR 2 - We want to execute the special write operation that
-                * turns on DQS right away and then skip directly to the
-                * instruction that sends out the data. We set the counter to a
-                * large number so that the jump is always taken.
-                */
-               writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2);
-
-               /* CNTR 3 - Not used */
-               if (test_dm) {
-                       mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0_WL_1;
-                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DATA,
-                              &sdr_rw_load_jump_mgr_regs->load_jump_add2);
-                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP,
-                              &sdr_rw_load_jump_mgr_regs->load_jump_add3);
-               } else {
-                       mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0_WL_1;
-                       writel(RW_MGR_LFSR_WR_RD_BANK_0_DATA,
-                               &sdr_rw_load_jump_mgr_regs->load_jump_add2);
-                       writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP,
-                               &sdr_rw_load_jump_mgr_regs->load_jump_add3);
-               }
-       } else if (rw_wl_nop_cycles == 0) {
-               /*
-                * CNTR 2 - We want to skip the NOP operation and go straight
-                * to the DQS enable instruction. We set the counter to a large
-                * number so that the jump is always taken.
-                */
-               writel(0xFF, &sdr_rw_load_mgr_regs->load_cntr2);
-
-               /* CNTR 3 - Not used */
-               if (test_dm) {
-                       mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0;
-                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_DQS,
-                              &sdr_rw_load_jump_mgr_regs->load_jump_add2);
-               } else {
-                       mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0;
-                       writel(RW_MGR_LFSR_WR_RD_BANK_0_DQS,
-                               &sdr_rw_load_jump_mgr_regs->load_jump_add2);
-               }
-       } else {
-               /*
-                * CNTR 2 - In this case we want to execute the next instruction
-                * and NOT take the jump. So we set the counter to 0. The jump
-                * address doesn't count.
-                */
-               writel(0x0, &sdr_rw_load_mgr_regs->load_cntr2);
-               writel(0x0, &sdr_rw_load_jump_mgr_regs->load_jump_add2);
-
-               /*
-                * CNTR 3 - Set the nop counter to the number of cycles we
-                * need to loop for, minus 1.
-                */
-               writel(rw_wl_nop_cycles - 1, &sdr_rw_load_mgr_regs->load_cntr3);
-               if (test_dm) {
-                       mcc_instruction = RW_MGR_LFSR_WR_RD_DM_BANK_0;
-                       writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_NOP,
-                               &sdr_rw_load_jump_mgr_regs->load_jump_add3);
+       u32 bit_chk;
+       const int max = IO_IO_OUT1_DELAY_MAX - new_dqs;
+       int d, di;
+
+       /* Search for the/part of the window with DM/DQS shift. */
+       for (di = max; di >= 0; di -= DELTA_D) {
+               if (search_dm) {
+                       d = di;
+                       scc_mgr_apply_group_dm_out1_delay(d);
                } else {
-                       mcc_instruction = RW_MGR_LFSR_WR_RD_BANK_0;
-                       writel(RW_MGR_LFSR_WR_RD_BANK_0_NOP,
-                               &sdr_rw_load_jump_mgr_regs->load_jump_add3);
+                       /* For DQS, we go from 0...max */
+                       d = max - di;
+                       /*
+                        * Note: This only shifts DQS, so are we limiting ourselve to
+                        * width of DQ unnecessarily.
+                        */
+                       scc_mgr_apply_group_dqs_io_and_oct_out1(write_group,
+                                                               d + new_dqs);
                }
-       }
-
-       writel(0, SDR_PHYGRP_RWMGRGRP_ADDRESS |
-                 RW_MGR_RESET_READ_DATAPATH_OFFSET);
-
-       if (quick_write_mode)
-               writel(0x08, &sdr_rw_load_mgr_regs->load_cntr0);
-       else
-               writel(0x40, &sdr_rw_load_mgr_regs->load_cntr0);
-
-       writel(mcc_instruction, &sdr_rw_load_jump_mgr_regs->load_jump_add0);
-
-       /*
-        * CNTR 1 - This is used to ensure enough time elapses
-        * for read data to come back.
-        */
-       writel(0x30, &sdr_rw_load_mgr_regs->load_cntr1);
 
-       if (test_dm) {
-               writel(RW_MGR_LFSR_WR_RD_DM_BANK_0_WAIT,
-                       &sdr_rw_load_jump_mgr_regs->load_jump_add1);
-       } else {
-               writel(RW_MGR_LFSR_WR_RD_BANK_0_WAIT,
-                       &sdr_rw_load_jump_mgr_regs->load_jump_add1);
-       }
-
-       addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_RUN_SINGLE_GROUP_OFFSET;
-       writel(mcc_instruction, addr + (group << 2));
-}
-
-/* Test writes, can check for a single bit pass or multiple bit pass */
-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)
-{
-       uint32_t r;
-       uint32_t correct_mask_vg;
-       uint32_t tmp_bit_chk;
-       uint32_t vg;
-       uint32_t rank_end = all_ranks ? RW_MGR_MEM_NUMBER_OF_RANKS :
-               (rank_bgn + NUM_RANKS_PER_SHADOW_REG);
-       uint32_t addr_rw_mgr;
-       uint32_t base_rw_mgr;
-
-       *bit_chk = param->write_correct_mask;
-       correct_mask_vg = param->write_correct_mask_vg;
+               writel(0, &sdr_scc_mgr->update);
 
-       for (r = rank_bgn; r < rank_end; r++) {
-               if (param->skip_ranks[r]) {
-                       /* request to skip the rank */
-                       continue;
-               }
+               if (rw_mgr_mem_calibrate_write_test(rank_bgn, write_group, 1,
+                                                   PASS_ALL_BITS, &bit_chk,
+                                                   0)) {
+                       /* Set current end of the window. */
+                       *end_curr = search_dm ? -d : d;
 
-               /* set rank */
-               set_rank_and_odt_mask(r, RW_MGR_ODT_MODE_READ_WRITE);
+                       /*
+                        * If a starting edge of our window has not been seen
+                        * this is our current start of the DM window.
+                        */
+                       if (*bgn_curr == IO_IO_OUT1_DELAY_MAX + 1)
+                               *bgn_curr = search_dm ? -d : d;
 
-               tmp_bit_chk = 0;
-               addr_rw_mgr = SDR_PHYGRP_RWMGRGRP_ADDRESS;
-               for (vg = RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS-1; ; vg--) {
-                       /* reset the fifos to get pointers to known state */
-                       writel(0, &phy_mgr_cmd->fifo_reset);
+                       /*
+                        * If current window is bigger than best seen.
+                        * Set best seen to be current window.
+                        */
+                       if ((*end_curr - *bgn_curr + 1) > *win_best) {
+                               *win_best = *end_curr - *bgn_curr + 1;
+                               *bgn_best = *bgn_curr;
+                               *end_best = *end_curr;
+                       }
+               } else {
+                       /* We just saw a failing test. Reset temp edge. */
+                       *bgn_curr = IO_IO_OUT1_DELAY_MAX + 1;
+                       *end_curr = IO_IO_OUT1_DELAY_MAX + 1;
 
-                       tmp_bit_chk = tmp_bit_chk <<
-                               (RW_MGR_MEM_DQ_PER_WRITE_DQS /
-                               RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS);
-                       rw_mgr_mem_calibrate_write_test_issue(write_group *
-                               RW_MGR_MEM_VIRTUAL_GROUPS_PER_WRITE_DQS+vg,
-                               use_dm);
+                       /* Early exit is only applicable to DQS. */
+                       if (search_dm)
+                               continue;
 
-                       base_rw_mgr = readl(addr_rw_mgr);
-                       tmp_bit_chk = tmp_bit_chk | (correct_mask_vg & ~(base_rw_mgr));
-                       if (vg == 0)
+                       /*
+                        * Early exit optimization: if the remaining delay
+                        * chain space is less than already seen largest
+                        * window we can exit.
+                        */
+                       if (*win_best - 1 > IO_IO_OUT1_DELAY_MAX - new_dqs - d)
                                break;
                }
-               *bit_chk &= tmp_bit_chk;
-       }
-
-       if (all_correct) {
-               set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF);
-               debug_cond(DLEVEL == 2, "write_test(%u,%u,ALL) : %u == \
-                          %u => %lu", write_group, use_dm,
-                          *bit_chk, param->write_correct_mask,
-                          (long unsigned int)(*bit_chk ==
-                          param->write_correct_mask));
-               return *bit_chk == param->write_correct_mask;
-       } else {
-               set_rank_and_odt_mask(0, RW_MGR_ODT_MODE_OFF);
-               debug_cond(DLEVEL == 2, "write_test(%u,%u,ONE) : %u != ",
-                      write_group, use_dm, *bit_chk);
-               debug_cond(DLEVEL == 2, "%lu" " => %lu", (long unsigned int)0,
-                       (long unsigned int)(*bit_chk != 0));
-               return *bit_chk != 0x00;
        }
 }
 
 /*
- * center all windows. do per-bit-deskew to possibly increase size of
+ * rw_mgr_mem_calibrate_writes_center() - Center all windows
+ * @rank_bgn:          Rank number
+ * @write_group:       Write group
+ * @test_bgn:          Rank at which the test begins
+ *
+ * Center all windows. Do per-bit-deskew to possibly increase size of
  * certain windows.
  */
-static uint32_t rw_mgr_mem_calibrate_writes_center(uint32_t rank_bgn,
-       uint32_t write_group, uint32_t test_bgn)
+static int
+rw_mgr_mem_calibrate_writes_center(const u32 rank_bgn, const u32 write_group,
+                                  const u32 test_bgn)
 {
-       uint32_t i, min_index;
-       int32_t d;
-       /*
-        * Store these as signed since there are comparisons with
-        * signed numbers.
-        */
-       uint32_t bit_chk;
-       uint32_t sticky_bit_chk;
-       int32_t left_edge[RW_MGR_MEM_DQ_PER_WRITE_DQS];
-       int32_t right_edge[RW_MGR_MEM_DQ_PER_WRITE_DQS];
-       int32_t mid;
-       int32_t mid_min, orig_mid_min;
-       int32_t new_dqs, start_dqs;
-       int32_t dq_margin, dqs_margin, dm_margin;
-       uint32_t addr;
+       int i;
+       u32 sticky_bit_chk;
+       u32 min_index;
+       int left_edge[RW_MGR_MEM_DQ_PER_WRITE_DQS];
+       int right_edge[RW_MGR_MEM_DQ_PER_WRITE_DQS];
+       int mid;
+       int mid_min, orig_mid_min;
+       int new_dqs, start_dqs;
+       int dq_margin, dqs_margin, dm_margin;
+       int bgn_curr = IO_IO_OUT1_DELAY_MAX + 1;
+       int end_curr = IO_IO_OUT1_DELAY_MAX + 1;
+       int bgn_best = IO_IO_OUT1_DELAY_MAX + 1;
+       int end_best = IO_IO_OUT1_DELAY_MAX + 1;
+       int win_best = 0;
 
        int ret;
 
@@ -2912,15 +2996,15 @@ static uint32_t rw_mgr_mem_calibrate_writes_center(uint32_t rank_bgn,
 
        dm_margin = 0;
 
-       addr = SDR_PHYGRP_SCCGRP_ADDRESS | SCC_MGR_IO_OUT1_DELAY_OFFSET;
-       start_dqs = readl(addr +
+       start_dqs = readl((SDR_PHYGRP_SCCGRP_ADDRESS |
+                         SCC_MGR_IO_OUT1_DELAY_OFFSET) +
                          (RW_MGR_MEM_DQ_PER_WRITE_DQS << 2));
 
-       /* per-bit deskew */
+       /* Per-bit deskew. */
 
        /*
-        * set the left and right edge of each bit to an illegal value
-        * use (IO_IO_OUT1_DELAY_MAX + 1) as an illegal value.
+        * Set the left and right edge of each bit to an illegal value.
+        * Use (IO_IO_OUT1_DELAY_MAX + 1) as an illegal value.
         */
        sticky_bit_chk = 0;
        for (i = 0; i < RW_MGR_MEM_DQ_PER_WRITE_DQS; i++) {
@@ -2928,12 +3012,12 @@ static uint32_t rw_mgr_mem_calibrate_writes_center(uint32_t rank_bgn,
                right_edge[i] = IO_IO_OUT1_DELAY_MAX + 1;
        }
 
-       /* Search for the left edge of the window for each bit */
+       /* Search for the left edge of the window for each bit. */
        search_left_edge(1, rank_bgn, write_group, 0, test_bgn,
                         &sticky_bit_chk,
                         left_edge, right_edge, 0);
 
-       /* Search for the right edge of the window for each bit */
+       /* Search for the right edge of the window for each bit. */
        ret = search_right_edge(1, rank_bgn, write_group, 0,
                                start_dqs, 0,
                                &sticky_bit_chk,
@@ -2941,17 +3025,18 @@ static uint32_t rw_mgr_mem_calibrate_writes_center(uint32_t rank_bgn,
        if (ret) {
                set_failing_group_stage(test_bgn + ret - 1, CAL_STAGE_WRITES,
                                        CAL_SUBSTAGE_WRITES_CENTER);
-               return 0;
+               return -EINVAL;
        }
 
        min_index = get_window_mid_index(1, left_edge, right_edge, &mid_min);
 
-       /* Determine the amount we can change DQS (which is -mid_min) */
+       /* Determine the amount we can change DQS (which is -mid_min). */
        orig_mid_min = mid_min;
        new_dqs = start_dqs;
        mid_min = 0;
-       debug_cond(DLEVEL == 1, "%s:%d write_center: start_dqs=%d new_dqs=%d \
-                  mid_min=%d\n", __func__, __LINE__, start_dqs, new_dqs, mid_min);
+       debug_cond(DLEVEL == 1,
+                  "%s:%d write_center: start_dqs=%d new_dqs=%d mid_min=%d\n",
+                  __func__, __LINE__, start_dqs, new_dqs, mid_min);
 
        /* Add delay to bring centre of all DQ windows to the same "level". */
        center_dq_windows(1, left_edge, right_edge, mid_min, orig_mid_min,
@@ -2965,129 +3050,53 @@ static uint32_t rw_mgr_mem_calibrate_writes_center(uint32_t rank_bgn,
        debug_cond(DLEVEL == 2, "%s:%d write_center: DM\n", __func__, __LINE__);
 
        /*
-        * set the left and right edge of each bit to an illegal value,
-        * use (IO_IO_OUT1_DELAY_MAX + 1) as an illegal value,
+        * Set the left and right edge of each bit to an illegal value.
+        * Use (IO_IO_OUT1_DELAY_MAX + 1) as an illegal value.
         */
        left_edge[0]  = IO_IO_OUT1_DELAY_MAX + 1;
        right_edge[0] = IO_IO_OUT1_DELAY_MAX + 1;
-       int32_t bgn_curr = IO_IO_OUT1_DELAY_MAX + 1;
-       int32_t end_curr = IO_IO_OUT1_DELAY_MAX + 1;
-       int32_t bgn_best = IO_IO_OUT1_DELAY_MAX + 1;
-       int32_t end_best = IO_IO_OUT1_DELAY_MAX + 1;
-       int32_t win_best = 0;
-
-       /* Search for the/part of the window with DM shift */
-       for (d = IO_IO_OUT1_DELAY_MAX; d >= 0; d -= DELTA_D) {
-               scc_mgr_apply_group_dm_out1_delay(d);
-               writel(0, &sdr_scc_mgr->update);
-
-               if (rw_mgr_mem_calibrate_write_test(rank_bgn, write_group, 1,
-                                                   PASS_ALL_BITS, &bit_chk,
-                                                   0)) {
-                       /* USE Set current end of the window */
-                       end_curr = -d;
-                       /*
-                        * If a starting edge of our window has not been seen
-                        * this is our current start of the DM window.
-                        */
-                       if (bgn_curr == IO_IO_OUT1_DELAY_MAX + 1)
-                               bgn_curr = -d;
-
-                       /*
-                        * If current window is bigger than best seen.
-                        * Set best seen to be current window.
-                        */
-                       if ((end_curr-bgn_curr+1) > win_best) {
-                               win_best = end_curr-bgn_curr+1;
-                               bgn_best = bgn_curr;
-                               end_best = end_curr;
-                       }
-               } else {
-                       /* We just saw a failing test. Reset temp edge */
-                       bgn_curr = IO_IO_OUT1_DELAY_MAX + 1;
-                       end_curr = IO_IO_OUT1_DELAY_MAX + 1;
-                       }
-               }
 
+       /* Search for the/part of the window with DM shift. */
+       search_window(1, rank_bgn, write_group, &bgn_curr, &end_curr,
+                     &bgn_best, &end_best, &win_best, 0);
 
-       /* Reset DM delay chains to 0 */
+       /* Reset DM delay chains to 0. */
        scc_mgr_apply_group_dm_out1_delay(0);
 
        /*
         * Check to see if the current window nudges up aganist 0 delay.
         * If so we need to continue the search by shifting DQS otherwise DQS
-        * search begins as a new search. */
+        * search begins as a new search.
+        */
        if (end_curr != 0) {
                bgn_curr = IO_IO_OUT1_DELAY_MAX + 1;
                end_curr = IO_IO_OUT1_DELAY_MAX + 1;
        }
 
-       /* Search for the/part of the window with DQS shifts */
-       for (d = 0; d <= IO_IO_OUT1_DELAY_MAX - new_dqs; d += DELTA_D) {
-               /*
-                * Note: This only shifts DQS, so are we limiting ourselve to
-                * width of DQ unnecessarily.
-                */
-               scc_mgr_apply_group_dqs_io_and_oct_out1(write_group,
-                                                       d + new_dqs);
-
-               writel(0, &sdr_scc_mgr->update);
-               if (rw_mgr_mem_calibrate_write_test(rank_bgn, write_group, 1,
-                                                   PASS_ALL_BITS, &bit_chk,
-                                                   0)) {
-                       /* USE Set current end of the window */
-                       end_curr = d;
-                       /*
-                        * If a beginning edge of our window has not been seen
-                        * this is our current begin of the DM window.
-                        */
-                       if (bgn_curr == IO_IO_OUT1_DELAY_MAX + 1)
-                               bgn_curr = d;
-
-                       /*
-                        * If current window is bigger than best seen. Set best
-                        * seen to be current window.
-                        */
-                       if ((end_curr-bgn_curr+1) > win_best) {
-                               win_best = end_curr-bgn_curr+1;
-                               bgn_best = bgn_curr;
-                               end_best = end_curr;
-                       }
-               } else {
-                       /* We just saw a failing test. Reset temp edge */
-                       bgn_curr = IO_IO_OUT1_DELAY_MAX + 1;
-                       end_curr = IO_IO_OUT1_DELAY_MAX + 1;
-
-                       /* Early exit optimization: if ther remaining delay
-                       chain space is less than already seen largest window
-                       we can exit */
-                       if ((win_best-1) >
-                               (IO_IO_OUT1_DELAY_MAX - new_dqs - d)) {
-                                       break;
-                               }
-                       }
-               }
+       /* Search for the/part of the window with DQS shifts. */
+       search_window(0, rank_bgn, write_group, &bgn_curr, &end_curr,
+                     &bgn_best, &end_best, &win_best, new_dqs);
 
-       /* assign left and right edge for cal and reporting; */
-       left_edge[0] = -1*bgn_best;
+       /* Assign left and right edge for cal and reporting. */
+       left_edge[0] = -1 * bgn_best;
        right_edge[0] = end_best;
 
-       debug_cond(DLEVEL == 2, "%s:%d dm_calib: left=%d right=%d\n", __func__,
-                  __LINE__, left_edge[0], right_edge[0]);
+       debug_cond(DLEVEL == 2, "%s:%d dm_calib: left=%d right=%d\n",
+                  __func__, __LINE__, left_edge[0], right_edge[0]);
 
-       /* Move DQS (back to orig) */
+       /* Move DQS (back to orig). */
        scc_mgr_apply_group_dqs_io_and_oct_out1(write_group, new_dqs);
 
        /* Move DM */
 
-       /* Find middle of window for the DM bit */
+       /* Find middle of window for the DM bit. */
        mid = (left_edge[0] - right_edge[0]) / 2;
 
-       /* only move right, since we are not moving DQS/DQ */
+       /* Only move right, since we are not moving DQS/DQ. */
        if (mid < 0)
                mid = 0;
 
-       /* dm_marign should fail if we never find a window */
+       /* dm_marign should fail if we never find a window. */
        if (win_best == 0)
                dm_margin = -1;
        else
@@ -3096,22 +3105,27 @@ static uint32_t rw_mgr_mem_calibrate_writes_center(uint32_t rank_bgn,
        scc_mgr_apply_group_dm_out1_delay(mid);
        writel(0, &sdr_scc_mgr->update);
 
-       debug_cond(DLEVEL == 2, "%s:%d dm_calib: left=%d right=%d mid=%d \
-                  dm_margin=%d\n", __func__, __LINE__, left_edge[0],
-                  right_edge[0], mid, dm_margin);
-       /* Export values */
+       debug_cond(DLEVEL == 2,
+                  "%s:%d dm_calib: left=%d right=%d mid=%d dm_margin=%d\n",
+                  __func__, __LINE__, left_edge[0], right_edge[0],
+                  mid, dm_margin);
+       /* Export values. */
        gbl->fom_out += dq_margin + dqs_margin;
 
-       debug_cond(DLEVEL == 2, "%s:%d write_center: dq_margin=%d \
-                  dqs_margin=%d dm_margin=%d\n", __func__, __LINE__,
-                  dq_margin, dqs_margin, dm_margin);
+       debug_cond(DLEVEL == 2,
+                  "%s:%d write_center: dq_margin=%d dqs_margin=%d dm_margin=%d\n",
+                  __func__, __LINE__, dq_margin, dqs_margin, dm_margin);
 
        /*
         * Do not remove this line as it makes sure all of our
         * decisions have been applied.
         */
        writel(0, &sdr_scc_mgr->update);
-       return (dq_margin >= 0) && (dqs_margin >= 0) && (dm_margin >= 0);
+
+       if ((dq_margin < 0) || (dqs_margin < 0) || (dm_margin < 0))
+               return -EINVAL;
+
+       return 0;
 }
 
 /**
@@ -3138,13 +3152,11 @@ static int rw_mgr_mem_calibrate_writes(const u32 rank_bgn, const u32 group,
        reg_file_set_sub_stage(CAL_SUBSTAGE_WRITES_CENTER);
 
        ret = rw_mgr_mem_calibrate_writes_center(rank_bgn, group, test_bgn);
-       if (!ret) {
+       if (ret)
                set_failing_group_stage(group, CAL_STAGE_WRITES,
                                        CAL_SUBSTAGE_WRITES_CENTER);
-               return -EIO;
-       }
 
-       return 0;
+       return ret;
 }
 
 /**
@@ -3459,7 +3471,7 @@ static uint32_t mem_calibrate(void)
                                if (STATIC_CALIB_STEPS & CALIB_SKIP_WRITES)
                                        continue;
 
-                               if (rw_mgr_mem_calibrate_vfifo_end(read_group,
+                               if (!rw_mgr_mem_calibrate_vfifo_end(read_group,
                                                                read_test_bgn))
                                        continue;