119fe0475SArnd Bergmann /* 219fe0475SArnd Bergmann * SMP support for BPA machines. 319fe0475SArnd Bergmann * 419fe0475SArnd Bergmann * Dave Engebretsen, Peter Bergner, and 519fe0475SArnd Bergmann * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com 619fe0475SArnd Bergmann * 719fe0475SArnd Bergmann * Plus various changes from other IBM teams... 819fe0475SArnd Bergmann * 919fe0475SArnd Bergmann * This program is free software; you can redistribute it and/or 1019fe0475SArnd Bergmann * modify it under the terms of the GNU General Public License 1119fe0475SArnd Bergmann * as published by the Free Software Foundation; either version 1219fe0475SArnd Bergmann * 2 of the License, or (at your option) any later version. 1319fe0475SArnd Bergmann */ 1419fe0475SArnd Bergmann 1519fe0475SArnd Bergmann #undef DEBUG 1619fe0475SArnd Bergmann 1719fe0475SArnd Bergmann #include <linux/kernel.h> 1819fe0475SArnd Bergmann #include <linux/sched.h> 1919fe0475SArnd Bergmann #include <linux/smp.h> 2019fe0475SArnd Bergmann #include <linux/interrupt.h> 2119fe0475SArnd Bergmann #include <linux/delay.h> 2219fe0475SArnd Bergmann #include <linux/init.h> 2319fe0475SArnd Bergmann #include <linux/spinlock.h> 2419fe0475SArnd Bergmann #include <linux/cache.h> 2519fe0475SArnd Bergmann #include <linux/err.h> 26edbaa603SKay Sievers #include <linux/device.h> 2719fe0475SArnd Bergmann #include <linux/cpu.h> 2819fe0475SArnd Bergmann 2919fe0475SArnd Bergmann #include <asm/ptrace.h> 3060063497SArun Sharma #include <linux/atomic.h> 3119fe0475SArnd Bergmann #include <asm/irq.h> 3219fe0475SArnd Bergmann #include <asm/page.h> 3319fe0475SArnd Bergmann #include <asm/pgtable.h> 3419fe0475SArnd Bergmann #include <asm/io.h> 3519fe0475SArnd Bergmann #include <asm/prom.h> 3619fe0475SArnd Bergmann #include <asm/smp.h> 3719fe0475SArnd Bergmann #include <asm/paca.h> 3819fe0475SArnd Bergmann #include <asm/machdep.h> 3919fe0475SArnd Bergmann #include <asm/cputable.h> 4019fe0475SArnd Bergmann #include <asm/firmware.h> 4119fe0475SArnd Bergmann #include <asm/rtas.h> 428d089085SBenjamin Herrenschmidt #include <asm/cputhreads.h> 432751b628SAnton Blanchard #include <asm/code-patching.h> 4419fe0475SArnd Bergmann 4519fe0475SArnd Bergmann #include "interrupt.h" 46aa39be09Swill schmidt #include <asm/udbg.h> 4719fe0475SArnd Bergmann 4819fe0475SArnd Bergmann #ifdef DEBUG 4919fe0475SArnd Bergmann #define DBG(fmt...) udbg_printf(fmt) 5019fe0475SArnd Bergmann #else 5119fe0475SArnd Bergmann #define DBG(fmt...) 5219fe0475SArnd Bergmann #endif 5319fe0475SArnd Bergmann 5419fe0475SArnd Bergmann /* 556a75a6b8SMilton Miller * The Primary thread of each non-boot processor was started from the OF client 566a75a6b8SMilton Miller * interface by prom_hold_cpus and is spinning on secondary_hold_spinloop. 5719fe0475SArnd Bergmann */ 5819fe0475SArnd Bergmann static cpumask_t of_spin_map; 5919fe0475SArnd Bergmann 6019fe0475SArnd Bergmann /** 6119fe0475SArnd Bergmann * smp_startup_cpu() - start the given cpu 6219fe0475SArnd Bergmann * 6319fe0475SArnd Bergmann * At boot time, there is nothing to do for primary threads which were 6419fe0475SArnd Bergmann * started from Open Firmware. For anything else, call RTAS with the 6519fe0475SArnd Bergmann * appropriate start location. 6619fe0475SArnd Bergmann * 6719fe0475SArnd Bergmann * Returns: 6819fe0475SArnd Bergmann * 0 - failure 6919fe0475SArnd Bergmann * 1 - success 7019fe0475SArnd Bergmann */ 71cad5cef6SGreg Kroah-Hartman static inline int smp_startup_cpu(unsigned int lcpu) 7219fe0475SArnd Bergmann { 7319fe0475SArnd Bergmann int status; 742751b628SAnton Blanchard unsigned long start_here = 752751b628SAnton Blanchard __pa(ppc_function_entry(generic_secondary_smp_init)); 7619fe0475SArnd Bergmann unsigned int pcpu; 7719fe0475SArnd Bergmann int start_cpu; 7819fe0475SArnd Bergmann 79104699c0SKOSAKI Motohiro if (cpumask_test_cpu(lcpu, &of_spin_map)) 8019fe0475SArnd Bergmann /* Already started by OF and sitting in spin loop */ 8119fe0475SArnd Bergmann return 1; 8219fe0475SArnd Bergmann 8319fe0475SArnd Bergmann pcpu = get_hard_smp_processor_id(lcpu); 8419fe0475SArnd Bergmann 8519fe0475SArnd Bergmann /* Fixup atomic count: it exited inside IRQ handler. */ 86b5e2fc1cSAl Viro task_thread_info(paca[lcpu].__current)->preempt_count = 0; 8719fe0475SArnd Bergmann 8819fe0475SArnd Bergmann /* 8919fe0475SArnd Bergmann * If the RTAS start-cpu token does not exist then presume the 9019fe0475SArnd Bergmann * cpu is already spinning. 9119fe0475SArnd Bergmann */ 9219fe0475SArnd Bergmann start_cpu = rtas_token("start-cpu"); 9319fe0475SArnd Bergmann if (start_cpu == RTAS_UNKNOWN_SERVICE) 9419fe0475SArnd Bergmann return 1; 9519fe0475SArnd Bergmann 9619fe0475SArnd Bergmann status = rtas_call(start_cpu, 3, 1, NULL, pcpu, start_here, lcpu); 9719fe0475SArnd Bergmann if (status != 0) { 9819fe0475SArnd Bergmann printk(KERN_ERR "start-cpu failed: %i\n", status); 9919fe0475SArnd Bergmann return 0; 10019fe0475SArnd Bergmann } 10119fe0475SArnd Bergmann 10219fe0475SArnd Bergmann return 1; 10319fe0475SArnd Bergmann } 10419fe0475SArnd Bergmann 10519fe0475SArnd Bergmann static int __init smp_iic_probe(void) 10619fe0475SArnd Bergmann { 10719fe0475SArnd Bergmann iic_request_IPIs(); 10819fe0475SArnd Bergmann 109104699c0SKOSAKI Motohiro return cpumask_weight(cpu_possible_mask); 11019fe0475SArnd Bergmann } 11119fe0475SArnd Bergmann 112cad5cef6SGreg Kroah-Hartman static void smp_cell_setup_cpu(int cpu) 11319fe0475SArnd Bergmann { 11419fe0475SArnd Bergmann if (cpu != boot_cpuid) 11519fe0475SArnd Bergmann iic_setup_cpu(); 116960cedb4SArnd Bergmann 117960cedb4SArnd Bergmann /* 118960cedb4SArnd Bergmann * change default DABRX to allow user watchpoints 119960cedb4SArnd Bergmann */ 120960cedb4SArnd Bergmann mtspr(SPRN_DABRX, DABRX_KERNEL | DABRX_USER); 12119fe0475SArnd Bergmann } 12219fe0475SArnd Bergmann 123cad5cef6SGreg Kroah-Hartman static int smp_cell_kick_cpu(int nr) 12419fe0475SArnd Bergmann { 12519fe0475SArnd Bergmann BUG_ON(nr < 0 || nr >= NR_CPUS); 12619fe0475SArnd Bergmann 12719fe0475SArnd Bergmann if (!smp_startup_cpu(nr)) 128de300974SMichael Ellerman return -ENOENT; 12919fe0475SArnd Bergmann 13019fe0475SArnd Bergmann /* 13119fe0475SArnd Bergmann * The processor is currently spinning, waiting for the 13219fe0475SArnd Bergmann * cpu_start field to become non-zero After we set cpu_start, 13319fe0475SArnd Bergmann * the processor will continue on to secondary_start 13419fe0475SArnd Bergmann */ 13519fe0475SArnd Bergmann paca[nr].cpu_start = 1; 136de300974SMichael Ellerman 137de300974SMichael Ellerman return 0; 13819fe0475SArnd Bergmann } 13919fe0475SArnd Bergmann 14019fe0475SArnd Bergmann static struct smp_ops_t bpa_iic_smp_ops = { 141d5a1c193SMilton Miller .message_pass = iic_message_pass, 14219fe0475SArnd Bergmann .probe = smp_iic_probe, 14319fe0475SArnd Bergmann .kick_cpu = smp_cell_kick_cpu, 144960cedb4SArnd Bergmann .setup_cpu = smp_cell_setup_cpu, 14539fd4027SAndy Fleming .cpu_bootable = smp_generic_cpu_bootable, 14619fe0475SArnd Bergmann }; 14719fe0475SArnd Bergmann 14819fe0475SArnd Bergmann /* This is called very early */ 14919fe0475SArnd Bergmann void __init smp_init_cell(void) 15019fe0475SArnd Bergmann { 15119fe0475SArnd Bergmann int i; 15219fe0475SArnd Bergmann 15319fe0475SArnd Bergmann DBG(" -> smp_init_cell()\n"); 15419fe0475SArnd Bergmann 15519fe0475SArnd Bergmann smp_ops = &bpa_iic_smp_ops; 15619fe0475SArnd Bergmann 15719fe0475SArnd Bergmann /* Mark threads which are still spinning in hold loops. */ 15819fe0475SArnd Bergmann if (cpu_has_feature(CPU_FTR_SMT)) { 15919fe0475SArnd Bergmann for_each_present_cpu(i) { 1606a75a6b8SMilton Miller if (cpu_thread_in_core(i) == 0) 161104699c0SKOSAKI Motohiro cpumask_set_cpu(i, &of_spin_map); 16219fe0475SArnd Bergmann } 163104699c0SKOSAKI Motohiro } else 164104699c0SKOSAKI Motohiro cpumask_copy(&of_spin_map, cpu_present_mask); 16519fe0475SArnd Bergmann 166104699c0SKOSAKI Motohiro cpumask_clear_cpu(boot_cpuid, &of_spin_map); 16719fe0475SArnd Bergmann 16819fe0475SArnd Bergmann /* Non-lpar has additional take/give timebase */ 16919fe0475SArnd Bergmann if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { 170c4007a2fSBenjamin Herrenschmidt smp_ops->give_timebase = rtas_give_timebase; 171c4007a2fSBenjamin Herrenschmidt smp_ops->take_timebase = rtas_take_timebase; 17219fe0475SArnd Bergmann } 17319fe0475SArnd Bergmann 17419fe0475SArnd Bergmann DBG(" <- smp_init_cell()\n"); 17519fe0475SArnd Bergmann } 176