ColdFire: Add M54455EVB for MCF5445x

Signed-off-by: TsiChungLiew <Tsi-Chung.Liew@freescale.com>
diff --git a/lib_m68k/m68k_linux.c b/lib_m68k/m68k_linux.c
index 6c194f8..fde1744 100644
--- a/lib_m68k/m68k_linux.c
+++ b/lib_m68k/m68k_linux.c
@@ -25,6 +25,8 @@
 #include <command.h>
 #include <image.h>
 #include <zlib.h>
+#include <bzlib.h>
+#include <environment.h>
 #include <asm/byteorder.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -34,103 +36,189 @@
 #define LINUX_MAX_ENVS		256
 #define LINUX_MAX_ARGS		256
 
-extern image_header_t header;	/* from cmd_bootm.c */
+#ifdef CONFIG_SHOW_BOOT_PROGRESS
+# include <status_led.h>
+# define SHOW_BOOT_PROGRESS(arg)	show_boot_progress(arg)
+#else
+# define SHOW_BOOT_PROGRESS(arg)
+#endif
 
-extern int do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]);
+extern image_header_t header;
 
-static int linux_argc;
-static char **linux_argv;
-
-static char **linux_env;
-static char *linux_env_p;
-static int linux_env_idx;
-
-static void linux_params_init (ulong start, char *commandline);
-static void linux_env_set (char *env_name, char *env_val);
-
-void do_bootm_linux (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[],
-		     ulong addr, ulong * len_ptr, int verify)
+void do_bootm_linux(cmd_tbl_t * cmdtp, int flag,
+		    int argc, char *argv[],
+		    ulong addr, ulong * len_ptr, int verify)
 {
-	ulong len = 0, checksum;
+	ulong sp;
+	ulong len, checksum;
 	ulong initrd_start, initrd_end;
+	ulong cmd_start, cmd_end;
+	ulong initrd_high;
 	ulong data;
-	void (*theKernel) (int, char **, char **, int *);
+	int initrd_copy_to_ram = 1;
+	char *cmdline;
+	char *s;
+	bd_t *kbd;
+	void (*kernel) (bd_t *, ulong, ulong, ulong, ulong);
 	image_header_t *hdr = &header;
-	char *commandline = getenv ("bootargs");
-	char env_buf[12];
 
-	theKernel =
-		(void (*)(int, char **, char **, int *)) ntohl (hdr->ih_ep);
+	if ((s = getenv("initrd_high")) != NULL) {
+		/* a value of "no" or a similar string will act like 0,
+		 * turning the "load high" feature off. This is intentional.
+		 */
+		initrd_high = simple_strtoul(s, NULL, 16);
+		if (initrd_high == ~0)
+			initrd_copy_to_ram = 0;
+	} else {		/* not set, no restrictions to load high */
+		initrd_high = ~0;
+	}
+
+#ifdef CONFIG_LOGBUFFER
+	kbd = gd->bd;
+	/* Prevent initrd from overwriting logbuffer */
+	if (initrd_high < (kbd->bi_memsize - LOGBUFF_LEN - LOGBUFF_OVERHEAD))
+		initrd_high = kbd->bi_memsize - LOGBUFF_LEN - LOGBUFF_OVERHEAD;
+	debug("## Logbuffer at 0x%08lX ", kbd->bi_memsize - LOGBUFF_LEN);
+#endif
+
+	/*
+	 * Booting a (Linux) kernel image
+	 *
+	 * Allocate space for command line and board info - the
+	 * address should be as high as possible within the reach of
+	 * the kernel (see CFG_BOOTMAPSZ settings), but in unused
+	 * memory, which means far enough below the current stack
+	 * pointer.
+	 */
+	asm("movel %%a7, %%d0\n"
+	    "movel %%d0, %0\n": "=d"(sp): :"%d0");
+	
debug("## Current stack ends at 0x%08lX ", sp);
+
+	sp -= 2048;		/* just to be sure */
+	if (sp > CFG_BOOTMAPSZ)
+		sp = CFG_BOOTMAPSZ;
+	sp &= ~0xF;
+
+	debug("=> set upper limit to 0x%08lX\n", sp);
+
+	cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF);
+	kbd = (bd_t *) (((ulong) cmdline - sizeof(bd_t)) & ~0xF);
+
+	if ((s = getenv("bootargs")) == NULL)
+		s = "";
+
+	strcpy(cmdline, s);
+
+	cmd_start = (ulong) & cmdline[0];
+	cmd_end = cmd_start + strlen(cmdline);
+
+	*kbd = *(gd->bd);
+
+#ifdef	DEBUG
+	printf("## cmdline at 0x%08lX ... 0x%08lX\n", cmd_start, cmd_end);
+
+	do_bdinfo(NULL, 0, 0, NULL);
+#endif
+
+	if ((s = getenv("clocks_in_mhz")) != NULL) {
+		/* convert all clock information to MHz */
+		kbd->bi_intfreq /= 1000000L;
+		kbd->bi_busfreq /= 1000000L;
+	}
+
+	kernel =
+	    (void (*)(bd_t *, ulong, ulong, ulong, ulong))ntohl(hdr->ih_ep);
 
 	/*
 	 * Check if there is an initrd image
 	 */
