diff options
Diffstat (limited to 'arch/arm/mach-tegra/platsmp.c')
| -rw-r--r-- | arch/arm/mach-tegra/platsmp.c | 355 |
1 files changed, 130 insertions, 225 deletions
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index bea6c3e176cc..3bd0300dc9b3 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -3,7 +3,7 @@ * * SMP management routines for SMP Tegra SoCs * - * Copyright (c) 2009, NVIDIA Corporation. + * 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 as published by @@ -24,259 +24,164 @@ #include <linux/smp.h> #include <asm/cacheflush.h> #include <asm/localtimer.h> - -#include "mach/nvrm_linux.h" -#include "nvrm_module.h" -#include "nvrm_init.h" -#include "nvrm_drf.h" -#include "nvrm_hardware_access.h" -#include "nvcommon.h" -#include "ap20/arscu.h" -#include "ap20/arevp.h" -#include "ap20/arclk_rst.h" -#include "ap20/arfic_proc_if.h" -#include "ap20/arflow_ctlr.h" +#include <linux/io.h> +#include <mach/iomap.h> static DEFINE_SPINLOCK(boot_lock); - extern void exit_lp2(void); +extern void tegra_secondary_startup(void); + +#define SCU_CONTROL_0 0x0 +#define SCU_CONFIG_0 0x4 + +#define EVP_CPU_RESET_VECTOR_0 0x100 + +/* takes cpu out of reset */ +#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR_0 0x344 +#define CPU_RESET(cpu) (0x1011ul<<(cpu)) + +/* used as mask to enable clock to cpu */ +#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0 0x4c +#define CPU_CLK_STOP(cpu) (0x1<<(8+cpu)) -static inline volatile NvU8 *TegraScuAddress(void) +/* write 0 to take cpu out of flow controlled state */ +#define FLOW_CTRL_HALT_CPUx_EVENTS(cpu) ((cpu)?((cpu-1)*0x8 + 0x14) : 0x0) + +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) + +static u32 orig_reset; + +void platform_secondary_init(unsigned int cpu) { - NvRmPhysAddr Pa; - NvU32 Len; - volatile NvU8 *pScu = NULL; - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_ArmPerif, 0), &Pa, &Len); - - if (Pa==0xffffffffUL || !Len) { - printk("TegraSMP: No SCU present\n"); - return NULL; - } - - NvRmPhysicalMemMap(Pa, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void**)&pScu); - return pScu; + if (cpumask_test_cpu(cpu, cpu_init_mask)) + return; + + trace_hardirqs_off(); + spin_lock(&boot_lock); + cpu_set(cpu, cpu_init_map); + spin_unlock(&boot_lock); + + gic_cpu_init(0, IO_ADDRESS(TEGRA_GIC_PROC_IF_BASE)); } -#if 0 -#define TegraCoreCount() 1 -#else -static unsigned int __init TegraCoreCount(void) +void __init smp_init_cpus() { - volatile NvU8 *pScu = TegraScuAddress(); - unsigned int Cores = 1; - if (pScu) { - Cores = NV_READ32(pScu + SCU_CONFIG_0); - Cores = NV_DRF_VAL(SCU, CONFIG, CPU_NUM, Cores) + 1; - } - - if (Cores>NR_CPUS) { - printk("TegraSMP: Kernel configured for fewer NR_CPUS than hardware\n"); - Cores = NR_CPUS; - } - return Cores; + unsigned int cfg; + unsigned int cpus; + void __iomem *evp = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE); + + cfg = __raw_readl(IO_ADDRESS(TEGRA_SCU_BASE) + SCU_CONFIG_0); + + cpus = min_t(unsigned int, NR_CPUS, (cfg & 3) + 1); + + while (cpus--) + cpu_set(cpus, cpu_possible_map); + + orig_reset = __raw_readl(evp + EVP_CPU_RESET_VECTOR_0); } -#endif -void platform_secondary_init(unsigned int cpu) +void __init smp_prepare_cpus(unsigned int max) { - NvRmPhysAddr Pa; - NvU32 Len; - volatile NvU8* pArm = NULL; - static unsigned int first_init = 1; - - if (first_init == 0) - return; - - trace_hardirqs_off(); - - spin_lock(&boot_lock); - spin_unlock(&boot_lock); - - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_ArmPerif,0), &Pa, &Len); - BUG_ON(Pa==-1UL || !Len); - NvRmPhysicalMemMap(Pa, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void**)&pArm); - BUG_ON(!pArm); - - gic_cpu_init(0, (void __iomem*)pArm + FIC_PROC_IF_CONTROL_0); - - first_init = 0; + unsigned int cpu; + + smp_store_cpu_info(smp_processor_id()); + for_each_possible_cpu(cpu) + cpu_set(cpu, cpu_present_map); + + if (num_present_cpus()>1) { + u32 ctrl; + + percpu_timer_setup(); + ctrl = __raw_readl(IO_ADDRESS(TEGRA_SCU_BASE) + SCU_CONTROL_0); + ctrl |= 1; + __raw_writel(ctrl, IO_ADDRESS(TEGRA_SCU_BASE) + SCU_CONTROL_0); + } } -void __init smp_init_cpus(void) +static inline void bwritel(unsigned long v, void __iomem *a) { - unsigned int i; - unsigned int Cores = TegraCoreCount(); - - for (i=0; i<Cores; i++) - cpu_set(i, cpu_possible_map); + __raw_writel(v, a); + dsb(); + isb(); } -void __init smp_prepare_cpus(unsigned int Max) +int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { - unsigned int Cores = TegraCoreCount(); - unsigned int Cpu = smp_processor_id(); - volatile NvU8 *pScu = NULL; - unsigned int i; + void __iomem *clk = IO_ADDRESS(TEGRA_CLK_RESET_BASE); + void __iomem *flow = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE); + void __iomem *evp = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE); + unsigned long boot; + unsigned long timeout; + u32 r; + + spin_lock(&boot_lock); + + if (likely(cpumask_test_cpu(cpu, cpu_init_mask))) + boot = virt_to_phys((void *)exit_lp2); + else + boot = virt_to_phys((void *)tegra_secondary_startup); + + flush_cache_all(); + smp_wmb(); + + bwritel(boot, evp + EVP_CPU_RESET_VECTOR_0); + + bwritel(0, flow + FLOW_CTRL_HALT_CPUx_EVENTS(cpu)); - smp_store_cpu_info(Cpu); + r = __raw_readl(clk + CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0); + r &= ~CPU_CLK_STOP(cpu); + bwritel(r, clk + CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0); + bwritel(CPU_RESET(cpu), clk + CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR_0); - pScu = TegraScuAddress(); - if (!pScu) - Cores = 1; + timeout = jiffies + 10*HZ; - Max = NV_MIN(Max, Cores); + do { + r = __raw_readl(evp + EVP_CPU_RESET_VECTOR_0); + if (r!=boot) + break; + cpu_relax(); + } while (time_before(jiffies, timeout)); - for (i=0; i<Max; i++) - cpu_set(i, cpu_present_map); + __raw_writel(orig_reset, evp + EVP_CPU_RESET_VECTOR_0); + spin_unlock(&boot_lock); - if (Max > 1) - { - /* - * Enable the local timer or broadcast device for the - * boot CPU, but only if we have more than one CPU. - */ - percpu_timer_setup(); + if (r==boot) { + pr_err("failed to initialize CPU %u\n", cpu); + return -EIO; + } - NvU32 v = NV_READ32(pScu + SCU_CONTROL_0); - v = NV_FLD_SET_DRF_NUM(SCU, CONTROL, SCU_ENABLE, 1, v); - NV_WRITE32(pScu + SCU_CONTROL_0, v); - } + return 0; } -#define CHECK_ADDR(P,L,N) \ - do { \ - if ((P)==-1UL || !(L)) \ - { \ - printk("TegraSMP: No %s module present\n", #N); \ - return -ENOSYS; \ - } \ - } while (0); +#ifdef CONFIG_HOTPLUG_CPU -int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) +static DECLARE_COMPLETION(cpu_killed); +extern void cpu_ap20_do_lp2(void); + +int platform_cpu_kill(unsigned int cpu) { - volatile NvU8 *pEvp = NULL; - volatile NvU8 *pFlowCtrl = NULL; - volatile NvU8 *pClkRst = NULL; - NvUPtr BootFunc; - NvRmPhysAddr Pa; - NvU32 Len; - NvU32 HaltAddr; - NvU32 ResetVal; - NvU32 ClkEnable; - NvU32 OldReset; - NvU32 v, Msg; - extern void tegra_secondary_startup(void); -#ifdef CONFIG_HOTPLUG_CPU - extern void TegraHotplugStartup(void); - static NvU32 EnabledCores = 0; -#endif - unsigned long timeout; - - spin_lock(&boot_lock); - - /* Map exception vector, flow controller and clock & reset module */ - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_ExceptionVector, 0), &Pa, &Len); - CHECK_ADDR(Pa, Len, EVP); - NvRmPhysicalMemMap(Pa, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void**)&pEvp); - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_FlowCtrl, 0), &Pa, &Len); - CHECK_ADDR(Pa, Len, FlowCtrl); - NvRmPhysicalMemMap(Pa, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void**)&pFlowCtrl); - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_ClockAndReset,0), &Pa, &Len); - CHECK_ADDR(Pa, Len, ClockReset); - NvRmPhysicalMemMap(Pa, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void**)&pClkRst); - - if (!pClkRst || !pFlowCtrl || !pEvp) - { - printk("TegraSMP: Unable to map necessary modules for SMP start-up\n"); - return -ENOSYS; - } - - ResetVal = - NV_DRF_NUM(CLK_RST_CONTROLLER, RST_CPU_CMPLX_CLR, CLR_CPURESET0, 1)| - NV_DRF_NUM(CLK_RST_CONTROLLER, RST_CPU_CMPLX_CLR, CLR_DBGRESET0, 1)| - NV_DRF_NUM(CLK_RST_CONTROLLER, RST_CPU_CMPLX_CLR, CLR_DERESET0, 1); - ResetVal <<= cpu; - - switch (cpu) { - case 0: - // Kernel should never call this, since master CPU will always be up - HaltAddr = FLOW_CTLR_HALT_CPU_EVENTS_0; - ClkEnable = - ~NV_DRF_NUM(CLK_RST_CONTROLLER, CLK_CPU_CMPLX, CPU0_CLK_STP, 1); - break; - case 1: - HaltAddr = FLOW_CTLR_HALT_CPU1_EVENTS_0; - ClkEnable = - ~NV_DRF_NUM(CLK_RST_CONTROLLER, CLK_CPU_CMPLX, CPU1_CLK_STP, 1); - break; - default: - panic("Unsupported cpu ID: %u\n", cpu); - } + return wait_for_completion_timeout(&cpu_killed, 5000); +} -#ifdef CONFIG_HOTPLUG_CPU - if (EnabledCores & (1<<cpu)) { - BootFunc = virt_to_phys((void*)exit_lp2); - } - else -#endif - { - BootFunc = virt_to_phys((void*)tegra_secondary_startup); - } - OldReset = NV_READ32(pEvp + EVP_CPU_RESET_VECTOR_0); - smp_wmb(); - flush_cache_all(); - - NV_WRITE32(pEvp + EVP_CPU_RESET_VECTOR_0, BootFunc); - dsb(); - isb(); - NV_WRITE32(pFlowCtrl + HaltAddr, - NV_DRF_DEF(FLOW_CTLR, HALT_CPU_EVENTS, MODE, FLOW_MODE_NONE)); - v = NV_READ32(pClkRst + CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0); - dsb(); - isb(); - v &= ClkEnable; - NV_WRITE32(pClkRst + CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0, v); - dsb(); - isb(); - NV_WRITE32(pClkRst + CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR_0, ResetVal); - dsb(); - isb(); - - timeout = jiffies + (10 * HZ); - do { - /* The slave CPU will put its ID into the reset vector register after - * it initializes its cache */ - Msg = NV_READ32(pEvp + EVP_CPU_RESET_VECTOR_0); - if (Msg != BootFunc) - break; - } while (time_before(jiffies, timeout)); - - /* Restore the original reset vector, after either the slave processor - * wakes up, or we timeout waiting for it */ - NV_WRITE32(pEvp + EVP_CPU_RESET_VECTOR_0, OldReset); - - spin_unlock(&boot_lock); - if (Msg == BootFunc) { - printk(KERN_INFO "TegraSMP: Failed to init CPU %u\n", cpu); - return -ENOSYS; - } - - printk(KERN_INFO "TegraSMP: CPU %u responded with \"0x%08x\"\n", cpu, Msg); +void platform_cpu_die(unsigned int cpu) +{ + flush_cache_all(); + preempt_enable_no_resched(); + complete(&cpu_killed); + cpu_ap20_do_lp2(); +} -#ifdef CONFIG_HOTPLUG_CPU - EnabledCores |= (1<<cpu); -#endif +int mach_cpu_disable(unsigned int cpu) +{ + WARN_ON(!cpu); + if (!cpu) + return -EPERM; - return 0; + return 0; } + + +#endif |
