enable pause(0) for BPA This patch enables support for pause(0) power management state for the Broadband Processor, which is import for power efficient operation. The bpa_pmd.c infrastructure will in the future enable us to introduce more functionality specific to the Cell's pervasive unit. From: Maximino Aguilar --- arch/ppc64/kernel/Makefile | 2 arch/ppc64/kernel/bpa_pmd.c | 146 ++++++++++++++++++++++++++++++++++++++++++ arch/ppc64/kernel/bpa_pmd.h | 64 ++++++++++++++++++ arch/ppc64/kernel/bpa_setup.c | 2 arch/ppc64/kernel/cputable.c | 3 arch/ppc64/kernel/head.S | 27 +++++++ arch/ppc64/kernel/idle.c | 3 include/asm-ppc64/cputable.h | 1 include/asm-ppc64/processor.h | 24 ++++++ 9 files changed, 265 insertions(+), 7 deletions(-) Index: linux-cg/arch/ppc64/kernel/Makefile =================================================================== --- linux-cg.orig/arch/ppc64/kernel/Makefile +++ linux-cg/arch/ppc64/kernel/Makefile @@ -34,7 +34,7 @@ obj-$(CONFIG_PPC_PSERIES) += pSeries_pci pSeries_setup.o pSeries_iommu.o udbg_16550.o obj-$(CONFIG_PPC_BPA) += bpa_setup.o bpa_iommu.o bpa_nvram.o \ - bpa_iic.o spider-pic.o + bpa_iic.o spider-pic.o bpa_pmd.o obj-$(CONFIG_KEXEC) += machine_kexec.o obj-$(CONFIG_EEH) += eeh.o Index: linux-cg/arch/ppc64/kernel/bpa_pmd.c =================================================================== --- /dev/null +++ linux-cg/arch/ppc64/kernel/bpa_pmd.c @@ -0,0 +1,146 @@ +/* + * BPA Pervasive Monitor and Debug + * + * (C) Copyright IBM Corporation 2005 + * + * Authors: Maximino Aguilar (maguilar@us.ibm.com) + * Michael N. Day (mnday@us.ibm.com) + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bpa_pmd.h" + +struct pmd { + struct pmd_regs __iomem *regs; + int power_management_enable; +}; + +static DEFINE_PER_CPU(struct pmd, pmd); + +void pause_zero(void) +{ + unsigned int multi_threading_control; + unsigned long long machine_state; + + /* Reset Thread Run Latch (latch is set in idle.c) */ + ppc64_runlatch_off(); + + if (__get_cpu_var(pmd).power_management_enable) + { + /* Disable EE during check for pause */ + machine_state=mfmsr(); + machine_state &= ~MSR_EE; + mtmsrd(machine_state); + /* Pause the PU */ + HMT_low(); + multi_threading_control = 0; + mtspr(SPRN_CTRLT,multi_threading_control); + + /* Re-enable EE after resuming */ + machine_state=mfmsr(); + machine_state |= MSR_EE; + mtmsrd(machine_state); + } +} + +void enable_pause_zero(void * data) +{ + unsigned long thread_switch_control; + unsigned long temp_register; + struct pmd *pmd; + + pmd = &get_cpu_var(pmd); + + if (!pmd->regs) + return; + + pr_debug("Power Management: CPU %d\n", smp_processor_id()); + + /* Enable Pause(0) control bit */ + temp_register = in_be64(&pmd->regs->pm_control); + + out_be64(&pmd->regs->pm_control, temp_register|PMD_PAUSE_ZERO_CONTROL); + + /* Enable DEC and EE interrupt request */ + thread_switch_control = mfspr(SPRN_TSC_BE); + thread_switch_control |= TSCR_EE_ENABLE | TSCR_EE_BOOST; + + if (smp_processor_id()%2) + thread_switch_control |= TSC_DEC_ENABLE_1; + else + thread_switch_control |= TSC_DEC_ENABLE_0; + + mtspr(SPRN_TSC_BE,thread_switch_control); + + pmd->power_management_enable = 1; + put_cpu_var(pmd); +} + +static struct pmd_regs __iomem *find_pmd_mmio(int cpu) +{ + struct device_node *node; + int node_number = cpu / 2; + struct pmd_regs __iomem *pmd_mmio_area; + unsigned long real_address; + + for (node = of_find_node_by_type(NULL, "cpu"); node; + node = of_find_node_by_type(node, "cpu")) { + if (node_number == *(int *)get_property(node, "node-id", NULL)) + break; + } + + if (!node) { + printk(KERN_WARNING "PMD: CPU %d not found\n", cpu); + pmd_mmio_area = NULL; + } else { + real_address = *(long *)get_property(node, "pervasive", NULL); + pr_debug("PMD for CPU %d at %lx\n", cpu, real_address); + pmd_mmio_area = __ioremap(real_address, 0x1000, _PAGE_NO_CACHE); + } + return pmd_mmio_area; +} + +void __init bpa_pmd_init(void) +{ + struct pmd *pmd; + int cpu; + + if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) + return; + + for_each_cpu(cpu) { + pmd = &per_cpu(pmd, cpu); + pmd->regs = find_pmd_mmio(cpu); + } +} + +int __init enable_pause_zero_init(void) +{ + on_each_cpu(enable_pause_zero, NULL, 0, 1); + return 0; +} + +arch_initcall(enable_pause_zero_init); Index: linux-cg/arch/ppc64/kernel/bpa_pmd.h =================================================================== --- /dev/null +++ linux-cg/arch/ppc64/kernel/bpa_pmd.h @@ -0,0 +1,64 @@ +/* + * BPA Pervasive Monitor and Debug interface and HW structures + * + * (C) Copyright IBM Corporation 2005 + * + * Authors: Maximino Aguilar (maguilar@us.ibm.com) + * David J. Erb (djerb@us.ibm.com) + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#ifndef BPA_PMD_H +#define BPA_PMD_H + +struct pmd_regs { + u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */ + + /* Thermal Sensor Registers */ + u64 ts_ctsr1; /* 0x0800 */ + u64 ts_ctsr2; /* 0x0808 */ + u64 ts_mtsr1; /* 0x0810 */ + u64 ts_mtsr2; /* 0x0818 */ + u64 ts_itr1; /* 0x0820 */ + u64 ts_itr2; /* 0x0828 */ + u64 ts_gitr; /* 0x0830 */ + u64 ts_isr; /* 0x0838 */ + u64 ts_imr; /* 0x0840 */ + u64 tm_cr1; /* 0x0848 */ + u64 tm_cr2; /* 0x0850 */ + u64 tm_simr; /* 0x0858 */ + u64 tm_tpr; /* 0x0860 */ + u64 tm_str1; /* 0x0868 */ + u64 tm_str2; /* 0x0870 */ + u64 tm_tsr; /* 0x0878 */ + + /* Power Management */ + u64 pm_control; /* 0x0880 */ +#define PMD_PAUSE_ZERO_CONTROL 0x10000 + u64 pm_status; /* 0x0888 */ + + /* Time Base Register */ + u64 tbr; /* 0x0890 */ + + u8 pad_0x0898_0x1000 [0x1000 - 0x0898]; /* 0x0898 */ +}; + +void __init bpa_pmd_init(void); +void enable_pause_zero(void *); +void _pause_zero(void); + +#endif Index: linux-cg/arch/ppc64/kernel/bpa_setup.c =================================================================== --- linux-cg.orig/arch/ppc64/kernel/bpa_setup.c +++ linux-cg/arch/ppc64/kernel/bpa_setup.c @@ -47,6 +47,7 @@ #include "pci.h" #include "bpa_iic.h" #include "bpa_iommu.h" +#include "bpa_pmd.h" #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -163,6 +164,7 @@ static void __init bpa_setup_arch(void) init_pci_config_tokens(); find_and_init_phbs(); spider_init_IRQ(); + bpa_pmd_init(); #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif Index: linux-cg/arch/ppc64/kernel/cputable.c =================================================================== --- linux-cg.orig/arch/ppc64/kernel/cputable.c +++ linux-cg/arch/ppc64/kernel/cputable.c @@ -285,7 +285,8 @@ struct cpu_spec cpu_specs[] = { .cpu_features = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_ALTIVEC_COMP | - CPU_FTR_SMT, + CPU_FTR_SMT | CPU_FTR_CTRL | + CPU_FTR_PAUSE_ZERO, .cpu_user_features = COMMON_USER_PPC64 | PPC_FEATURE_HAS_ALTIVEC_COMP, .icache_bsize = 128, Index: linux-cg/arch/ppc64/kernel/head.S =================================================================== --- linux-cg.orig/arch/ppc64/kernel/head.S +++ linux-cg/arch/ppc64/kernel/head.S @@ -383,7 +383,7 @@ label##_common: \ .globl __start_interrupts __start_interrupts: - STD_EXCEPTION_PSERIES(0x100, system_reset) + STD_EXCEPTION_PSERIES(0x100, system_reset_check) . = 0x200 _machine_check_pSeries: @@ -779,6 +779,31 @@ unrecov_fer: bl .unrecoverable_exception b 1b +/* This is a new system reset handler for the BE processor. + * SRR1 stores wake information that must be decoded to determine why + * the processor was at the system reset handler. + */ + + .align 7 + .globl system_reset_check_common +system_reset_check_common: +BEGIN_FTR_SECTION + mr r22,r12 /* r12 has SRR1 saved */ + srwi r22,r22,16 + andi. r22,r22,MSR_WAKEMASK + cmpwi r22,MSR_WAKEEE + beq 40f + cmpwi r22,MSR_WAKEDEC + beq 42f + cmpwi r22,MSR_WAKEMT + beq 43f +END_FTR_SECTION_IFSET(CPU_FTR_PAUSE_ZERO) + b system_reset_common +40: b hardware_interrupt_common +42: b decrementer_common +43: EXCEPTION_PROLOG_COMMON(0x100, PACA_EXGEN); + b fast_exception_return + /* * Here r13 points to the paca, r9 contains the saved CR, * SRR0 and SRR1 are saved in r11 and r12, Index: linux-cg/arch/ppc64/kernel/idle.c =================================================================== --- linux-cg.orig/arch/ppc64/kernel/idle.c +++ linux-cg/arch/ppc64/kernel/idle.c @@ -44,7 +44,8 @@ int default_idle(void) while (!need_resched() && !cpu_is_offline(cpu)) { ppc64_runlatch_off(); - + if (cpu_has_feature(CPU_FTR_PAUSE_ZERO)) + pause_zero(); /* * Go into low thread priority and possibly * low power mode. Index: linux-cg/include/asm-ppc64/cputable.h =================================================================== --- linux-cg.orig/include/asm-ppc64/cputable.h +++ linux-cg/include/asm-ppc64/cputable.h @@ -111,6 +111,7 @@ static inline unsigned long cpu_has_feat #define CPU_FTR_LOCKLESS_TLBIE ASM_CONST(0x0000040000000000) #define CPU_FTR_MMCRA_SIHV ASM_CONST(0x0000080000000000) #define CPU_FTR_CTRL ASM_CONST(0x0000100000000000) +#define CPU_FTR_PAUSE_ZERO ASM_CONST(0x0000200000000000) #ifndef __ASSEMBLY__ Index: linux-cg/include/asm-ppc64/processor.h =================================================================== --- linux-cg.orig/include/asm-ppc64/processor.h +++ linux-cg/include/asm-ppc64/processor.h @@ -83,6 +83,15 @@ #define MSR_RI __MASK(MSR_RI_LG) /* Recoverable Exception */ #define MSR_LE __MASK(MSR_LE_LG) /* Little Endian */ +/* Wake Events */ +#define MSR_WAKEMASK 0x0038 +#define MSR_WAKERESET 0x0038 +#define MSR_WAKESYSERR 0x0030 +#define MSR_WAKEEE 0x0020 +#define MSR_WAKEMT 0x0028 +#define MSR_WAKEDEC 0x0018 +#define MSR_WAKETHERM 0x0010 + #define MSR_ MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF #define MSR_KERNEL MSR_ | MSR_SF | MSR_HV @@ -142,9 +151,10 @@ #define SPRN_HID6 0x3F9 /* BE HID 6 */ #define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */ #define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */ -#define SPRN_TSCR 0x399 /* Thread switch control on BE */ -#define SPRN_TTR 0x39A /* Thread switch timeout on BE */ -#define TSCR_DEC_ENABLE 0x200000 /* Decrementer Interrupt */ +#define SPRN_TSC_BE 0x399 /* Thread switch control BE */ +#define SPRN_TST_BE 0x39A /* Thread switch timeout BE */ +#define TSC_DEC_ENABLE_0 0x400000 /* Decrementer Interrupt */ +#define TSC_DEC_ENABLE_1 0x200000 /* Decrementer Interrupt */ #define TSCR_EE_ENABLE 0x100000 /* External Interrupt */ #define TSCR_EE_BOOST 0x080000 /* External Interrupt Boost */ #define SPRN_TSC 0x3FD /* Thread switch control on others */ @@ -536,6 +546,14 @@ static inline void ppc64_runlatch_off(vo } } +#ifdef CONFIG_PPC_BPA +extern void pause_zero(void); +#else +static inline void pause_zero(void) +{ +} +#endif + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ --