|  | /* | 
|  | * Copyright 2010 Tilera Corporation. All Rights Reserved. | 
|  | * | 
|  | *   This program is free software; you can redistribute it and/or | 
|  | *   modify it under the terms of the GNU General Public License | 
|  | *   as published by the Free Software Foundation, version 2. | 
|  | * | 
|  | *   This program is distributed in the hope that it will be useful, but | 
|  | *   WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
|  | *   NON INFRINGEMENT.  See the GNU General Public License for | 
|  | *   more details. | 
|  | */ | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/mm.h> | 
|  | #include <linux/sched.h> | 
|  | #include <linux/kernel_stat.h> | 
|  | #include <linux/bootmem.h> | 
|  | #include <linux/notifier.h> | 
|  | #include <linux/cpu.h> | 
|  | #include <linux/percpu.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/irq.h> | 
|  | #include <asm/mmu_context.h> | 
|  | #include <asm/tlbflush.h> | 
|  | #include <asm/sections.h> | 
|  |  | 
|  | /* State of each CPU. */ | 
|  | static DEFINE_PER_CPU(int, cpu_state) = { 0 }; | 
|  |  | 
|  | /* The messaging code jumps to this pointer during boot-up */ | 
|  | unsigned long start_cpu_function_addr; | 
|  |  | 
|  | /* Called very early during startup to mark boot cpu as online */ | 
|  | void __init smp_prepare_boot_cpu(void) | 
|  | { | 
|  | int cpu = smp_processor_id(); | 
|  | set_cpu_online(cpu, 1); | 
|  | set_cpu_present(cpu, 1); | 
|  | __get_cpu_var(cpu_state) = CPU_ONLINE; | 
|  |  | 
|  | init_messaging(); | 
|  | } | 
|  |  | 
|  | static void start_secondary(void); | 
|  |  | 
|  | /* | 
|  | * Called at the top of init() to launch all the other CPUs. | 
|  | * They run free to complete their initialization and then wait | 
|  | * until they get an IPI from the boot cpu to come online. | 
|  | */ | 
|  | void __init smp_prepare_cpus(unsigned int max_cpus) | 
|  | { | 
|  | long rc; | 
|  | int cpu, cpu_count; | 
|  | int boot_cpu = smp_processor_id(); | 
|  |  | 
|  | current_thread_info()->cpu = boot_cpu; | 
|  |  | 
|  | /* | 
|  | * Pin this task to the boot CPU while we bring up the others, | 
|  | * just to make sure we don't uselessly migrate as they come up. | 
|  | */ | 
|  | rc = sched_setaffinity(current->pid, cpumask_of(boot_cpu)); | 
|  | if (rc != 0) | 
|  | pr_err("Couldn't set init affinity to boot cpu (%ld)\n", rc); | 
|  |  | 
|  | /* Print information about disabled and dataplane cpus. */ | 
|  | print_disabled_cpus(); | 
|  |  | 
|  | /* | 
|  | * Tell the messaging subsystem how to respond to the | 
|  | * startup message.  We use a level of indirection to avoid | 
|  | * confusing the linker with the fact that the messaging | 
|  | * subsystem is calling __init code. | 
|  | */ | 
|  | start_cpu_function_addr = (unsigned long) &online_secondary; | 
|  |  | 
|  | /* Set up thread context for all new processors. */ | 
|  | cpu_count = 1; | 
|  | for (cpu = 0; cpu < NR_CPUS; ++cpu)	{ | 
|  | struct task_struct *idle; | 
|  |  | 
|  | if (cpu == boot_cpu) | 
|  | continue; | 
|  |  | 
|  | if (!cpu_possible(cpu)) { | 
|  | /* | 
|  | * Make this processor do nothing on boot. | 
|  | * Note that we don't give the boot_pc function | 
|  | * a stack, so it has to be assembly code. | 
|  | */ | 
|  | per_cpu(boot_sp, cpu) = 0; | 
|  | per_cpu(boot_pc, cpu) = (unsigned long) smp_nap; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Create a new idle thread to run start_secondary() */ | 
|  | idle = fork_idle(cpu); | 
|  | if (IS_ERR(idle)) | 
|  | panic("failed fork for CPU %d", cpu); | 
|  | idle->thread.pc = (unsigned long) start_secondary; | 
|  |  | 
|  | /* Make this thread the boot thread for this processor */ | 
|  | per_cpu(boot_sp, cpu) = task_ksp0(idle); | 
|  | per_cpu(boot_pc, cpu) = idle->thread.pc; | 
|  |  | 
|  | ++cpu_count; | 
|  | } | 
|  | BUG_ON(cpu_count > (max_cpus ? max_cpus : 1)); | 
|  |  | 
|  | /* Fire up the other tiles, if any */ | 
|  | init_cpu_present(cpu_possible_mask); | 
|  | if (cpumask_weight(cpu_present_mask) > 1) { | 
|  | mb();  /* make sure all data is visible to new processors */ | 
|  | hv_start_all_tiles(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static __initdata struct cpumask init_affinity; | 
|  |  | 
|  | static __init int reset_init_affinity(void) | 
|  | { | 
|  | long rc = sched_setaffinity(current->pid, &init_affinity); | 
|  | if (rc != 0) | 
|  | pr_warning("couldn't reset init affinity (%ld)\n", | 
|  | rc); | 
|  | return 0; | 
|  | } | 
|  | late_initcall(reset_init_affinity); | 
|  |  | 
|  | static struct cpumask cpu_started; | 
|  |  | 
|  | /* | 
|  | * Activate a secondary processor.  Very minimal; don't add anything | 
|  | * to this path without knowing what you're doing, since SMP booting | 
|  | * is pretty fragile. | 
|  | */ | 
|  | static void start_secondary(void) | 
|  | { | 
|  | int cpuid; | 
|  |  | 
|  | preempt_disable(); | 
|  |  | 
|  | cpuid = smp_processor_id(); | 
|  |  | 
|  | /* Set our thread pointer appropriately. */ | 
|  | set_my_cpu_offset(__per_cpu_offset[cpuid]); | 
|  |  | 
|  | /* | 
|  | * In large machines even this will slow us down, since we | 
|  | * will be contending for for the printk spinlock. | 
|  | */ | 
|  | /* printk(KERN_DEBUG "Initializing CPU#%d\n", cpuid); */ | 
|  |  | 
|  | /* Initialize the current asid for our first page table. */ | 
|  | __get_cpu_var(current_asid) = min_asid; | 
|  |  | 
|  | /* Set up this thread as another owner of the init_mm */ | 
|  | atomic_inc(&init_mm.mm_count); | 
|  | current->active_mm = &init_mm; | 
|  | if (current->mm) | 
|  | BUG(); | 
|  | enter_lazy_tlb(&init_mm, current); | 
|  |  | 
|  | /* Allow hypervisor messages to be received */ | 
|  | init_messaging(); | 
|  | local_irq_enable(); | 
|  |  | 
|  | /* Indicate that we're ready to come up. */ | 
|  | /* Must not do this before we're ready to receive messages */ | 
|  | if (cpumask_test_and_set_cpu(cpuid, &cpu_started)) { | 
|  | pr_warning("CPU#%d already started!\n", cpuid); | 
|  | for (;;) | 
|  | local_irq_enable(); | 
|  | } | 
|  |  | 
|  | smp_nap(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Bring a secondary processor online. | 
|  | */ | 
|  | void online_secondary(void) | 
|  | { | 
|  | /* | 
|  | * low-memory mappings have been cleared, flush them from | 
|  | * the local TLBs too. | 
|  | */ | 
|  | local_flush_tlb(); | 
|  |  | 
|  | BUG_ON(in_interrupt()); | 
|  |  | 
|  | /* This must be done before setting cpu_online_mask */ | 
|  | wmb(); | 
|  |  | 
|  | notify_cpu_starting(smp_processor_id()); | 
|  |  | 
|  | set_cpu_online(smp_processor_id(), 1); | 
|  | __get_cpu_var(cpu_state) = CPU_ONLINE; | 
|  |  | 
|  | /* Set up tile-specific state for this cpu. */ | 
|  | setup_cpu(0); | 
|  |  | 
|  | /* Set up tile-timer clock-event device on this cpu */ | 
|  | setup_tile_timer(); | 
|  |  | 
|  | cpu_startup_entry(CPUHP_ONLINE); | 
|  | } | 
|  |  | 
|  | int __cpu_up(unsigned int cpu, struct task_struct *tidle) | 
|  | { | 
|  | /* Wait 5s total for all CPUs for them to come online */ | 
|  | static int timeout; | 
|  | for (; !cpumask_test_cpu(cpu, &cpu_started); timeout++) { | 
|  | if (timeout >= 50000) { | 
|  | pr_info("skipping unresponsive cpu%d\n", cpu); | 
|  | local_irq_enable(); | 
|  | return -EIO; | 
|  | } | 
|  | udelay(100); | 
|  | } | 
|  |  | 
|  | local_irq_enable(); | 
|  | per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; | 
|  |  | 
|  | /* Unleash the CPU! */ | 
|  | send_IPI_single(cpu, MSG_TAG_START_CPU); | 
|  | while (!cpumask_test_cpu(cpu, cpu_online_mask)) | 
|  | cpu_relax(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void panic_start_cpu(void) | 
|  | { | 
|  | panic("Received a MSG_START_CPU IPI after boot finished."); | 
|  | } | 
|  |  | 
|  | void __init smp_cpus_done(unsigned int max_cpus) | 
|  | { | 
|  | int cpu, next, rc; | 
|  |  | 
|  | /* Reset the response to a (now illegal) MSG_START_CPU IPI. */ | 
|  | start_cpu_function_addr = (unsigned long) &panic_start_cpu; | 
|  |  | 
|  | cpumask_copy(&init_affinity, cpu_online_mask); | 
|  |  | 
|  | /* | 
|  | * Pin ourselves to a single cpu in the initial affinity set | 
|  | * so that kernel mappings for the rootfs are not in the dataplane, | 
|  | * if set, and to avoid unnecessary migrating during bringup. | 
|  | * Use the last cpu just in case the whole chip has been | 
|  | * isolated from the scheduler, to keep init away from likely | 
|  | * more useful user code.  This also ensures that work scheduled | 
|  | * via schedule_delayed_work() in the init routines will land | 
|  | * on this cpu. | 
|  | */ | 
|  | for (cpu = cpumask_first(&init_affinity); | 
|  | (next = cpumask_next(cpu, &init_affinity)) < nr_cpu_ids; | 
|  | cpu = next) | 
|  | ; | 
|  | rc = sched_setaffinity(current->pid, cpumask_of(cpu)); | 
|  | if (rc != 0) | 
|  | pr_err("Couldn't set init affinity to cpu %d (%d)\n", cpu, rc); | 
|  | } |