]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - arch/powerpc/kernel/idle_power7.S
powerpc/powernv: Switch off MMU before entering nap/sleep/rvwinkle mode
[mirror_ubuntu-zesty-kernel.git] / arch / powerpc / kernel / idle_power7.S
1 /*
2 * This file contains the power_save function for Power7 CPUs.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10 #include <linux/threads.h>
11 #include <asm/processor.h>
12 #include <asm/page.h>
13 #include <asm/cputable.h>
14 #include <asm/thread_info.h>
15 #include <asm/ppc_asm.h>
16 #include <asm/asm-offsets.h>
17 #include <asm/ppc-opcode.h>
18 #include <asm/hw_irq.h>
19 #include <asm/kvm_book3s_asm.h>
20 #include <asm/opal.h>
21
22 #undef DEBUG
23
24 /* Idle state entry routines */
25
26 #define IDLE_STATE_ENTER_SEQ(IDLE_INST) \
27 /* Magic NAP/SLEEP/WINKLE mode enter sequence */ \
28 std r0,0(r1); \
29 ptesync; \
30 ld r0,0(r1); \
31 1: cmp cr0,r0,r0; \
32 bne 1b; \
33 IDLE_INST; \
34 b .
35
36 .text
37
38 /*
39 * Pass requested state in r3:
40 * 0 - nap
41 * 1 - sleep
42 *
43 * To check IRQ_HAPPENED in r4
44 * 0 - don't check
45 * 1 - check
46 */
47 _GLOBAL(power7_powersave_common)
48 /* Use r3 to pass state nap/sleep/winkle */
49 /* NAP is a state loss, we create a regs frame on the
50 * stack, fill it up with the state we care about and
51 * stick a pointer to it in PACAR1. We really only
52 * need to save PC, some CR bits and the NV GPRs,
53 * but for now an interrupt frame will do.
54 */
55 mflr r0
56 std r0,16(r1)
57 stdu r1,-INT_FRAME_SIZE(r1)
58 std r0,_LINK(r1)
59 std r0,_NIP(r1)
60
61 #ifndef CONFIG_SMP
62 /* Make sure FPU, VSX etc... are flushed as we may lose
63 * state when going to nap mode
64 */
65 bl discard_lazy_cpu_state
66 #endif /* CONFIG_SMP */
67
68 /* Hard disable interrupts */
69 mfmsr r9
70 rldicl r9,r9,48,1
71 rotldi r9,r9,16
72 mtmsrd r9,1 /* hard-disable interrupts */
73
74 /* Check if something happened while soft-disabled */
75 lbz r0,PACAIRQHAPPENED(r13)
76 andi. r0,r0,~PACA_IRQ_HARD_DIS@l
77 beq 1f
78 cmpwi cr0,r4,0
79 beq 1f
80 addi r1,r1,INT_FRAME_SIZE
81 ld r0,16(r1)
82 mtlr r0
83 blr
84
85 1: /* We mark irqs hard disabled as this is the state we'll
86 * be in when returning and we need to tell arch_local_irq_restore()
87 * about it
88 */
89 li r0,PACA_IRQ_HARD_DIS
90 stb r0,PACAIRQHAPPENED(r13)
91
92 /* We haven't lost state ... yet */
93 li r0,0
94 stb r0,PACA_NAPSTATELOST(r13)
95
96 /* Continue saving state */
97 SAVE_GPR(2, r1)
98 SAVE_NVGPRS(r1)
99 mfcr r4
100 std r4,_CCR(r1)
101 std r9,_MSR(r1)
102 std r1,PACAR1(r13)
103
104 /*
105 * Go to real mode to do the nap, as required by the architecture.
106 * Also, we need to be in real mode before setting hwthread_state,
107 * because as soon as we do that, another thread can switch
108 * the MMU context to the guest.
109 */
110 LOAD_REG_IMMEDIATE(r5, MSR_IDLE)
111 li r6, MSR_RI
112 andc r6, r9, r6
113 LOAD_REG_ADDR(r7, power7_enter_nap_mode)
114 mtmsrd r6, 1 /* clear RI before setting SRR0/1 */
115 mtspr SPRN_SRR0, r7
116 mtspr SPRN_SRR1, r5
117 rfid
118
119 .globl power7_enter_nap_mode
120 power7_enter_nap_mode:
121 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
122 /* Tell KVM we're napping */
123 li r4,KVM_HWTHREAD_IN_NAP
124 stb r4,HSTATE_HWTHREAD_STATE(r13)
125 #endif
126 cmpwi cr0,r3,1
127 beq 2f
128 IDLE_STATE_ENTER_SEQ(PPC_NAP)
129 /* No return */
130 2: IDLE_STATE_ENTER_SEQ(PPC_SLEEP)
131 /* No return */
132
133 _GLOBAL(power7_idle)
134 /* Now check if user or arch enabled NAP mode */
135 LOAD_REG_ADDRBASE(r3,powersave_nap)
136 lwz r4,ADDROFF(powersave_nap)(r3)
137 cmpwi 0,r4,0
138 beqlr
139 li r3, 1
140 /* fall through */
141
142 _GLOBAL(power7_nap)
143 mr r4,r3
144 li r3,0
145 b power7_powersave_common
146 /* No return */
147
148 _GLOBAL(power7_sleep)
149 li r3,1
150 li r4,1
151 b power7_powersave_common
152 /* No return */
153
154 /*
155 * Make opal call in realmode. This is a generic function to be called
156 * from realmode from reset vector. It handles endianess.
157 *
158 * r13 - paca pointer
159 * r1 - stack pointer
160 * r3 - opal token
161 */
162 opal_call_realmode:
163 mflr r12
164 std r12,_LINK(r1)
165 ld r2,PACATOC(r13)
166 /* Set opal return address */
167 LOAD_REG_ADDR(r0,return_from_opal_call)
168 mtlr r0
169 /* Handle endian-ness */
170 li r0,MSR_LE
171 mfmsr r12
172 andc r12,r12,r0
173 mtspr SPRN_HSRR1,r12
174 mr r0,r3 /* Move opal token to r0 */
175 LOAD_REG_ADDR(r11,opal)
176 ld r12,8(r11)
177 ld r2,0(r11)
178 mtspr SPRN_HSRR0,r12
179 hrfid
180
181 return_from_opal_call:
182 FIXUP_ENDIAN
183 ld r0,_LINK(r1)
184 mtlr r0
185 blr
186
187 #define CHECK_HMI_INTERRUPT \
188 mfspr r0,SPRN_SRR1; \
189 BEGIN_FTR_SECTION_NESTED(66); \
190 rlwinm r0,r0,45-31,0xf; /* extract wake reason field (P8) */ \
191 FTR_SECTION_ELSE_NESTED(66); \
192 rlwinm r0,r0,45-31,0xe; /* P7 wake reason field is 3 bits */ \
193 ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66); \
194 cmpwi r0,0xa; /* Hypervisor maintenance ? */ \
195 bne 20f; \
196 /* Invoke opal call to handle hmi */ \
197 ld r2,PACATOC(r13); \
198 ld r1,PACAR1(r13); \
199 std r3,ORIG_GPR3(r1); /* Save original r3 */ \
200 li r3,OPAL_HANDLE_HMI; /* Pass opal token argument*/ \
201 bl opal_call_realmode; \
202 ld r3,ORIG_GPR3(r1); /* Restore original r3 */ \
203 20: nop;
204
205
206 _GLOBAL(power7_wakeup_tb_loss)
207 ld r2,PACATOC(r13);
208 ld r1,PACAR1(r13)
209
210 BEGIN_FTR_SECTION
211 CHECK_HMI_INTERRUPT
212 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
213 /* Time base re-sync */
214 li r3,OPAL_RESYNC_TIMEBASE
215 bl opal_call_realmode;
216
217 /* TODO: Check r3 for failure */
218
219 REST_NVGPRS(r1)
220 REST_GPR(2, r1)
221 ld r3,_CCR(r1)
222 ld r4,_MSR(r1)
223 ld r5,_NIP(r1)
224 addi r1,r1,INT_FRAME_SIZE
225 mtcr r3
226 mfspr r3,SPRN_SRR1 /* Return SRR1 */
227 mtspr SPRN_SRR1,r4
228 mtspr SPRN_SRR0,r5
229 rfid
230
231 /*
232 * R3 here contains the value that will be returned to the caller
233 * of power7_nap.
234 */
235 _GLOBAL(power7_wakeup_loss)
236 ld r1,PACAR1(r13)
237 BEGIN_FTR_SECTION
238 CHECK_HMI_INTERRUPT
239 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
240 REST_NVGPRS(r1)
241 REST_GPR(2, r1)
242 ld r6,_CCR(r1)
243 ld r4,_MSR(r1)
244 ld r5,_NIP(r1)
245 addi r1,r1,INT_FRAME_SIZE
246 mtcr r6
247 mtspr SPRN_SRR1,r4
248 mtspr SPRN_SRR0,r5
249 rfid
250
251 /*
252 * R3 here contains the value that will be returned to the caller
253 * of power7_nap.
254 */
255 _GLOBAL(power7_wakeup_noloss)
256 lbz r0,PACA_NAPSTATELOST(r13)
257 cmpwi r0,0
258 bne power7_wakeup_loss
259 BEGIN_FTR_SECTION
260 CHECK_HMI_INTERRUPT
261 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
262 ld r1,PACAR1(r13)
263 ld r4,_MSR(r1)
264 ld r5,_NIP(r1)
265 addi r1,r1,INT_FRAME_SIZE
266 mtspr SPRN_SRR1,r4
267 mtspr SPRN_SRR0,r5
268 rfid