| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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>
 | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | #include <linux/irq.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | #include <asm/mmu_context.h>
 | 
					
						
							|  |  |  | #include <asm/tlbflush.h>
 | 
					
						
							|  |  |  | #include <asm/sections.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* State of each CPU. */ | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | static DEFINE_PER_CPU(int, cpu_state) = { 0 }; | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* 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) | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 		pr_err("Couldn't set init affinity to boot cpu (%ld)\n", rc); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* 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) | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 		pr_warning("couldn't reset init affinity (%ld)\n", | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 		       rc); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | late_initcall(reset_init_affinity); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 17:28:07 -04:00
										 |  |  | static struct cpumask cpu_started; | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-06-18 17:28:07 -04:00
										 |  |  | static void start_secondary(void) | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-07 11:36:54 -04:00
										 |  |  | 	int cpuid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	preempt_disable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpuid = smp_processor_id(); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* 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)) { | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 		pr_warning("CPU#%d already started!\n", cpuid); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 		for (;;) | 
					
						
							|  |  |  | 			local_irq_enable(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	smp_nap(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Bring a secondary processor online. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-06-18 17:28:07 -04:00
										 |  |  | void online_secondary(void) | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * 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(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-22 16:59:11 +05:30
										 |  |  | 	notify_cpu_starting(smp_processor_id()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 	set_cpu_online(smp_processor_id(), 1); | 
					
						
							|  |  |  | 	__get_cpu_var(cpu_state) = CPU_ONLINE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 	/* Set up tile-specific state for this cpu. */ | 
					
						
							|  |  |  | 	setup_cpu(0); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up tile-timer clock-event device on this cpu */ | 
					
						
							|  |  |  | 	setup_tile_timer(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-21 22:50:01 +01:00
										 |  |  | 	cpu_startup_entry(CPUHP_ONLINE); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 17:28:07 -04:00
										 |  |  | int __cpu_up(unsigned int cpu, struct task_struct *tidle) | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* 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) { | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 			pr_info("skipping unresponsive cpu%d\n", cpu); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 			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) | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 		pr_err("Couldn't set init affinity to cpu %d (%d)\n", cpu, rc); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } |