Commit f39224a8 authored by Paul Mackerras's avatar Paul Mackerras

powerpc: Use correct sequence for putting CPU into nap mode

We weren't using the recommended sequence for putting the CPU into
nap mode.  When I changed the idle loop, for some reason 7447A cpus
started hanging when we put them into nap mode.  Changing to the
recommended sequence fixes that.

The complexity here is that the recommended sequence is a loop that
keeps putting the cpu back into nap mode.  Clearly we need some way
to break out of the loop when an interrupt (external interrupt,
decrementer, performance monitor) occurs.  Here we use a bit in
the thread_info struct to indicate that we need this, and the exception
entry code notices this and arranges for the exception to return
to the value in the link register, thus breaking out of the loop.
We use a new `local_flags' field in the thread_info which we can
alter without needing to use an atomic update sequence.

The PPC970 has the same recommended sequence, so we do the same thing
there too.

This also fixes a bug in the kernel stack overflow handling code on
32-bit, since it was causing a value that we needed in a register to
get trashed.
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 183b73ae
...@@ -366,6 +366,7 @@ config PPC_PMAC64 ...@@ -366,6 +366,7 @@ config PPC_PMAC64
select U3_DART select U3_DART
select MPIC_BROKEN_U3 select MPIC_BROKEN_U3
select GENERIC_TBSYNC select GENERIC_TBSYNC
select PPC_970_NAP
default y default y
config PPC_PREP config PPC_PREP
...@@ -383,6 +384,7 @@ config PPC_MAPLE ...@@ -383,6 +384,7 @@ config PPC_MAPLE
select MPIC_BROKEN_U3 select MPIC_BROKEN_U3
select GENERIC_TBSYNC select GENERIC_TBSYNC
select PPC_UDBG_16550 select PPC_UDBG_16550
select PPC_970_NAP
default n default n
help help
This option enables support for the Maple 970FX Evaluation Board. This option enables support for the Maple 970FX Evaluation Board.
...@@ -457,6 +459,10 @@ config PPC_MPC106 ...@@ -457,6 +459,10 @@ config PPC_MPC106
bool bool
default n default n
config PPC_970_NAP
bool
default n
source "drivers/cpufreq/Kconfig" source "drivers/cpufreq/Kconfig"
config CPU_FREQ_PMAC config CPU_FREQ_PMAC
......
...@@ -20,7 +20,7 @@ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ ...@@ -20,7 +20,7 @@ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \
firmware.o sysfs.o firmware.o sysfs.o
obj-$(CONFIG_PPC64) += vdso64/ obj-$(CONFIG_PPC64) += vdso64/
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
obj-$(CONFIG_POWER4) += idle_power4.o obj-$(CONFIG_PPC_970_NAP) += idle_power4.o
obj-$(CONFIG_PPC_OF) += of_device.o prom_parse.o obj-$(CONFIG_PPC_OF) += of_device.o prom_parse.o
procfs-$(CONFIG_PPC64) := proc_ppc64.o procfs-$(CONFIG_PPC64) := proc_ppc64.o
obj-$(CONFIG_PROC_FS) += $(procfs-y) obj-$(CONFIG_PROC_FS) += $(procfs-y)
......
...@@ -91,6 +91,7 @@ int main(void) ...@@ -91,6 +91,7 @@ int main(void)
#endif /* CONFIG_PPC64 */ #endif /* CONFIG_PPC64 */
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_TASK, offsetof(struct thread_info, task)); DEFINE(TI_TASK, offsetof(struct thread_info, task));
#ifdef CONFIG_PPC32 #ifdef CONFIG_PPC32
......
...@@ -128,37 +128,36 @@ transfer_to_handler: ...@@ -128,37 +128,36 @@ transfer_to_handler:
stw r12,4(r11) stw r12,4(r11)
#endif #endif
b 3f b 3f
2: /* if from kernel, check interrupted DOZE/NAP mode and 2: /* if from kernel, check interrupted DOZE/NAP mode and
* check for stack overflow * check for stack overflow
*/ */
lwz r9,THREAD_INFO-THREAD(r12)
cmplw r1,r9 /* if r1 <= current->thread_info */
ble- stack_ovf /* then the kernel stack overflowed */
5:
#ifdef CONFIG_6xx #ifdef CONFIG_6xx
mfspr r11,SPRN_HID0 tophys(r9,r9) /* check local flags */
mtcr r11 lwz r12,TI_LOCAL_FLAGS(r9)
BEGIN_FTR_SECTION mtcrf 0x01,r12
bt- 8,4f /* Check DOZE */ bt- 31-TLF_NAPPING,4f
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
BEGIN_FTR_SECTION
bt- 9,4f /* Check NAP */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
#endif /* CONFIG_6xx */ #endif /* CONFIG_6xx */
.globl transfer_to_handler_cont .globl transfer_to_handler_cont
transfer_to_handler_cont: transfer_to_handler_cont:
lwz r11,THREAD_INFO-THREAD(r12)
cmplw r1,r11 /* if r1 <= current->thread_info */
ble- stack_ovf /* then the kernel stack overflowed */
3: 3:
mflr r9 mflr r9
lwz r11,0(r9) /* virtual address of handler */ lwz r11,0(r9) /* virtual address of handler */
lwz r9,4(r9) /* where to go when done */ lwz r9,4(r9) /* where to go when done */
FIX_SRR1(r10,r12)
mtspr SPRN_SRR0,r11 mtspr SPRN_SRR0,r11
mtspr SPRN_SRR1,r10 mtspr SPRN_SRR1,r10
mtlr r9 mtlr r9
SYNC SYNC
RFI /* jump to handler, enable MMU */ RFI /* jump to handler, enable MMU */
#ifdef CONFIG_6xx #ifdef CONFIG_6xx
4: b power_save_6xx_restore 4: rlwinm r12,r12,0,~_TLF_NAPPING
stw r12,TI_LOCAL_FLAGS(r9)
b power_save_6xx_restore
#endif #endif
/* /*
...@@ -167,10 +166,10 @@ transfer_to_handler_cont: ...@@ -167,10 +166,10 @@ transfer_to_handler_cont:
*/ */
stack_ovf: stack_ovf:
/* sometimes we use a statically-allocated stack, which is OK. */ /* sometimes we use a statically-allocated stack, which is OK. */
lis r11,_end@h lis r12,_end@h
ori r11,r11,_end@l ori r12,r12,_end@l
cmplw r1,r11 cmplw r1,r12
ble 3b /* r1 <= &_end is OK */ ble 5b /* r1 <= &_end is OK */
SAVE_NVGPRS(r11) SAVE_NVGPRS(r11)
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
lis r1,init_thread_union@ha lis r1,init_thread_union@ha
......
...@@ -376,17 +376,53 @@ label##_common: \ ...@@ -376,17 +376,53 @@ label##_common: \
bl hdlr; \ bl hdlr; \
b .ret_from_except b .ret_from_except
/*
* Like STD_EXCEPTION_COMMON, but for exceptions that can occur
* in the idle task and therefore need the special idle handling.
*/
#define STD_EXCEPTION_COMMON_IDLE(trap, label, hdlr) \
.align 7; \
.globl label##_common; \
label##_common: \
EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \
FINISH_NAP; \
DISABLE_INTS; \
bl .save_nvgprs; \
addi r3,r1,STACK_FRAME_OVERHEAD; \
bl hdlr; \
b .ret_from_except
#define STD_EXCEPTION_COMMON_LITE(trap, label, hdlr) \ #define STD_EXCEPTION_COMMON_LITE(trap, label, hdlr) \
.align 7; \ .align 7; \
.globl label##_common; \ .globl label##_common; \
label##_common: \ label##_common: \
EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \ EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \
FINISH_NAP; \
DISABLE_INTS; \ DISABLE_INTS; \
bl .ppc64_runlatch_on; \ bl .ppc64_runlatch_on; \
addi r3,r1,STACK_FRAME_OVERHEAD; \ addi r3,r1,STACK_FRAME_OVERHEAD; \
bl hdlr; \ bl hdlr; \
b .ret_from_except_lite b .ret_from_except_lite
/*
* When the idle code in power4_idle puts the CPU into NAP mode,
* it has to do so in a loop, and relies on the external interrupt
* and decrementer interrupt entry code to get it out of the loop.
* It sets the _TLF_NAPPING bit in current_thread_info()->local_flags
* to signal that it is in the loop and needs help to get out.
*/
#ifdef CONFIG_PPC_970_NAP
#define FINISH_NAP \
BEGIN_FTR_SECTION \
clrrdi r11,r1,THREAD_SHIFT; \
ld r9,TI_LOCAL_FLAGS(r11); \
andi. r10,r9,_TLF_NAPPING; \
bnel power4_fixup_nap; \
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
#else
#define FINISH_NAP
#endif
/* /*
* Start of pSeries system interrupt routines * Start of pSeries system interrupt routines
*/ */
...@@ -772,6 +808,7 @@ hardware_interrupt_iSeries_masked: ...@@ -772,6 +808,7 @@ hardware_interrupt_iSeries_masked:
.globl machine_check_common .globl machine_check_common
machine_check_common: machine_check_common:
EXCEPTION_PROLOG_COMMON(0x200, PACA_EXMC) EXCEPTION_PROLOG_COMMON(0x200, PACA_EXMC)
FINISH_NAP
DISABLE_INTS DISABLE_INTS
bl .save_nvgprs bl .save_nvgprs
addi r3,r1,STACK_FRAME_OVERHEAD addi r3,r1,STACK_FRAME_OVERHEAD
...@@ -783,7 +820,7 @@ machine_check_common: ...@@ -783,7 +820,7 @@ machine_check_common:
STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception) STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception)
STD_EXCEPTION_COMMON(0xd00, single_step, .single_step_exception) STD_EXCEPTION_COMMON(0xd00, single_step, .single_step_exception)
STD_EXCEPTION_COMMON(0xe00, trap_0e, .unknown_exception) STD_EXCEPTION_COMMON(0xe00, trap_0e, .unknown_exception)
STD_EXCEPTION_COMMON(0xf00, performance_monitor, .performance_monitor_exception) STD_EXCEPTION_COMMON_IDLE(0xf00, performance_monitor, .performance_monitor_exception)
STD_EXCEPTION_COMMON(0x1300, instruction_breakpoint, .instruction_breakpoint_exception) STD_EXCEPTION_COMMON(0x1300, instruction_breakpoint, .instruction_breakpoint_exception)
#ifdef CONFIG_ALTIVEC #ifdef CONFIG_ALTIVEC
STD_EXCEPTION_COMMON(0x1700, altivec_assist, .altivec_assist_exception) STD_EXCEPTION_COMMON(0x1700, altivec_assist, .altivec_assist_exception)
...@@ -1034,6 +1071,7 @@ unrecov_slb: ...@@ -1034,6 +1071,7 @@ unrecov_slb:
.globl hardware_interrupt_entry .globl hardware_interrupt_entry
hardware_interrupt_common: hardware_interrupt_common:
EXCEPTION_PROLOG_COMMON(0x500, PACA_EXGEN) EXCEPTION_PROLOG_COMMON(0x500, PACA_EXGEN)
FINISH_NAP
hardware_interrupt_entry: hardware_interrupt_entry:
DISABLE_INTS DISABLE_INTS
bl .ppc64_runlatch_on bl .ppc64_runlatch_on
...@@ -1041,6 +1079,15 @@ hardware_interrupt_entry: ...@@ -1041,6 +1079,15 @@ hardware_interrupt_entry:
bl .do_IRQ bl .do_IRQ
b .ret_from_except_lite b .ret_from_except_lite
#ifdef CONFIG_PPC_970_NAP
power4_fixup_nap:
andc r9,r9,r10
std r9,TI_LOCAL_FLAGS(r11)
ld r10,_LINK(r1) /* make idle task do the */
std r10,_NIP(r1) /* equivalent of a blr */
blr
#endif
.align 7 .align 7
.globl alignment_common .globl alignment_common
alignment_common: alignment_common:
......
...@@ -22,8 +22,6 @@ ...@@ -22,8 +22,6 @@
#include <asm/ppc_asm.h> #include <asm/ppc_asm.h>
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
#undef DEBUG
.text .text
/* /*
...@@ -109,12 +107,6 @@ BEGIN_FTR_SECTION ...@@ -109,12 +107,6 @@ BEGIN_FTR_SECTION
dcbf 0,r4 dcbf 0,r4
dcbf 0,r4 dcbf 0,r4
END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR)
#ifdef DEBUG
lis r6,nap_enter_count@ha
lwz r4,nap_enter_count@l(r6)
addi r4,r4,1
stw r4,nap_enter_count@l(r6)
#endif
2: 2:
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
/* Go to low speed mode on some 750FX */ /* Go to low speed mode on some 750FX */
...@@ -144,48 +136,42 @@ BEGIN_FTR_SECTION ...@@ -144,48 +136,42 @@ BEGIN_FTR_SECTION
DSSALL DSSALL
sync sync
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
rlwinm r9,r1,0,0,31-THREAD_SHIFT /* current thread_info */
lwz r8,TI_LOCAL_FLAGS(r9) /* set napping bit */
ori r8,r8,_TLF_NAPPING /* so when we take an exception */
stw r8,TI_LOCAL_FLAGS(r9) /* it will return to our caller */
mfmsr r7 mfmsr r7
ori r7,r7,MSR_EE ori r7,r7,MSR_EE
oris r7,r7,MSR_POW@h oris r7,r7,MSR_POW@h
sync 1: sync
isync
mtmsr r7 mtmsr r7
isync isync
sync b 1b
blr
/* /*
* Return from NAP/DOZE mode, restore some CPU specific registers, * Return from NAP/DOZE mode, restore some CPU specific registers,
* we are called with DR/IR still off and r2 containing physical * we are called with DR/IR still off and r2 containing physical
* address of current. * address of current. R11 points to the exception frame (physical
* address). We have to preserve r10.
*/ */
_GLOBAL(power_save_6xx_restore) _GLOBAL(power_save_6xx_restore)
mfspr r11,SPRN_HID0 lwz r9,_LINK(r11) /* interrupted in ppc6xx_idle: */
rlwinm. r11,r11,0,10,8 /* Clear NAP & copy NAP bit !state to cr1 EQ */ stw r9,_NIP(r11) /* make it do a blr */
cror 4*cr1+eq,4*cr0+eq,4*cr0+eq
BEGIN_FTR_SECTION
rlwinm r11,r11,0,9,7 /* Clear DOZE */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
mtspr SPRN_HID0, r11
#ifdef DEBUG #ifdef CONFIG_SMP
beq cr1,1f mfspr r12,SPRN_SPRG3
lis r11,(nap_return_count-KERNELBASE)@ha lwz r11,TI_CPU(r12) /* get cpu number * 4 */
lwz r9,nap_return_count@l(r11)
addi r9,r9,1
stw r9,nap_return_count@l(r11)
1:
#endif
rlwinm r9,r1,0,0,18
tophys(r9,r9)
lwz r11,TI_CPU(r9)
slwi r11,r11,2 slwi r11,r11,2
#else
li r11,0
#endif
/* Todo make sure all these are in the same page /* Todo make sure all these are in the same page
* and load r22 (@ha part + CPU offset) only once * and load r11 (@ha part + CPU offset) only once
*/ */
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
beq cr1,1f mfspr r9,SPRN_HID0
andis. r9,r9,HID0_NAP@h
beq 1f
addis r9,r11,(nap_save_msscr0-KERNELBASE)@ha addis r9,r11,(nap_save_msscr0-KERNELBASE)@ha
lwz r9,nap_save_msscr0@l(r9) lwz r9,nap_save_msscr0@l(r9)
mtspr SPRN_MSSCR0, r9 mtspr SPRN_MSSCR0, r9
...@@ -210,10 +196,3 @@ _GLOBAL(nap_save_hid1) ...@@ -210,10 +196,3 @@ _GLOBAL(nap_save_hid1)
_GLOBAL(powersave_lowspeed) _GLOBAL(powersave_lowspeed)
.long 0 .long 0
#ifdef DEBUG
_GLOBAL(nap_enter_count)
.space 4
_GLOBAL(nap_return_count)
.space 4
#endif
...@@ -35,12 +35,16 @@ BEGIN_FTR_SECTION ...@@ -35,12 +35,16 @@ BEGIN_FTR_SECTION
DSSALL DSSALL
sync sync
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
clrrdi r9,r1,THREAD_SHIFT /* current thread_info */
ld r8,TI_LOCAL_FLAGS(r9) /* set napping bit */
ori r8,r8,_TLF_NAPPING /* so when we take an exception */
std r8,TI_LOCAL_FLAGS(r9) /* it will return to our caller */
mfmsr r7 mfmsr r7
ori r7,r7,MSR_EE ori r7,r7,MSR_EE
oris r7,r7,MSR_POW@h oris r7,r7,MSR_POW@h
sync 1: sync
isync isync
mtmsrd r7 mtmsrd r7
isync isync
sync b 1b
blr
...@@ -37,6 +37,8 @@ struct thread_info { ...@@ -37,6 +37,8 @@ struct thread_info {
int preempt_count; /* 0 => preemptable, int preempt_count; /* 0 => preemptable,
<0 => BUG */ <0 => BUG */
struct restart_block restart_block; struct restart_block restart_block;
unsigned long local_flags; /* private flags for thread */
/* low level flags - has atomic operations done on it */ /* low level flags - has atomic operations done on it */
unsigned long flags ____cacheline_aligned_in_smp; unsigned long flags ____cacheline_aligned_in_smp;
}; };
...@@ -143,6 +145,12 @@ static inline struct thread_info *current_thread_info(void) ...@@ -143,6 +145,12 @@ static inline struct thread_info *current_thread_info(void)
_TIF_NEED_RESCHED | _TIF_RESTORE_SIGMASK) _TIF_NEED_RESCHED | _TIF_RESTORE_SIGMASK)
#define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR) #define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR)
/* Bits in local_flags */
/* Don't move TLF_NAPPING without adjusting the code in entry_32.S */
#define TLF_NAPPING 0 /* idle thread enabled NAP mode */
#define _TLF_NAPPING (1 << TLF_NAPPING)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_THREAD_INFO_H */ #endif /* _ASM_POWERPC_THREAD_INFO_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment