diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index a1611807f25..82468c838d2 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -177,6 +177,7 @@ config ARCH_MSM8960 select ARM_HAS_SG_CHAIN select MSM_KRAIT_WFE_FIXUP select MSM_IOMMU_GPU_SYNC + select MSM_CPU_PWRCTL config ARCH_MSM8930 bool "MSM8930" @@ -210,6 +211,7 @@ config ARCH_MSM8930 select ARM_HAS_SG_CHAIN select MSM_KRAIT_WFE_FIXUP select MSM_IOMMU_GPU_SYNC + select MSM_CPU_PWRCTL config ARCH_APQ8064 bool "APQ8064" @@ -238,6 +240,7 @@ config ARCH_APQ8064 select ARM_HAS_SG_CHAIN select MSM_KRAIT_WFE_FIXUP select MSM_IOMMU_GPU_SYNC + select MSM_CPU_PWRCTL config ARCH_MSM8974 bool "MSM8974" @@ -2593,4 +2596,11 @@ config MSM_HSIC_SYSMON_TEST can be read and written to send character data to the sysmon port of the modem over USB. +config MSM_CPU_PWRCTL + bool "Ensures that krait droop detectors are always off" + help + Droop detector mechanism can adversely affect krait plls during + stand alone power collapse operation. Selecting this option + ensures that they are always off. + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 492e30500ea..e496a9e95b6 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -389,3 +389,4 @@ obj-$(CONFIG_MSM_VP_REGULATOR) += msm_vp.o ifdef CONFIG_MSM_CPR obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o endif +obj-$(CONFIG_MSM_CPU_PWRCTL) += msm_cpu_pwrctl.o diff --git a/arch/arm/mach-msm/msm_cpu_pwrctl.c b/arch/arm/mach-msm/msm_cpu_pwrctl.c new file mode 100644 index 00000000000..6e339dd0567 --- /dev/null +++ b/arch/arm/mach-msm/msm_cpu_pwrctl.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2012, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_CPU_SECONDARY_CORE_OFFSET 0x10000 + +static const phys_addr_t primary_cpu_pwrctl_phys = 0x2088004; +static DEFINE_PER_CPU(int, pll_clamp_set); +static void msm_cpu_pwrctl_work_cb(struct work_struct *work); +static __cpuinitdata DECLARE_WORK(msm_cpu_pwrctl_work, msm_cpu_pwrctl_work_cb); +static int nr_cpus_done; +static int __cpuinit msm_cpu_pwrctl_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu); +static struct notifier_block __cpuinitdata msm_cpu_pwrctl_cpu_notifier = { + .notifier_call = msm_cpu_pwrctl_cpu_callback, +}; + +static void __cpuinit msm_cpu_pwrctl_work_cb(struct work_struct *work) +{ + unregister_hotcpu_notifier(&msm_cpu_pwrctl_cpu_notifier); +} + +static int __cpuinit msm_cpu_pwrctl_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + int cpu = (int) hcpu; + int *pll_clamp; + void *pwrctl_ptr; + unsigned int value; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_ONLINE: + pll_clamp = &per_cpu(pll_clamp_set, cpu); + if (likely(*pll_clamp)) + goto done; + + pwrctl_ptr = ioremap_nocache(primary_cpu_pwrctl_phys + + (cpu * MSM_CPU_SECONDARY_CORE_OFFSET), SZ_4K); + if (unlikely(!pwrctl_ptr)) + goto done; + + value = readl_relaxed(pwrctl_ptr); + value |= 0x100; + writel_relaxed(value, pwrctl_ptr); + *pll_clamp = 1; + iounmap(pwrctl_ptr); + + if (++nr_cpus_done == cpumask_weight(cpu_possible_mask)) + schedule_work(&msm_cpu_pwrctl_work); +done: + break; + default: + break; + } + + return NOTIFY_OK; + +} + +static int __init msm_cpu_pwrctl_init(void) +{ + int cpu = smp_processor_id(); + + /* We won't get cpu online notification for this CPU, + * so take this opportunity to process this CPU. + */ + msm_cpu_pwrctl_cpu_callback(&msm_cpu_pwrctl_cpu_notifier, + CPU_ONLINE, (void *) cpu); + + register_hotcpu_notifier(&msm_cpu_pwrctl_cpu_notifier); + return 0; +} + +early_initcall(msm_cpu_pwrctl_init);