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(&regs->pmcr, in_be64(&regs->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