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> 3355190f88SBenjamin Herrenschmidt 3455190f88SBenjamin Herrenschmidt #include "powernv.h" 3555190f88SBenjamin Herrenschmidt 36344eb010SBenjamin Herrenschmidt #ifdef DEBUG 37344eb010SBenjamin Herrenschmidt #include <asm/udbg.h> 38344eb010SBenjamin Herrenschmidt #define DBG(fmt...) udbg_printf(fmt) 39344eb010SBenjamin Herrenschmidt #else 40344eb010SBenjamin Herrenschmidt #define DBG(fmt...) 41344eb010SBenjamin Herrenschmidt #endif 42344eb010SBenjamin Herrenschmidt 43344eb010SBenjamin Herrenschmidt static void __cpuinit pnv_smp_setup_cpu(int cpu) 4455190f88SBenjamin Herrenschmidt { 4555190f88SBenjamin Herrenschmidt if (cpu != boot_cpuid) 4655190f88SBenjamin Herrenschmidt xics_setup_cpu(); 4755190f88SBenjamin Herrenschmidt } 4855190f88SBenjamin Herrenschmidt 4955190f88SBenjamin Herrenschmidt static int pnv_smp_cpu_bootable(unsigned int nr) 5055190f88SBenjamin Herrenschmidt { 5155190f88SBenjamin Herrenschmidt /* Special case - we inhibit secondary thread startup 5255190f88SBenjamin Herrenschmidt * during boot if the user requests it. 5355190f88SBenjamin Herrenschmidt */ 5455190f88SBenjamin Herrenschmidt if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) { 5555190f88SBenjamin Herrenschmidt if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0) 5655190f88SBenjamin Herrenschmidt return 0; 5755190f88SBenjamin Herrenschmidt if (smt_enabled_at_boot 5855190f88SBenjamin Herrenschmidt && cpu_thread_in_core(nr) >= smt_enabled_at_boot) 5955190f88SBenjamin Herrenschmidt return 0; 6055190f88SBenjamin Herrenschmidt } 6155190f88SBenjamin Herrenschmidt 6255190f88SBenjamin Herrenschmidt return 1; 6355190f88SBenjamin Herrenschmidt } 6455190f88SBenjamin Herrenschmidt 65344eb010SBenjamin Herrenschmidt #ifdef CONFIG_HOTPLUG_CPU 66344eb010SBenjamin Herrenschmidt 67344eb010SBenjamin Herrenschmidt static int pnv_smp_cpu_disable(void) 68344eb010SBenjamin Herrenschmidt { 69344eb010SBenjamin Herrenschmidt int cpu = smp_processor_id(); 70344eb010SBenjamin Herrenschmidt 71344eb010SBenjamin Herrenschmidt /* This is identical to pSeries... might consolidate by 72344eb010SBenjamin Herrenschmidt * moving migrate_irqs_away to a ppc_md with default to 73344eb010SBenjamin Herrenschmidt * the generic fixup_irqs. --BenH. 74344eb010SBenjamin Herrenschmidt */ 75344eb010SBenjamin Herrenschmidt set_cpu_online(cpu, false); 76344eb010SBenjamin Herrenschmidt vdso_data->processorCount--; 77344eb010SBenjamin Herrenschmidt if (cpu == boot_cpuid) 78344eb010SBenjamin Herrenschmidt boot_cpuid = cpumask_any(cpu_online_mask); 79344eb010SBenjamin Herrenschmidt xics_migrate_irqs_away(); 80344eb010SBenjamin Herrenschmidt return 0; 81344eb010SBenjamin Herrenschmidt } 82344eb010SBenjamin Herrenschmidt 83344eb010SBenjamin Herrenschmidt static void pnv_smp_cpu_kill_self(void) 84344eb010SBenjamin Herrenschmidt { 85344eb010SBenjamin Herrenschmidt unsigned int cpu; 86344eb010SBenjamin Herrenschmidt 87344eb010SBenjamin Herrenschmidt /* If powersave_nap is enabled, use NAP mode, else just 88344eb010SBenjamin Herrenschmidt * spin aimlessly 89344eb010SBenjamin Herrenschmidt */ 90344eb010SBenjamin Herrenschmidt if (!powersave_nap) { 91344eb010SBenjamin Herrenschmidt generic_mach_cpu_die(); 92344eb010SBenjamin Herrenschmidt return; 93344eb010SBenjamin Herrenschmidt } 94344eb010SBenjamin Herrenschmidt 95344eb010SBenjamin Herrenschmidt /* Standard hot unplug procedure */ 96344eb010SBenjamin Herrenschmidt local_irq_disable(); 97344eb010SBenjamin Herrenschmidt idle_task_exit(); 98344eb010SBenjamin Herrenschmidt current->active_mm = NULL; /* for sanity */ 99344eb010SBenjamin Herrenschmidt cpu = smp_processor_id(); 100344eb010SBenjamin Herrenschmidt DBG("CPU%d offline\n", cpu); 101344eb010SBenjamin Herrenschmidt generic_set_cpu_dead(cpu); 102344eb010SBenjamin Herrenschmidt smp_wmb(); 103344eb010SBenjamin Herrenschmidt 104344eb010SBenjamin Herrenschmidt /* We don't want to take decrementer interrupts while we are offline, 105344eb010SBenjamin Herrenschmidt * so clear LPCR:PECE1. We keep PECE2 enabled. 106344eb010SBenjamin Herrenschmidt */ 107344eb010SBenjamin Herrenschmidt mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); 108344eb010SBenjamin Herrenschmidt while (!generic_check_cpu_restart(cpu)) { 109344eb010SBenjamin Herrenschmidt power7_idle(); 110344eb010SBenjamin Herrenschmidt if (!generic_check_cpu_restart(cpu)) { 111344eb010SBenjamin Herrenschmidt DBG("CPU%d Unexpected exit while offline !\n", cpu); 112344eb010SBenjamin Herrenschmidt /* We may be getting an IPI, so we re-enable 113344eb010SBenjamin Herrenschmidt * interrupts to process it, it will be ignored 114344eb010SBenjamin Herrenschmidt * since we aren't online (hopefully) 115344eb010SBenjamin Herrenschmidt */ 116344eb010SBenjamin Herrenschmidt local_irq_enable(); 117344eb010SBenjamin Herrenschmidt local_irq_disable(); 118344eb010SBenjamin Herrenschmidt } 119344eb010SBenjamin Herrenschmidt } 120344eb010SBenjamin Herrenschmidt mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); 121344eb010SBenjamin Herrenschmidt DBG("CPU%d coming online...\n", cpu); 122344eb010SBenjamin Herrenschmidt } 123344eb010SBenjamin Herrenschmidt 124344eb010SBenjamin Herrenschmidt #endif /* CONFIG_HOTPLUG_CPU */ 125344eb010SBenjamin Herrenschmidt 12655190f88SBenjamin Herrenschmidt static struct smp_ops_t pnv_smp_ops = { 12755190f88SBenjamin Herrenschmidt .message_pass = smp_muxed_ipi_message_pass, 12855190f88SBenjamin Herrenschmidt .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ 12955190f88SBenjamin Herrenschmidt .probe = xics_smp_probe, 13055190f88SBenjamin Herrenschmidt .kick_cpu = smp_generic_kick_cpu, 13155190f88SBenjamin Herrenschmidt .setup_cpu = pnv_smp_setup_cpu, 13255190f88SBenjamin Herrenschmidt .cpu_bootable = pnv_smp_cpu_bootable, 133344eb010SBenjamin Herrenschmidt #ifdef CONFIG_HOTPLUG_CPU 134344eb010SBenjamin Herrenschmidt .cpu_disable = pnv_smp_cpu_disable, 135344eb010SBenjamin Herrenschmidt .cpu_die = generic_cpu_die, 136344eb010SBenjamin Herrenschmidt #endif /* CONFIG_HOTPLUG_CPU */ 13755190f88SBenjamin Herrenschmidt }; 13855190f88SBenjamin Herrenschmidt 13955190f88SBenjamin Herrenschmidt /* This is called very early during platform setup_arch */ 14055190f88SBenjamin Herrenschmidt void __init pnv_smp_init(void) 14155190f88SBenjamin Herrenschmidt { 14255190f88SBenjamin Herrenschmidt smp_ops = &pnv_smp_ops; 14355190f88SBenjamin Herrenschmidt 14455190f88SBenjamin Herrenschmidt /* XXX We don't yet have a proper entry point from HAL, for 14555190f88SBenjamin Herrenschmidt * now we rely on kexec-style entry from BML 14655190f88SBenjamin Herrenschmidt */ 14755190f88SBenjamin Herrenschmidt 14855190f88SBenjamin Herrenschmidt #ifdef CONFIG_PPC_RTAS 14955190f88SBenjamin Herrenschmidt /* Non-lpar has additional take/give timebase */ 15055190f88SBenjamin Herrenschmidt if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { 15155190f88SBenjamin Herrenschmidt smp_ops->give_timebase = rtas_give_timebase; 15255190f88SBenjamin Herrenschmidt smp_ops->take_timebase = rtas_take_timebase; 15355190f88SBenjamin Herrenschmidt } 15455190f88SBenjamin Herrenschmidt #endif /* CONFIG_PPC_RTAS */ 155344eb010SBenjamin Herrenschmidt 156344eb010SBenjamin Herrenschmidt #ifdef CONFIG_HOTPLUG_CPU 157344eb010SBenjamin Herrenschmidt ppc_md.cpu_die = pnv_smp_cpu_kill_self; 158344eb010SBenjamin Herrenschmidt #endif 15955190f88SBenjamin Herrenschmidt } 160