/* * linux/arch/arm/mach-tegra/platsmp.c * * Copyright (C) 2002 ARM Ltd. * All Rights Reserved * * Copyright (C) 2009 Palm * All Rights Reserved * * Copyright (C) 2010 NVIDIA Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power.h" extern void tegra_secondary_startup(void); static DEFINE_SPINLOCK(boot_lock); static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); extern void __cortex_a9_restore(void); extern void __shut_off_mmu(void); #ifdef CONFIG_HOTPLUG_CPU static DEFINE_PER_CPU(struct completion, cpu_killed); extern void tegra_hotplug_startup(void); #endif static DECLARE_BITMAP(cpu_init_bits, CONFIG_NR_CPUS) __read_mostly; const struct cpumask *const cpu_init_mask = to_cpumask(cpu_init_bits); #define cpu_init_map (*(cpumask_t *)cpu_init_mask) #define EVP_CPU_RESET_VECTOR \ (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c) #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340) #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344) unsigned long tegra_pgd_phys; /* pgd used by hotplug & LP2 bootup */ static pgd_t *tegra_pgd; void *tegra_context_area = NULL; void __cpuinit platform_secondary_init(unsigned int cpu) { #ifdef CONFIG_CPU_V7 /* enable dynamic clock gating */ unsigned int reg; asm volatile ("mrc p15, 0, %0, c15, c0, 0" : "=r" (reg) : : "cc"); reg |= 1; asm volatile ("mcr p15, 0, %0, c15, c0, 0" : : "r" (reg) : "cc"); #endif trace_hardirqs_off(); gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x100); /* * Synchronise with the boot thread. */ spin_lock(&boot_lock); #ifdef CONFIG_HOTPLUG_CPU cpu_set(cpu, cpu_init_map); INIT_COMPLETION(per_cpu(cpu_killed, cpu)); #endif spin_unlock(&boot_lock); } int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { unsigned long old_boot_vector; unsigned long boot_vector; unsigned long timeout; u32 reg; /* * set synchronisation state between this boot processor * and the secondary one */ spin_lock(&boot_lock); /* set the reset vector to point to the secondary_startup routine */ #ifdef CONFIG_HOTPLUG_CPU if (cpumask_test_cpu(cpu, cpu_init_mask)) boot_vector = virt_to_phys(tegra_hotplug_startup); else #endif boot_vector = virt_to_phys(tegra_secondary_startup); smp_wmb(); old_boot_vector = readl(EVP_CPU_RESET_VECTOR); writel(boot_vector, EVP_CPU_RESET_VECTOR); /* enable cpu clock on cpu */ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); writel(reg & ~(1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX); reg = 0x1111< ncores) max_cpus = ncores; tegra_context_area = kzalloc(512 * ncores, GFP_KERNEL); if (tegra_context_area && create_suspend_pgtable()) { kfree(tegra_context_area); tegra_context_area = NULL; } /* * Initialise the present map, which describes the set of CPUs * actually populated at the present time. */ for (i = 0; i < max_cpus; i++) set_cpu_present(i, true); #ifdef CONFIG_HOTPLUG_CPU for_each_present_cpu(i) { init_completion(&per_cpu(cpu_killed, i)); } #endif /* * Initialise the SCU if there are more than one CPU and let * them know where to start. Note that, on modern versions of * MILO, the "poke" doesn't actually do anything until each * individual core is sent a soft interrupt to get it out of * WFI */ if (max_cpus > 1) { percpu_timer_setup(); scu_enable(scu_base); } } #ifdef CONFIG_HOTPLUG_CPU extern void vfp_sync_state(struct thread_info *thread); void __cpuinit secondary_start_kernel(void); int platform_cpu_kill(unsigned int cpu) { unsigned int reg; int e; e = wait_for_completion_timeout(&per_cpu(cpu_killed, cpu), 100); printk(KERN_NOTICE "CPU%u: %s shutdown\n", cpu, (e) ? "clean":"forced"); if (e) { do { reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); cpu_relax(); } while (!(reg & (1<