Add copy command to FSL DDR interactive

Add copy command which allows copying of DIMM/controller settings.
This saves tedious retyping of parameters for each identical DIMM
or controller.

Signed-off-by: James Yang <James.Yang@freescale.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/interactive.c b/arch/powerpc/cpu/mpc8xxx/ddr/interactive.c
index 0474acc..e5ee775 100644
--- a/arch/powerpc/cpu/mpc8xxx/ddr/interactive.c
+++ b/arch/powerpc/cpu/mpc8xxx/ddr/interactive.c
@@ -1445,6 +1445,7 @@
 		"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"
@@ -1477,6 +1478,132 @@
 			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;
diff --git a/doc/README.fsl-ddr b/doc/README.fsl-ddr
index 59583b3..b2a7c0f 100644
--- a/doc/README.fsl-ddr
+++ b/doc/README.fsl-ddr
@@ -279,6 +279,7 @@
 type command "compute" to calculate the parameters from the default
 type command "print" with arguments to show SPD, options, registers
 type command "edit" with arguments to change any if desired
+type command "copy" with arguments to copy controller/dimm settings
 type command "go" to continue calculation and enable DDR controller
 type command "reset" to reset the board
 type command "recompute" to reload SPD and start over
@@ -313,6 +314,10 @@
 			  byte number if the object is SPD
 	<value>		- decimal or heximal (prefixed with 0x) numbers
 
+copy <src c#> <src d#> <spd|dimmparms|commonparms|opts|addresses|regs> <dst c#> <dst d#>
+	same as for "edit" command
+	DIMM numbers ignored for commonparms, opts, and regs
+
 reset
 	no arguement	- reset the board