+
 	if (argc >= 3) {
-		show_boot_progress (9);
+		debug("Not skipping initrd\n");
+		SHOW_BOOT_PROGRESS(9);
 
-		addr = simple_strtoul (argv[2], NULL, 16);
+		addr = simple_strtoul(argv[2], NULL, 16);
 
-		printf ("## Loading Ramdisk Image at %08lx ...\n", addr);
+		printf("## Loading RAMDisk Image at %08lx ...\n", addr);
 
 		/* Copy header so we can blank CRC field for re-calculation */
-		memcpy (&header, (char *) addr, sizeof (image_header_t));
+		memmove(&header, (char *)addr, sizeof(image_header_t));
 
-		if (ntohl (hdr->ih_magic) != IH_MAGIC) {
-			printf ("Bad Magic Number\n");
-			show_boot_progress (-10);
-			do_reset (cmdtp, flag, argc, argv);
+		if (ntohl(hdr->ih_magic) != IH_MAGIC) {
+			puts("Bad Magic Number\n");
+			SHOW_BOOT_PROGRESS(-10);
+			do_reset(cmdtp, flag, argc, argv);
 		}
 
 		data = (ulong) & header;
-		len = sizeof (image_header_t);
+		len = sizeof(image_header_t);
 
-		checksum = ntohl (hdr->ih_hcrc);
+		checksum = ntohl(hdr->ih_hcrc);
 		hdr->ih_hcrc = 0;
 
-		if (crc32 (0, (char *) data, len) != checksum) {
-			printf ("Bad Header Checksum\n");
-			show_boot_progress (-11);
-			do_reset (cmdtp, flag, argc, argv);
+		if (crc32(0, (uchar *) data, len) != checksum) {
+			puts("Bad Header Checksum\n");
+			SHOW_BOOT_PROGRESS(-11);
+			do_reset(cmdtp, flag, argc, argv);
 		}
 
-		show_boot_progress (10);
+		SHOW_BOOT_PROGRESS(10);
 
-		print_image_hdr (hdr);
+		print_image_hdr(hdr);
 
-		data = addr + sizeof (image_header_t);
-		len = ntohl (hdr->ih_size);
+		data = addr + sizeof(image_header_t);
+		len = ntohl(hdr->ih_size);
 
 		if (verify) {
 			ulong csum = 0;
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+			ulong cdata = data, edata = cdata + len;
+#endif				/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
 
-			printf ("   Verifying Checksum ... ");
-			csum = crc32 (0, (char *) data, len);
-			if (csum != ntohl (hdr->ih_dcrc)) {
-				printf ("Bad Data CRC\n");
-				show_boot_progress (-12);
-				do_reset (cmdtp, flag, argc, argv);
+			puts("   Verifying Checksum ... ");
+
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+
+			while (cdata < edata) {
+				ulong chunk = edata - cdata;
+
+				if (chunk > CHUNKSZ)
+					chunk = CHUNKSZ;
+				csum = crc32(csum, (uchar *) cdata, chunk);
+				cdata += chunk;
+
+				WATCHDOG_RESET();
 			}
-			printf ("OK\n");
+#else				/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
+			csum = crc32(0, (uchar *) data, len);
+#endif				/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+
+			if (csum != ntohl(hdr->ih_dcrc)) {
+				puts("Bad Data CRC\n");
+				SHOW_BOOT_PROGRESS(-12);
+				do_reset(cmdtp, flag, argc, argv);
+			}
+			puts("OK\n");
 		}
 
-		show_boot_progress (11);
+		SHOW_BOOT_PROGRESS(11);
 
 		if ((hdr->ih_os != IH_OS_LINUX) ||
 		    (hdr->ih_arch != IH_CPU_M68K) ||
 		    (hdr->ih_type != IH_TYPE_RAMDISK)) {
-			printf ("No Linux M68K Ramdisk Image\n");
-			show_boot_progress (-13);
-			do_reset (cmdtp, flag, argc, argv);
+			puts("No Linux ColdFire Ramdisk Image\n");
+			SHOW_BOOT_PROGRESS(-13);
+			do_reset(cmdtp, flag, argc, argv);
 		}
 
 		/*
 		 * Now check if we have a multifile image
 		 */
 	} else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
-		ulong tail = ntohl (len_ptr[0]) % 4;
+		u_long tail = ntohl(len_ptr[0]) % 4;
 		int i;
 
-		show_boot_progress (13);
+		SHOW_BOOT_PROGRESS(13);
 
 		/* skip kernel length and terminator */
 		data = (ulong) (&len_ptr[2]);
@@ -138,130 +226,110 @@
 		for (i = 1; len_ptr[i]; ++i)
 			data += 4;
 		/* add kernel length, and align */
-		data += ntohl (len_ptr[0]);
+		data += ntohl(len_ptr[0]);
 		if (tail) {
 			data += 4 - tail;
 		}
 
-		len = ntohl (len_ptr[1]);
+		len = ntohl(len_ptr[1]);
 
 	} else {
 		/*
 		 * no initrd image
 		 */
-		show_boot_progress (14);
+		SHOW_BOOT_PROGRESS(14);
 
-		data = 0;
+		len = data = 0;
 	}
 
-#ifdef	DEBUG
 	if (!data) {
-		printf ("No initrd\n");
+		debug("No initrd\n");
 	}
-#endif
 
 	if (data) {
-		initrd_start = data;
-		initrd_end = initrd_start + len;
+		if (!initrd_copy_to_ram) {	/* zero-copy ramdisk support */
+			initrd_start = data;
+			initrd_end = initrd_start + len;
+		} else {
+			initrd_start = (ulong) kbd - len;
+			initrd_start &= ~(4096 - 1);	/* align on page */
+
+			if (initrd_high) {
+				ulong nsp;
+
+				/*
+				 * the inital ramdisk does not need to be within
+				 * CFG_BOOTMAPSZ as it is not accessed until after
+				 * the mm system is initialised.
+				 *
+				 * do the stack bottom calculation again and see if
+				 * the initrd will fit just below the monitor stack
+				 * bottom without overwriting the area allocated
+				 * above for command line args and board info.
+				 */
+				asm("movel %%a7, %%d0\n"
+				    "movel %%d0, %0\n": "=d"(nsp): :"%d0");
+				
				nsp -= 2048;	/* just to be sure */
+				nsp &= ~0xF;
+
+				if (nsp > initrd_high)	/* limit as specified */
+					nsp = initrd_high;
+
+					nsp -= len;
+				nsp &= ~(4096 - 1);	/* align on page */
+
+				if (nsp >= sp)
+					initrd_start = nsp;
+			}
+
+			SHOW_BOOT_PROGRESS(12);
+
+			debug
+			    ("## initrd at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
+			     data, data + len - 1, len, len);
+
+			initrd_end = initrd_start + len;
+			printf("   Loading Ramdisk to %08lx, end %08lx ... ",
+			       initrd_start, initrd_end);
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+			{
+				size_t l = len;
+				void *to = (void *)initrd_start;
+				void *from = (void *)data;
+
+				while (l > 0) {
+					size_t tail =
+					    (l > CHUNKSZ) ? CHUNKSZ : l;
+					WATCHDOG_RESET();
+					memmove(to, from, tail);
+					to += tail;
+					from += tail;
+					l -= tail;
+				}
+			}
+#else				/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
+			memmove((void *)initrd_start, (void *)data, len);
+#endif				/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
+			puts("OK\n");
+		}
 	} else {
 		initrd_start = 0;
 		initrd_end = 0;
 	}
 
-	show_boot_progress (15);
+	debug("## Transferring control to Linux (at address %08lx) ...\n",
+	      (ulong) kernel);
 
-#ifdef DEBUG
-	printf ("## Transferring control to Linux (at address %08lx) ...\n",
-		(ulong) theKernel);
-#endif
+	SHOW_BOOT_PROGRESS(15);
 
-	linux_params_init (PHYSADDR (gd->bd->bi_boot_params), commandline);
-
-	sprintf (env_buf, "%lu", gd->ram_size >> 20);
-	linux_env_set ("memsize", env_buf);
-
-	sprintf (env_buf, "0x%08X", (uint) PHYSADDR (initrd_start));
-	linux_env_set ("initrd_start", env_buf);
-
-	sprintf (env_buf, "0x%X", (uint) (initrd_end - initrd_start));
-	linux_env_set ("initrd_size", env_buf);
-
-	sprintf (env_buf, "0x%08X", (uint) (gd->bd->bi_flashstart));
-	linux_env_set ("flash_start", env_buf);
-
-	sprintf (env_buf, "0x%X", (uint) (gd->bd->bi_flashsize));
-	linux_env_set ("flash_size", env_buf);
-
-	/* we assume that the kernel is in place */
-	printf ("\nStarting kernel ...\n\n");
-
-	theKernel (linux_argc, linux_argv, linux_env, 0);
-}
-
-static void linux_params_init (ulong start, char *line)
-{
-	char *next, *quote, *argp;
-
-	linux_argc = 1;
-	linux_argv = (char **) start;
-	linux_argv[0] = 0;
-	argp = (char *) (linux_argv + LINUX_MAX_ARGS);
-
-	next = line;
-
-	while (line && *line && linux_argc < LINUX_MAX_ARGS) {
-		quote = strchr (line, '"');
-		next = strchr (line, ' ');
-
-		while (next != NULL && quote != NULL && quote < next) {
-			/* we found a left quote before the next blank
-			 * now we have to find the matching right quote
-			 */
-			next = strchr (quote + 1, '"');
-			if (next != NULL) {
-				quote = strchr (next + 1, '"');
-				next = strchr (next + 1, ' ');
-			}
-		}
-
-		if (next == NULL) {
-			next = line + strlen (line);
-		}
-
-		linux_argv[linux_argc] = argp;
-		memcpy (argp, line, next - line);
-		argp[next - line] = 0;
-
-		argp += next - line + 1;
-		linux_argc++;
-
-		if (*next)
-			next++;
-
-		line = next;
-	}
-
-	linux_env = (char **) (((ulong) argp + 15) & ~15);
-	linux_env[0] = 0;
-	linux_env_p = (char *) (linux_env + LINUX_MAX_ENVS);
-	linux_env_idx = 0;
-}
-
-static void linux_env_set (char *env_name, char *env_val)
-{
-	if (linux_env_idx < LINUX_MAX_ENVS - 1) {
-		linux_env[linux_env_idx] = linux_env_p;
-
-		strcpy (linux_env_p, env_name);
-		linux_env_p += strlen (env_name);
-
-		strcpy (linux_env_p, "=");
-		linux_env_p += 1;
-
-		strcpy (linux_env_p, env_val);
-		linux_env_p += strlen (env_val);
-
-		linux_env_p++;
-		linux_env[++linux_env_idx] = 0;
-	}
+	/*
+	 * Linux Kernel Parameters (passing board info data):
+	 *   r3: ptr to board info data
+	 *   r4: initrd_start or 0 if no initrd
+	 *   r5: initrd_end - unused if r4 is 0
+	 *   r6: Start of command line string
+	 *   r7: End   of command line string
+	 */
+	(*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end);
+	/* does not return */
 }