summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/edp_core.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-11-21 18:00:13 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:43:25 -0700
commit31c351b2b5a794df6289955a970148787137a84c (patch)
treeccd1a2e15f097af10b7e7687535a4aefe2915be1 /arch/arm/mach-tegra/edp_core.c
parentf82d61bee32355213b824a887dca0ea37f2df887 (diff)
ARM: tegra11: power: Expand core EDP fs interfaces
Created core EDP profile sysfs interface to change edp profile: /sys/kernel/tegra_core_edp/available_profiles /sys/kernel/tegra_core_edp/profile Added core EDP disable/enable debugfs interface: /d/edp/vdd_core/disable_edp When core EDP is disabled the EDP limits are set to maximum rate of the respective clock. Always enable core EDP by default, even if applying initial limits failed (generate WARN in this case). Bug 1165638 Change-Id: Iacbcaae1e9570e7d6cfa754e3b4774ad5c1e31c7 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/165618 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com> Reviewed-by: Prashant Malani <pmalani@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/edp_core.c')
-rw-r--r--arch/arm/mach-tegra/edp_core.c178
1 files changed, 177 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/edp_core.c b/arch/arm/mach-tegra/edp_core.c
index 1dfc6f29d672..ab3fcda16000 100644
--- a/arch/arm/mach-tegra/edp_core.c
+++ b/arch/arm/mach-tegra/edp_core.c
@@ -32,6 +32,8 @@ static DEFINE_MUTEX(core_edp_lock);
static struct tegra_core_edp_limits core_edp_limits;
static const struct tegra_core_edp_limits *limits;
+static bool core_edp_disabled;
+
static bool core_edp_scpu_state;
static int core_edp_profile;
static int core_edp_modules_state;
@@ -62,10 +64,32 @@ static unsigned long *get_current_cap_rates(void)
core_edp_modules_state, core_edp_thermal_idx);
}
+static int set_max_rates(void)
+{
+ int i, ret;
+
+ if (core_edp_disabled)
+ return 0;
+
+ for (i = 0; i < limits->cap_clocks_num; i++) {
+ struct clk *c = limits->cap_clocks[i];
+ ret = clk_set_rate(c, clk_get_max_rate(c));
+ if (ret) {
+ pr_err("%s: Failed to set %s max rate %lu\n",
+ __func__, c->name, clk_get_max_rate(c));
+ return ret;
+ }
+ }
+ return 0;
+}
+
static int set_cap_rates(unsigned long *new_rates)
{
int i, ret;
+ if (core_edp_disabled)
+ return 0;
+
for (i = 0; i < limits->cap_clocks_num; i++) {
struct clk *c = limits->cap_clocks[i];
ret = clk_set_rate(c, new_rates[i]);
@@ -82,6 +106,9 @@ static int update_cap_rates(unsigned long *new_rates, unsigned long *old_rates)
{
int i, ret;
+ if (core_edp_disabled)
+ return 0;
+
/* 1st lower caps */
for (i = 0; i < limits->cap_clocks_num; i++) {
if (new_rates[i] < old_rates[i]) {
@@ -157,7 +184,7 @@ void __init tegra_init_core_edp_limits(unsigned int regulator_mA)
limits = &core_edp_limits;
if (start_core_edp()) {
- limits = NULL;
+ WARN(1, "Core EDP failed to set initiali limits");
return;
}
@@ -197,6 +224,115 @@ int tegra_core_edp_cpu_state_update(bool scpu_state)
return ret;
}
+/* core edp profiles update */
+static int profile_update(int profile)
+{
+ int ret = 0;
+ unsigned long *old_cap_rates;
+ unsigned long *new_cap_rates;
+
+ if (!limits) {
+ core_edp_profile = profile;
+ return 0;
+ }
+
+ mutex_lock(&core_edp_lock);
+
+ if (core_edp_profile != profile) {
+ old_cap_rates = get_current_cap_rates();
+ new_cap_rates = get_cap_rates(core_edp_scpu_state, profile,
+ core_edp_modules_state, core_edp_thermal_idx);
+ ret = update_cap_rates(new_cap_rates, old_cap_rates);
+ if (ret)
+ update_cap_rates(old_cap_rates, new_cap_rates);
+ else
+ core_edp_profile = profile;
+ }
+ mutex_unlock(&core_edp_lock);
+
+ return ret;
+}
+
+static ssize_t
+core_edp_profile_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", profile_names[core_edp_profile]);
+}
+static ssize_t
+core_edp_profile_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int i, ret;
+ size_t l;
+ const char *name;
+
+ for (i = 0; i < ARRAY_SIZE(profile_names); i++) {
+ name = profile_names[i];
+ l = strlen(name);
+ if ((l < count) && (strncmp(buf, name, l) == 0))
+ break;
+ }
+ if (i == ARRAY_SIZE(profile_names))
+ return -ENOENT;
+
+ ret = profile_update(i);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t
+available_profiles_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int i;
+ ssize_t n = 0;
+ const char *name;
+
+ for (i = 0; i < ARRAY_SIZE(profile_names); i++) {
+ name = profile_names[i];
+ if ((n + strlen(name) + 2) > PAGE_SIZE)
+ break;
+ n += sprintf(&buf[n], "%s\n", name);
+ }
+
+ return n;
+}
+
+static struct kobj_attribute profile_attribute =
+ __ATTR(profile, 0644, core_edp_profile_show, core_edp_profile_store);
+static struct kobj_attribute available_profiles_attribute =
+ __ATTR_RO(available_profiles);
+
+const struct attribute *core_edp_attributes[] = {
+ &profile_attribute.attr,
+ &available_profiles_attribute.attr,
+ NULL,
+};
+
+static struct kobject *core_edp_kobj;
+
+static int __init tegra_core_edp_late_init(void)
+{
+ core_edp_kobj = kobject_create_and_add("tegra_core_edp", kernel_kobj);
+ if (!core_edp_kobj) {
+ pr_err("%s: failed to create edp sysfs object\n", __func__);
+ return 0;
+ }
+
+ if (sysfs_create_files(core_edp_kobj, core_edp_attributes)) {
+ pr_err("%s: failed to create edp profile sysfs interface\n",
+ __func__);
+ return 0;
+ }
+ pr_info("Core EDP sysfs interface is initialized\n");
+
+ return 0;
+}
+late_initcall(tegra_core_edp_late_init);
+
#ifdef CONFIG_DEBUG_FS
static int edp_table_show(struct seq_file *s, void *data)
@@ -316,6 +452,42 @@ static const struct file_operations rates_fops = {
.release = single_release,
};
+static int disable_edp_get(void *data, u64 *val)
+{
+ *val = core_edp_disabled;
+ return 0;
+}
+static int disable_edp_set(void *data, u64 val)
+{
+ int ret;
+ unsigned long *cap_rates;
+ bool disable = val ? true : false;
+
+ if (!limits) {
+ core_edp_disabled = disable;
+ return 0;
+ }
+
+ mutex_lock(&core_edp_lock);
+
+ if (core_edp_disabled != disable) {
+ if (disable) {
+ ret = set_max_rates();
+ core_edp_disabled = true;
+ } else {
+ core_edp_disabled = false;
+ cap_rates = get_current_cap_rates();
+ ret = set_cap_rates(cap_rates);
+ }
+ pr_info("Core EDP %s%s\n", disable ? "disabled" : "enabled",
+ ret ? " with incomplete limits" : "");
+ }
+ mutex_unlock(&core_edp_lock);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(disable_edp_fops,
+ disable_edp_get, disable_edp_set, "%llu\n");
+
int __init tegra_core_edp_debugfs_init(struct dentry *edp_dir)
{
struct dentry *dir, *d;
@@ -353,6 +525,10 @@ int __init tegra_core_edp_debugfs_init(struct dentry *edp_dir)
if (!d)
goto err_out;
+ d = debugfs_create_file("disable_edp", S_IRUGO | S_IWUSR, dir, NULL,
+ &disable_edp_fops);
+ if (!d)
+ goto err_out;
return 0;
err_out: