Commit | Line | Data |
---|---|---|
55190f88 BH |
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> | |
55190f88 BH |
28 | #include <asm/rtas.h> |
29 | #include <asm/vdso_datapage.h> | |
30 | #include <asm/cputhreads.h> | |
31 | #include <asm/xics.h> | |
14a43e69 | 32 | #include <asm/opal.h> |
55190f88 BH |
33 | |
34 | #include "powernv.h" | |
35 | ||
344eb010 BH |
36 | #ifdef DEBUG |
37 | #include <asm/udbg.h> | |
38 | #define DBG(fmt...) udbg_printf(fmt) | |
39 | #else | |
40 | #define DBG(fmt...) | |
41 | #endif | |
42 | ||
43 | static void __cpuinit pnv_smp_setup_cpu(int cpu) | |
55190f88 BH |
44 | { |
45 | if (cpu != boot_cpuid) | |
46 | xics_setup_cpu(); | |
47 | } | |
48 | ||
49 | static int pnv_smp_cpu_bootable(unsigned int nr) | |
50 | { | |
51 | /* Special case - we inhibit secondary thread startup | |
52 | * during boot if the user requests it. | |
53 | */ | |
54 | if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) { | |
55 | if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0) | |
56 | return 0; | |
57 | if (smt_enabled_at_boot | |
58 | && cpu_thread_in_core(nr) >= smt_enabled_at_boot) | |
59 | return 0; | |
60 | } | |
61 | ||
62 | return 1; | |
63 | } | |
64 | ||
cad5cef6 | 65 | int pnv_smp_kick_cpu(int nr) |
14a43e69 BH |
66 | { |
67 | unsigned int pcpu = get_hard_smp_processor_id(nr); | |
68 | unsigned long start_here = __pa(*((unsigned long *) | |
69 | generic_secondary_smp_init)); | |
70 | long rc; | |
71 | ||
72 | BUG_ON(nr < 0 || nr >= NR_CPUS); | |
73 | ||
74 | /* On OPAL v2 the CPU are still spinning inside OPAL itself, | |
75 | * get them back now | |
76 | */ | |
cba313da | 77 | if (!paca[nr].cpu_start && firmware_has_feature(FW_FEATURE_OPALv2)) { |
14a43e69 BH |
78 | pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); |
79 | rc = opal_start_cpu(pcpu, start_here); | |
80 | if (rc != OPAL_SUCCESS) | |
81 | pr_warn("OPAL Error %ld starting CPU %d\n", | |
82 | rc, nr); | |
83 | } | |
84 | return smp_generic_kick_cpu(nr); | |
85 | } | |
86 | ||
344eb010 BH |
87 | #ifdef CONFIG_HOTPLUG_CPU |
88 | ||
89 | static int pnv_smp_cpu_disable(void) | |
90 | { | |
91 | int cpu = smp_processor_id(); | |
92 | ||
93 | /* This is identical to pSeries... might consolidate by | |
94 | * moving migrate_irqs_away to a ppc_md with default to | |
95 | * the generic fixup_irqs. --BenH. | |
96 | */ | |
97 | set_cpu_online(cpu, false); | |
98 | vdso_data->processorCount--; | |
99 | if (cpu == boot_cpuid) | |
100 | boot_cpuid = cpumask_any(cpu_online_mask); | |
101 | xics_migrate_irqs_away(); | |
102 | return 0; | |
103 | } | |
104 | ||
105 | static void pnv_smp_cpu_kill_self(void) | |
106 | { | |
107 | unsigned int cpu; | |
108 | ||
344eb010 BH |
109 | /* Standard hot unplug procedure */ |
110 | local_irq_disable(); | |
111 | idle_task_exit(); | |
112 | current->active_mm = NULL; /* for sanity */ | |
113 | cpu = smp_processor_id(); | |
114 | DBG("CPU%d offline\n", cpu); | |
115 | generic_set_cpu_dead(cpu); | |
116 | smp_wmb(); | |
117 | ||
118 | /* We don't want to take decrementer interrupts while we are offline, | |
119 | * so clear LPCR:PECE1. We keep PECE2 enabled. | |
120 | */ | |
121 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); | |
122 | while (!generic_check_cpu_restart(cpu)) { | |
375f561a | 123 | power7_nap(); |
344eb010 BH |
124 | if (!generic_check_cpu_restart(cpu)) { |
125 | DBG("CPU%d Unexpected exit while offline !\n", cpu); | |
126 | /* We may be getting an IPI, so we re-enable | |
127 | * interrupts to process it, it will be ignored | |
128 | * since we aren't online (hopefully) | |
129 | */ | |
130 | local_irq_enable(); | |
131 | local_irq_disable(); | |
132 | } | |
133 | } | |
134 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); | |
135 | DBG("CPU%d coming online...\n", cpu); | |
136 | } | |
137 | ||
138 | #endif /* CONFIG_HOTPLUG_CPU */ | |
139 | ||
55190f88 BH |
140 | static struct smp_ops_t pnv_smp_ops = { |
141 | .message_pass = smp_muxed_ipi_message_pass, | |
142 | .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ | |
143 | .probe = xics_smp_probe, | |
14a43e69 | 144 | .kick_cpu = pnv_smp_kick_cpu, |
55190f88 BH |
145 | .setup_cpu = pnv_smp_setup_cpu, |
146 | .cpu_bootable = pnv_smp_cpu_bootable, | |
344eb010 BH |
147 | #ifdef CONFIG_HOTPLUG_CPU |
148 | .cpu_disable = pnv_smp_cpu_disable, | |
149 | .cpu_die = generic_cpu_die, | |
150 | #endif /* CONFIG_HOTPLUG_CPU */ | |
55190f88 BH |
151 | }; |
152 | ||
153 | /* This is called very early during platform setup_arch */ | |
154 | void __init pnv_smp_init(void) | |
155 | { | |
156 | smp_ops = &pnv_smp_ops; | |
157 | ||
158 | /* XXX We don't yet have a proper entry point from HAL, for | |
159 | * now we rely on kexec-style entry from BML | |
160 | */ | |
161 | ||
162 | #ifdef CONFIG_PPC_RTAS | |
163 | /* Non-lpar has additional take/give timebase */ | |
164 | if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { | |
165 | smp_ops->give_timebase = rtas_give_timebase; | |
166 | smp_ops->take_timebase = rtas_take_timebase; | |
167 | } | |
168 | #endif /* CONFIG_PPC_RTAS */ | |
344eb010 BH |
169 | |
170 | #ifdef CONFIG_HOTPLUG_CPU | |
171 | ppc_md.cpu_die = pnv_smp_cpu_kill_self; | |
172 | #endif | |
55190f88 | 173 | } |