summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-04-30 23:23:42 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:42:36 -0800
commitf628c14c12da489c09aa5ce3e7aa4f04fe38081b (patch)
tree31f89125f1cdcf6d9cd44042d1e91a62a2daef4c /arch/arm/mach-tegra
parent928f76ecb233edd5ae5504bde7aecbc033d9ff72 (diff)
ARM: tegra: power: Add Tegra3 CPU/CORE rails dependencies
On Tegra3 VDD_CPU must be within [VDD_CORE - 300, VDD_CORE] range. Updated tegra dvfs accordingly, and resolved circular dependencies between CPU and CORE rails created by this requirement. Original-Change-Id: I9c332ca2b4f4ed1599cb0712eb3eca55a1fa1539 Reviewed-on: http://git-master/r/29935 Reviewed-by: Aleksandr Frid <afrid@nvidia.com> Tested-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> Tested-by: Diwakar Tundlam <dtundlam@nvidia.com> Rebase-Id: R6aa2bc61513ab16c4551ebeb193e01803501f596
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/dvfs.c23
-rw-r--r--arch/arm/mach-tegra/dvfs.h2
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c41
3 files changed, 56 insertions, 10 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 0e7586f84a6d..64d55aed3454 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -107,6 +107,7 @@ static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
if (rail->disabled)
return 0;
+ rail->resolving_to = true;
steps = DIV_ROUND_UP(abs(millivolts - rail->millivolts), rail->step);
for (i = 0; i < steps; i++) {
@@ -124,7 +125,7 @@ static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
list_for_each_entry(rel, &rail->relationships_to, to_node) {
ret = dvfs_rail_update(rel->to);
if (ret)
- return ret;
+ goto out;
}
if (!rail->disabled) {
@@ -136,7 +137,7 @@ static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
}
if (ret) {
pr_err("Failed to set dvfs regulator %s\n", rail->reg_id);
- return ret;
+ goto out;
}
rail->millivolts = rail->new_millivolts;
@@ -148,16 +149,18 @@ static int dvfs_rail_set_voltage(struct dvfs_rail *rail, int millivolts)
list_for_each_entry(rel, &rail->relationships_to, to_node) {
ret = dvfs_rail_update(rel->to);
if (ret)
- return ret;
+ goto out;
}
}
if (unlikely(rail->millivolts != millivolts)) {
pr_err("%s: rail didn't reach target %d in %d steps (%d)\n",
__func__, millivolts, steps, rail->millivolts);
- return -EINVAL;
+ ret = -EINVAL;
}
+out:
+ rail->resolving_to = false;
return ret;
}
@@ -180,6 +183,11 @@ static int dvfs_rail_update(struct dvfs_rail *rail)
if (!rail->reg)
return 0;
+ /* if rail update is entered while resolving circular dependencies,
+ abort recursion */
+ if (rail->resolving_to)
+ return 0;
+
/* Find the maximum voltage requested by any clock */
list_for_each_entry(d, &rail->dvfs, reg_node)
millivolts = max(d->cur_millivolts, millivolts);
@@ -311,13 +319,14 @@ static bool tegra_dvfs_all_rails_suspended(void)
return all_suspended;
}
-static bool tegra_dvfs_from_rails_suspended(struct dvfs_rail *to)
+static bool tegra_dvfs_from_rails_suspended_or_solved(struct dvfs_rail *to)
{
struct dvfs_relationship *rel;
bool all_suspended = true;
list_for_each_entry(rel, &to->relationships_from, from_node)
- if (!rel->from->suspended && !rel->from->disabled)
+ if (!rel->from->suspended && !rel->from->disabled &&
+ !rel->solved_at_nominal)
all_suspended = false;
return all_suspended;
@@ -330,7 +339,7 @@ static int tegra_dvfs_suspend_one(void)
list_for_each_entry(rail, &dvfs_rail_list, node) {
if (!rail->suspended && !rail->disabled &&
- tegra_dvfs_from_rails_suspended(rail)) {
+ tegra_dvfs_from_rails_suspended_or_solved(rail)) {
ret = dvfs_rail_set_voltage(rail,
rail->nominal_millivolts);
if (ret)
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index f33cb2c6408c..6de35a525411 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -40,6 +40,7 @@ struct dvfs_relationship {
struct list_head to_node; /* node in relationship_to list */
struct list_head from_node; /* node in relationship_from list */
+ bool solved_at_nominal;
};
struct dvfs_rail {
@@ -50,6 +51,7 @@ struct dvfs_rail {
int step;
bool disabled;
bool updating;
+ bool resolving_to;
struct list_head node; /* node in dvfs_rail_list */
struct list_head dvfs; /* list head of attached dvfs clocks */
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
index 24685fa271c8..3e04bd5d3059 100644
--- a/arch/arm/mach-tegra/tegra3_dvfs.c
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -35,11 +35,14 @@ static const int core_millivolts[MAX_DVFS_FREQS] =
#define KHZ 1000
#define MHZ 1000000
+#define VDD_CPU_BELOW_VDD_CORE_MAX 300
+
static struct dvfs_rail tegra3_dvfs_rail_vdd_cpu = {
.reg_id = "vdd_cpu",
.max_millivolts = 1125,
.min_millivolts = 800,
.nominal_millivolts = 1000,
+ .step = VDD_CPU_BELOW_VDD_CORE_MAX,
};
static struct dvfs_rail tegra3_dvfs_rail_vdd_core = {
@@ -47,7 +50,7 @@ static struct dvfs_rail tegra3_dvfs_rail_vdd_core = {
.max_millivolts = 1300,
.min_millivolts = 950,
.nominal_millivolts = 1200,
- .step = 100, /* FIXME: step vdd_core by 100 mV - maybe not needed */
+ .step = VDD_CPU_BELOW_VDD_CORE_MAX,
.disabled = true, /* FIXME: replace with sysfs control */
};
@@ -56,6 +59,38 @@ static struct dvfs_rail *tegra3_dvfs_rails[] = {
&tegra3_dvfs_rail_vdd_core,
};
+
+/* vdd_core must not be lower than vdd_cpu */
+static int tegra3_dvfs_rel_vdd_cpu_vdd_core(struct dvfs_rail *vdd_cpu,
+ struct dvfs_rail *vdd_core)
+{
+ int core_floor = max(vdd_cpu->new_millivolts, vdd_cpu->millivolts);
+ return max(vdd_core->new_millivolts, core_floor);
+}
+
+/* vdd_cpu must be within VDD_CPU_BELOW_VDD_CORE_MAX below vdd_core */
+static int tegra3_dvfs_rel_vdd_core_vdd_cpu(struct dvfs_rail *vdd_core,
+ struct dvfs_rail *vdd_cpu)
+{
+ int cpu_floor = max(vdd_core->new_millivolts, vdd_core->millivolts) -
+ VDD_CPU_BELOW_VDD_CORE_MAX;
+ return max(vdd_cpu->new_millivolts, cpu_floor);
+}
+
+static struct dvfs_relationship tegra3_dvfs_relationships[] = {
+ {
+ .from = &tegra3_dvfs_rail_vdd_cpu,
+ .to = &tegra3_dvfs_rail_vdd_core,
+ .solve = tegra3_dvfs_rel_vdd_cpu_vdd_core,
+ .solved_at_nominal = true,
+ },
+ {
+ .from = &tegra3_dvfs_rail_vdd_core,
+ .to = &tegra3_dvfs_rail_vdd_cpu,
+ .solve = tegra3_dvfs_rel_vdd_core_vdd_cpu,
+ },
+};
+
#define CPU_DVFS(_clk_name, _speedo_id, _process_id, _mult, _freqs...) \
{ \
.clk_name = _clk_name, \
@@ -220,8 +255,8 @@ void __init tegra_soc_init_dvfs(void)
#endif
tegra_dvfs_init_rails(tegra3_dvfs_rails, ARRAY_SIZE(tegra3_dvfs_rails));
-
- /* FIXME: add [CPU/CORE/AON] relationships here */
+ tegra_dvfs_add_relationships(tegra3_dvfs_relationships,
+ ARRAY_SIZE(tegra3_dvfs_relationships));
init_dvfs_from_table(cpu_dvfs_table, ARRAY_SIZE(cpu_dvfs_table),
speedo_id, cpu_process_id);