Merge tag 'pm+acpi-3.19-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafae...
[cascardo/linux.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 _GLOBAL(power7_enter_nap_mode)
105 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
106         /* Tell KVM we're napping */
107         li      r4,KVM_HWTHREAD_IN_NAP
108         stb     r4,HSTATE_HWTHREAD_STATE(r13)
109 #endif
110         cmpwi   cr0,r3,1
111         beq     2f
112         IDLE_STATE_ENTER_SEQ(PPC_NAP)
113         /* No return */
114 2:      IDLE_STATE_ENTER_SEQ(PPC_SLEEP)
115         /* No return */
116
117 _GLOBAL(power7_idle)
118         /* Now check if user or arch enabled NAP mode */
119         LOAD_REG_ADDRBASE(r3,powersave_nap)
120         lwz     r4,ADDROFF(powersave_nap)(r3)
121         cmpwi   0,r4,0
122         beqlr
123         li      r3, 1
124         /* fall through */
125
126 _GLOBAL(power7_nap)
127         mr      r4,r3
128         li      r3,0
129         b       power7_powersave_common
130         /* No return */
131
132 _GLOBAL(power7_sleep)
133         li      r3,1
134         li      r4,1
135         b       power7_powersave_common
136         /* No return */
137
138 /*
139  * Make opal call in realmode. This is a generic function to be called
140  * from realmode from reset vector. It handles endianess.
141  *
142  * r13 - paca pointer
143  * r1  - stack pointer
144  * r3  - opal token
145  */
146 opal_call_realmode:
147         mflr    r12
148         std     r12,_LINK(r1)
149         ld      r2,PACATOC(r13)
150         /* Set opal return address */
151         LOAD_REG_ADDR(r0,return_from_opal_call)
152         mtlr    r0
153         /* Handle endian-ness */
154         li      r0,MSR_LE
155         mfmsr   r12
156         andc    r12,r12,r0
157         mtspr   SPRN_HSRR1,r12
158         mr      r0,r3                   /* Move opal token to r0 */
159         LOAD_REG_ADDR(r11,opal)
160         ld      r12,8(r11)
161         ld      r2,0(r11)
162         mtspr   SPRN_HSRR0,r12
163         hrfid
164
165 return_from_opal_call:
166         FIXUP_ENDIAN
167         ld      r0,_LINK(r1)
168         mtlr    r0
169         blr
170
171 #define CHECK_HMI_INTERRUPT                                             \
172         mfspr   r0,SPRN_SRR1;                                           \
173 BEGIN_FTR_SECTION_NESTED(66);                                           \
174         rlwinm  r0,r0,45-31,0xf;  /* extract wake reason field (P8) */  \
175 FTR_SECTION_ELSE_NESTED(66);                                            \
176         rlwinm  r0,r0,45-31,0xe;  /* P7 wake reason field is 3 bits */  \
177 ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66);                \
178         cmpwi   r0,0xa;                 /* Hypervisor maintenance ? */  \
179         bne     20f;                                                    \
180         /* Invoke opal call to handle hmi */                            \
181         ld      r2,PACATOC(r13);                                        \
182         ld      r1,PACAR1(r13);                                         \
183         std     r3,ORIG_GPR3(r1);       /* Save original r3 */          \
184         li      r3,OPAL_HANDLE_HMI;     /* Pass opal token argument*/   \
185         bl      opal_call_realmode;                                     \
186         ld      r3,ORIG_GPR3(r1);       /* Restore original r3 */       \
187 20:     nop;
188
189
190 _GLOBAL(power7_wakeup_tb_loss)
191         ld      r2,PACATOC(r13);
192         ld      r1,PACAR1(r13)
193
194 BEGIN_FTR_SECTION
195         CHECK_HMI_INTERRUPT
196 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
197         /* Time base re-sync */
198         li      r3,OPAL_RESYNC_TIMEBASE
199         bl      opal_call_realmode;
200
201         /* TODO: Check r3 for failure */
202
203         REST_NVGPRS(r1)
204         REST_GPR(2, r1)
205         ld      r3,_CCR(r1)
206         ld      r4,_MSR(r1)
207         ld      r5,_NIP(r1)
208         addi    r1,r1,INT_FRAME_SIZE
209         mtcr    r3
210         mfspr   r3,SPRN_SRR1            /* Return SRR1 */
211         mtspr   SPRN_SRR1,r4
212         mtspr   SPRN_SRR0,r5
213         rfid
214
215 /*
216  * R3 here contains the value that will be returned to the caller
217  * of power7_nap.
218  */
219 _GLOBAL(power7_wakeup_loss)
220         ld      r1,PACAR1(r13)
221 BEGIN_FTR_SECTION
222         CHECK_HMI_INTERRUPT
223 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
224         REST_NVGPRS(r1)
225         REST_GPR(2, r1)
226         ld      r6,_CCR(r1)
227         ld      r4,_MSR(r1)
228         ld      r5,_NIP(r1)
229         addi    r1,r1,INT_FRAME_SIZE
230         mtcr    r6
231         mtspr   SPRN_SRR1,r4
232         mtspr   SPRN_SRR0,r5
233         rfid
234
235 /*
236  * R3 here contains the value that will be returned to the caller
237  * of power7_nap.
238  */
239 _GLOBAL(power7_wakeup_noloss)
240         lbz     r0,PACA_NAPSTATELOST(r13)
241         cmpwi   r0,0
242         bne     power7_wakeup_loss
243 BEGIN_FTR_SECTION
244         CHECK_HMI_INTERRUPT
245 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
246         ld      r1,PACAR1(r13)
247         ld      r4,_MSR(r1)
248         ld      r5,_NIP(r1)
249         addi    r1,r1,INT_FRAME_SIZE
250         mtspr   SPRN_SRR1,r4
251         mtspr   SPRN_SRR0,r5
252         rfid