/* * 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); #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) void __cpuinit platform_secondary_init(unsigned int cpu) { 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); } #ifdef CONFIG_TRUSTED_FOUNDATIONS void callGenericSMC(u32 param0, u32 param1, u32 param2); #endif int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { unsigned long old_boot_vector; unsigned long boot_vector; unsigned long timeout; #ifndef CONFIG_TRUSTED_FOUNDATIONS u32 reg; static void __iomem *vector_base = (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100); #endif /* * 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(); #if CONFIG_TRUSTED_FOUNDATIONS callGenericSMC(0xFFFFFFFC, 0xFFFFFFE5, boot_vector); #else old_boot_vector = readl(vector_base); writel(boot_vector, vector_base); /* 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; /* * 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<