diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/smp.c')
-rw-r--r-- | arch/powerpc/platforms/powernv/smp.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c new file mode 100644 index 000000000000..e87736685243 --- /dev/null +++ b/arch/powerpc/platforms/powernv/smp.c | |||
@@ -0,0 +1,182 @@ | |||
1 | /* | ||
2 | * SMP support for PowerNV machines. | ||
3 | * | ||
4 | * Copyright 2011 IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/smp.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/cpu.h> | ||
21 | |||
22 | #include <asm/irq.h> | ||
23 | #include <asm/smp.h> | ||
24 | #include <asm/paca.h> | ||
25 | #include <asm/machdep.h> | ||
26 | #include <asm/cputable.h> | ||
27 | #include <asm/firmware.h> | ||
28 | #include <asm/system.h> | ||
29 | #include <asm/rtas.h> | ||
30 | #include <asm/vdso_datapage.h> | ||
31 | #include <asm/cputhreads.h> | ||
32 | #include <asm/xics.h> | ||
33 | #include <asm/opal.h> | ||
34 | |||
35 | #include "powernv.h" | ||
36 | |||
37 | #ifdef DEBUG | ||
38 | #include <asm/udbg.h> | ||
39 | #define DBG(fmt...) udbg_printf(fmt) | ||
40 | #else | ||
41 | #define DBG(fmt...) | ||
42 | #endif | ||
43 | |||
44 | static void __cpuinit pnv_smp_setup_cpu(int cpu) | ||
45 | { | ||
46 | if (cpu != boot_cpuid) | ||
47 | xics_setup_cpu(); | ||
48 | } | ||
49 | |||
50 | static int pnv_smp_cpu_bootable(unsigned int nr) | ||
51 | { | ||
52 | /* Special case - we inhibit secondary thread startup | ||
53 | * during boot if the user requests it. | ||
54 | */ | ||
55 | if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) { | ||
56 | if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0) | ||
57 | return 0; | ||
58 | if (smt_enabled_at_boot | ||
59 | && cpu_thread_in_core(nr) >= smt_enabled_at_boot) | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | return 1; | ||
64 | } | ||
65 | |||
66 | int __devinit pnv_smp_kick_cpu(int nr) | ||
67 | { | ||
68 | unsigned int pcpu = get_hard_smp_processor_id(nr); | ||
69 | unsigned long start_here = __pa(*((unsigned long *) | ||
70 | generic_secondary_smp_init)); | ||
71 | long rc; | ||
72 | |||
73 | BUG_ON(nr < 0 || nr >= NR_CPUS); | ||
74 | |||
75 | /* On OPAL v2 the CPU are still spinning inside OPAL itself, | ||
76 | * get them back now | ||
77 | */ | ||
78 | if (firmware_has_feature(FW_FEATURE_OPALv2)) { | ||
79 | pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); | ||
80 | rc = opal_start_cpu(pcpu, start_here); | ||
81 | if (rc != OPAL_SUCCESS) | ||
82 | pr_warn("OPAL Error %ld starting CPU %d\n", | ||
83 | rc, nr); | ||
84 | } | ||
85 | return smp_generic_kick_cpu(nr); | ||
86 | } | ||
87 | |||
88 | #ifdef CONFIG_HOTPLUG_CPU | ||
89 | |||
90 | static int pnv_smp_cpu_disable(void) | ||
91 | { | ||
92 | int cpu = smp_processor_id(); | ||
93 | |||
94 | /* This is identical to pSeries... might consolidate by | ||
95 | * moving migrate_irqs_away to a ppc_md with default to | ||
96 | * the generic fixup_irqs. --BenH. | ||
97 | */ | ||
98 | set_cpu_online(cpu, false); | ||
99 | vdso_data->processorCount--; | ||
100 | if (cpu == boot_cpuid) | ||
101 | boot_cpuid = cpumask_any(cpu_online_mask); | ||
102 | xics_migrate_irqs_away(); | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static void pnv_smp_cpu_kill_self(void) | ||
107 | { | ||
108 | unsigned int cpu; | ||
109 | |||
110 | /* If powersave_nap is enabled, use NAP mode, else just | ||
111 | * spin aimlessly | ||
112 | */ | ||
113 | if (!powersave_nap) { | ||
114 | generic_mach_cpu_die(); | ||
115 | return; | ||
116 | } | ||
117 | |||
118 | /* Standard hot unplug procedure */ | ||
119 | local_irq_disable(); | ||
120 | idle_task_exit(); | ||
121 | current->active_mm = NULL; /* for sanity */ | ||
122 | cpu = smp_processor_id(); | ||
123 | DBG("CPU%d offline\n", cpu); | ||
124 | generic_set_cpu_dead(cpu); | ||
125 | smp_wmb(); | ||
126 | |||
127 | /* We don't want to take decrementer interrupts while we are offline, | ||
128 | * so clear LPCR:PECE1. We keep PECE2 enabled. | ||
129 | */ | ||
130 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); | ||
131 | while (!generic_check_cpu_restart(cpu)) { | ||
132 | power7_idle(); | ||
133 | if (!generic_check_cpu_restart(cpu)) { | ||
134 | DBG("CPU%d Unexpected exit while offline !\n", cpu); | ||
135 | /* We may be getting an IPI, so we re-enable | ||
136 | * interrupts to process it, it will be ignored | ||
137 | * since we aren't online (hopefully) | ||
138 | */ | ||
139 | local_irq_enable(); | ||
140 | local_irq_disable(); | ||
141 | } | ||
142 | } | ||
143 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); | ||
144 | DBG("CPU%d coming online...\n", cpu); | ||
145 | } | ||
146 | |||
147 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
148 | |||
149 | static struct smp_ops_t pnv_smp_ops = { | ||
150 | .message_pass = smp_muxed_ipi_message_pass, | ||
151 | .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ | ||
152 | .probe = xics_smp_probe, | ||
153 | .kick_cpu = pnv_smp_kick_cpu, | ||
154 | .setup_cpu = pnv_smp_setup_cpu, | ||
155 | .cpu_bootable = pnv_smp_cpu_bootable, | ||
156 | #ifdef CONFIG_HOTPLUG_CPU | ||
157 | .cpu_disable = pnv_smp_cpu_disable, | ||
158 | .cpu_die = generic_cpu_die, | ||
159 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
160 | }; | ||
161 | |||
162 | /* This is called very early during platform setup_arch */ | ||
163 | void __init pnv_smp_init(void) | ||
164 | { | ||
165 | smp_ops = &pnv_smp_ops; | ||
166 | |||
167 | /* XXX We don't yet have a proper entry point from HAL, for | ||
168 | * now we rely on kexec-style entry from BML | ||
169 | */ | ||
170 | |||
171 | #ifdef CONFIG_PPC_RTAS | ||
172 | /* Non-lpar has additional take/give timebase */ | ||
173 | if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { | ||
174 | smp_ops->give_timebase = rtas_give_timebase; | ||
175 | smp_ops->take_timebase = rtas_take_timebase; | ||
176 | } | ||
177 | #endif /* CONFIG_PPC_RTAS */ | ||
178 | |||
179 | #ifdef CONFIG_HOTPLUG_CPU | ||
180 | ppc_md.cpu_die = pnv_smp_cpu_kill_self; | ||
181 | #endif | ||
182 | } | ||