summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-10-21 22:02:53 -0700
committerSimone Willett <swillett@nvidia.com>2011-10-27 15:32:07 -0700
commitbff814b2e46e67defde178b72bd379003b5429c2 (patch)
tree928fcadb5b0198b07f9f60a821912de3735c8bdc
parenta9469db8c4c04fa7cd8f080bafdca26d99a3018c (diff)
ARM: tegra: dvfs: Enable EMC bridge if rail is disabled
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. Change-Id: I0cb67cf14ee3849076b63a737fd8c11356cfbf14 Reviewed-on: http://git-master/r/60119 Tested-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Bo Yan <byan@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/dvfs.c71
-rw-r--r--arch/arm/mach-tegra/dvfs.h10
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c43
3 files changed, 94 insertions, 30 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 021ea8c45913..54e8e9afe7c9 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -47,6 +47,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);
@@ -549,57 +550,67 @@ 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;
- 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;
- }
- }
-
- ret = -EINVAL;
+ struct dvfs_rail *rail = tegra_dvfs_get_rail_by_name(reg_id);
+ if (!rail)
+ return -EINVAL;
-out:
- mutex_unlock(&dvfs_lock);
- return ret;
+ tegra_dvfs_rail_disable(rail);
+ return 0;
}
struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id)
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index 06a5bb7f8488..2c1d4a93e430 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -152,4 +152,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
*/