Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f39224a8 authored by Paul Mackerras's avatar Paul Mackerras
Browse files

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
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -366,6 +366,7 @@ config PPC_PMAC64
	select U3_DART
	select MPIC_BROKEN_U3
	select GENERIC_TBSYNC
	select PPC_970_NAP
	default y

config PPC_PREP
@@ -383,6 +384,7 @@ config PPC_MAPLE
	select MPIC_BROKEN_U3
	select GENERIC_TBSYNC
	select PPC_UDBG_16550
	select PPC_970_NAP
	default n
	help
          This option enables support for the Maple 970FX Evaluation Board.
@@ -457,6 +459,10 @@ config PPC_MPC106
	bool
	default n

config PPC_970_NAP
	bool
	default n

source "drivers/cpufreq/Kconfig"

config CPU_FREQ_PMAC
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \
				   firmware.o sysfs.o
obj-$(CONFIG_PPC64)		+= vdso64/
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
procfs-$(CONFIG_PPC64)		:= proc_ppc64.o
obj-$(CONFIG_PROC_FS)		+= $(procfs-y)
+1 −0
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ int main(void)
#endif /* CONFIG_PPC64 */

	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_TASK, offsetof(struct thread_info, task));
#ifdef CONFIG_PPC32
+17 −18
Original line number Diff line number Diff line
@@ -128,29 +128,26 @@ transfer_to_handler:
	stw	r12,4(r11)
#endif
	b	3f

2:	/* if from kernel, check interrupted DOZE/NAP mode and
         * 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
	mfspr	r11,SPRN_HID0
	mtcr	r11
BEGIN_FTR_SECTION
	bt-	8,4f			/* Check DOZE */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
BEGIN_FTR_SECTION
	bt-	9,4f			/* Check NAP */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
	tophys(r9,r9)			/* check local flags */
	lwz	r12,TI_LOCAL_FLAGS(r9)
	mtcrf	0x01,r12
	bt-	31-TLF_NAPPING,4f
#endif /* CONFIG_6xx */
	.globl 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:
	mflr	r9
	lwz	r11,0(r9)		/* virtual address of handler */
	lwz	r9,4(r9)		/* where to go when done */
	FIX_SRR1(r10,r12)
	mtspr	SPRN_SRR0,r11
	mtspr	SPRN_SRR1,r10
	mtlr	r9
@@ -158,7 +155,9 @@ transfer_to_handler_cont:
	RFI				/* jump to handler, enable MMU */

#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

/*
@@ -167,10 +166,10 @@ transfer_to_handler_cont:
 */
stack_ovf:
	/* sometimes we use a statically-allocated stack, which is OK. */
	lis	r11,_end@h
	ori	r11,r11,_end@l
	cmplw	r1,r11
	ble	3b			/* r1 <= &_end is OK */
	lis	r12,_end@h
	ori	r12,r12,_end@l
	cmplw	r1,r12
	ble	5b			/* r1 <= &_end is OK */
	SAVE_NVGPRS(r11)
	addi	r3,r1,STACK_FRAME_OVERHEAD
	lis	r1,init_thread_union@ha
+48 −1
Original line number Diff line number Diff line
@@ -376,17 +376,53 @@ label##_common: \
	bl	hdlr;					\
	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)	\
	.align	7;					\
	.globl label##_common;				\
label##_common:						\
	EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN);	\
	FINISH_NAP;					\
	DISABLE_INTS;					\
	bl	.ppc64_runlatch_on;			\
	addi	r3,r1,STACK_FRAME_OVERHEAD;		\
	bl	hdlr;					\
	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
 */
@@ -772,6 +808,7 @@ hardware_interrupt_iSeries_masked:
	.globl machine_check_common
machine_check_common:
	EXCEPTION_PROLOG_COMMON(0x200, PACA_EXMC)
	FINISH_NAP
	DISABLE_INTS
	bl	.save_nvgprs
	addi	r3,r1,STACK_FRAME_OVERHEAD
@@ -783,7 +820,7 @@ machine_check_common:
	STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception)
	STD_EXCEPTION_COMMON(0xd00, single_step, .single_step_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)
#ifdef CONFIG_ALTIVEC
	STD_EXCEPTION_COMMON(0x1700, altivec_assist, .altivec_assist_exception)
@@ -1034,6 +1071,7 @@ unrecov_slb:
	.globl hardware_interrupt_entry
hardware_interrupt_common:
	EXCEPTION_PROLOG_COMMON(0x500, PACA_EXGEN)
	FINISH_NAP
hardware_interrupt_entry:
	DISABLE_INTS
	bl	.ppc64_runlatch_on
@@ -1041,6 +1079,15 @@ hardware_interrupt_entry:
	bl	.do_IRQ
	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
	.globl alignment_common
alignment_common:
Loading