ARM: tegra: dvfs: Enable EMC bridge if rail is disabled
Alex Frid [Sat, 22 Oct 2011 05:02:53 +0000 (22:02 -0700)]
When core rail is disabled it is set to nominal voltage underneath
clock framework. On Tegra3 DDR3 platforms low EMC rates are not safe
at high voltage that exceeds EMC bridge minimum level. Enable EMC
bridge explicitly in this case to set safe floor for EMC. Similarly
need to enable EMC bridge when CPU rail is disabled and pushing core
voltage (cpu-to-core voltage dependency) over bridge minimum level.

(cherry picked from commit bff814b2e46e67defde178b72bd379003b5429c2)
(cherry picked from commit e5567cb8dafcbd30797237e7bb91d77ce57de66a)

Change-Id: Ibb8dad5132f69e3325d793658b3dcc8b887974bf
Reviewed-on: http://git-master/r/62031
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>

Rebase-Id: R56f360c3b1ee25bf2dae4b886399b83e357f0225

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

index 7d60cd1..6a832b3 100644 (file)
@@ -40,6 +40,7 @@
 
 static LIST_HEAD(dvfs_rail_list);
 static DEFINE_MUTEX(dvfs_lock);
+static DEFINE_MUTEX(rail_disable_lock);
 
 static int dvfs_rail_update(struct dvfs_rail *rail);
 
@@ -475,57 +476,82 @@ static void __tegra_dvfs_rail_disable(struct dvfs_rail *rail)
 {
        int ret;
 
-       if (!rail->disabled) {
-               ret = dvfs_rail_set_voltage(rail, rail->nominal_millivolts);
-               if (ret)
-                       pr_info("dvfs: failed to set regulator %s to disable "
-                               "voltage %d\n", rail->reg_id,
-                               rail->nominal_millivolts);
-               rail->disabled = true;
-       }
+       ret = dvfs_rail_set_voltage(rail, rail->nominal_millivolts);
+       if (ret)
+               pr_info("dvfs: failed to set regulator %s to disable "
+                       "voltage %d\n", rail->reg_id,
+                       rail->nominal_millivolts);
+       rail->disabled = true;
 }
 
 /* must be called with dvfs lock held */
 static void __tegra_dvfs_rail_enable(struct dvfs_rail *rail)
 {
-       if (rail->disabled) {
-               rail->disabled = false;
-               dvfs_rail_update(rail);
-       }
+       rail->disabled = false;
+       dvfs_rail_update(rail);
 }
 
 void tegra_dvfs_rail_enable(struct dvfs_rail *rail)
 {
-       mutex_lock(&dvfs_lock);
-       __tegra_dvfs_rail_enable(rail);
-       mutex_unlock(&dvfs_lock);
+       mutex_lock(&rail_disable_lock);
+
+       if (rail->disabled) {
+               mutex_lock(&dvfs_lock);
+               __tegra_dvfs_rail_enable(rail);
+               mutex_unlock(&dvfs_lock);
+
+               tegra_dvfs_rail_post_enable(rail);
+       }
+       mutex_unlock(&rail_disable_lock);
+
 }
 
 void tegra_dvfs_rail_disable(struct dvfs_rail *rail)
 {
+       mutex_lock(&rail_disable_lock);
+       if (rail->disabled)
+               goto out;
+
+       /* rail disable will set it to nominal voltage underneath clock
+          framework - need to re-configure clock rates that are not safe
+          at nominal (yes, unsafe at nominal is ugly, but possible). Rate
+          change must be done outside of dvfs lock. */
+       if (tegra_dvfs_rail_disable_prepare(rail)) {
+               pr_info("dvfs: failed to prepare regulator %s to disable\n",
+                       rail->reg_id);
+               goto out;
+       }
+
        mutex_lock(&dvfs_lock);
        __tegra_dvfs_rail_disable(rail);
        mutex_unlock(&dvfs_lock);
+out:
+       mutex_unlock(&rail_disable_lock);
 }
 
 int tegra_dvfs_rail_disable_by_name(const char *reg_id)
 {
+       struct dvfs_rail *rail = tegra_dvfs_get_rail_by_name(reg_id);
+       if (!rail)
+               return -EINVAL;
+
+       tegra_dvfs_rail_disable(rail);
+       return 0;
+}
+
+struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id)
+{
        struct dvfs_rail *rail;
-       int ret = 0;
 
        mutex_lock(&dvfs_lock);
        list_for_each_entry(rail, &dvfs_rail_list, node) {
                if (!strcmp(reg_id, rail->reg_id)) {
-                       __tegra_dvfs_rail_disable(rail);
-                       goto out;
+                       mutex_unlock(&dvfs_lock);
+                       return rail;
                }
        }
-
-       ret = -EINVAL;
-
-out:
        mutex_unlock(&dvfs_lock);
-       return ret;
+       return NULL;
 }
 
 bool tegra_dvfs_rail_updating(struct clk *clk)
index e80eb72..93d0009 100644 (file)
@@ -96,6 +96,7 @@ void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n);
 void tegra_dvfs_rail_enable(struct dvfs_rail *rail);
 void tegra_dvfs_rail_disable(struct dvfs_rail *rail);
 bool tegra_dvfs_rail_updating(struct clk *clk);
+struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id);
 int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate);
 void tegra_dvfs_core_cap_enable(bool enable);
 void tegra_dvfs_core_cap_level_set(int level);
@@ -116,6 +117,8 @@ static inline void tegra_dvfs_rail_disable(struct dvfs_rail *rail)
 {}
 static inline bool tegra_dvfs_rail_updating(struct clk *clk)
 { return false; }
+static inline struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id)
+{ return NULL; }
 static inline int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
 { return 0; }
 static inline void tegra_dvfs_core_cap_enable(bool enable)
@@ -124,4 +127,14 @@ static inline void tegra_dvfs_core_cap_level_set(int level)
 {}
 #endif
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail);
+int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail);
+#else
+static inline int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
+{ return 0; }
+static inline int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail)
+{ return 0; }
+#endif
+
 #endif