Initial untested board code for the VIA EPIA-M700 Mini-ITX board.

The patch has been submitted by bari <bari@onelabs.com> and written
by OLPC.

Signed-off-by: Uwe Hermann <uwe@hermann-uwe.de>
Acked-by: Uwe Hermann <uwe@hermann-uwe.de>



git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4342 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
diff --git a/src/mainboard/via/epia-m700/wakeup.c b/src/mainboard/via/epia-m700/wakeup.c
new file mode 100644
index 0000000..37dec7d
--- /dev/null
+++ b/src/mainboard/via/epia-m700/wakeup.c
@@ -0,0 +1,447 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008 Rudolf Marek <r.marek@assembler.cz>
+ * Copyright (C) 2009 One Laptop per Child, Association, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+ //reboot.c from linux
+/*this file mostly copied from Rudolf's S3 patch, some changes in acpi_jump_wake()*/
+#include <stdint.h>
+#include <string.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <delay.h>
+#include <part/init_timer.h>//for jaon_tsc_count_end
+#include "wakeup.h"
+
+
+
+
+int enable_a20(void);
+
+/* The following code and data reboots the machine by switching to real
+   mode and jumping to the BIOS reset entry point, as if the CPU has
+   really been reset.  The previous version asked the keyboard
+   controller to pulse the CPU reset line, which is more thorough, but
+   doesn't work with at least one type of 486 motherboard.  It is easy
+   to stop this code working; hence the copious comments. */
+
+static unsigned long long
+real_mode_gdt_entries [3] =
+{
+	0x0000000000000000ULL,	/* Null descriptor */
+	0x00009a000000ffffULL,	/* 16-bit real-mode 64k code at 0x00000000 */
+	0x000092000100ffffULL	/* 16-bit real-mode 64k data at 0x00000100 */
+};
+
+struct Xgt_desc_struct {
+         unsigned short size;
+         unsigned long address __attribute__((packed));
+         unsigned short pad;
+ } __attribute__ ((packed));
+
+static struct Xgt_desc_struct
+real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
+real_mode_idt = { 0x3ff, 0 },
+no_idt = { 0, 0 };
+
+
+/* This is 16-bit protected mode code to disable paging and the cache,
+   switch to real mode and jump to the BIOS reset code.
+
+   The instruction that switches to real mode by writing to CR0 must be
+   followed immediately by a far jump instruction, which set CS to a
+   valid value for real mode, and flushes the prefetch queue to avoid
+   running instructions that have already been decoded in protected
+   mode.
+
+   Clears all the flags except ET, especially PG (paging), PE
+   (protected-mode enable) and TS (task switch for coprocessor state
+   save).  Flushes the TLB after paging has been disabled.  Sets CD and
+   NW, to disable the cache on a 486, and invalidates the cache.  This
+   is more like the state of a 486 after reset.  I don't know if
+   something else should be done for other chips.
+
+   More could be done here to set up the registers as if a CPU reset had
+   occurred; hopefully real BIOSs don't assume much. */
+
+
+//	0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,	/*    orl   $0x60000000,%eax */
+
+static unsigned char real_mode_switch [] =
+{
+	0x66, 0x0f, 0x20, 0xc0,			/*    movl  %cr0,%eax        */
+	0x24, 0xfe,				/*    andb  $0xfe,al         */
+	0x66, 0x0f, 0x22, 0xc0			/*    movl  %eax,%cr0        */
+};
+static unsigned char jump_to_wakeup [] =
+{
+	0xea, 0x00, 0x00, 0x00, 0xe0		/*    ljmp  $0xffff,$0x0000  */
+};
+
+/*
+ * Switch to real mode and then execute the code
+ * specified by the code and length parameters.
+ * We assume that length will aways be less that 100!
+ */ 
+static unsigned char show31 [6] =
+{
+	0xb0, 0x31, 0xe6, 0x80, 0xeb ,0xFA		/*    ljmp  $0xffff,$0x0000  */
+}; 
+static unsigned char show32 [6] =
+{
+	0xb0, 0x32, 0xe6, 0x80, 0xeb ,0xFA		/*    ljmp  $0xffff,$0x0000  */
+}; 
+void acpi_jump_wake(u32 vector)
+{
+u32 tmp;
+u16 tmpvector;
+u32 dwEip;
+u8   Data;
+struct Xgt_desc_struct * wake_thunk16_Xgt_desc;
+
+	printk_debug("IN ACPI JUMP WAKE TO %x\n", vector);
+	if (enable_a20())
+		die("failed to enable A20\n");
+	printk_debug("IN ACPI JUMP WAKE TO 3 %x\n", vector);
+	
+	* ((u16 *) (jump_to_wakeup+3)) = (u16)(vector>>4);
+	printk_debug("%x %x %x %x %x\n", jump_to_wakeup[0], jump_to_wakeup[1], jump_to_wakeup[2], jump_to_wakeup[3],jump_to_wakeup[4]);
+
+	memcpy ((void *) (WAKE_THUNK16_ADDR - sizeof (real_mode_switch) - 100),
+		real_mode_switch, sizeof (real_mode_switch));
+	memcpy ((void *) (WAKE_THUNK16_ADDR - 100), jump_to_wakeup, sizeof(jump_to_wakeup));
+
+	jason_tsc_count();
+	printk_emerg("file '%s', line %d\n\n", __FILE__, __LINE__); 
+	jason_tsc_count_end();
+
+
+	unsigned long long * real_mode_gdt_entries_at_eseg;
+	real_mode_gdt_entries_at_eseg=WAKE_THUNK16_GDT; //copy from real_mode_gdt_entries and change limition to 1M and data base to 0;
+	real_mode_gdt_entries_at_eseg [0] =	0x0000000000000000ULL;	/* Null descriptor */
+	real_mode_gdt_entries_at_eseg [1] =	0x000f9a000000ffffULL;	/* 16-bit real-mode 1M code at 0x00000000 */
+	real_mode_gdt_entries_at_eseg [2] =	0x000f93000000ffffULL;	/* 16-bit real-mode 1M data at 0x00000000 */
+
+	wake_thunk16_Xgt_desc=WAKE_THUNK16_XDTR;
+	wake_thunk16_Xgt_desc[0].size=sizeof (real_mode_gdt_entries) - 1;
+	wake_thunk16_Xgt_desc[0].address=(long)real_mode_gdt_entries_at_eseg;
+	wake_thunk16_Xgt_desc[1].size=0x3ff;
+	wake_thunk16_Xgt_desc[1].address=0;
+	wake_thunk16_Xgt_desc[2].size=0;
+	wake_thunk16_Xgt_desc[2].address=0;
+
+      /*added this code to get current value of EIP
+         */
+	__asm__ volatile (
+     "calll   geip\n\t"
+     "geip: \n\t" 
+     "popl %0\n\t"
+		 :"=a"(dwEip)
+	);
+
+	unsigned char *dest;
+	unsigned char *src;
+	src= (unsigned char *)dwEip;
+	dest=WAKE_RECOVER1M_CODE;
+	u32 i;
+  for (i = 0; i < 0x200; i++)
+		dest[i] = src[i];	
+		
+	__asm__ __volatile__ ("ljmp $0x0010,%0"//08 error
+				:
+				: "i" ((void *) (WAKE_RECOVER1M_CODE+0x20)));					
+
+       /*added 0x20 "nop" to make sure the ljmp will not jump then halt*/
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	asm volatile("nop");
+	
+
+	__asm__ volatile (
+                /* set new esp, maybe ebp should not equal to esp?, 
+                due to the variable in acpi_jump_wake?, anyway, this may be not a big problem.
+                and I didnt clear the area (ef000+-0x200) to zero.
+                */
+                "movl %0, %%ebp\n\t"
+                "movl %0, %%esp\n\t"		
+		 ::"a"(WAKE_THUNK16_STACK)
+	);
+
+
+	/* added this
+	only "src" and "dest" use the new stack, and the esp maybe also used in resumevector
+	*/
+#if PAYLOAD_IS_SEABIOS==1	
+	// WAKE_MEM_INFO inited in get_set_top_available_mem in tables.c
+	src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000);
+	  dest = 0;
+	  for (i = 0; i < 0xa0000; i++)//if recovered 0-e0000, then  when resume, before winxp turn on the desktop screen ,there is gray background which last 1sec.
+		dest[i] = src[i];
+		/*__asm__ volatile (		
+	 			"movl    %0, %%esi\n\t"
+        "movl    $0, %%edi\n\t"
+       	"movl    $0xa0000, %%ecx\n\t"
+       	"shrl    $2, %%ecx\n\t"
+        "rep movsd\n\t"    
+        ::"a"(src)        
+   	);*/
+  src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000+0xc0000);
+	//dest = 0xc0000;
+	//for (i = 0; i < 0x20000; i++)
+	//	dest[i] = src[i];		
+	/*	__asm__ volatile (		
+	 			"movl    %0, %%esi\n\t"
+        "movl    $0xc0000, %%edi\n\t"
+       	"movl    $0x20000, %%ecx\n\t"
+       	"shrl    $2, %%ecx\n\t"
+        "rep movsd\n\t"    
+        ::"a"(src)        
+   	);*/
+
+
+  src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000+0xe0000+WAKE_SPECIAL_SIZE);
+  //dest = 0xf0000;
+  //for (i = 0; i < 0x10000; i++)
+	//	dest[i] = src[i];		
+  	__asm__ volatile (		
+	 			"movl    %0, %%esi\n\t"
+        "movl    %1, %%edi\n\t"
+       	"movl    %2, %%ecx\n\t"
+       	"shrl    $2, %%ecx\n\t"
+        "rep movsd\n\t"    
+        ::"r"(src),"r"(0xe0000+WAKE_SPECIAL_SIZE), "r"(0x10000-WAKE_SPECIAL_SIZE)
+   	);
+	
+  src = (unsigned char *)((* (u32*)WAKE_MEM_INFO)- 64*1024-0x100000+0xf0000);
+  //dest = 0xf0000;
+  //for (i = 0; i < 0x10000; i++)
+	//	dest[i] = src[i];		
+  	__asm__ volatile (		
+	 			"movl    %0, %%esi\n\t"
+        "movl    $0xf0000, %%edi\n\t"
+       	"movl    $0x10000, %%ecx\n\t"
+       	"shrl    $2, %%ecx\n\t"
+        "rep movsd\n\t"    
+        ::"a"(src)        
+   	);
+  
+	asm volatile("wbinvd");
+#endif
+	/* Set up the IDT for real mode. */
+	asm volatile("lidt %0"::"m" (wake_thunk16_Xgt_desc[1]));
+	
+	/* Set up a GDT from which we can load segment descriptors for real
+	   mode.  The GDT is not used in real mode; it is just needed here to
+	   prepare the descriptors. */
+	asm volatile("lgdt %0"::"m" (wake_thunk16_Xgt_desc[0]));
+
+	/* Load the data segment registers, and thus the descriptors ready for
+	   real mode.  The base address of each segment is 0x100, 16 times the
+	   selector value being loaded here.  This is so that the segment
+	   registers don't have to be reloaded after switching to real mode:
+	   the values are consistent for real mode operation already. */
+
+	__asm__ __volatile__ ("movl $0x0010,%%eax\n"
+				"\tmovl %%eax,%%ds\n"
+				"\tmovl %%eax,%%es\n"
+				"\tmovl %%eax,%%fs\n"
+				"\tmovl %%eax,%%gs\n"
+				"\tmovl %%eax,%%ss" : : : "eax");
+
+	/* Jump to the 16-bit code that we copied earlier.  It disables paging
+	   and the cache, switches to real mode, and jumps to the BIOS reset
+	   entry point. */
+	   
+	__asm__ __volatile__ ("ljmp $0x0008,%0"
+				:
+				: "i" ((void *) (WAKE_THUNK16_ADDR - sizeof (real_mode_switch) - 100)));
+}
+
+
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ *   Copyright (C) 1991, 1992 Linus Torvalds
+ *   Copyright 2007 rPath, Inc. - All Rights Reserved
+ *
+ *   This file is part of the Linux kernel, and is made available under
+ *   the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * arch/i386/boot/a20.c
+ *
+ * Enable A20 gate (return -1 on failure)
+ */
+
+//#include "boot.h"
+
+#define MAX_8042_LOOPS	100000
+
+static int empty_8042(void)
+{
+	u8 status;
+	int loops = MAX_8042_LOOPS;
+
+	while (loops--) {
+		udelay(1);
+
+		status = inb(0x64);
+		if (status & 1) {
+			/* Read and discard input data */
+			udelay(1);
+			(void)inb(0x60);
+		} else if (!(status & 2)) {
+			/* Buffers empty, finished! */
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+/* Returns nonzero if the A20 line is enabled.  The memory address
+   used as a test is the int $0x80 vector, which should be safe. */
+
+#define A20_TEST_ADDR	(4*0x80)
+#define A20_TEST_SHORT  32
+#define A20_TEST_LONG	2097152	/* 2^21 */
+
+static int a20_test(int loops)
+{
+	int ok = 0;
+	int saved, ctr;
+
+//	set_fs(0x0000);
+//	set_gs(0xffff);
+
+	saved = ctr =  *((u32*) A20_TEST_ADDR);
+
+	while (loops--) {
+		//wrfs32(++ctr, A20_TEST_ADDR);
+		
+		*((u32*) A20_TEST_ADDR) = ++ctr;
+		
+		udelay(1);	/* Serialize and make delay constant */
+		
+		ok = *((u32 *) A20_TEST_ADDR+0xffff0+0x10) ^ ctr;
+		if (ok)
+			break;
+	}
+
+	*((u32*) A20_TEST_ADDR) = saved;
+	return ok;
+}
+
+/* Quick test to see if A20 is already enabled */
+static int a20_test_short(void)
+{
+	return a20_test(A20_TEST_SHORT);
+}
+
+/* Longer test that actually waits for A20 to come on line; this
+   is useful when dealing with the KBC or other slow external circuitry. */
+static int a20_test_long(void)
+{
+	return a20_test(A20_TEST_LONG);
+}
+
+static void enable_a20_kbc(void)
+{
+	empty_8042();
+
+	outb(0xd1, 0x64);	/* Command write */
+	empty_8042();
+
+	outb(0xdf, 0x60);	/* A20 on */
+	empty_8042();
+}
+
+static void enable_a20_fast(void)
+{
+	u8 port_a;
+
+	port_a = inb(0x92);	/* Configuration port A */
+	port_a |=  0x02;	/* Enable A20 */
+	port_a &= ~0x01;	/* Do not reset machine */
+	outb(port_a, 0x92);
+}
+
+/*
+ * Actual routine to enable A20; return 0 on ok, -1 on failure
+ */
+
+#define A20_ENABLE_LOOPS 255	/* Number of times to try */
+
+int enable_a20(void)
+{
+	int loops = A20_ENABLE_LOOPS;
+
+	while (loops--) {
+		/* First, check to see if A20 is already enabled
+		   (legacy free, etc.) */
+		if (a20_test_short())
+			return 0;
+
+		/* Try enabling A20 through the keyboard controller */
+		empty_8042();
+//if (a20_test_short())
+//	return 0; /* BIOS worked, but with delayed reaction */
+
+		enable_a20_kbc();
+		if (a20_test_long())
+			return 0;
+
+		/* Finally, try enabling the "fast A20 gate" */
+		enable_a20_fast();
+		if (a20_test_long())
+			return 0;
+	}
+
+	return -1;
+}