blob: 59999902e4a6aff631b1ba7efab56efb6080ef23 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CBE Pervasive Monitor and Debug
*
* (C) Copyright IBM Corporation 2005
*
* Authors: Maximino Aguilar (maguilar@us.ibm.com)
* Michael N. Day (mnday@us.ibm.com)
*/
#undef DEBUG
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/percpu.h>
#include <linux/types.h>
#include <linux/kallsyms.h>
#include <linux/pgtable.h>
#include <asm/io.h>
#include <asm/machdep.h>
#include <asm/prom.h>
#include <asm/reg.h>
#include <asm/cell-regs.h>
#include <asm/cpu_has_feature.h>
#include "pervasive.h"
static void cbe_power_save(void)
{
unsigned long ctrl, thread_switch_control;
/* Ensure our interrupt state is properly tracked */
if (!prep_irq_for_idle())
return;
ctrl = mfspr(SPRN_CTRLF);
/* Enable DEC and EE interrupt request */
thread_switch_control = mfspr(SPRN_TSC_CELL);
thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST;
switch (ctrl & CTRL_CT) {
case CTRL_CT0:
thread_switch_control |= TSC_CELL_DEC_ENABLE_0;
break;
case CTRL_CT1:
thread_switch_control |= TSC_CELL_DEC_ENABLE_1;
break;
default:
printk(KERN_WARNING "%s: unknown configuration\n",
__func__);
break;
}
mtspr(SPRN_TSC_CELL, thread_switch_control);
/*
* go into low thread priority, medium priority will be
* restored for us after wake-up.
*/
HMT_low();
/*
* atomically disable thread execution and runlatch.
* External and Decrementer exceptions are still handled when the
* thread is disabled but now enter in cbe_system_reset_exception()
*/
ctrl &= ~(CTRL_RUNLATCH | CTRL_TE);
mtspr(SPRN_CTRLT, ctrl);
/* Re-enable interrupts in MSR */
__hard_irq_enable();
}
static int cbe_system_reset_exception(struct pt_regs *regs)
{
switch (regs->msr & SRR1_WAKEMASK) {
case SRR1_WAKEDEC:
set_dec(1);
break;
case SRR1_WAKEEE:
/*
* Handle these when interrupts get re-enabled and we take
* them as regular exceptions. We are in an NMI context
* and can't handle these here.
*/
break;
case SRR1_WAKEMT:
return cbe_sysreset_hack();
#ifdef CONFIG_CBE_RAS
case SRR1_WAKESYSERR:
cbe_system_error_exception(regs);
break;
case SRR1_WAKETHERM:
cbe_thermal_exception(regs);
break;
#endif /* CONFIG_CBE_RAS */
default:
/* do system reset */
return 0;
}
/* everything handled */
return 1;
}
void __init cbe_pervasive_init(void)
{
int cpu;
if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO))
return;
for_each_possible_cpu(cpu) {
struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu);
if (!regs)
continue;
/* Enable Pause(0) control bit */
out_be64(®s->pmcr, in_be64(®s->pmcr) |
CBE_PMD_PAUSE_ZERO_CONTROL);
}
ppc_md.power_save = cbe_power_save;
ppc_md.system_reset_exception = cbe_system_reset_exception;
}
|