summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/dvfs.c72
-rw-r--r--arch/arm/mach-tegra/dvfs.h13
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c43
3 files changed, 105 insertions, 23 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 5fdb8e797650..dc0d779870a6 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -41,6 +41,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);
@@ -476,57 +477,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)
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index 85077c005525..d867ac4299cf 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -97,6 +97,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);
@@ -119,6 +120,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)
@@ -127,4 +130,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
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
index d07195366bf6..1acc76a3eda3 100644
--- a/arch/arm/mach-tegra/tegra3_dvfs.c
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -26,6 +26,7 @@
#include "dvfs.h"
#include "fuse.h"
#include "board.h"
+#include "tegra3_emc.h"
static bool tegra_dvfs_cpu_disabled;
static bool tegra_dvfs_core_disabled;
@@ -518,6 +519,48 @@ void __init tegra_soc_init_dvfs(void)
tegra_dvfs_core_disabled ? "disabled" : "enabled");
}
+int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
+{
+ int ret = 0;
+
+ if (tegra_emc_get_dram_type() != DRAM_TYPE_DDR3)
+ return ret;
+
+ if (((&tegra3_dvfs_rail_vdd_core == rail) &&
+ (rail->nominal_millivolts > TEGRA_EMC_BRIDGE_MVOLTS_MIN)) ||
+ ((&tegra3_dvfs_rail_vdd_cpu == rail) &&
+ (tegra3_get_core_floor_mv(rail->nominal_millivolts) >
+ TEGRA_EMC_BRIDGE_MVOLTS_MIN))) {
+ struct clk *bridge = tegra_get_clock_by_name("bridge.emc");
+ BUG_ON(!bridge);
+
+ ret = clk_enable(bridge);
+ pr_info("%s: %s: %s bridge.emc\n", __func__,
+ rail->reg_id, ret ? "failed to enable" : "enabled");
+ }
+ return ret;
+}
+
+int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail)
+{
+ if (tegra_emc_get_dram_type() != DRAM_TYPE_DDR3)
+ return 0;
+
+ if (((&tegra3_dvfs_rail_vdd_core == rail) &&
+ (rail->nominal_millivolts > TEGRA_EMC_BRIDGE_MVOLTS_MIN)) ||
+ ((&tegra3_dvfs_rail_vdd_cpu == rail) &&
+ (tegra3_get_core_floor_mv(rail->nominal_millivolts) >
+ TEGRA_EMC_BRIDGE_MVOLTS_MIN))) {
+ struct clk *bridge = tegra_get_clock_by_name("bridge.emc");
+ BUG_ON(!bridge);
+
+ clk_disable(bridge);
+ pr_info("%s: %s: disabled bridge.emc\n",
+ __func__, rail->reg_id);
+ }
+ return 0;
+}
+
/*
* sysfs and dvfs interfaces to cap tegra core domains frequencies
*/