Skip to content
Snippets Groups Projects
interactive.c 55.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	       " spd->psi_ta_pll,    * 58 Thermal Resistance of PLL Package form"
    
    	       " Top (Case) to Ambient (Psi T-A PLL) *");
    
    	printf("%-3d    : %02x %s\n", 59, spd->psi_ta_reg,
    	       " spd->psi_ta_reg,    * 59 Thermal Reisitance of Register Package"
    
    	       " from Top (Case) to Ambient (Psi T-A Register) *");
    	printf("%-3d    : %02x %s\n", 60, spd->dtpllactive,
    	       " spd->dtpllactive, * 60 PLL Case Temperature Rise from "
    	       "Ambient due to PLL Active (DT PLL Active) *");
    	printf("%-3d    : %02x %s\n", 61, spd->dtregact,
    	       " spd->dtregact,    "
    	       "* 61 Register Case Temperature Rise from Ambient due to "
    	       "Register Active/Mode Bit (DT Register Active/Mode Bit) *");
    	printf("%-3d    : %02x %s\n", 62, spd->spd_rev,
    	       " spd->spd_rev,     * 62 SPD Data Revision Code *");
    	printf("%-3d    : %02x %s\n", 63, spd->cksum,
    	       " spd->cksum,       * 63 Checksum for bytes 0-62 *");
    
    	printf("%-3d-%3d: ",  64, 71);
    
    	for (i = 0; i < 8; i++)
    		printf("%02x", spd->mid[i]);
    
    	printf("* 64 Mfr's JEDEC ID code per JEP-108E *\n");
    
    	printf("%-3d    : %02x %s\n", 72, spd->mloc,
    	       " spd->mloc,        * 72 Manufacturing Location *");
    
    	printf("%-3d-%3d: >>",  73, 90);
    	for (i = 0; i < 18; i++)
    		printf("%c", spd->mpart[i]);
    
    
    	printf("<<* 73 Manufacturer's Part Number *\n");
    
    	printf("%-3d-%3d: %02x %02x %s\n", 91, 92, spd->rev[0], spd->rev[1],
    	       "* 91 Revision Code *");
    	printf("%-3d-%3d: %02x %02x %s\n", 93, 94, spd->mdate[0], spd->mdate[1],
    	       "* 93 Manufacturing Date *");
    	printf("%-3d-%3d: ", 95, 98);
    
    	for (i = 0; i < 4; i++)
    		printf("%02x", spd->sernum[i]);
    
    	printf("* 95 Assembly Serial Number *\n");
    
    	printf("%-3d-%3d: ", 99, 127);
    	for (i = 0; i < 27; i++)
    		printf("%02x", spd->mspec[i]);
    
    
    	printf("* 99 Manufacturer Specific Data *\n");
    }
    #endif
    
    
    #ifdef CONFIG_SYS_FSL_DDR3
    
    void ddr3_spd_dump(const ddr3_spd_eeprom_t *spd)
    {
    	unsigned int i;
    
    	/* General Section: Bytes 0-59 */
    
    
    #define PRINT_NXS(x, y, z...) printf("%-3d    : %02x " z "\n", x, (u8)y);
    
    #define PRINT_NNXXS(n0, n1, x0, x1, s) \
    	printf("%-3d-%3d: %02x %02x " s "\n", n0, n1, x0, x1);
    
    	PRINT_NXS(0, spd->info_size_crc,
    		"info_size_crc  bytes written into serial memory, "
    		"CRC coverage");
    	PRINT_NXS(1, spd->spd_rev,
    		"spd_rev        SPD Revision");
    	PRINT_NXS(2, spd->mem_type,
    		"mem_type       Key Byte / DRAM Device Type");
    	PRINT_NXS(3, spd->module_type,
    		"module_type    Key Byte / Module Type");
    	PRINT_NXS(4, spd->density_banks,
    		"density_banks  SDRAM Density and Banks");
    	PRINT_NXS(5, spd->addressing,
    		"addressing     SDRAM Addressing");
    	PRINT_NXS(6, spd->module_vdd,
    		"module_vdd     Module Nominal Voltage, VDD");
    	PRINT_NXS(7, spd->organization,
    		"organization   Module Organization");
    	PRINT_NXS(8, spd->bus_width,
    		"bus_width      Module Memory Bus Width");
    	PRINT_NXS(9, spd->ftb_div,
    		"ftb_div        Fine Timebase (FTB) Dividend / Divisor");
    	PRINT_NXS(10, spd->mtb_dividend,
    		"mtb_dividend   Medium Timebase (MTB) Dividend");
    	PRINT_NXS(11, spd->mtb_divisor,
    		"mtb_divisor    Medium Timebase (MTB) Divisor");
    
    	PRINT_NXS(12, spd->tck_min,
    		  "tck_min        SDRAM Minimum Cycle Time");
    
    	PRINT_NXS(13, spd->res_13,
    		"res_13         Reserved");
    	PRINT_NXS(14, spd->caslat_lsb,
    		"caslat_lsb     CAS Latencies Supported, LSB");
    	PRINT_NXS(15, spd->caslat_msb,
    		"caslat_msb     CAS Latencies Supported, MSB");
    
    	PRINT_NXS(16, spd->taa_min,
    		  "taa_min        Min CAS Latency Time");
    	PRINT_NXS(17, spd->twr_min,
    		  "twr_min        Min Write REcovery Time");
    	PRINT_NXS(18, spd->trcd_min,
    		  "trcd_min       Min RAS# to CAS# Delay Time");
    	PRINT_NXS(19, spd->trrd_min,
    		  "trrd_min       Min Row Active to Row Active Delay Time");
    	PRINT_NXS(20, spd->trp_min,
    		  "trp_min        Min Row Precharge Delay Time");
    	PRINT_NXS(21, spd->tras_trc_ext,
    		  "tras_trc_ext   Upper Nibbles for tRAS and tRC");
    	PRINT_NXS(22, spd->tras_min_lsb,
    		  "tras_min_lsb   Min Active to Precharge Delay Time, LSB");
    	PRINT_NXS(23, spd->trc_min_lsb,
    		  "trc_min_lsb Min Active to Active/Refresh Delay Time, LSB");
    	PRINT_NXS(24, spd->trfc_min_lsb,
    		  "trfc_min_lsb   Min Refresh Recovery Delay Time LSB");
    	PRINT_NXS(25, spd->trfc_min_msb,
    		  "trfc_min_msb   Min Refresh Recovery Delay Time MSB");
    	PRINT_NXS(26, spd->twtr_min,
    		  "twtr_min Min Internal Write to Read Command Delay Time");
    	PRINT_NXS(27, spd->trtp_min,
    		  "trtp_min "
    		  "Min Internal Read to Precharge Command Delay Time");
    	PRINT_NXS(28, spd->tfaw_msb,
    		  "tfaw_msb       Upper Nibble for tFAW");
    	PRINT_NXS(29, spd->tfaw_min,
    		  "tfaw_min       Min Four Activate Window Delay Time");
    
    	PRINT_NXS(30, spd->opt_features,
    		"opt_features   SDRAM Optional Features");
    	PRINT_NXS(31, spd->therm_ref_opt,
    		"therm_ref_opt  SDRAM Thermal and Refresh Opts");
    	PRINT_NXS(32, spd->therm_sensor,
    		"therm_sensor  SDRAM Thermal Sensor");
    	PRINT_NXS(33, spd->device_type,
    		"device_type  SDRAM Device Type");
    
    	PRINT_NXS(34, spd->fine_tck_min,
    		  "fine_tck_min  Fine offset for tCKmin");
    	PRINT_NXS(35, spd->fine_taa_min,
    		  "fine_taa_min  Fine offset for tAAmin");
    	PRINT_NXS(36, spd->fine_trcd_min,
    		  "fine_trcd_min Fine offset for tRCDmin");
    	PRINT_NXS(37, spd->fine_trp_min,
    		  "fine_trp_min  Fine offset for tRPmin");
    	PRINT_NXS(38, spd->fine_trc_min,
    		  "fine_trc_min  Fine offset for tRCmin");
    
    
    	printf("%-3d-%3d: ",  39, 59);  /* Reserved, General Section */
    
    	for (i = 39; i <= 59; i++)
    		printf("%02x ", spd->res_39_59[i - 39]);
    
    
    	puts("\n");
    
    	switch (spd->module_type) {
    	case 0x02:  /* UDIMM */
    	case 0x03:  /* SO-DIMM */
    	case 0x04:  /* Micro-DIMM */
    	case 0x06:  /* Mini-UDIMM */
    		PRINT_NXS(60, spd->mod_section.unbuffered.mod_height,
    			"mod_height    (Unbuffered) Module Nominal Height");
    		PRINT_NXS(61, spd->mod_section.unbuffered.mod_thickness,
    			"mod_thickness (Unbuffered) Module Maximum Thickness");
    		PRINT_NXS(62, spd->mod_section.unbuffered.ref_raw_card,
    			"ref_raw_card  (Unbuffered) Reference Raw Card Used");
    		PRINT_NXS(63, spd->mod_section.unbuffered.addr_mapping,
    			"addr_mapping  (Unbuffered) Address mapping from "
    			"Edge Connector to DRAM");
    		break;
    	case 0x01:  /* RDIMM */
    	case 0x05:  /* Mini-RDIMM */
    		PRINT_NXS(60, spd->mod_section.registered.mod_height,
    			"mod_height    (Registered) Module Nominal Height");
    		PRINT_NXS(61, spd->mod_section.registered.mod_thickness,
    			"mod_thickness (Registered) Module Maximum Thickness");
    		PRINT_NXS(62, spd->mod_section.registered.ref_raw_card,
    			"ref_raw_card  (Registered) Reference Raw Card Used");
    		PRINT_NXS(63, spd->mod_section.registered.modu_attr,
    			"modu_attr     (Registered) DIMM Module Attributes");
    		PRINT_NXS(64, spd->mod_section.registered.thermal,
    			"thermal       (Registered) Thermal Heat "
    			"Spreader Solution");
    		PRINT_NXS(65, spd->mod_section.registered.reg_id_lo,
    			"reg_id_lo     (Registered) Register Manufacturer ID "
    			"Code, LSB");
    		PRINT_NXS(66, spd->mod_section.registered.reg_id_hi,
    			"reg_id_hi     (Registered) Register Manufacturer ID "
    			"Code, MSB");
    		PRINT_NXS(67, spd->mod_section.registered.reg_rev,
    			"reg_rev       (Registered) Register "
    			"Revision Number");
    		PRINT_NXS(68, spd->mod_section.registered.reg_type,
    			"reg_type      (Registered) Register Type");
    		for (i = 69; i <= 76; i++) {
    			printf("%-3d    : %02x rcw[%d]\n", i,
    				spd->mod_section.registered.rcw[i-69], i-69);
    		}
    		break;
    	default:
    		/* Module-specific Section, Unsupported Module Type */
    		printf("%-3d-%3d: ", 60, 116);
    
    		for (i = 60; i <= 116; i++)
    			printf("%02x", spd->mod_section.uc[i - 60]);
    
    		break;
    	}
    
    	/* Unique Module ID: Bytes 117-125 */
    	PRINT_NXS(117, spd->mmid_lsb, "Module MfgID Code LSB - JEP-106");
    	PRINT_NXS(118, spd->mmid_msb, "Module MfgID Code MSB - JEP-106");
    	PRINT_NXS(119, spd->mloc,     "Mfg Location");
    	PRINT_NNXXS(120, 121, spd->mdate[0], spd->mdate[1], "Mfg Date");
    
    	printf("%-3d-%3d: ", 122, 125);
    
    	for (i = 122; i <= 125; i++)
    		printf("%02x ", spd->sernum[i - 122]);
    	printf("   Module Serial Number\n");
    
    	/* CRC: Bytes 126-127 */
    	PRINT_NNXXS(126, 127, spd->crc[0], spd->crc[1], "  SPD CRC");
    
    	/* Other Manufacturer Fields and User Space: Bytes 128-255 */
    	printf("%-3d-%3d: ", 128, 145);
    	for (i = 128; i <= 145; i++)
    		printf("%02x ", spd->mpart[i - 128]);
    	printf("   Mfg's Module Part Number\n");
    
    	PRINT_NNXXS(146, 147, spd->mrev[0], spd->mrev[1],
    		"Module Revision code");
    
    	PRINT_NXS(148, spd->dmid_lsb, "DRAM MfgID Code LSB - JEP-106");
    	PRINT_NXS(149, spd->dmid_msb, "DRAM MfgID Code MSB - JEP-106");
    
    	printf("%-3d-%3d: ", 150, 175);
    	for (i = 150; i <= 175; i++)
    		printf("%02x ", spd->msd[i - 150]);
    	printf("   Mfg's Specific Data\n");
    
    	printf("%-3d-%3d: ", 176, 255);
    	for (i = 176; i <= 255; i++)
    		printf("%02x", spd->cust[i - 176]);
    	printf("   Mfg's Specific Data\n");
    
    }
    #endif
    
    static inline void generic_spd_dump(const generic_spd_eeprom_t *spd)
    {
    
    #if defined(CONFIG_SYS_FSL_DDR1)
    
    #elif defined(CONFIG_SYS_FSL_DDR2)
    
    #elif defined(CONFIG_SYS_FSL_DDR3)
    
    	ddr3_spd_dump(spd);
    #endif
    }
    
    static void fsl_ddr_printinfo(const fsl_ddr_info_t *pinfo,
    			unsigned int ctrl_mask,
    			unsigned int dimm_mask,
    			unsigned int do_mask)
    {
    	unsigned int i, j, retval;
    
    	/* STEP 1:  DIMM SPD data */
    	if (do_mask & STEP_GET_SPD) {
    		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
    			if (!(ctrl_mask & (1 << i)))
    				continue;
    
    			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
    				if (!(dimm_mask & (1 << j)))
    					continue;
    
    				printf("SPD info:  Controller=%u "
    						"DIMM=%u\n", i, j);
    				generic_spd_dump(
    					&(pinfo->spd_installed_dimms[i][j]));
    				printf("\n");
    			}
    			printf("\n");
    		}
    		printf("\n");
    	}
    
    	/* STEP 2:  DIMM Parameters */
    	if (do_mask & STEP_COMPUTE_DIMM_PARMS) {
    		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
    			if (!(ctrl_mask & (1 << i)))
    				continue;
    			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
    				if (!(dimm_mask & (1 << j)))
    					continue;
    				printf("DIMM parameters:  Controller=%u "
    						"DIMM=%u\n", i, j);
    				print_dimm_parameters(
    					&(pinfo->dimm_params[i][j]));
    				printf("\n");
    			}
    			printf("\n");
    		}
    		printf("\n");
    	}
    
    	/* STEP 3:  Common Parameters */
    	if (do_mask & STEP_COMPUTE_COMMON_PARMS) {
    		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
    			if (!(ctrl_mask & (1 << i)))
    				continue;
    			printf("\"lowest common\" DIMM parameters:  "
    					"Controller=%u\n", i);
    			print_lowest_common_dimm_parameters(
    				&pinfo->common_timing_params[i]);
    			printf("\n");
    		}
    		printf("\n");
    	}
    
    	/* STEP 4:  User Configuration Options */
    	if (do_mask & STEP_GATHER_OPTS) {
    		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
    			if (!(ctrl_mask & (1 << i)))
    				continue;
    			printf("User Config Options: Controller=%u\n", i);
    			print_memctl_options(&pinfo->memctl_opts[i]);
    			printf("\n");
    		}
    		printf("\n");
    	}
    
    	/* STEP 5:  Address assignment */
    	if (do_mask & STEP_ASSIGN_ADDRESSES) {
    		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
    			if (!(ctrl_mask & (1 << i)))
    				continue;
    			for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
    				printf("Address Assignment: Controller=%u "
    						"DIMM=%u\n", i, j);
    				printf("Don't have this functionality yet\n");
    			}
    			printf("\n");
    		}
    		printf("\n");
    	}
    
    	/* STEP 6:  computed controller register values */
    	if (do_mask & STEP_COMPUTE_REGS) {
    		for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
    			if (!(ctrl_mask & (1 << i)))
    				continue;
    			printf("Computed Register Values: Controller=%u\n", i);
    			print_fsl_memctl_config_regs(
    				&pinfo->fsl_ddr_config_reg[i]);
    			retval = check_fsl_memctl_config_regs(
    				&pinfo->fsl_ddr_config_reg[i]);
    			if (retval) {
    				printf("check_fsl_memctl_config_regs "
    					"result = %u\n", retval);
    			}
    			printf("\n");
    		}
    		printf("\n");
    	}
    }
    
    struct data_strings {
    	const char *data_name;
    	unsigned int step_mask;
    	unsigned int dimm_number_required;
    };
    
    #define DATA_OPTIONS(name, step, dimm) {#name, step, dimm}
    
    
    static unsigned int fsl_ddr_parse_interactive_cmd(
    	char **argv,
    	int argc,
    	unsigned int *pstep_mask,
    	unsigned int *pctlr_mask,
    	unsigned int *pdimm_mask,
    	unsigned int *pdimm_number_required
    	 ) {
    
    
    	static const struct data_strings options[] = {
    		DATA_OPTIONS(spd, STEP_GET_SPD, 1),
    		DATA_OPTIONS(dimmparms, STEP_COMPUTE_DIMM_PARMS, 1),
    		DATA_OPTIONS(commonparms, STEP_COMPUTE_COMMON_PARMS, 0),
    		DATA_OPTIONS(opts, STEP_GATHER_OPTS, 0),
    		DATA_OPTIONS(addresses, STEP_ASSIGN_ADDRESSES, 0),
    		DATA_OPTIONS(regs, STEP_COMPUTE_REGS, 0),
    	};
    	static const unsigned int n_opts = ARRAY_SIZE(options);
    
    
    	unsigned int i, j;
    	unsigned int error = 0;
    
    	for (i = 1; i < argc; i++) {
    
    		unsigned int matched = 0;
    
    
    		for (j = 0; j < n_opts; j++) {
    			if (strcmp(options[j].data_name, argv[i]) != 0)
    				continue;
    			*pstep_mask |= options[j].step_mask;
    			*pdimm_number_required =
    				options[j].dimm_number_required;
    			matched = 1;
    			break;
    		}
    
    		if (matched)
    			continue;
    
    		if (argv[i][0] == 'c') {
    			char c = argv[i][1];
    			if (isdigit(c))
    				*pctlr_mask |= 1 << (c - '0');
    			continue;
    		}
    
    		if (argv[i][0] == 'd') {
    			char c = argv[i][1];
    			if (isdigit(c))
    				*pdimm_mask |= 1 << (c - '0');
    			continue;
    		}
    
    		printf("unknown arg %s\n", argv[i]);
    		*pstep_mask = 0;
    		error = 1;
    		break;
    	}
    
    	return error;
    }
    
    
    int fsl_ddr_interactive_env_var_exists(void)
    {
    	char buffer[CONFIG_SYS_CBSIZE];
    
    	if (getenv_f("ddr_interactive", buffer, CONFIG_SYS_CBSIZE) >= 0)
    		return 1;
    
    	return 0;
    }
    
    unsigned long long fsl_ddr_interactive(fsl_ddr_info_t *pinfo, int var_is_set)
    
    {
    	unsigned long long ddrsize;
    	const char *prompt = "FSL DDR>";
    	char buffer[CONFIG_SYS_CBSIZE];
    
    	char buffer2[CONFIG_SYS_CBSIZE];
    	char *p = NULL;
    
    	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated */
    	int argc;
    	unsigned int next_step = STEP_GET_SPD;
    
    	const char *usage = {
    		"commands:\n"
    		"print      print SPD and intermediate computed data\n"
    		"reset      reboot machine\n"
    		"recompute  reload SPD and options to default and recompute regs\n"
    		"edit       modify spd, parameter, or option\n"
    		"compute    recompute registers from current next_step to end\n"
    
    		"copy       copy parameters\n"
    
    		"next_step  shows current next_step\n"
    		"help       this message\n"
    		"go         program the memory controller and continue with u-boot\n"
    	};
    
    
    	if (var_is_set) {
    		if (getenv_f("ddr_interactive", buffer2, CONFIG_SYS_CBSIZE) > 0) {
    			p = buffer2;
    		} else {
    			var_is_set = 0;
    		}
    	}
    
    
    	/*
    	 * The strategy for next_step is that it points to the next
    	 * step in the computation process that needs to be done.
    	 */
    	while (1) {
    
    		if (var_is_set) {
    			char *pend = strchr(p, ';');
    			if (pend) {
    				/* found command separator, copy sub-command */
    				*pend = '\0';
    				strcpy(buffer, p);
    				p = pend + 1;
    			} else {
    				/* separator not found, copy whole string */
    				strcpy(buffer, p);
    				p = NULL;
    				var_is_set = 0;
    			}
    		} else {
    			/*
    			 * No need to worry for buffer overflow here in
    			 * this function;  readline() maxes out at CFG_CBSIZE
    			 */
    			readline_into_buffer(prompt, buffer, 0);
    		}
    
    		argc = parse_line(buffer, argv);
    		if (argc == 0)
    			continue;
    
    
    		if (strcmp(argv[0], "help") == 0) {
    			puts(usage);
    			continue;
    		}
    
    		if (strcmp(argv[0], "next_step") == 0) {
    			printf("next_step = 0x%02X (%s)\n",
    			       next_step,
    			       step_to_string(next_step));
    			continue;
    		}
    
    
    		if (strcmp(argv[0], "copy") == 0) {
    			unsigned int error = 0;
    			unsigned int step_mask = 0;
    			unsigned int src_ctlr_mask = 0;
    			unsigned int src_dimm_mask = 0;
    			unsigned int dimm_number_required = 0;
    			unsigned int src_ctlr_num = 0;
    			unsigned int src_dimm_num = 0;
    			unsigned int dst_ctlr_num = -1;
    			unsigned int dst_dimm_num = -1;
    			unsigned int i, num_dest_parms;
    
    			if (argc == 1) {
    				printf("copy <src c#> <src d#> <spd|dimmparms|commonparms|opts|addresses|regs> <dst c#> <dst d#>\n");
    				continue;
    			}
    
    			error = fsl_ddr_parse_interactive_cmd(
    				argv, argc,
    				&step_mask,
    				&src_ctlr_mask,
    				&src_dimm_mask,
    				&dimm_number_required
    			);
    
    			/* XXX: only dimm_number_required and step_mask will
    			   be used by this function.  Parse the controller and
    			   DIMM number separately because it is easier.  */
    
    			if (error)
    				continue;
    
    			/* parse source destination controller / DIMM */
    
    			num_dest_parms = dimm_number_required ? 2 : 1;
    
    			for (i = 0; i < argc; i++) {
    				if (argv[i][0] == 'c') {
    					char c = argv[i][1];
    					if (isdigit(c)) {
    						src_ctlr_num = (c - '0');
    						break;
    					}
    				}
    			}
    
    			for (i = 0; i < argc; i++) {
    				if (argv[i][0] == 'd') {
    					char c = argv[i][1];
    					if (isdigit(c)) {
    						src_dimm_num = (c - '0');
    						break;
    					}
    				}
    			}
    
    			/* parse destination controller / DIMM */
    
    			for (i = argc - 1; i >= argc - num_dest_parms; i--) {
    				if (argv[i][0] == 'c') {
    					char c = argv[i][1];
    					if (isdigit(c)) {
    						dst_ctlr_num = (c - '0');
    						break;
    					}
    				}
    			}
    
    			for (i = argc - 1; i >= argc - num_dest_parms; i--) {
    				if (argv[i][0] == 'd') {
    					char c = argv[i][1];
    					if (isdigit(c)) {
    						dst_dimm_num = (c - '0');
    						break;
    					}
    				}
    			}
    
    			/* TODO: validate inputs */
    
    			debug("src_ctlr_num = %u, src_dimm_num = %u, dst_ctlr_num = %u, dst_dimm_num = %u, step_mask = %x\n",
    				src_ctlr_num, src_dimm_num, dst_ctlr_num, dst_dimm_num, step_mask);
    
    
    			switch (step_mask) {
    
    			case STEP_GET_SPD:
    				memcpy(&(pinfo->spd_installed_dimms[dst_ctlr_num][dst_dimm_num]),
    					&(pinfo->spd_installed_dimms[src_ctlr_num][src_dimm_num]),
    					sizeof(pinfo->spd_installed_dimms[0][0]));
    				break;
    
    			case STEP_COMPUTE_DIMM_PARMS:
    				memcpy(&(pinfo->dimm_params[dst_ctlr_num][dst_dimm_num]),
    					&(pinfo->dimm_params[src_ctlr_num][src_dimm_num]),
    					sizeof(pinfo->dimm_params[0][0]));
    				break;
    
    			case STEP_COMPUTE_COMMON_PARMS:
    				memcpy(&(pinfo->common_timing_params[dst_ctlr_num]),
    					&(pinfo->common_timing_params[src_ctlr_num]),
    					sizeof(pinfo->common_timing_params[0]));
    				break;
    
    			case STEP_GATHER_OPTS:
    				memcpy(&(pinfo->memctl_opts[dst_ctlr_num]),
    					&(pinfo->memctl_opts[src_ctlr_num]),
    					sizeof(pinfo->memctl_opts[0]));
    				break;
    
    			/* someday be able to have addresses to copy addresses... */
    
    			case STEP_COMPUTE_REGS:
    				memcpy(&(pinfo->fsl_ddr_config_reg[dst_ctlr_num]),
    					&(pinfo->fsl_ddr_config_reg[src_ctlr_num]),
    					sizeof(pinfo->memctl_opts[0]));
    				break;
    
    			default:
    				printf("unexpected step_mask value\n");
    			}
    
    			continue;
    
    		}
    
    
    		if (strcmp(argv[0], "edit") == 0) {
    			unsigned int error = 0;
    			unsigned int step_mask = 0;
    			unsigned int ctlr_mask = 0;
    			unsigned int dimm_mask = 0;
    			char *p_element = NULL;
    			char *p_value = NULL;
    			unsigned int dimm_number_required = 0;
    			unsigned int ctrl_num;
    			unsigned int dimm_num;
    
    			if (argc == 1) {
    				/* Only the element and value must be last */
    				printf("edit <c#> <d#> "
    					"<spd|dimmparms|commonparms|opts|"
    					"addresses|regs> <element> <value>\n");
    				printf("for spd, specify byte number for "
    					"element\n");
    				continue;
    			}
    
    
    			error = fsl_ddr_parse_interactive_cmd(
    				argv, argc - 2,
    				&step_mask,
    				&ctlr_mask,
    				&dimm_mask,
    				&dimm_number_required
    			);
    
    
    			if (error)
    				continue;
    
    
    			/* Check arguments */
    
    			/* ERROR: If no steps were found */
    			if (step_mask == 0) {
    				printf("Error: No valid steps were specified "
    						"in argument.\n");
    				continue;
    			}
    
    			/* ERROR: If multiple steps were found */
    			if (step_mask & (step_mask - 1)) {
    				printf("Error: Multiple steps specified in "
    						"argument.\n");
    				continue;
    			}
    
    			/* ERROR: Controller not specified */
    			if (ctlr_mask == 0) {
    				printf("Error: controller number not "
    					"specified or no element and "
    					"value specified\n");
    				continue;
    			}
    
    			if (ctlr_mask & (ctlr_mask - 1)) {
    				printf("Error: multiple controllers "
    						"specified, %X\n", ctlr_mask);
    				continue;
    			}
    
    			/* ERROR: DIMM number not specified */
    			if (dimm_number_required && dimm_mask == 0) {
    				printf("Error: DIMM number number not "
    					"specified or no element and "
    					"value specified\n");
    				continue;
    			}
    
    			if (dimm_mask & (dimm_mask - 1)) {
    				printf("Error: multipled DIMMs specified\n");
    				continue;
    			}
    
    			p_element = argv[argc - 2];
    			p_value = argv[argc - 1];
    
    			ctrl_num = __ilog2(ctlr_mask);
    			dimm_num = __ilog2(dimm_mask);
    
    			switch (step_mask) {
    			case STEP_GET_SPD:
    				{
    					unsigned int element_num;
    					unsigned int value;
    
    					element_num = simple_strtoul(p_element,
    								     NULL, 0);
    					value = simple_strtoul(p_value,
    							       NULL, 0);
    					fsl_ddr_spd_edit(pinfo,
    							       ctrl_num,
    							       dimm_num,
    							       element_num,
    							       value);
    					next_step = STEP_COMPUTE_DIMM_PARMS;
    				}
    				break;
    
    			case STEP_COMPUTE_DIMM_PARMS:
    				fsl_ddr_dimm_parameters_edit(
    						 pinfo, ctrl_num, dimm_num,
    						 p_element, p_value);
    				next_step = STEP_COMPUTE_COMMON_PARMS;
    				break;
    
    			case STEP_COMPUTE_COMMON_PARMS:
    				lowest_common_dimm_parameters_edit(pinfo,
    						ctrl_num, p_element, p_value);
    				next_step = STEP_GATHER_OPTS;
    				break;
    
    			case STEP_GATHER_OPTS:
    				fsl_ddr_options_edit(pinfo, ctrl_num,
    							   p_element, p_value);
    				next_step = STEP_ASSIGN_ADDRESSES;
    				break;
    
    			case STEP_ASSIGN_ADDRESSES:
    				printf("editing of address assignment "
    						"not yet implemented\n");
    				break;
    
    			case STEP_COMPUTE_REGS:
    				{
    					fsl_ddr_regs_edit(pinfo,
    								ctrl_num,
    								p_element,
    								p_value);
    					next_step = STEP_PROGRAM_REGS;
    				}
    				break;
    
    			default:
    				printf("programming error\n");
    				while (1)
    					;
    				break;
    			}
    			continue;
    		}
    
    		if (strcmp(argv[0], "reset") == 0) {
    			/*
    			 * Reboot machine.
    			 * Args don't seem to matter because this
    			 * doesn't return
    			 */
    			do_reset(NULL, 0, 0, NULL);
    
    			printf("Reset didn't work\n");
    
    		}
    
    		if (strcmp(argv[0], "recompute") == 0) {
    			/*
    			 * Recalculate everything, starting with
    			 * loading SPD EEPROM from DIMMs
    			 */
    			next_step = STEP_GET_SPD;
    			ddrsize = fsl_ddr_compute(pinfo, next_step, 0);
    			continue;
    		}
    
    		if (strcmp(argv[0], "compute") == 0) {
    			/*
    			 * Compute rest of steps starting at
    			 * the current next_step/
    			 */
    			ddrsize = fsl_ddr_compute(pinfo, next_step, 0);
    			continue;
    		}
    
    		if (strcmp(argv[0], "print") == 0) {
    			unsigned int error = 0;
    			unsigned int step_mask = 0;
    			unsigned int ctlr_mask = 0;
    			unsigned int dimm_mask = 0;
    
    			unsigned int dimm_number_required = 0;
    
    
    			if (argc == 1) {
    				printf("print [c<n>] [d<n>] [spd] [dimmparms] "
    				  "[commonparms] [opts] [addresses] [regs]\n");
    				continue;
    			}
    
    
    			error = fsl_ddr_parse_interactive_cmd(
    				argv, argc,
    				&step_mask,
    				&ctlr_mask,
    				&dimm_mask,
    				&dimm_number_required
    			);
    
    
    			if (error)
    				continue;
    
    			/* If no particular controller was found, print all */
    			if (ctlr_mask == 0)
    				ctlr_mask = 0xFF;
    
    			/* If no particular dimm was found, print all dimms. */
    			if (dimm_mask == 0)
    				dimm_mask = 0xFF;
    
    			/* If no steps were found, print all steps. */
    			if (step_mask == 0)
    				step_mask = STEP_ALL;
    
    			fsl_ddr_printinfo(pinfo, ctlr_mask,
    						dimm_mask, step_mask);
    			continue;
    		}
    
    		if (strcmp(argv[0], "go") == 0) {
    			if (next_step)
    				ddrsize = fsl_ddr_compute(pinfo, next_step, 0);
    			break;
    		}
    
    		printf("unknown command %s\n", argv[0]);
    	}
    
    	debug("end of memory = %llu\n", (u64)ddrsize);
    
    	return ddrsize;
    }