Commit a0652fc9 authored by Paul Mackerras's avatar Paul Mackerras

powerpc: Unify the 32 and 64 bit idle loops

This unifies the 32-bit (ARCH=ppc and ARCH=powerpc) and 64-bit idle
loops.  It brings over the concept of having a ppc_md.power_save
function from 32-bit to ARCH=powerpc, which lets us get rid of
native_idle().  With this we will also be able to simplify the idle
handling for pSeries and cell.
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 55aab8cd
...@@ -12,12 +12,12 @@ endif ...@@ -12,12 +12,12 @@ endif
obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ obj-y := semaphore.o cputable.o ptrace.o syscalls.o \
irq.o align.o signal_32.o pmc.o vdso.o \ irq.o align.o signal_32.o pmc.o vdso.o \
init_task.o process.o systbl.o init_task.o process.o systbl.o idle.o
obj-y += vdso32/ obj-y += vdso32/
obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \
signal_64.o ptrace32.o \ signal_64.o ptrace32.o \
paca.o cpu_setup_power4.o \ paca.o cpu_setup_power4.o \
firmware.o sysfs.o idle_64.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_POWER4) += idle_power4.o
...@@ -34,6 +34,7 @@ obj-$(CONFIG_IBMEBUS) += ibmebus.o ...@@ -34,6 +34,7 @@ obj-$(CONFIG_IBMEBUS) += ibmebus.o
obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o
obj64-$(CONFIG_PPC_MULTIPLATFORM) += nvram_64.o obj64-$(CONFIG_PPC_MULTIPLATFORM) += nvram_64.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_6xx) += idle_6xx.o
ifeq ($(CONFIG_PPC_MERGE),y) ifeq ($(CONFIG_PPC_MERGE),y)
...@@ -51,7 +52,6 @@ obj-$(CONFIG_PPC64) += misc_64.o dma_64.o iommu.o ...@@ -51,7 +52,6 @@ obj-$(CONFIG_PPC64) += misc_64.o dma_64.o iommu.o
obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o
obj-$(CONFIG_MODULES) += ppc_ksyms.o obj-$(CONFIG_MODULES) += ppc_ksyms.o
obj-$(CONFIG_BOOTX_TEXT) += btext.o obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_6xx) += idle_6xx.o
obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o
......
...@@ -135,10 +135,10 @@ transfer_to_handler: ...@@ -135,10 +135,10 @@ transfer_to_handler:
mfspr r11,SPRN_HID0 mfspr r11,SPRN_HID0
mtcr r11 mtcr r11
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
bt- 8,power_save_6xx_restore /* Check DOZE */ bt- 8,4f /* Check DOZE */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
bt- 9,power_save_6xx_restore /* Check NAP */ bt- 9,4f /* Check NAP */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_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
...@@ -157,6 +157,10 @@ transfer_to_handler_cont: ...@@ -157,6 +157,10 @@ transfer_to_handler_cont:
SYNC SYNC
RFI /* jump to handler, enable MMU */ RFI /* jump to handler, enable MMU */
#ifdef CONFIG_6xx
4: b power_save_6xx_restore
#endif
/* /*
* On kernel stack overflow, load up an initial stack pointer * On kernel stack overflow, load up an initial stack pointer
* and call StackOverflow(regs), which should not return. * and call StackOverflow(regs), which should not return.
......
...@@ -2,13 +2,17 @@ ...@@ -2,13 +2,17 @@
* Idle daemon for PowerPC. Idle daemon will handle any action * Idle daemon for PowerPC. Idle daemon will handle any action
* that needs to be taken when the system becomes idle. * that needs to be taken when the system becomes idle.
* *
* Originally Written by Cort Dougan (cort@cs.nmt.edu) * Originally written by Cort Dougan (cort@cs.nmt.edu).
* Subsequent 32-bit hacking by Tom Rini, Armin Kuster,
* Paul Mackerras and others.
* *
* iSeries supported added by Mike Corrigan <mikejc@us.ibm.com> * iSeries supported added by Mike Corrigan <mikejc@us.ibm.com>
* *
* Additional shared processor, SMT, and firmware support * Additional shared processor, SMT, and firmware support
* Copyright (c) 2003 Dave Engebretsen <engebret@us.ibm.com> * Copyright (c) 2003 Dave Engebretsen <engebret@us.ibm.com>
* *
* 32-bit and 64-bit versions merged by Paul Mackerras <paulus@samba.org>
*
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
...@@ -29,18 +33,43 @@ ...@@ -29,18 +33,43 @@
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/smp.h> #include <asm/smp.h>
extern void power4_idle(void); #ifdef CONFIG_HOTPLUG_CPU
#define cpu_should_die() (cpu_is_offline(smp_processor_id()) && \
system_state == SYSTEM_RUNNING)
#else
#define cpu_should_die() 0
#endif
void default_idle(void) /*
* The body of the idle task.
*/
void cpu_idle(void)
{ {
unsigned int cpu = smp_processor_id(); if (ppc_md.idle_loop)
set_thread_flag(TIF_POLLING_NRFLAG); ppc_md.idle_loop(); /* doesn't return */
set_thread_flag(TIF_POLLING_NRFLAG);
while (1) { while (1) {
if (!need_resched()) {
while (!need_resched() && !cpu_is_offline(cpu)) {
ppc64_runlatch_off(); ppc64_runlatch_off();
while (!need_resched() && !cpu_should_die()) {
if (ppc_md.power_save) {
clear_thread_flag(TIF_POLLING_NRFLAG);
/*
* smp_mb is so clearing of TIF_POLLING_NRFLAG
* is ordered w.r.t. need_resched() test.
*/
smp_mb();
local_irq_disable();
/* check again after disabling irqs */
if (!need_resched() && !cpu_should_die())
ppc_md.power_save();
local_irq_enable();
set_thread_flag(TIF_POLLING_NRFLAG);
} else {
/* /*
* Go into low thread priority and possibly * Go into low thread priority and possibly
* low power mode. * low power mode.
...@@ -48,44 +77,16 @@ void default_idle(void) ...@@ -48,44 +77,16 @@ void default_idle(void)
HMT_low(); HMT_low();
HMT_very_low(); HMT_very_low();
} }
HMT_medium();
} }
HMT_medium();
ppc64_runlatch_on(); ppc64_runlatch_on();
preempt_enable_no_resched(); if (cpu_should_die())
schedule();
preempt_disable();
if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
cpu_die(); cpu_die();
}
}
void native_idle(void)
{
while (1) {
ppc64_runlatch_off();
if (!need_resched())
power4_idle();
if (need_resched()) {
ppc64_runlatch_on();
preempt_enable_no_resched(); preempt_enable_no_resched();
schedule(); schedule();
preempt_disable(); preempt_disable();
} }
if (cpu_is_offline(smp_processor_id()) &&
system_state == SYSTEM_RUNNING)
cpu_die();
}
}
void cpu_idle(void)
{
BUG_ON(NULL == ppc_md.idle_loop);
ppc_md.idle_loop();
} }
int powersave_nap; int powersave_nap;
......
...@@ -87,19 +87,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) ...@@ -87,19 +87,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
cmpwi 0,r3,0 cmpwi 0,r3,0
beqlr beqlr
/* Clear MSR:EE */
mfmsr r7
rlwinm r0,r7,0,17,15
mtmsr r0
/* Check current_thread_info()->flags */
rlwinm r4,r1,0,0,18
lwz r4,TI_FLAGS(r4)
andi. r0,r4,_TIF_NEED_RESCHED
beq 1f
mtmsr r7 /* out of line this ? */
blr
1:
/* Some pre-nap cleanups needed on some CPUs */ /* Some pre-nap cleanups needed on some CPUs */
andis. r0,r3,HID0_NAP@h andis. r0,r3,HID0_NAP@h
beq 2f beq 2f
...@@ -220,8 +207,6 @@ _GLOBAL(nap_save_msscr0) ...@@ -220,8 +207,6 @@ _GLOBAL(nap_save_msscr0)
_GLOBAL(nap_save_hid1) _GLOBAL(nap_save_hid1)
.space 4*NR_CPUS .space 4*NR_CPUS
_GLOBAL(powersave_nap)
.long 0
_GLOBAL(powersave_lowspeed) _GLOBAL(powersave_lowspeed)
.long 0 .long 0
......
...@@ -49,21 +49,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) ...@@ -49,21 +49,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP)
cmpwi 0,r4,0 cmpwi 0,r4,0
beqlr beqlr
/* Clear MSR:EE */
mfmsr r7
li r4,0
ori r4,r4,MSR_EE
andc r0,r7,r4
mtmsrd r0
/* Check current_thread_info()->flags */
clrrdi r4,r1,THREAD_SHIFT
ld r4,TI_FLAGS(r4)
andi. r0,r4,_TIF_NEED_RESCHED
beq 1f
mtmsrd r7 /* out of line this ? */
blr
1:
/* Go to NAP now */ /* Go to NAP now */
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
DSSALL DSSALL
......
...@@ -53,9 +53,6 @@ ...@@ -53,9 +53,6 @@
extern void platform_init(void); extern void platform_init(void);
extern void bootx_init(unsigned long r4, unsigned long phys); extern void bootx_init(unsigned long r4, unsigned long phys);
extern void ppc6xx_idle(void);
extern void power4_idle(void);
boot_infos_t *boot_infos; boot_infos_t *boot_infos;
struct ide_machdep_calls ppc_ide_md; struct ide_machdep_calls ppc_ide_md;
...@@ -194,6 +191,8 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys) ...@@ -194,6 +191,8 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys)
platform_init(); platform_init();
#ifdef CONFIG_6xx #ifdef CONFIG_6xx
if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||
cpu_has_feature(CPU_FTR_CAN_NAP))
ppc_md.power_save = ppc6xx_idle; ppc_md.power_save = ppc6xx_idle;
#endif #endif
......
...@@ -607,12 +607,6 @@ void __init setup_arch(char **cmdline_p) ...@@ -607,12 +607,6 @@ void __init setup_arch(char **cmdline_p)
ppc_md.setup_arch(); ppc_md.setup_arch();
/* Use the default idle loop if the platform hasn't provided one. */
if (NULL == ppc_md.idle_loop) {
ppc_md.idle_loop = default_idle;
printk(KERN_INFO "Using default idle loop\n");
}
paging_init(); paging_init();
ppc64_boot_msg(0x15, "Setup Done"); ppc64_boot_msg(0x15, "Setup Done");
} }
......
...@@ -290,7 +290,7 @@ struct machdep_calls __initdata maple_md = { ...@@ -290,7 +290,7 @@ struct machdep_calls __initdata maple_md = {
.get_rtc_time = maple_get_rtc_time, .get_rtc_time = maple_get_rtc_time,
.calibrate_decr = generic_calibrate_decr, .calibrate_decr = generic_calibrate_decr,
.progress = maple_progress, .progress = maple_progress,
.idle_loop = native_idle, .power_save = power4_idle,
#ifdef CONFIG_KEXEC #ifdef CONFIG_KEXEC
.machine_kexec = default_machine_kexec, .machine_kexec = default_machine_kexec,
.machine_kexec_prepare = default_machine_kexec_prepare, .machine_kexec_prepare = default_machine_kexec_prepare,
......
...@@ -733,7 +733,7 @@ struct machdep_calls __initdata pmac_md = { ...@@ -733,7 +733,7 @@ struct machdep_calls __initdata pmac_md = {
.progress = udbg_progress, .progress = udbg_progress,
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
.pci_probe_mode = pmac_pci_probe_mode, .pci_probe_mode = pmac_pci_probe_mode,
.idle_loop = native_idle, .power_save = power4_idle,
.enable_pmcs = power4_enable_pmcs, .enable_pmcs = power4_enable_pmcs,
#ifdef CONFIG_KEXEC #ifdef CONFIG_KEXEC
.machine_kexec = default_machine_kexec, .machine_kexec = default_machine_kexec,
......
...@@ -59,8 +59,6 @@ head-$(CONFIG_4xx) := arch/ppc/kernel/head_4xx.o ...@@ -59,8 +59,6 @@ head-$(CONFIG_4xx) := arch/ppc/kernel/head_4xx.o
head-$(CONFIG_44x) := arch/ppc/kernel/head_44x.o head-$(CONFIG_44x) := arch/ppc/kernel/head_44x.o
head-$(CONFIG_FSL_BOOKE) := arch/ppc/kernel/head_fsl_booke.o head-$(CONFIG_FSL_BOOKE) := arch/ppc/kernel/head_fsl_booke.o
head-$(CONFIG_6xx) += arch/ppc/kernel/idle_6xx.o
head-$(CONFIG_POWER4) += arch/ppc/kernel/idle_power4.o
head-$(CONFIG_PPC_FPU) += arch/powerpc/kernel/fpu.o head-$(CONFIG_PPC_FPU) += arch/powerpc/kernel/fpu.o
core-y += arch/ppc/kernel/ arch/powerpc/kernel/ \ core-y += arch/ppc/kernel/ arch/powerpc/kernel/ \
......
...@@ -8,10 +8,9 @@ extra-$(CONFIG_40x) := head_4xx.o ...@@ -8,10 +8,9 @@ extra-$(CONFIG_40x) := head_4xx.o
extra-$(CONFIG_44x) := head_44x.o extra-$(CONFIG_44x) := head_44x.o
extra-$(CONFIG_FSL_BOOKE) := head_fsl_booke.o extra-$(CONFIG_FSL_BOOKE) := head_fsl_booke.o
extra-$(CONFIG_8xx) := head_8xx.o extra-$(CONFIG_8xx) := head_8xx.o
extra-$(CONFIG_6xx) += idle_6xx.o
extra-y += vmlinux.lds extra-y += vmlinux.lds
obj-y := entry.o traps.o idle.o time.o misc.o \ obj-y := entry.o traps.o time.o misc.o \
setup.o \ setup.o \
ppc_htab.o ppc_htab.o
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
...@@ -35,7 +34,6 @@ endif ...@@ -35,7 +34,6 @@ endif
# These are here while we do the architecture merge # These are here while we do the architecture merge
else else
obj-y := idle.o
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULES) += module.o
......
...@@ -135,10 +135,10 @@ transfer_to_handler: ...@@ -135,10 +135,10 @@ transfer_to_handler:
mfspr r11,SPRN_HID0 mfspr r11,SPRN_HID0
mtcr r11 mtcr r11
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
bt- 8,power_save_6xx_restore /* Check DOZE */ bt- 8,4f /* Check DOZE */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
bt- 9,power_save_6xx_restore /* Check NAP */ bt- 9,4f /* Check NAP */
END_FTR_SECTION_IFSET(CPU_FTR_CAN_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
...@@ -157,6 +157,10 @@ transfer_to_handler_cont: ...@@ -157,6 +157,10 @@ transfer_to_handler_cont:
SYNC SYNC
RFI /* jump to handler, enable MMU */ RFI /* jump to handler, enable MMU */
#ifdef CONFIG_6xx
4: b power_save_6xx_restore
#endif
/* /*
* On kernel stack overflow, load up an initial stack pointer * On kernel stack overflow, load up an initial stack pointer
* and call StackOverflow(regs), which should not return. * and call StackOverflow(regs), which should not return.
......
/*
* Idle daemon for PowerPC. Idle daemon will handle any action
* that needs to be taken when the system becomes idle.
*
* Written by Cort Dougan (cort@cs.nmt.edu). Subsequently hacked
* on by Tom Rini, Armin Kuster, Paul Mackerras and others.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/cpu.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/mmu.h>
#include <asm/cache.h>
#include <asm/cputable.h>
#include <asm/machdep.h>
#include <asm/smp.h>
void default_idle(void)
{
void (*powersave)(void);
powersave = ppc_md.power_save;
if (!need_resched()) {
if (powersave != NULL)
powersave();
#ifdef CONFIG_SMP
else {
set_thread_flag(TIF_POLLING_NRFLAG);
while (!need_resched() &&
!cpu_is_offline(smp_processor_id()))
barrier();
clear_thread_flag(TIF_POLLING_NRFLAG);
}
#endif
}
}
/*
* The body of the idle task.
*/
void cpu_idle(void)
{
int cpu = smp_processor_id();
for (;;) {
while (!need_resched()) {
if (ppc_md.idle != NULL)
ppc_md.idle();
else
default_idle();
}
if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
cpu_die();
preempt_enable_no_resched();
schedule();
preempt_disable();
}
}
#if defined(CONFIG_SYSCTL) && defined(CONFIG_6xx)
/*
* Register the sysctl to set/clear powersave_nap.
*/
extern int powersave_nap;
static ctl_table powersave_nap_ctl_table[]={
{
.ctl_name = KERN_PPC_POWERSAVE_NAP,
.procname = "powersave-nap",
.data = &powersave_nap,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{ 0, },
};
static ctl_table powersave_nap_sysctl_root[] = {
{ 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, },
{ 0,},
};
static int __init
register_powersave_nap_sysctl(void)
{
register_sysctl_table(powersave_nap_sysctl_root, 0);
return 0;
}
__initcall(register_powersave_nap_sysctl);
#endif
/*
* This file contains the power_save function for 6xx & 7xxx CPUs
* rewritten in assembler
*
* Warning ! This code assumes that if your machine has a 750fx
* it will have PLL 1 set to low speed mode (used during NAP/DOZE).
* if this is not the case some additional changes will have to
* be done to check a runtime var (a bit like powersave-nap)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/threads.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/cputable.h>
#include <asm/thread_info.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#undef DEBUG
.text
/*
* Init idle, called at early CPU setup time from head.S for each CPU
* Make sure no rest of NAP mode remains in HID0, save default
* values for some CPU specific registers. Called with r24
* containing CPU number and r3 reloc offset
*/
_GLOBAL(init_idle_6xx)
BEGIN_FTR_SECTION
mfspr r4,SPRN_HID0
rlwinm r4,r4,0,10,8 /* Clear NAP */
mtspr SPRN_HID0, r4
b 1f
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
blr
1:
slwi r5,r24,2
add r5,r5,r3
BEGIN_FTR_SECTION
mfspr r4,SPRN_MSSCR0
addis r6,r5, nap_save_msscr0@ha
stw r4,nap_save_msscr0@l(r6)
END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR)
BEGIN_FTR_SECTION
mfspr r4,SPRN_HID1
addis r6,r5,nap_save_hid1@ha
stw r4,nap_save_hid1@l(r6)
END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX)
blr
/*
* Here is the power_save_6xx function. This could eventually be
* split into several functions & changing the function pointer
* depending on the various features.
*/
_GLOBAL(ppc6xx_idle)
/* Check if we can nap or doze, put HID0 mask in r3
*/
lis r3, 0
BEGIN_FTR_SECTION
lis r3,HID0_DOZE@h
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
BEGIN_FTR_SECTION
/* We must dynamically check for the NAP feature as it
* can be cleared by CPU init after the fixups are done
*/
lis r4,cur_cpu_spec@ha
lwz r4,cur_cpu_spec@l(r4)
lwz r4,CPU_SPEC_FEATURES(r4)
andi. r0,r4,CPU_FTR_CAN_NAP
beq 1f
/* Now check if user or arch enabled NAP mode */
lis r4,powersave_nap@ha
lwz r4,powersave_nap@l(r4)
cmpwi 0,r4,0
beq 1f
lis r3,HID0_NAP@h
1:
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
cmpwi 0,r3,0
beqlr
/* Clear MSR:EE */
mfmsr r7
rlwinm r0,r7,0,17,15
mtmsr r0
/* Check current_thread_info()->flags */
rlwinm r4,r1,0,0,18
lwz r4,TI_FLAGS(r4)
andi. r0,r4,_TIF_NEED_RESCHED
beq 1f
mtmsr r7 /* out of line this ? */
blr
1:
/* Some pre-nap cleanups needed on some CPUs */
andis. r0,r3,HID0_NAP@h
beq 2f
BEGIN_FTR_SECTION
/* Disable L2 prefetch on some 745x and try to ensure
* L2 prefetch engines are idle. As explained by errata
* text, we can't be sure they are, we just hope very hard
* that well be enough (sic !). At least I noticed Apple
* doesn't even bother doing the dcbf's here...
*/
mfspr r4,SPRN_MSSCR0
rlwinm r4,r4,0,0,29
sync
mtspr SPRN_MSSCR0,r4
sync
isync
lis r4,KERNELBASE@h
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
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:
BEGIN_FTR_SECTION
/* Go to low speed mode on some 750FX */
lis r4,powersave_lowspeed@ha
lwz r4,powersave_lowspeed@l(r4)
cmpwi 0,r4,0
beq 1f
mfspr r4,SPRN_HID1
oris r4,r4,0x0001
mtspr SPRN_HID1,r4
1:
END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX)
/* Go to NAP or DOZE now */
mfspr r4,SPRN_HID0
lis r5,(HID0_NAP|HID0_SLEEP)@h
BEGIN_FTR_SECTION
oris r5,r5,HID0_DOZE@h
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
andc r4,r4,r5
or r4,r4,r3
BEGIN_FTR_SECTION
oris r4,r4,HID0_DPM@h /* that should be done once for all */
END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM)
mtspr SPRN_HID0,r4
BEGIN_FTR_SECTION
DSSALL
sync
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
ori r7,r7,MSR_EE /* Could be ommited (already set) */
oris r7,r7,MSR_POW@h
sync
isync
mtmsr r7
isync
sync
blr
/*
* Return from NAP/DOZE mode, restore some CPU specific registers,
* we are called with DR/IR still off and r2 containing physical
* address of current.
*/
_GLOBAL(power_save_6xx_restore)
mfspr r11,SPRN_HID0
rlwinm. r11,r11,0,10,8 /* Clear NAP & copy NAP bit !state to cr1 EQ */
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
beq cr1,1f
lis r11,(nap_return_count-KERNELBASE)@ha
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
/* Todo make sure all these are in the same page
* and load r22 (@ha part + CPU offset) only once
*/
BEGIN_FTR_SECTION
beq cr1,1f
addis r9,r11,(nap_save_msscr0-KERNELBASE)@ha
lwz r9,nap_save_msscr0@l(r9)
mtspr SPRN_MSSCR0, r9
sync
isync
1:
END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR)
BEGIN_FTR_SECTION
addis r9,r11,(nap_save_hid1-KERNELBASE)@ha
lwz r9,nap_save_hid1@l(r9)
mtspr SPRN_HID1, r9
END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX)
b transfer_to_handler_cont
.data
_GLOBAL(nap_save_msscr0)
.space 4*NR_CPUS
_GLOBAL(nap_save_hid1)
.space 4*NR_CPUS
_GLOBAL(powersave_nap)
.long 0
_GLOBAL(powersave_lowspeed)
.long 0
#ifdef DEBUG
_GLOBAL(nap_enter_count)
.space 4
_GLOBAL(nap_return_count)
.space 4
#endif
/*
* This file contains the power_save function for 6xx & 7xxx CPUs
* rewritten in assembler
*
* Warning ! This code assumes that if your machine has a 750fx
* it will have PLL 1 set to low speed mode (used during NAP/DOZE).
* if this is not the case some additional changes will have to
* be done to check a runtime var (a bit like powersave-nap)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/threads.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/cputable.h>
#include <asm/thread_info.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#undef DEBUG
.text
/*
* Init idle, called at early CPU setup time from head.S for each CPU
* So nothing for now. Called with r24 containing CPU number and r3
* reloc offset
*/
.globl init_idle_power4
init_idle_power4:
blr
/*
* Here is the power_save_6xx function. This could eventually be
* split into several functions & changing the function pointer
* depending on the various features.
*/
.globl power4_idle
power4_idle:
BEGIN_FTR_SECTION
blr
END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP)
/* We must dynamically check for the NAP feature as it
* can be cleared by CPU init after the fixups are done
*/
lis r4,cur_cpu_spec@ha
lwz r4,cur_cpu_spec@l(r4)
lwz r4,CPU_SPEC_FEATURES(r4)
andi. r0,r4,CPU_FTR_CAN_NAP
beqlr
/* Now check if user or arch enabled NAP mode */
lis r4,powersave_nap@ha
lwz r4,powersave_nap@l(r4)
cmpwi 0,r4,0
beqlr
/* Clear MSR:EE */
mfmsr r7
rlwinm r0,r7,0,17,15
mtmsr r0
/* Check current_thread_info()->flags */
rlwinm r4,r1,0,0,18
lwz r4,TI_FLAGS(r4)
andi. r0,r4,_TIF_NEED_RESCHED
beq 1f
mtmsr r7 /* out of line this ? */
blr
1:
/* Go to NAP now */
BEGIN_FTR_SECTION
DSSALL
sync
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
ori r7,r7,MSR_EE /* Could be ommited (already set) */
oris r7,r7,MSR_POW@h
sync
isync
mtmsr r7
isync
sync
blr
.globl powersave_nap
powersave_nap:
.long 0
...@@ -158,6 +158,12 @@ struct machdep_calls { ...@@ -158,6 +158,12 @@ struct machdep_calls {
/* Idle loop for this platform, leave empty for default idle loop */ /* Idle loop for this platform, leave empty for default idle loop */
void (*idle_loop)(void); void (*idle_loop)(void);
/*
* Function for waiting for work with reduced power in idle loop;
* called with interrupts disabled.
*/
void (*power_save)(void);
/* Function to enable performance monitor counters for this /* Function to enable performance monitor counters for this
platform, called once per cpu. */ platform, called once per cpu. */
void (*enable_pmcs)(void); void (*enable_pmcs)(void);
...@@ -170,9 +176,6 @@ struct machdep_calls { ...@@ -170,9 +176,6 @@ struct machdep_calls {
May be NULL. */ May be NULL. */
void (*init)(void); void (*init)(void);
void (*idle)(void);
void (*power_save)(void);
void (*heartbeat)(void); void (*heartbeat)(void);
unsigned long heartbeat_reset; unsigned long heartbeat_reset;
unsigned long heartbeat_count; unsigned long heartbeat_count;
...@@ -242,8 +245,8 @@ struct machdep_calls { ...@@ -242,8 +245,8 @@ struct machdep_calls {
#endif /* CONFIG_KEXEC */ #endif /* CONFIG_KEXEC */
}; };
extern void default_idle(void); extern void power4_idle(void);
extern void native_idle(void); extern void ppc6xx_idle(void);
extern struct machdep_calls ppc_md; extern struct machdep_calls ppc_md;
extern char cmd_line[COMMAND_LINE_SIZE]; extern char cmd_line[COMMAND_LINE_SIZE];
......
...@@ -622,6 +622,10 @@ extern void ppc64_runlatch_off(void); ...@@ -622,6 +622,10 @@ extern void ppc64_runlatch_off(void);
extern unsigned long scom970_read(unsigned int address); extern unsigned long scom970_read(unsigned int address);
extern void scom970_write(unsigned int address, unsigned long value); extern void scom970_write(unsigned int address, unsigned long value);
#else
#define ppc64_runlatch_on()
#define ppc64_runlatch_off()
#endif /* CONFIG_PPC64 */ #endif /* CONFIG_PPC64 */
#define __get_SP() ({unsigned long sp; \ #define __get_SP() ({unsigned long sp; \
......
...@@ -44,7 +44,7 @@ struct machdep_calls { ...@@ -44,7 +44,7 @@ struct machdep_calls {
void (*power_off)(void); void (*power_off)(void);
void (*halt)(void); void (*halt)(void);
void (*idle)(void); void (*idle_loop)(void);
void (*power_save)(void); void (*power_save)(void);
long (*time_init)(void); /* Optional, may be NULL */ long (*time_init)(void); /* Optional, may be NULL */
......
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