Overclocking and expand voltages
/arch/arm/mach-msm/acpuclock-scorpion.c
blob:a7972a36b43819435884b04882703f69eac3d595 -> blob:da51162d5ad0246de7a488ffefec9e192a73472e
--- arch/arm/mach-msm/acpuclock-scorpion.c
+++ arch/arm/mach-msm/acpuclock-scorpion.c
@@ -27,6 +27,9 @@
#include <mach/msm_iomap.h>
#include "acpuclock.h"
+#ifdef CONFIG_MSM_CPU_AVS
+#include "avs.h"
+#endif
#include "proc_comm.h"
#include "clock.h"
@@ -76,8 +79,6 @@ struct clkctl_acpu_speed acpu_freq_tbl[]
{ 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 925, 14000},
{ 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 925, 14000 },
{ 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 925, 29000 },
- /* Work arround for acpu resume hung, GPLL is turn off by arm9 */
- /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050, 29000 },*/
{ 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 975, 58000 },
{ 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 975, 117000 },
{ 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1000, 117000 },
@@ -155,14 +156,15 @@ static void __init acpuclk_init_cpufreq_
struct clock_state {
struct clkctl_acpu_speed *current_speed;
- struct mutex lock;
+ struct mutex lock;
uint32_t acpu_switch_time_us;
uint32_t max_speed_delta_khz;
uint32_t vdd_switch_time_us;
- unsigned long power_collapse_khz;
- unsigned long wait_for_irq_khz;
+ unsigned long power_collapse_khz;
+ unsigned long wait_for_irq_khz;
struct clk* clk_ebi1;
- struct regulator *regulator;
+ struct regulator *regulator;
+ int (*acpu_set_vdd) (int mvolts);
};
static struct clock_state drv_state = { 0 };
@@ -286,27 +288,41 @@ static void select_clock(unsigned src, u
writel(val | ((src & 3) << 1), SPSS_CLK_SEL_ADDR);
}
-static int acpuclk_set_vdd_level(int vdd)
+static int acpu_set_vdd(int vdd)
{
if (!drv_state.regulator || IS_ERR(drv_state.regulator)) {
drv_state.regulator = regulator_get(NULL, "acpu_vcore");
if (IS_ERR(drv_state.regulator)) {
- pr_info("acpuclk_set_vdd_level %d no regulator\n", vdd);
+ pr_info("acpu_set_vdd %d no regulator\n", vdd);
/* Assume that the PMIC supports scaling the processor
* to its maximum frequency at its default voltage.
*/
- return 0;
+ return -ENODEV;
}
- pr_info("acpuclk_set_vdd_level got regulator\n");
+ pr_info("acpu_set_vdd got regulator\n");
}
vdd *= 1000; /* mV -> uV */
return regulator_set_voltage(drv_state.regulator, vdd, vdd);
}
+static int acpuclk_set_vdd_level(int vdd)
+{
+ if (drv_state.acpu_set_vdd)
+ return drv_state.acpu_set_vdd(vdd);
+ else {
+ /* Assume that the PMIC supports scaling the processor
+ * to its maximum frequency at its default voltage.
+ */
+ return 0;
+ }
+}
+
int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason)
{
struct clkctl_acpu_speed *cur, *next;
unsigned long flags;
+ int rc = 0;
+ int freq_index = 0;
cur = drv_state.current_speed;
@@ -325,16 +341,29 @@ int acpuclk_set_rate(unsigned long rate,
if (next->acpu_khz == 0)
return -EINVAL;
next++;
+ freq_index++;
}
if (reason == SETRATE_CPUFREQ) {
mutex_lock(&drv_state.lock);
+#ifdef CONFIG_MSM_CPU_AVS
+ /* Notify avs before changing frequency */
+ rc = avs_adjust_freq(freq_index, 1);
+ if (rc) {
+ printk(KERN_ERR
+ "acpuclock: Unable to increase ACPU "
+ "vdd: %d.\n", (int) rate);
+ mutex_unlock(&drv_state.lock);
+ return rc;
+ }
+#endif
/* Increase VDD if needed. */
if (next->vdd > cur->vdd) {
- if (acpuclk_set_vdd_level(next->vdd)) {
- pr_err("acpuclock: Unable to increase ACPU VDD.\n");
+ rc = acpuclk_set_vdd_level(next->vdd);
+ if (rc) {
+ pr_err("acpuclock: Unable to increase ACPU VDD from %d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate);
mutex_unlock(&drv_state.lock);
- return -EINVAL;
+ return rc;
}
}
}
@@ -367,24 +396,28 @@ int acpuclk_set_rate(unsigned long rate,
spin_unlock_irqrestore(&acpu_lock, flags);
-#ifndef CONFIG_AXI_SCREEN_POLICY
if (reason == SETRATE_CPUFREQ || reason == SETRATE_PC) {
if (cur->axiclk_khz != next->axiclk_khz)
clk_set_rate(drv_state.clk_ebi1, next->axiclk_khz * 1000);
- DEBUG("acpuclk_set_rate switch axi to %d\n",
- clk_get_rate(drv_state.clk_ebi1));
}
-#endif
+
if (reason == SETRATE_CPUFREQ) {
+#ifdef CONFIG_MSM_CPU_AVS
+ /* notify avs after changing frequency */
+ rc = avs_adjust_freq(freq_index, 0);
+ if (rc)
+ printk(KERN_ERR "acpuclock: Unable to drop ACPU vdd: %d.\n", (int) rate);
+#endif
/* Drop VDD level if we can. */
if (next->vdd < cur->vdd) {
- if (acpuclk_set_vdd_level(next->vdd))
- pr_err("acpuclock: Unable to drop ACPU VDD.\n");
+ rc = acpuclk_set_vdd_level(next->vdd);
+ if (rc)
+ pr_err("acpuclock: Unable to drop ACPU VDD from %d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate);
}
mutex_unlock(&drv_state.lock);
}
- return 0;
+ return rc;
}
static unsigned __init acpuclk_find_speed(void)
@@ -435,15 +468,15 @@ static void __init acpuclk_init(void)
BUG();
}
- /* Move to 768MHz for boot, which is a safe frequency
+ /* Move to 998MHz for boot, which is a safe frequency
* for all versions of Scorpion at the moment.
*/
speed = acpu_freq_tbl;
for (;;) {
- if (speed->acpu_khz == 768000)
+ if (speed->acpu_khz == 998400)
break;
if (speed->acpu_khz == 0) {
- pr_err("acpuclk_init: cannot find 768MHz\n");
+ pr_err("acpuclk_init: cannot find 998MHz\n");
BUG();
}
speed++;
@@ -501,6 +534,23 @@ unsigned long acpuclk_wait_for_irq(void)
return ret * 1000;
}
+#ifdef CONFIG_MSM_CPU_AVS
+static int __init acpu_avs_init(int (*set_vdd) (int), int khz)
+{
+ int i;
+ int freq_count = 0;
+ int freq_index = -1;
+
+ for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) {
+ freq_count++;
+ if (acpu_freq_tbl[i].acpu_khz == khz)
+ freq_index = i;
+ }
+
+ return avs_init(set_vdd, freq_count, freq_index);
+}
+#endif
+
void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata)
{
spin_lock_init(&acpu_lock);
@@ -511,14 +561,66 @@ void __init msm_acpu_clock_init(struct m
drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us;
drv_state.power_collapse_khz = clkdata->power_collapse_khz;
drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz;
-
+ drv_state.acpu_set_vdd = acpu_set_vdd;
+
if (clkdata->mpll_khz)
acpu_mpll->acpu_khz = clkdata->mpll_khz;
acpuclk_init();
acpuclk_init_cpufreq_table();
drv_state.clk_ebi1 = clk_get(NULL,"ebi1_clk");
-#ifndef CONFIG_AXI_SCREEN_POLICY
clk_set_rate(drv_state.clk_ebi1, drv_state.current_speed->axiclk_khz * 1000);
+
+#ifdef CONFIG_MSM_CPU_AVS
+ if (!acpu_avs_init(drv_state.acpu_set_vdd,
+ drv_state.current_speed->acpu_khz)) {
+ /* avs init successful. avs will handle voltage changes */
+ drv_state.acpu_set_vdd = NULL;
+ }
#endif
}
+
+#ifdef CONFIG_CPU_FREQ_VDD_LEVELS
+#ifndef CONFIG_MSM_CPU_AVS
+ssize_t acpuclk_get_vdd_levels_str(char *buf)
+{
+ int i, len = 0;
+ if (buf)
+ {
+ mutex_lock(&drv_state.lock);
+ for (i = 0; acpu_freq_tbl[i].acpu_khz; i++)
+ {
+ if (freq_table[i].frequency != CPUFREQ_ENTRY_INVALID)
+ len += sprintf(buf + len, "%8u: %4d\n", acpu_freq_tbl[i].acpu_khz, acpu_freq_tbl[i].vdd);
+ }
+ mutex_unlock(&drv_state.lock);
+ }
+ return len;
+}
+
+void acpuclk_set_vdd(unsigned acpu_khz, int max_vdd)
+{
+ int i;
+ struct clkctl_acpu_speed *cur;
+ cur = drv_state.current_speed;
+ max_vdd = max_vdd / 25 * 25; //! regulator only accepts multiples of 25 (mV)
+ mutex_lock(&drv_state.lock);
+ for (i = 0; acpu_freq_tbl[i].acpu_khz; i++)
+ {
+ if (freq_table[i].frequency != CPUFREQ_ENTRY_INVALID)
+ {
+ if (acpu_khz == 0) {
+ acpu_freq_tbl[i].vdd = min(max((acpu_freq_tbl[i].vdd + max_vdd), CONFIG_CPU_FREQ_VDD_LEVELS_MIN), CONFIG_CPU_FREQ_VDD_LEVELS_MAX);
+ if(cur->acpu_khz == acpu_freq_tbl[i].acpu_khz)
+ acpuclk_set_vdd_level(acpu_freq_tbl[i].vdd);
+ } else if (acpu_freq_tbl[i].acpu_khz == acpu_khz) {
+ acpu_freq_tbl[i].vdd = min(max(max_vdd, CONFIG_CPU_FREQ_VDD_LEVELS_MIN), CONFIG_CPU_FREQ_VDD_LEVELS_MAX);
+ if(cur->acpu_khz == acpu_freq_tbl[i].acpu_khz)
+ acpuclk_set_vdd_level(acpu_freq_tbl[i].vdd);
+ }
+ }
+ }
+ mutex_unlock(&drv_state.lock);
+}
+#endif
+#endif