diff options
Diffstat (limited to 'src/mainboard/via/epia-m700/wakeup.c')
-rw-r--r-- | src/mainboard/via/epia-m700/wakeup.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/src/mainboard/via/epia-m700/wakeup.c b/src/mainboard/via/epia-m700/wakeup.c new file mode 100644 index 0000000000..37dec7dab5 --- /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; +} |