diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 72 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 13 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_dvfs.c | 43 |
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 */ |