ARM: tegra: power: Add Tegra3 CPU/CORE rails dependencies
Alex Frid [Sun, 1 May 2011 06:23:42 +0000 (23:23 -0700)]
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

arch/arm/mach-tegra/dvfs.c
arch/arm/mach-tegra/dvfs.h

index d7bec55..3f43766 100644 (file)
@@ -106,6 +106,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++) {
@@ -123,7 +124,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) {
@@ -135,7 +136,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;
@@ -147,16 +148,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;
 }
 
@@ -179,6 +182,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);
@@ -310,13 +318,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;
@@ -329,7 +338,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)
index fff196a..c507861 100644 (file)
@@ -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 */