1 /* 2 * CBE Pervasive Monitor and Debug 3 * 4 * (C) Copyright IBM Corporation 2005 5 * 6 * Authors: Maximino Aguilar (maguilar@us.ibm.com) 7 * Michael N. Day (mnday@us.ibm.com) 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2, or (at your option) 12 * any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24 #undef DEBUG 25 26 #include <linux/interrupt.h> 27 #include <linux/irq.h> 28 #include <linux/percpu.h> 29 #include <linux/types.h> 30 #include <linux/kallsyms.h> 31 32 #include <asm/io.h> 33 #include <asm/machdep.h> 34 #include <asm/prom.h> 35 #include <asm/pgtable.h> 36 #include <asm/reg.h> 37 38 #include "pervasive.h" 39 #include "cbe_regs.h" 40 41 static DEFINE_SPINLOCK(cbe_pervasive_lock); 42 43 static void __init cbe_enable_pause_zero(void) 44 { 45 unsigned long thread_switch_control; 46 unsigned long temp_register; 47 struct cbe_pmd_regs __iomem *pregs; 48 49 spin_lock_irq(&cbe_pervasive_lock); 50 pregs = cbe_get_cpu_pmd_regs(smp_processor_id()); 51 if (pregs == NULL) 52 goto out; 53 54 pr_debug("Power Management: CPU %d\n", smp_processor_id()); 55 56 /* Enable Pause(0) control bit */ 57 temp_register = in_be64(&pregs->pmcr); 58 59 out_be64(&pregs->pmcr, 60 temp_register | CBE_PMD_PAUSE_ZERO_CONTROL); 61 62 /* Enable DEC and EE interrupt request */ 63 thread_switch_control = mfspr(SPRN_TSC_CELL); 64 thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST; 65 66 switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) { 67 case CTRL_CT0: 68 thread_switch_control |= TSC_CELL_DEC_ENABLE_0; 69 break; 70 case CTRL_CT1: 71 thread_switch_control |= TSC_CELL_DEC_ENABLE_1; 72 break; 73 default: 74 printk(KERN_WARNING "%s: unknown configuration\n", 75 __FUNCTION__); 76 break; 77 } 78 79 mtspr(SPRN_TSC_CELL, thread_switch_control); 80 81 out: 82 spin_unlock_irq(&cbe_pervasive_lock); 83 } 84 85 static void cbe_idle(void) 86 { 87 unsigned long ctrl; 88 89 /* Why do we do that on every idle ? Couldn't that be done once for 90 * all or do we lose the state some way ? Also, the pmcr 91 * register setting, that can't be set once at boot ? We really want 92 * to move that away in order to implement a simple powersave 93 */ 94 cbe_enable_pause_zero(); 95 96 while (1) { 97 if (!need_resched()) { 98 local_irq_disable(); 99 while (!need_resched()) { 100 /* go into low thread priority */ 101 HMT_low(); 102 103 /* 104 * atomically disable thread execution 105 * and runlatch. 106 * External and Decrementer exceptions 107 * are still handled when the thread 108 * is disabled but now enter in 109 * cbe_system_reset_exception() 110 */ 111 ctrl = mfspr(SPRN_CTRLF); 112 ctrl &= ~(CTRL_RUNLATCH | CTRL_TE); 113 mtspr(SPRN_CTRLT, ctrl); 114 } 115 /* restore thread prio */ 116 HMT_medium(); 117 local_irq_enable(); 118 } 119 120 /* 121 * turn runlatch on again before scheduling the 122 * process we just woke up 123 */ 124 ppc64_runlatch_on(); 125 126 preempt_enable_no_resched(); 127 schedule(); 128 preempt_disable(); 129 } 130 } 131 132 static int cbe_system_reset_exception(struct pt_regs *regs) 133 { 134 switch (regs->msr & SRR1_WAKEMASK) { 135 case SRR1_WAKEEE: 136 do_IRQ(regs); 137 break; 138 case SRR1_WAKEDEC: 139 timer_interrupt(regs); 140 break; 141 case SRR1_WAKEMT: 142 break; 143 #ifdef CONFIG_CBE_RAS 144 case SRR1_WAKESYSERR: 145 cbe_system_error_exception(regs); 146 break; 147 case SRR1_WAKETHERM: 148 cbe_thermal_exception(regs); 149 break; 150 #endif /* CONFIG_CBE_RAS */ 151 default: 152 /* do system reset */ 153 return 0; 154 } 155 /* everything handled */ 156 return 1; 157 } 158 159 void __init cbe_pervasive_init(void) 160 { 161 if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) 162 return; 163 164 ppc_md.idle_loop = cbe_idle; 165 ppc_md.system_reset_exception = cbe_system_reset_exception; 166 } 167