aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/idle/mrst_s0i3_asm.S
blob: 10872fa4a3c574159039ec9e619d14458f231dd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
 * mrst_s0i3_asm.S - super-deep sleep state for the Moorestown MID platform
 *
 * Copyright (c) 2010, Intel Corporation.
 * H. Peter Anvin <hpa@linux.intel.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope 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.
 */

#include <linux/linkage.h>
#include <asm/asm.h>
#include <asm/msr-index.h>
#include <asm/page.h>
#include <asm/segment.h>

#include "mrst_s0i3.h"

/* Physical address */
#define pa(X) ((X) - __PAGE_OFFSET)

	.code32		/* MRST boots up in flat 32-bit mode */
	.text
ENTRY(mrst_s0i3_entry)
	pushl	%edi
	pushl	%esi
	pushl	%ebp
	pushl	%ebx

	movl	%esp, %esi

	sldt	%eax
	pushl	%eax

	subl	$8, %esp
	sidtl	2(%esp)

	pushl	%gs
	pushl	%fs

	movl	%cr3, %eax
	pushl	%eax

	subl	$8, %esp
	sgdtl	2(%esp)

	movl	%cr0, %eax
	pushl	%eax

	movl	%cr4, %eax
	pushl	%eax

	movl	$(MSR_EFER), %ecx
	rdmsr
	pushl	%edx
	pushl	%eax

	movl	$(MSR_IA32_MISC_ENABLE), %ecx
	rdmsr
	pushl	%edx
	pushl	%eax

	movl	28(%esp), %eax	/* GDT base */
	subl	$(__PAGE_OFFSET), %eax	/* Convert to a physical address */
	pushl	%eax
	pushl	28(%esp)		/* GDT length, pad */

	pushfl

	leal	(-__PAGE_OFFSET)(%esp), %eax
	movl	%eax, mrst_s0i3_resume_stack

	wbinvd

	movl	%esp, %eax		/* As good as anything... */
	xorl	%edx, %edx
	xorl	%ecx, %ecx
	monitor

	movl	$MRST_C6_HINTS_EAX, %eax
	movl	$MRST_C6_HINTS_ECX, %ecx
	sti
	mwait

	/* If MWAIT wakes us up, assume something happened... */
	cli
	/* jmp	simulate_resume */
	movl	%esi, %esp

	xorl	%eax, %eax		/* Not really S0i3 */
	popl	%ebx
	popl	%ebp
	popl	%esi
	popl	%edi
	ret
ENDPROC(mrst_s0i3_entry)

/*
 * Hack for testing: simulate a wakeup
 */
simulate_resume:
	movl	$pa(initial_page_table), %eax
	movl	%eax, %cr3
	ljmpl	$(__KERNEL_CS), $pa(1f)
1:
	movl	%cr0, %eax
	andl	$~0x80000000, %eax
	movl	%eax, %cr0		/* Disable paging */

	xorl	%eax, %eax
	movl	%eax, %cr3
	movl	%eax, %cr4

	xorl	%edx, %edx
	movl	$(MSR_EFER), %ecx
	wrmsr

	lldt	%ax
	movl	%eax, %esp

	/* Fall through */
/*
 * After S0i3 the MRST firmare will put us back in 32-bit flat mode
 */
ENTRY(mrst_s0i3_resume)
	cli

	movl	pa(mrst_s0i3_resume_stack), %esp
	popfl
	lgdtl	2(%esp)			/* Physical GDT pointer */

	addl	$8, %esp
	movl	$(__KERNEL_DS), %eax
	movl	%eax, %ss
	movl	%eax, %ds
	movl	%eax, %es

	popl	%eax
	popl	%edx
	movl	$(MSR_IA32_MISC_ENABLE), %ecx
	wrmsr

	popl	%eax
	popl	%edx
	movl	$(MSR_EFER), %ecx
	wrmsr

	popl	%eax
	movl	%eax, %cr4

	movl	$pa(initial_page_table), %eax
	movl	%eax, %cr3

	popl	%eax
	movl	%eax, %cr0		/* Enables paging! */
	ljmpl	$(__KERNEL_CS), $1f
1:

	addl	$__PAGE_OFFSET, %esp
	lgdtl	2(%esp)			/* Linear GDT pointer */
	popl	%eax			/* GDT length + junk */
	popl	%ebx			/* Linear address of GDT */

	popl	%eax
	movl	%eax, %cr3

	movl	$(GDT_ENTRY_TSS*8), %eax

	/* Clear the TSS busy bit: %ebx == GDT.base */
	andb	$~0x02, (GDT_ENTRY_TSS*8+5)(%ebx)
	ltr	%ax			/* Set the TSS */

	popl	%fs
	popl	%gs

	/* x86-64 would need to restore MSR_GS_BASE too */

	lidtl	2(%esp)
	addl	$8, %esp

	popl	%eax
	lldt	%ax

	movl	$1, %eax		/* Resume from actual S0i3 */
	popl	%ebx
	popl	%ebp
	popl	%esi
	popl	%edi

	ret
ENDPROC(mrst_s0i3_resume)

	.bss
	.balign	4
mrst_s0i3_resume_stack:
	.space	4
END(mrst_s0i3_resume_stack)