From 44d215b19e2a90005cbf7c03586f30b712883a5e Mon Sep 17 00:00:00 2001 From: Alex Frid Date: Wed, 6 Apr 2011 20:43:55 -0700 Subject: ARM: tegra: power: Add CPU EDP support CPU electrical design point (EDP) limits specify maximum CPU frequency depending on number of CPU cores on-line, and chip temperature. This commit added initial edp governor to cpufreq driver. Governor is aware of CPU departure/arrival, but temperature dependency is yet to be added. Therefore CPU EDP support is left disabled for now. Original-Change-Id: Ia875aa6904df7ec25ac98863d59a173703034241 Reviewed-on: http://git-master/r/26982 Tested-by: Aleksandr Frid Reviewed-by: Diwakar Tundlam Tested-by: Diwakar Tundlam Reviewed-by: Scott Williams Original-Change-Id: Iae2e9d47c2d3fd4cb32104adbad4f4b26c46064c Rebase-Id: Rde24788e86558e1c21b18a1857a8b52220ba8e2a --- arch/arm/mach-tegra/Kconfig | 9 +++ arch/arm/mach-tegra/cpu-tegra.c | 122 ++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/pm.h | 12 ++++ 3 files changed, 143 insertions(+) (limited to 'arch/arm/mach-tegra') diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 72be8786a165..4ff216ea7e25 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -242,4 +242,13 @@ config TEGRA_MC_PROFILE sampling of the memory controller usage on a client-by-client basis, and report the log through sysfs. +config TEGRA_EDP_LIMITS + bool "Enforce electrical design limits" + depends on CPU_FREQ + default n + help + Limit maximum CPU frequency based on temperature and number + of on-line CPUs to keep CPU rail current within power supply + capabilities. + endif diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index ac86d84a7710..dae7042c350f 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -40,6 +41,8 @@ #include "clock.h" #include "pm.h" +/* tegra throttling and edp governors require frequencies in the table + to be in ascending order */ static struct cpufreq_frequency_table *freq_table; @@ -187,6 +190,121 @@ void tegra_throttling_enable(bool enable) } #endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ +#ifdef CONFIG_TEGRA_EDP_LIMITS + +static const struct tegra_edp_limits *cpu_edp_limits; +static int cpu_edp_limits_size; +static int edp_thermal_index; +static cpumask_t edp_cpumask; +static unsigned int edp_limit; + +static void edp_update_limit(void) +{ + int i; + unsigned int limit; + + if (!cpu_edp_limits) + return; + + limit = cpu_edp_limits[edp_thermal_index].freq_limits[ + cpumask_weight(&edp_cpumask) - 1]; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + if (freq_table[i].frequency > limit) { + break; + } + } + BUG_ON(i == 0); /* min freq above the limit or table empty */ + edp_limit = freq_table[i-1].frequency; +} + +static unsigned int edp_governor_speed(unsigned int requested_speed) +{ + if ((!cpu_edp_limits) || (requested_speed <= edp_limit)) + return requested_speed; + else + return edp_limit; +} + +static int tegra_cpu_edp_notify( + struct notifier_block *nb, unsigned long event, void *hcpu) +{ + int ret = 0; + unsigned int cpu_speed, new_speed; + int cpu = (long)hcpu; + + switch (event) { + case CPU_UP_PREPARE: + mutex_lock(&tegra_cpu_lock); + cpu_set(cpu, edp_cpumask); + edp_update_limit(); + + cpu_speed = tegra_getspeed(0); + new_speed = edp_governor_speed(cpu_speed); + if (cpu_speed != new_speed) { + ret = tegra_update_cpu_speed(new_speed); + if (ret) { + cpu_clear(cpu, edp_cpumask); + edp_update_limit(); + } + printk(KERN_DEBUG "tegra CPU:%sforce EDP limit %u kHz" + "\n", ret ? " failed to " : " ", new_speed); + } + mutex_unlock(&tegra_cpu_lock); + break; + case CPU_DEAD: + mutex_lock(&tegra_cpu_lock); + cpu_clear(cpu, edp_cpumask); + edp_update_limit(); + mutex_unlock(&tegra_cpu_lock); + break; + } + return notifier_from_errno(ret); +} + +static struct notifier_block tegra_cpu_edp_notifier = { + .notifier_call = tegra_cpu_edp_notify, +}; + +static void tegra_cpu_edp_init(bool resume) +{ + if (!cpu_edp_limits) { + if (!resume) + pr_info("tegra CPU: no EDP table is provided\n"); + return; + } + + edp_thermal_index = 0; + edp_cpumask = *cpu_online_mask; + edp_update_limit(); + + if (!resume) + register_hotcpu_notifier(&tegra_cpu_edp_notifier); + + pr_info("tegra CPU: set EDP limit %u MHz\n", edp_limit / 1000); +} + +static void tegra_cpu_edp_exit(void) +{ + if (!cpu_edp_limits) + return; + + unregister_hotcpu_notifier(&tegra_cpu_edp_notifier); +} + +void tegra_init_cpu_edp_limits(const struct tegra_edp_limits *limits, int size) +{ + cpu_edp_limits = limits; + cpu_edp_limits_size = cpu_edp_limits_size; +} + +#else /* CONFIG_TEGRA_EDP_LIMITS */ + +#define edp_governor_speed(requested_speed) (requested_speed) +#define tegra_cpu_edp_init(resume) +#define tegra_cpu_edp_exit() +#endif /* CONFIG_TEGRA_EDP_LIMITS */ + int tegra_verify_speed(struct cpufreq_policy *policy) { return cpufreq_frequency_table_verify(policy, freq_table); @@ -278,6 +396,7 @@ static int tegra_target(struct cpufreq_policy *policy, target_cpu_speed[policy->cpu] = freq; new_speed = throttle_governor_speed(tegra_cpu_highest_speed()); + new_speed = edp_governor_speed(new_speed); ret = tegra_update_cpu_speed(new_speed); if (ret == 0) tegra_auto_hotplug_governor(new_speed); @@ -299,6 +418,7 @@ static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, tegra_update_cpu_speed(freq_table[0].frequency); } else if (event == PM_POST_SUSPEND) { is_suspended = false; + tegra_cpu_edp_init(true); } mutex_unlock(&tegra_cpu_lock); @@ -400,6 +520,7 @@ static int __init tegra_cpufreq_init(void) return ret; freq_table = table_data->freq_table; + tegra_cpu_edp_init(false); return cpufreq_register_driver(&tegra_cpufreq_driver); } @@ -408,6 +529,7 @@ static void __exit tegra_cpufreq_exit(void) #ifdef CONFIG_TEGRA_THERMAL_THROTTLE destroy_workqueue(workqueue); #endif + tegra_cpu_edp_exit(); tegra_auto_hotplug_exit(); cpufreq_unregister_driver(&tegra_cpufreq_driver); } diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 116885ad78db..b19209292a04 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -78,6 +78,18 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat); void tegra_idle_lp2(void); +struct tegra_edp_limits { + int temperature; + unsigned int freq_limits[CONFIG_NR_CPUS]; +}; +#ifdef CONFIG_TEGRA_EDP_LIMITS +void tegra_init_cpu_edp_limits(const struct tegra_edp_limits *limits, int size); +#else +static inline void tegra_init_cpu_edp_limits( + const struct tegra_edp_limits *limits, int size) +{ } +#endif + #if defined(CONFIG_TEGRA_AUTO_HOTPLUG) && !defined(CONFIG_ARCH_TEGRA_2x_SOC) int tegra_auto_hotplug_init(struct mutex *cpu_lock); void tegra_auto_hotplug_exit(void); -- cgit v1.2.3