summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/tegra_core_sysfs_limits.h4
-rw-r--r--arch/arm/mach-tegra/tegra_core_volt_cap.c225
2 files changed, 117 insertions, 112 deletions
diff --git a/arch/arm/mach-tegra/tegra_core_sysfs_limits.h b/arch/arm/mach-tegra/tegra_core_sysfs_limits.h
index 7d12748a1a71..842626622467 100644
--- a/arch/arm/mach-tegra/tegra_core_sysfs_limits.h
+++ b/arch/arm/mach-tegra/tegra_core_sysfs_limits.h
@@ -32,6 +32,10 @@ struct core_bus_limit_table {
int level;
struct notifier_block qos_nb;
int pm_qos_class;
+ bool applied;
+ int (*update)(struct core_bus_limit_table *bus_limit,
+ unsigned long qos_limit_level);
+
};
int tegra_init_core_cap(struct core_dvfs_cap_table *table, int table_size,
diff --git a/arch/arm/mach-tegra/tegra_core_volt_cap.c b/arch/arm/mach-tegra/tegra_core_volt_cap.c
index 7ba66e499d7b..a0818828e1f1 100644
--- a/arch/arm/mach-tegra/tegra_core_volt_cap.c
+++ b/arch/arm/mach-tegra/tegra_core_volt_cap.c
@@ -311,110 +311,158 @@ int __init tegra_init_core_cap(
#define MAX_BUS_NUM 8
-static DEFINE_MUTEX(bus_cap_lock);
+static DEFINE_MUTEX(bus_limit_lock);
static const struct attribute *bus_cap_attributes[2 * MAX_BUS_NUM + 1];
+static const struct attribute *bus_floor_attributes[2 * MAX_BUS_NUM + 1];
-static int _cap_update(struct core_bus_limit_table *bus_cap,
- unsigned long qos_cap_level)
+static int _floor_update(struct core_bus_limit_table *bus_limit,
+ unsigned long qos_limit_level)
{
int ret = 0;
unsigned long level, max_level;
- struct clk *c = bus_cap->limit_clk;
+ struct clk *c = bus_limit->limit_clk;
BUG_ON(!c);
max_level = clk_get_max_rate(c);
- level = bus_cap->refcnt ? bus_cap->level : max_level;
+
+ if (!bus_limit->refcnt && !qos_limit_level) {
+ if (bus_limit->applied) {
+ tegra_clk_disable_unprepare(c);
+ bus_limit->applied = false;
+ }
+ return 0;
+ }
+
+ level = bus_limit->refcnt ? bus_limit->level : 0;
+
+ /* qos level is in kHz, bus floor level is in Hz */
+ if (qos_limit_level < max_level / 1000)
+ qos_limit_level *= 1000;
+ else
+ qos_limit_level = max_level;
+
+ level = max(level, qos_limit_level);
+
+
+ ret = clk_set_rate(c, level);
+ if (!bus_limit->applied)
+ ret = tegra_clk_prepare_enable(c);
+
+ if (ret) {
+ pr_err("%s: Failed to floor %s at level %lu\n",
+ __func__, bus_limit->limit_clk_name, level);
+ return ret;
+ }
+ bus_limit->applied = true;
+
+ return 0;
+}
+
+static int _cap_update(struct core_bus_limit_table *bus_limit,
+ unsigned long qos_limit_level)
+{
+ int ret = 0;
+ unsigned long level, max_level;
+ struct clk *c = bus_limit->limit_clk;
+
+ BUG_ON(!c);
+ max_level = clk_get_max_rate(c);
+ level = bus_limit->refcnt ? bus_limit->level : max_level;
/* qos level is in kHz, bus cap level is in Hz */
- if (qos_cap_level < max_level / 1000)
- qos_cap_level *= 1000;
+ if (qos_limit_level < max_level / 1000)
+ qos_limit_level *= 1000;
else
- qos_cap_level = max_level;
+ qos_limit_level = max_level;
- level = min(level, qos_cap_level);
+ level = min(level, qos_limit_level);
ret = clk_set_rate(c, level);
if (ret)
pr_err("%s: Failed to cap %s at level %lu\n",
- __func__, bus_cap->limit_clk_name, level);
+ __func__, bus_limit->limit_clk_name, level);
return ret;
}
-static int bus_cap_update(struct core_bus_limit_table *bus_cap)
+static int bus_limit_update(struct core_bus_limit_table *bus_limit)
{
- unsigned long qos_cap_level = ULONG_MAX;
- if (bus_cap->pm_qos_class)
- qos_cap_level = pm_qos_request(bus_cap->pm_qos_class);
- return _cap_update(bus_cap, qos_cap_level);
+ unsigned long qos_limit_level;
+
+ if (bus_limit->pm_qos_class)
+ qos_limit_level = pm_qos_request(bus_limit->pm_qos_class);
+ else
+ qos_limit_level = bus_limit->update == _cap_update ?
+ ULONG_MAX : 0;
+ return bus_limit->update(bus_limit, qos_limit_level);
}
static ssize_t
-bus_cap_state_show(struct kobject *kobj, struct kobj_attribute *attr,
+bus_limit_state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
- struct core_bus_limit_table *bus_cap = refcnt_to_bus(attr);
- return sprintf(buf, "%d\n", bus_cap->refcnt ? 1 : 0);
+ struct core_bus_limit_table *bus_limit = refcnt_to_bus(attr);
+ return sprintf(buf, "%d\n", bus_limit->refcnt ? 1 : 0);
}
static ssize_t
-bus_cap_state_store(struct kobject *kobj, struct kobj_attribute *attr,
+bus_limit_state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int state;
- struct core_bus_limit_table *bus_cap = refcnt_to_bus(attr);
+ struct core_bus_limit_table *bus_limit = refcnt_to_bus(attr);
if (sscanf(buf, "%d", &state) != 1)
return -1;
- mutex_lock(&bus_cap_lock);
+ mutex_lock(&bus_limit_lock);
if (state) {
- bus_cap->refcnt++;
- if (bus_cap->refcnt == 1)
- bus_cap_update(bus_cap);
- } else if (bus_cap->refcnt) {
- bus_cap->refcnt--;
- if (bus_cap->refcnt == 0)
- bus_cap_update(bus_cap);
+ bus_limit->refcnt++;
+ if (bus_limit->refcnt == 1)
+ bus_limit_update(bus_limit);
+ } else if (bus_limit->refcnt) {
+ bus_limit->refcnt--;
+ if (bus_limit->refcnt == 0)
+ bus_limit_update(bus_limit);
}
- mutex_unlock(&bus_cap_lock);
+ mutex_unlock(&bus_limit_lock);
return count;
}
static ssize_t
-bus_cap_level_show(struct kobject *kobj, struct kobj_attribute *attr,
+bus_limit_level_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
- struct core_bus_limit_table *bus_cap = level_to_bus(attr);
- return sprintf(buf, "%d\n", bus_cap->level);
+ struct core_bus_limit_table *bus_limit = level_to_bus(attr);
+ return sprintf(buf, "%d\n", bus_limit->level);
}
static ssize_t
-bus_cap_level_store(struct kobject *kobj, struct kobj_attribute *attr,
+bus_limit_level_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int level;
- struct core_bus_limit_table *bus_cap = level_to_bus(attr);
+ struct core_bus_limit_table *bus_limit = level_to_bus(attr);
if (sscanf(buf, "%d", &level) != 1)
return -1;
- mutex_lock(&bus_cap_lock);
- if (bus_cap->level != level) {
- bus_cap->level = level;
- bus_cap_update(bus_cap);
+ mutex_lock(&bus_limit_lock);
+ if (bus_limit->level != level) {
+ bus_limit->level = level;
+ bus_limit_update(bus_limit);
}
- mutex_unlock(&bus_cap_lock);
+ mutex_unlock(&bus_limit_lock);
return count;
}
-static int qos_cap_notify(struct notifier_block *nb,
- unsigned long qos_cap_level, void *p)
+static int qos_limit_notify(struct notifier_block *nb,
+ unsigned long qos_limit_level, void *p)
{
- struct core_bus_limit_table *bus_cap = nb_to_bus(nb);
+ struct core_bus_limit_table *bus_limit = nb_to_bus(nb);
- mutex_lock(&bus_cap_lock);
- _cap_update(bus_cap, qos_cap_level);
- mutex_unlock(&bus_cap_lock);
+ mutex_lock(&bus_limit_lock);
+ bus_limit->update(bus_limit, qos_limit_level);
+ mutex_unlock(&bus_limit_lock);
return NOTIFY_OK;
}
@@ -438,14 +486,15 @@ int __init tegra_init_shared_bus_cap(
table[i].limit_clk = c;
table[i].level = clk_get_max_rate(c);
table[i].refcnt = 0;
- table[i].refcnt_attr.show = bus_cap_state_show;
- table[i].refcnt_attr.store = bus_cap_state_store;
- table[i].level_attr.show = bus_cap_level_show;
- table[i].level_attr.store = bus_cap_level_store;
+ table[i].refcnt_attr.show = bus_limit_state_show;
+ table[i].refcnt_attr.store = bus_limit_state_store;
+ table[i].level_attr.show = bus_limit_level_show;
+ table[i].level_attr.store = bus_limit_level_store;
+ table[i].update = _cap_update;
bus_cap_attributes[j++] = &table[i].refcnt_attr.attr;
bus_cap_attributes[j++] = &table[i].level_attr.attr;
if (table[i].pm_qos_class) {
- table[i].qos_nb.notifier_call = qos_cap_notify;
+ table[i].qos_nb.notifier_call = qos_limit_notify;
if (pm_qos_add_notifier(
table[i].pm_qos_class, &table[i].qos_nb)) {
pr_err("%s: Failed register %s with PM QoS\n",
@@ -461,64 +510,6 @@ int __init tegra_init_shared_bus_cap(
return 0;
}
-static DEFINE_MUTEX(bus_floor_lock);
-const struct attribute *bus_floor_attributes[2 * MAX_BUS_NUM + 1];
-
-static ssize_t
-bus_floor_state_show(struct kobject *kobj, struct kobj_attribute *attr,
- char *buf)
-{
- struct core_bus_limit_table *bus_floor = refcnt_to_bus(attr);
- struct clk *c = bus_floor->limit_clk;
- return sprintf(buf, "%d\n", tegra_is_clk_enabled(c) ? 1 : 0);
-}
-static ssize_t
-bus_floor_state_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
-{
- int state;
- struct core_bus_limit_table *bus_floor = refcnt_to_bus(attr);
- struct clk *c = bus_floor->limit_clk;
-
- if (sscanf(buf, "%d", &state) != 1)
- return -EINVAL;
-
- if (state) {
- int ret = tegra_clk_prepare_enable(c);
- if (ret)
- return ret;
- } else {
- tegra_clk_disable_unprepare(c);
- }
- return count;
-}
-
-static ssize_t
-bus_floor_level_show(struct kobject *kobj, struct kobj_attribute *attr,
- char *buf)
-{
- struct core_bus_limit_table *bus_floor = level_to_bus(attr);
- return sprintf(buf, "%d\n", bus_floor->level);
-}
-static ssize_t
-bus_floor_level_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
-{
- int level, ret;
- struct core_bus_limit_table *bus_floor = level_to_bus(attr);
- struct clk *c = bus_floor->limit_clk;
-
- if (sscanf(buf, "%d", &level) != 1)
- return -EINVAL;
-
- mutex_lock(&bus_floor_lock);
- ret = clk_set_rate(c, level);
- if (!ret)
- bus_floor->level = level;
- mutex_unlock(&bus_floor_lock);
- return ret ? : count;
-}
-
int __init tegra_init_shared_bus_floor(
struct core_bus_limit_table *table, int table_size,
struct kobject *floor_kobj)
@@ -538,12 +529,22 @@ int __init tegra_init_shared_bus_floor(
}
table[i].limit_clk = c;
table[i].level = clk_get_max_rate(c);
- table[i].refcnt_attr.show = bus_floor_state_show;
- table[i].refcnt_attr.store = bus_floor_state_store;
- table[i].level_attr.show = bus_floor_level_show;
- table[i].level_attr.store = bus_floor_level_store;
+ table[i].refcnt_attr.show = bus_limit_state_show;
+ table[i].refcnt_attr.store = bus_limit_state_store;
+ table[i].level_attr.show = bus_limit_level_show;
+ table[i].level_attr.store = bus_limit_level_store;
+ table[i].update = _floor_update;
bus_floor_attributes[j++] = &table[i].refcnt_attr.attr;
bus_floor_attributes[j++] = &table[i].level_attr.attr;
+ if (table[i].pm_qos_class) {
+ table[i].qos_nb.notifier_call = qos_limit_notify;
+ if (pm_qos_add_notifier(
+ table[i].pm_qos_class, &table[i].qos_nb)) {
+ pr_err("%s: Failed register %s with PM QoS\n",
+ __func__, table[i].limit_clk_name);
+ table[i].pm_qos_class = 0;
+ }
+ }
}
bus_floor_attributes[j] = NULL;