162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CBE Pervasive Monitor and Debug 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright IBM Corporation 2005 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Maximino Aguilar (maguilar@us.ibm.com) 862306a36Sopenharmony_ci * Michael N. Day (mnday@us.ibm.com) 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#undef DEBUG 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/percpu.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/kallsyms.h> 1862306a36Sopenharmony_ci#include <linux/pgtable.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/io.h> 2162306a36Sopenharmony_ci#include <asm/machdep.h> 2262306a36Sopenharmony_ci#include <asm/reg.h> 2362306a36Sopenharmony_ci#include <asm/cell-regs.h> 2462306a36Sopenharmony_ci#include <asm/cpu_has_feature.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "pervasive.h" 2762306a36Sopenharmony_ci#include "ras.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void cbe_power_save(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci unsigned long ctrl, thread_switch_control; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* Ensure our interrupt state is properly tracked */ 3462306a36Sopenharmony_ci if (!prep_irq_for_idle()) 3562306a36Sopenharmony_ci return; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ctrl = mfspr(SPRN_CTRLF); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Enable DEC and EE interrupt request */ 4062306a36Sopenharmony_ci thread_switch_control = mfspr(SPRN_TSC_CELL); 4162306a36Sopenharmony_ci thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci switch (ctrl & CTRL_CT) { 4462306a36Sopenharmony_ci case CTRL_CT0: 4562306a36Sopenharmony_ci thread_switch_control |= TSC_CELL_DEC_ENABLE_0; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case CTRL_CT1: 4862306a36Sopenharmony_ci thread_switch_control |= TSC_CELL_DEC_ENABLE_1; 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci default: 5162306a36Sopenharmony_ci printk(KERN_WARNING "%s: unknown configuration\n", 5262306a36Sopenharmony_ci __func__); 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci mtspr(SPRN_TSC_CELL, thread_switch_control); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * go into low thread priority, medium priority will be 5962306a36Sopenharmony_ci * restored for us after wake-up. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci HMT_low(); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* 6462306a36Sopenharmony_ci * atomically disable thread execution and runlatch. 6562306a36Sopenharmony_ci * External and Decrementer exceptions are still handled when the 6662306a36Sopenharmony_ci * thread is disabled but now enter in cbe_system_reset_exception() 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci ctrl &= ~(CTRL_RUNLATCH | CTRL_TE); 6962306a36Sopenharmony_ci mtspr(SPRN_CTRLT, ctrl); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Re-enable interrupts in MSR */ 7262306a36Sopenharmony_ci __hard_irq_enable(); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int cbe_system_reset_exception(struct pt_regs *regs) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci switch (regs->msr & SRR1_WAKEMASK) { 7862306a36Sopenharmony_ci case SRR1_WAKEDEC: 7962306a36Sopenharmony_ci set_dec(1); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case SRR1_WAKEEE: 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Handle these when interrupts get re-enabled and we take 8462306a36Sopenharmony_ci * them as regular exceptions. We are in an NMI context 8562306a36Sopenharmony_ci * and can't handle these here. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci case SRR1_WAKEMT: 8962306a36Sopenharmony_ci return cbe_sysreset_hack(); 9062306a36Sopenharmony_ci#ifdef CONFIG_CBE_RAS 9162306a36Sopenharmony_ci case SRR1_WAKESYSERR: 9262306a36Sopenharmony_ci cbe_system_error_exception(regs); 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case SRR1_WAKETHERM: 9562306a36Sopenharmony_ci cbe_thermal_exception(regs); 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci#endif /* CONFIG_CBE_RAS */ 9862306a36Sopenharmony_ci default: 9962306a36Sopenharmony_ci /* do system reset */ 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci /* everything handled */ 10362306a36Sopenharmony_ci return 1; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid __init cbe_pervasive_init(void) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci int cpu; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 11462306a36Sopenharmony_ci struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu); 11562306a36Sopenharmony_ci if (!regs) 11662306a36Sopenharmony_ci continue; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Enable Pause(0) control bit */ 11962306a36Sopenharmony_ci out_be64(®s->pmcr, in_be64(®s->pmcr) | 12062306a36Sopenharmony_ci CBE_PMD_PAUSE_ZERO_CONTROL); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci ppc_md.power_save = cbe_power_save; 12462306a36Sopenharmony_ci ppc_md.system_reset_exception = cbe_system_reset_exception; 12562306a36Sopenharmony_ci} 126