ARM: tegra: power: Set Tegra3 CPU/core rail nominal voltage
Alex Frid [Sun, 8 May 2011 04:01:24 +0000 (21:01 -0700)]
For different Tegra3 process corners/skus/revisions/boards set nominal
voltages for CPU and core rails as well as adjust maximum clock rates
as follows.

- VDD_CORE rail nominal voltage: default value is indexed by speedo_id
of the chip (speedo_id is determined by chip sku and revision). Minimum
of the default and board specific electrical design voltage is rounded
down against core dvfs voltage ladder. The result is set as nominal
core voltage (edp voltage API is not implemented, yet).

- VDD_CPU rail nominal voltage: default value is indexed by speedo_id
of the chip. If too high, it is lowered to core nominal voltage so that
core_on_cpu dependency is resolved at nominal core level. The result is
compared with voltage required to reach CPU maximum rate as specified
in the dvfs table for the particular process corner. Again, the minimal
level is selected, and finally set as CPU nominal voltage.

After nominal voltages are determined, maximum rate for each dvfs clock
is adjusted accordingly, so that it does not exceed the rate specified
in the respective DVFS table at nominal level.

Original-Change-Id: Ia6c1c5c853f98ab185f42bf1cfd7a1d7d54d10c3
Reviewed-on: http://git-master/r/30928
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>
Reviewed-by: Narendra Damahe <ndamahe@nvidia.com>

Rebase-Id: R30393515042d199154ba708afaefb134402f551a

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

index 9be8adf..992ca73 100644 (file)
@@ -597,6 +597,24 @@ out:
        return ret;
 }
 
+/* dvfs initialization may lower default maximum rate */
+void __init tegra_init_max_rate(struct clk *c, unsigned long max_rate)
+{
+       struct clk *shared_bus_user;
+
+       if (c->max_rate <= max_rate)
+               return;
+
+       pr_warning("Lowering %s maximum rate from %lu to %lu\n",
+               c->name, c->max_rate, max_rate);
+
+       c->max_rate = max_rate;
+       list_for_each_entry(shared_bus_user,
+                           &c->shared_bus_list, u.shared_bus_user.node) {
+               shared_bus_user->max_rate = max_rate;
+       }
+}
+
 static bool tegra_keep_boot_clocks = false;
 static int __init tegra_keep_boot_clocks_setup(char *__unused)
 {
index 7b109b0..f7bed91 100644 (file)
@@ -267,6 +267,7 @@ struct tegra_clk_init_table {
 };
 
 #ifndef CONFIG_COMMON_CLK
+void tegra_init_max_rate(struct clk *c, unsigned long max_rate);
 void clk_init(struct clk *clk);
 unsigned long clk_get_rate_locked(struct clk *c);
 void clk_set_cansleep(struct clk *c);
index 75e46c2..0ff6986 100644 (file)
@@ -207,15 +207,23 @@ static int dvfs_rail_update(struct dvfs_rail *rail)
 static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
 {
        struct regulator *reg;
+       int v;
 
        if (!rail->reg) {
                reg = regulator_get(NULL, rail->reg_id);
                if (IS_ERR(reg))
                        return -EINVAL;
        }
-
        rail->reg = reg;
 
+       v = regulator_get_voltage(reg);
+       if (v < 0) {
+               pr_err("tegra_dvfs: failed initial get %s voltage\n",
+                      rail->reg_id);
+               return v;
+       }
+       rail->millivolts = v / 1000;
+       rail->new_millivolts = rail->millivolts;
        return 0;
 }
 
@@ -241,6 +249,12 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
                        i++;
 
                d->cur_millivolts = d->millivolts[i];
+               if ((d->max_millivolts) &&
+                   (d->cur_millivolts > d->max_millivolts)) {
+                       pr_warn("tegra_dvfs: voltage %d too high for dvfs on"
+                               " %s\n", d->cur_millivolts, d->clk_name);
+                       return -EINVAL;
+               }
        }
 
        d->cur_rate = rate;