]>
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> | |
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 | ||
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 | ||
344eb010 BH |
65 | #ifdef CONFIG_HOTPLUG_CPU |
66 | ||
67 | static int pnv_smp_cpu_disable(void) | |
68 | { | |
69 | int cpu = smp_processor_id(); | |
70 | ||
71 | /* This is identical to pSeries... might consolidate by | |
72 | * moving migrate_irqs_away to a ppc_md with default to | |
73 | * the generic fixup_irqs. --BenH. | |
74 | */ | |
75 | set_cpu_online(cpu, false); | |
76 | vdso_data->processorCount--; | |
77 | if (cpu == boot_cpuid) | |
78 | boot_cpuid = cpumask_any(cpu_online_mask); | |
79 | xics_migrate_irqs_away(); | |
80 | return 0; | |
81 | } | |
82 | ||
83 | static void pnv_smp_cpu_kill_self(void) | |
84 | { | |
85 | unsigned int cpu; | |
86 | ||
87 | /* If powersave_nap is enabled, use NAP mode, else just | |
88 | * spin aimlessly | |
89 | */ | |
90 | if (!powersave_nap) { | |
91 | generic_mach_cpu_die(); | |
92 | return; | |
93 | } | |
94 | ||
95 | /* Standard hot unplug procedure */ | |
96 | local_irq_disable(); | |
97 | idle_task_exit(); | |
98 | current->active_mm = NULL; /* for sanity */ | |
99 | cpu = smp_processor_id(); | |
100 | DBG("CPU%d offline\n", cpu); | |
101 | generic_set_cpu_dead(cpu); | |
102 | smp_wmb(); | |
103 | ||
104 | /* We don't want to take decrementer interrupts while we are offline, | |
105 | * so clear LPCR:PECE1. We keep PECE2 enabled. | |
106 | */ | |
107 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); | |
108 | while (!generic_check_cpu_restart(cpu)) { | |
109 | power7_idle(); | |
110 | if (!generic_check_cpu_restart(cpu)) { | |
111 | DBG("CPU%d Unexpected exit while offline !\n", cpu); | |
112 | /* We may be getting an IPI, so we re-enable | |
113 | * interrupts to process it, it will be ignored | |
114 | * since we aren't online (hopefully) | |
115 | */ | |
116 | local_irq_enable(); | |
117 | local_irq_disable(); | |
118 | } | |
119 | } | |
120 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); | |
121 | DBG("CPU%d coming online...\n", cpu); | |
122 | } | |
123 | ||
124 | #endif /* CONFIG_HOTPLUG_CPU */ | |
125 | ||
55190f88 BH |
126 | static struct smp_ops_t pnv_smp_ops = { |
127 | .message_pass = smp_muxed_ipi_message_pass, | |
128 | .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ | |
129 | .probe = xics_smp_probe, | |
130 | .kick_cpu = smp_generic_kick_cpu, | |
131 | .setup_cpu = pnv_smp_setup_cpu, | |
132 | .cpu_bootable = pnv_smp_cpu_bootable, | |
344eb010 BH |
133 | #ifdef CONFIG_HOTPLUG_CPU |
134 | .cpu_disable = pnv_smp_cpu_disable, | |
135 | .cpu_die = generic_cpu_die, | |
136 | #endif /* CONFIG_HOTPLUG_CPU */ | |
55190f88 BH |
137 | }; |
138 | ||
139 | /* This is called very early during platform setup_arch */ | |
140 | void __init pnv_smp_init(void) | |
141 | { | |
142 | smp_ops = &pnv_smp_ops; | |
143 | ||
144 | /* XXX We don't yet have a proper entry point from HAL, for | |
145 | * now we rely on kexec-style entry from BML | |
146 | */ | |
147 | ||
148 | #ifdef CONFIG_PPC_RTAS | |
149 | /* Non-lpar has additional take/give timebase */ | |
150 | if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { | |
151 | smp_ops->give_timebase = rtas_give_timebase; | |
152 | smp_ops->take_timebase = rtas_take_timebase; | |
153 | } | |
154 | #endif /* CONFIG_PPC_RTAS */ | |
344eb010 BH |
155 | |
156 | #ifdef CONFIG_HOTPLUG_CPU | |
157 | ppc_md.cpu_die = pnv_smp_cpu_kill_self; | |
158 | #endif | |
55190f88 | 159 | } |