summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c156
1 files changed, 119 insertions, 37 deletions
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
index bfab1961bd30..34c308d29672 100644
--- a/arch/arm/mach-tegra/tegra3_dvfs.c
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -513,11 +513,19 @@ void __init tegra_soc_init_dvfs(void)
}
/*
- * sysfs interface to cap tegra dvsf domains frequencies
+ * sysfs and dvfs interfaces to cap tegra core domains frequencies
*/
+static DEFINE_MUTEX(core_cap_lock);
+
+struct core_cap {
+ int refcnt;
+ int level;
+};
+static struct core_cap tegra3_core_cap;
+static struct core_cap kdvfs_core_cap;
+static struct core_cap user_core_cap;
+
static struct kobject *cap_kobj;
-static int core_cap_count;
-static int core_cap_level;
/* Arranged in order required for enabling/lowering the cap */
static struct {
@@ -530,32 +538,95 @@ static struct {
{ .cap_name = "cap.emc" },
};
+
+static void core_cap_level_set(int level)
+{
+ int i, j;
+
+ for (j = 0; j < ARRAY_SIZE(core_millivolts); j++) {
+ int v = core_millivolts[j];
+ if ((v == 0) || (level < v))
+ break;
+ }
+ j = (j == 0) ? 0 : j - 1;
+ level = core_millivolts[j];
+
+ if (level < tegra3_core_cap.level) {
+ for (i = 0; i < ARRAY_SIZE(core_cap_table); i++)
+ if (core_cap_table[i].cap_clk)
+ clk_set_rate(core_cap_table[i].cap_clk,
+ core_cap_table[i].freqs[j]);
+ } else if (level > tegra3_core_cap.level) {
+ for (i = ARRAY_SIZE(core_cap_table) - 1; i >= 0; i--)
+ if (core_cap_table[i].cap_clk)
+ clk_set_rate(core_cap_table[i].cap_clk,
+ core_cap_table[i].freqs[j]);
+ }
+ tegra3_core_cap.level = level;
+}
+
+static void core_cap_update(void)
+{
+ int new_level = tegra3_dvfs_rail_vdd_core.max_millivolts;
+
+ if (kdvfs_core_cap.refcnt)
+ new_level = min(new_level, kdvfs_core_cap.level);
+ if (user_core_cap.refcnt)
+ new_level = min(new_level, user_core_cap.level);
+
+ if (tegra3_core_cap.level != new_level)
+ core_cap_level_set(new_level);
+}
+
+static void core_cap_enable(bool enable)
+{
+ int i;
+
+ if (enable) {
+ tegra3_core_cap.refcnt++;
+ if (tegra3_core_cap.refcnt == 1)
+ for (i = 0; i < ARRAY_SIZE(core_cap_table); i++)
+ if (core_cap_table[i].cap_clk)
+ clk_enable(core_cap_table[i].cap_clk);
+ } else if (tegra3_core_cap.refcnt) {
+ tegra3_core_cap.refcnt--;
+ if (tegra3_core_cap.refcnt == 0)
+ for (i = ARRAY_SIZE(core_cap_table) - 1; i >= 0; i--)
+ if (core_cap_table[i].cap_clk)
+ clk_disable(core_cap_table[i].cap_clk);
+ }
+ core_cap_update();
+}
+
static ssize_t
core_cap_state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
- return sprintf(buf, "%d\n", core_cap_count ? 1 : 0);
+ return sprintf(buf, "%d (%d)\n", tegra3_core_cap.refcnt ? 1 : 0,
+ user_core_cap.refcnt ? 1 : 0);
}
static ssize_t
core_cap_state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
- int i, state;
+ int state;
if (sscanf(buf, "%d", &state) != 1)
return -1;
+ mutex_lock(&core_cap_lock);
+
if (state) {
- core_cap_count++;
- for (i = 0; i < ARRAY_SIZE(core_cap_table); i++)
- if (core_cap_table[i].cap_clk)
- clk_enable(core_cap_table[i].cap_clk);
- } else if (core_cap_count) {
- core_cap_count--;
- for (i = ARRAY_SIZE(core_cap_table) - 1; i >= 0; i--)
- if (core_cap_table[i].cap_clk)
- clk_disable(core_cap_table[i].cap_clk);
+ user_core_cap.refcnt++;
+ if (user_core_cap.refcnt == 1)
+ core_cap_enable(true);
+ } else if (user_core_cap.refcnt) {
+ user_core_cap.refcnt--;
+ if (user_core_cap.refcnt == 0)
+ core_cap_enable(false);
}
+
+ mutex_unlock(&core_cap_lock);
return count;
}
@@ -563,37 +634,22 @@ static ssize_t
core_cap_level_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
- return sprintf(buf, "%d\n", core_cap_level);
+ return sprintf(buf, "%d (%d)\n", tegra3_core_cap.level,
+ user_core_cap.level);
}
static ssize_t
core_cap_level_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
- int i, j, level;
+ int level;
if (sscanf(buf, "%d", &level) != 1)
return -1;
- for (j = 0; j < ARRAY_SIZE(core_millivolts); j++) {
- int v = core_millivolts[j];
- if ((v == 0) || (level < v))
- break;
- }
- j = (j == 0) ? : j - 1;
- level = core_millivolts[j];
-
- if (level < core_cap_level) {
- for (i = 0; i < ARRAY_SIZE(core_cap_table); i++)
- if (core_cap_table[i].cap_clk)
- clk_set_rate(core_cap_table[i].cap_clk,
- core_cap_table[i].freqs[j]);
- } else if (level > core_cap_level) {
- for (i = ARRAY_SIZE(core_cap_table) - 1; i >= 0; i--)
- if (core_cap_table[i].cap_clk)
- clk_set_rate(core_cap_table[i].cap_clk,
- core_cap_table[i].freqs[j]);
- }
- core_cap_level = level;
+ mutex_lock(&core_cap_lock);
+ user_core_cap.level = level;
+ core_cap_update();
+ mutex_unlock(&core_cap_lock);
return count;
}
@@ -608,6 +664,30 @@ const struct attribute *cap_attributes[] = {
NULL,
};
+void tegra_dvfs_core_cap_enable(bool enable)
+{
+ mutex_lock(&core_cap_lock);
+
+ if (enable) {
+ kdvfs_core_cap.refcnt++;
+ if (kdvfs_core_cap.refcnt == 1)
+ core_cap_enable(true);
+ } else if (kdvfs_core_cap.refcnt) {
+ kdvfs_core_cap.refcnt--;
+ if (kdvfs_core_cap.refcnt == 0)
+ core_cap_enable(false);
+ }
+ mutex_unlock(&core_cap_lock);
+}
+
+void tegra_dvfs_core_cap_level_set(int level)
+{
+ mutex_lock(&core_cap_lock);
+ kdvfs_core_cap.level = level;
+ core_cap_update();
+ mutex_unlock(&core_cap_lock);
+}
+
static int __init init_core_cap_one(struct clk *c, unsigned long *freqs)
{
int i, v, next_v;
@@ -656,6 +736,9 @@ static int __init tegra_dvfs_init_core_cap(void)
int i;
struct clk *c = NULL;
+ tegra3_core_cap.level = kdvfs_core_cap.level = user_core_cap.level =
+ tegra3_dvfs_rail_vdd_core.max_millivolts;
+
for (i = 0; i < ARRAY_SIZE(core_cap_table); i++) {
c = tegra_get_clock_by_name(core_cap_table[i].cap_name);
if (!c || !c->parent ||
@@ -666,7 +749,6 @@ static int __init tegra_dvfs_init_core_cap(void)
}
core_cap_table[i].cap_clk = c;
}
- core_cap_level = tegra3_dvfs_rail_vdd_core.max_millivolts;
cap_kobj = kobject_create_and_add("tegra_cap", kernel_kobj);
if (!cap_kobj) {