155190f88SBenjamin Herrenschmidt /* 255190f88SBenjamin Herrenschmidt * SMP support for PowerNV machines. 355190f88SBenjamin Herrenschmidt * 455190f88SBenjamin Herrenschmidt * Copyright 2011 IBM Corp. 555190f88SBenjamin Herrenschmidt * 655190f88SBenjamin Herrenschmidt * This program is free software; you can redistribute it and/or 755190f88SBenjamin Herrenschmidt * modify it under the terms of the GNU General Public License 855190f88SBenjamin Herrenschmidt * as published by the Free Software Foundation; either version 955190f88SBenjamin Herrenschmidt * 2 of the License, or (at your option) any later version. 1055190f88SBenjamin Herrenschmidt */ 1155190f88SBenjamin Herrenschmidt 1255190f88SBenjamin Herrenschmidt #include <linux/kernel.h> 1355190f88SBenjamin Herrenschmidt #include <linux/module.h> 1455190f88SBenjamin Herrenschmidt #include <linux/sched.h> 1555190f88SBenjamin Herrenschmidt #include <linux/smp.h> 1655190f88SBenjamin Herrenschmidt #include <linux/interrupt.h> 1755190f88SBenjamin Herrenschmidt #include <linux/delay.h> 1855190f88SBenjamin Herrenschmidt #include <linux/init.h> 1955190f88SBenjamin Herrenschmidt #include <linux/spinlock.h> 2055190f88SBenjamin Herrenschmidt #include <linux/cpu.h> 2155190f88SBenjamin Herrenschmidt 2255190f88SBenjamin Herrenschmidt #include <asm/irq.h> 2355190f88SBenjamin Herrenschmidt #include <asm/smp.h> 2455190f88SBenjamin Herrenschmidt #include <asm/paca.h> 2555190f88SBenjamin Herrenschmidt #include <asm/machdep.h> 2655190f88SBenjamin Herrenschmidt #include <asm/cputable.h> 2755190f88SBenjamin Herrenschmidt #include <asm/firmware.h> 2855190f88SBenjamin Herrenschmidt #include <asm/system.h> 2955190f88SBenjamin Herrenschmidt #include <asm/rtas.h> 3055190f88SBenjamin Herrenschmidt #include <asm/vdso_datapage.h> 3155190f88SBenjamin Herrenschmidt #include <asm/cputhreads.h> 3255190f88SBenjamin Herrenschmidt #include <asm/xics.h> 3314a43e69SBenjamin Herrenschmidt #include <asm/opal.h> 3455190f88SBenjamin Herrenschmidt 3555190f88SBenjamin Herrenschmidt #include "powernv.h" 3655190f88SBenjamin Herrenschmidt 37344eb010SBenjamin Herrenschmidt #ifdef DEBUG 38344eb010SBenjamin Herrenschmidt #include <asm/udbg.h> 39344eb010SBenjamin Herrenschmidt #define DBG(fmt...) udbg_printf(fmt) 40344eb010SBenjamin Herrenschmidt #else 41344eb010SBenjamin Herrenschmidt #define DBG(fmt...) 42344eb010SBenjamin Herrenschmidt #endif 43344eb010SBenjamin Herrenschmidt 44344eb010SBenjamin Herrenschmidt static void __cpuinit pnv_smp_setup_cpu(int cpu) 4555190f88SBenjamin Herrenschmidt { 4655190f88SBenjamin Herrenschmidt if (cpu != boot_cpuid) 4755190f88SBenjamin Herrenschmidt xics_setup_cpu(); 4855190f88SBenjamin Herrenschmidt } 4955190f88SBenjamin Herrenschmidt 5055190f88SBenjamin Herrenschmidt static int pnv_smp_cpu_bootable(unsigned int nr) 5155190f88SBenjamin Herrenschmidt { 5255190f88SBenjamin Herrenschmidt /* Special case - we inhibit secondary thread startup 5355190f88SBenjamin Herrenschmidt * during boot if the user requests it. 5455190f88SBenjamin Herrenschmidt */ 5555190f88SBenjamin Herrenschmidt if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) { 5655190f88SBenjamin Herrenschmidt if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0) 5755190f88SBenjamin Herrenschmidt return 0; 5855190f88SBenjamin Herrenschmidt if (smt_enabled_at_boot 5955190f88SBenjamin Herrenschmidt && cpu_thread_in_core(nr) >= smt_enabled_at_boot) 6055190f88SBenjamin Herrenschmidt return 0; 6155190f88SBenjamin Herrenschmidt } 6255190f88SBenjamin Herrenschmidt 6355190f88SBenjamin Herrenschmidt return 1; 6455190f88SBenjamin Herrenschmidt } 6555190f88SBenjamin Herrenschmidt 6614a43e69SBenjamin Herrenschmidt int __devinit pnv_smp_kick_cpu(int nr) 6714a43e69SBenjamin Herrenschmidt { 6814a43e69SBenjamin Herrenschmidt unsigned int pcpu = get_hard_smp_processor_id(nr); 6914a43e69SBenjamin Herrenschmidt unsigned long start_here = __pa(*((unsigned long *) 7014a43e69SBenjamin Herrenschmidt generic_secondary_smp_init)); 7114a43e69SBenjamin Herrenschmidt long rc; 7214a43e69SBenjamin Herrenschmidt 7314a43e69SBenjamin Herrenschmidt BUG_ON(nr < 0 || nr >= NR_CPUS); 7414a43e69SBenjamin Herrenschmidt 7514a43e69SBenjamin Herrenschmidt /* On OPAL v2 the CPU are still spinning inside OPAL itself, 7614a43e69SBenjamin Herrenschmidt * get them back now 7714a43e69SBenjamin Herrenschmidt */ 78cba313daSPaul Mackerras if (!paca[nr].cpu_start && firmware_has_feature(FW_FEATURE_OPALv2)) { 7914a43e69SBenjamin Herrenschmidt pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); 8014a43e69SBenjamin Herrenschmidt rc = opal_start_cpu(pcpu, start_here); 8114a43e69SBenjamin Herrenschmidt if (rc != OPAL_SUCCESS) 8214a43e69SBenjamin Herrenschmidt pr_warn("OPAL Error %ld starting CPU %d\n", 8314a43e69SBenjamin Herrenschmidt rc, nr); 8414a43e69SBenjamin Herrenschmidt } 8514a43e69SBenjamin Herrenschmidt return smp_generic_kick_cpu(nr); 8614a43e69SBenjamin Herrenschmidt } 8714a43e69SBenjamin Herrenschmidt 88344eb010SBenjamin Herrenschmidt #ifdef CONFIG_HOTPLUG_CPU 89344eb010SBenjamin Herrenschmidt 90344eb010SBenjamin Herrenschmidt static int pnv_smp_cpu_disable(void) 91344eb010SBenjamin Herrenschmidt { 92344eb010SBenjamin Herrenschmidt int cpu = smp_processor_id(); 93344eb010SBenjamin Herrenschmidt 94344eb010SBenjamin Herrenschmidt /* This is identical to pSeries... might consolidate by 95344eb010SBenjamin Herrenschmidt * moving migrate_irqs_away to a ppc_md with default to 96344eb010SBenjamin Herrenschmidt * the generic fixup_irqs. --BenH. 97344eb010SBenjamin Herrenschmidt */ 98344eb010SBenjamin Herrenschmidt set_cpu_online(cpu, false); 99344eb010SBenjamin Herrenschmidt vdso_data->processorCount--; 100344eb010SBenjamin Herrenschmidt if (cpu == boot_cpuid) 101344eb010SBenjamin Herrenschmidt boot_cpuid = cpumask_any(cpu_online_mask); 102344eb010SBenjamin Herrenschmidt xics_migrate_irqs_away(); 103344eb010SBenjamin Herrenschmidt return 0; 104344eb010SBenjamin Herrenschmidt } 105344eb010SBenjamin Herrenschmidt 106344eb010SBenjamin Herrenschmidt static void pnv_smp_cpu_kill_self(void) 107344eb010SBenjamin Herrenschmidt { 108344eb010SBenjamin Herrenschmidt unsigned int cpu; 109344eb010SBenjamin Herrenschmidt 110344eb010SBenjamin Herrenschmidt /* If powersave_nap is enabled, use NAP mode, else just 111344eb010SBenjamin Herrenschmidt * spin aimlessly 112344eb010SBenjamin Herrenschmidt */ 113344eb010SBenjamin Herrenschmidt if (!powersave_nap) { 114344eb010SBenjamin Herrenschmidt generic_mach_cpu_die(); 115344eb010SBenjamin Herrenschmidt return; 116344eb010SBenjamin Herrenschmidt } 117344eb010SBenjamin Herrenschmidt 118344eb010SBenjamin Herrenschmidt /* Standard hot unplug procedure */ 119344eb010SBenjamin Herrenschmidt local_irq_disable(); 120344eb010SBenjamin Herrenschmidt idle_task_exit(); 121344eb010SBenjamin Herrenschmidt current->active_mm = NULL; /* for sanity */ 122344eb010SBenjamin Herrenschmidt cpu = smp_processor_id(); 123344eb010SBenjamin Herrenschmidt DBG("CPU%d offline\n", cpu); 124344eb010SBenjamin Herrenschmidt generic_set_cpu_dead(cpu); 125344eb010SBenjamin Herrenschmidt smp_wmb(); 126344eb010SBenjamin Herrenschmidt 127344eb010SBenjamin Herrenschmidt /* We don't want to take decrementer interrupts while we are offline, 128344eb010SBenjamin Herrenschmidt * so clear LPCR:PECE1. We keep PECE2 enabled. 129344eb010SBenjamin Herrenschmidt */ 130344eb010SBenjamin Herrenschmidt mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); 131344eb010SBenjamin Herrenschmidt while (!generic_check_cpu_restart(cpu)) { 132344eb010SBenjamin Herrenschmidt power7_idle(); 133344eb010SBenjamin Herrenschmidt if (!generic_check_cpu_restart(cpu)) { 134344eb010SBenjamin Herrenschmidt DBG("CPU%d Unexpected exit while offline !\n", cpu); 135344eb010SBenjamin Herrenschmidt /* We may be getting an IPI, so we re-enable 136344eb010SBenjamin Herrenschmidt * interrupts to process it, it will be ignored 137344eb010SBenjamin Herrenschmidt * since we aren't online (hopefully) 138344eb010SBenjamin Herrenschmidt */ 139344eb010SBenjamin Herrenschmidt local_irq_enable(); 140344eb010SBenjamin Herrenschmidt local_irq_disable(); 141344eb010SBenjamin Herrenschmidt } 142344eb010SBenjamin Herrenschmidt } 143344eb010SBenjamin Herrenschmidt mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); 144344eb010SBenjamin Herrenschmidt DBG("CPU%d coming online...\n", cpu); 145344eb010SBenjamin Herrenschmidt } 146344eb010SBenjamin Herrenschmidt 147344eb010SBenjamin Herrenschmidt #endif /* CONFIG_HOTPLUG_CPU */ 148344eb010SBenjamin Herrenschmidt 14955190f88SBenjamin Herrenschmidt static struct smp_ops_t pnv_smp_ops = { 15055190f88SBenjamin Herrenschmidt .message_pass = smp_muxed_ipi_message_pass, 15155190f88SBenjamin Herrenschmidt .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ 15255190f88SBenjamin Herrenschmidt .probe = xics_smp_probe, 15314a43e69SBenjamin Herrenschmidt .kick_cpu = pnv_smp_kick_cpu, 15455190f88SBenjamin Herrenschmidt .setup_cpu = pnv_smp_setup_cpu, 15555190f88SBenjamin Herrenschmidt .cpu_bootable = pnv_smp_cpu_bootable, 156344eb010SBenjamin Herrenschmidt #ifdef CONFIG_HOTPLUG_CPU 157344eb010SBenjamin Herrenschmidt .cpu_disable = pnv_smp_cpu_disable, 158344eb010SBenjamin Herrenschmidt .cpu_die = generic_cpu_die, 159344eb010SBenjamin Herrenschmidt #endif /* CONFIG_HOTPLUG_CPU */ 16055190f88SBenjamin Herrenschmidt }; 16155190f88SBenjamin Herrenschmidt 16255190f88SBenjamin Herrenschmidt /* This is called very early during platform setup_arch */ 16355190f88SBenjamin Herrenschmidt void __init pnv_smp_init(void) 16455190f88SBenjamin Herrenschmidt { 16555190f88SBenjamin Herrenschmidt smp_ops = &pnv_smp_ops; 16655190f88SBenjamin Herrenschmidt 16755190f88SBenjamin Herrenschmidt /* XXX We don't yet have a proper entry point from HAL, for 16855190f88SBenjamin Herrenschmidt * now we rely on kexec-style entry from BML 16955190f88SBenjamin Herrenschmidt */ 17055190f88SBenjamin Herrenschmidt 17155190f88SBenjamin Herrenschmidt #ifdef CONFIG_PPC_RTAS 17255190f88SBenjamin Herrenschmidt /* Non-lpar has additional take/give timebase */ 17355190f88SBenjamin Herrenschmidt if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { 17455190f88SBenjamin Herrenschmidt smp_ops->give_timebase = rtas_give_timebase; 17555190f88SBenjamin Herrenschmidt smp_ops->take_timebase = rtas_take_timebase; 17655190f88SBenjamin Herrenschmidt } 17755190f88SBenjamin Herrenschmidt #endif /* CONFIG_PPC_RTAS */ 178344eb010SBenjamin Herrenschmidt 179344eb010SBenjamin Herrenschmidt #ifdef CONFIG_HOTPLUG_CPU 180344eb010SBenjamin Herrenschmidt ppc_md.cpu_die = pnv_smp_cpu_kill_self; 181344eb010SBenjamin Herrenschmidt #endif 18255190f88SBenjamin Herrenschmidt } 183