Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/acpuclock-8x50.c b/arch/arm/mach-msm/acpuclock-8x50.c
new file mode 100644
index 0000000..1b2416a
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-8x50.c
@@ -0,0 +1,723 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+
+#include <mach/board.h>
+#include <mach/msm_iomap.h>
+
+#include "acpuclock.h"
+#include "avs.h"
+
+#define SHOT_SWITCH 4
+#define HOP_SWITCH 5
+#define SIMPLE_SLEW 6
+#define COMPLEX_SLEW 7
+
+#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
+#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104)
+
+/* Scorpion PLL registers */
+#define SCPLL_CTL_ADDR         (MSM_SCPLL_BASE + 0x4)
+#define SCPLL_STATUS_ADDR      (MSM_SCPLL_BASE + 0x18)
+#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10)
+
+enum {
+	ACPU_PLL_TCXO	= -1,
+	ACPU_PLL_0	= 0,
+	ACPU_PLL_1,
+	ACPU_PLL_2,
+	ACPU_PLL_3,
+	ACPU_PLL_END,
+};
+
+struct clkctl_acpu_speed {
+	unsigned int     use_for_scaling;
+	unsigned int     acpuclk_khz;
+	int              pll;
+	unsigned int     acpuclk_src_sel;
+	unsigned int     acpuclk_src_div;
+	unsigned int     ahbclk_khz;
+	unsigned int     ahbclk_div;
+	unsigned int     axiclk_khz;
+	unsigned int     sc_core_src_sel_mask;
+	unsigned int     sc_l_value;
+	int              vdd;
+	unsigned long    lpj; /* loops_per_jiffy */
+};
+
+struct clkctl_acpu_speed acpu_freq_tbl_998[] = {
+	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 0, 0, 14000, 0, 0, 1000},
+	{ 0, 128000, ACPU_PLL_1, 1, 5, 0, 0, 14000, 2, 0, 1000},
+	{ 1, 245760, ACPU_PLL_0, 4, 0, 0, 0, 29000, 0, 0, 1000},
+	/* Update AXI_S and PLL0_S macros if above row numbers change. */
+	{ 1, 384000, ACPU_PLL_3, 0, 0, 0, 0, 58000, 1, 0xA, 1000},
+	{ 0, 422400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xB, 1000},
+	{ 0, 460800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xC, 1000},
+	{ 0, 499200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xD, 1050},
+	{ 0, 537600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xE, 1050},
+	{ 1, 576000, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xF, 1050},
+	{ 0, 614400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x10, 1075},
+	{ 0, 652800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x11, 1100},
+	{ 0, 691200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x12, 1125},
+	{ 0, 729600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x13, 1150},
+	{ 1, 768000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x14, 1150},
+	{ 0, 806400, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x15, 1175},
+	{ 0, 844800, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x16, 1225},
+	{ 0, 883200, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x17, 1250},
+	{ 0, 921600, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x18, 1300},
+	{ 0, 960000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x19, 1300},
+	{ 1, 998400, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x1A, 1300},
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+struct clkctl_acpu_speed acpu_freq_tbl_768[] = {
+	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 0, 0, 14000, 0, 0, 1000},
+	{ 0, 128000, ACPU_PLL_1, 1, 5, 0, 0, 14000, 2, 0, 1000},
+	{ 1, 245760, ACPU_PLL_0, 4, 0, 0, 0, 29000, 0, 0, 1000},
+	/* Update AXI_S and PLL0_S macros if above row numbers change. */
+	{ 1, 384000, ACPU_PLL_3, 0, 0, 0, 0, 58000, 1, 0xA, 1075},
+	{ 0, 422400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xB, 1100},
+	{ 0, 460800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xC, 1125},
+	{ 0, 499200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xD, 1150},
+	{ 0, 537600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xE, 1150},
+	{ 1, 576000, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xF, 1150},
+	{ 0, 614400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x10, 1175},
+	{ 0, 652800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x11, 1200},
+	{ 0, 691200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x12, 1225},
+	{ 0, 729600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x13, 1250},
+	{ 1, 768000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x14, 1250},
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+static struct clkctl_acpu_speed *acpu_freq_tbl = acpu_freq_tbl_998;
+#define AXI_S	(&acpu_freq_tbl[1])
+#define PLL0_S	(&acpu_freq_tbl[2])
+
+/* Use 128MHz for PC since ACPU will auto-switch to AXI (128MHz) before
+ * coming back up. This allows detection of return-from-PC, since 128MHz
+ * is only used for power collapse. */
+#define POWER_COLLAPSE_KHZ (AXI_S->acpuclk_khz)
+/* Use 245MHz (not 128MHz) for SWFI to avoid unnecessary steps between
+ * 128MHz<->245MHz. Jumping to high frequencies from 128MHz directly
+ * is not allowed. */
+#define WAIT_FOR_IRQ_KHZ (PLL0_S->acpuclk_khz)
+
+#ifdef CONFIG_CPU_FREQ_MSM
+static struct cpufreq_frequency_table freq_table[20];
+
+static void __init cpufreq_table_init(void)
+{
+	unsigned int i;
+	unsigned int freq_cnt = 0;
+
+	/* Construct the freq_table table from acpu_freq_tbl since the
+	 * freq_table values need to match frequencies specified in
+	 * acpu_freq_tbl and acpu_freq_tbl needs to be fixed up during init.
+	 */
+	for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0
+			&& freq_cnt < ARRAY_SIZE(freq_table)-1; i++) {
+		if (acpu_freq_tbl[i].use_for_scaling) {
+			freq_table[freq_cnt].index = freq_cnt;
+			freq_table[freq_cnt].frequency
+				= acpu_freq_tbl[i].acpuclk_khz;
+			freq_cnt++;
+		}
+	}
+
+	/* freq_table not big enough to store all usable freqs. */
+	BUG_ON(acpu_freq_tbl[i].acpuclk_khz != 0);
+
+	freq_table[freq_cnt].index = freq_cnt;
+	freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
+
+	pr_info("%d scaling frequencies supported.\n", freq_cnt);
+}
+#endif
+
+struct clock_state {
+	struct clkctl_acpu_speed	*current_speed;
+	struct mutex			lock;
+	uint32_t			acpu_switch_time_us;
+	uint32_t			max_speed_delta_khz;
+	uint32_t			vdd_switch_time_us;
+	unsigned int			max_vdd;
+	struct clk			*ebi1_clk;
+	int (*acpu_set_vdd) (int mvolts);
+};
+
+static struct clock_state drv_state = { 0 };
+
+static void scpll_set_freq(uint32_t lval, unsigned freq_switch)
+{
+	uint32_t regval;
+
+	if (lval > 33)
+		lval = 33;
+	if (lval < 10)
+		lval = 10;
+
+	/* wait for any calibrations or frequency switches to finish */
+	while (readl(SCPLL_STATUS_ADDR) & 0x3)
+		;
+
+	/* write the new L val and switch mode */
+	regval = readl(SCPLL_FSM_CTL_EXT_ADDR);
+	regval &= ~(0x3f << 3);
+	regval |= (lval << 3);
+	if (freq_switch == SIMPLE_SLEW)
+		regval |= (0x1 << 9);
+
+	regval &= ~(0x3 << 0);
+	regval |= (freq_switch << 0);
+	writel(regval, SCPLL_FSM_CTL_EXT_ADDR);
+
+	dmb();
+
+	/* put in normal mode */
+	regval = readl(SCPLL_CTL_ADDR);
+	regval |= 0x7;
+	writel(regval, SCPLL_CTL_ADDR);
+
+	dmb();
+
+	/* wait for frequency switch to finish */
+	while (readl(SCPLL_STATUS_ADDR) & 0x1)
+		;
+
+	/* status bit seems to clear early, using
+	 * 100us to handle the worst case. */
+	udelay(100);
+}
+
+static void scpll_apps_enable(bool state)
+{
+	uint32_t regval;
+
+	if (state)
+		pr_debug("Enabling PLL 3\n");
+	else
+		pr_debug("Disabling PLL 3\n");
+
+	/* Wait for any frequency switches to finish. */
+	while (readl(SCPLL_STATUS_ADDR) & 0x1)
+		;
+
+	/* put the pll in standby mode */
+	regval = readl(SCPLL_CTL_ADDR);
+	regval &= ~(0x7);
+	regval |= (0x2);
+	writel(regval, SCPLL_CTL_ADDR);
+
+	dmb();
+
+	if (state) {
+		/* put the pll in normal mode */
+		regval = readl(SCPLL_CTL_ADDR);
+		regval |= (0x7);
+		writel(regval, SCPLL_CTL_ADDR);
+		udelay(200);
+	} else {
+		/* put the pll in power down mode */
+		regval = readl(SCPLL_CTL_ADDR);
+		regval &= ~(0x7);
+		writel(regval, SCPLL_CTL_ADDR);
+	}
+	udelay(drv_state.vdd_switch_time_us);
+
+	if (state)
+		pr_debug("PLL 3 Enabled\n");
+	else
+		pr_debug("PLL 3 Disabled\n");
+}
+
+static void scpll_init(void)
+{
+	uint32_t regval;
+#define L_VAL_384MHZ	0xA
+#define L_VAL_768MHZ	0x14
+
+	pr_debug("Initializing PLL 3\n");
+
+	/* power down scpll */
+	writel(0x0, SCPLL_CTL_ADDR);
+
+	dmb();
+
+	/* set bypassnl, put into standby */
+	writel(0x00400002, SCPLL_CTL_ADDR);
+
+	/* set bypassnl, reset_n, full calibration */
+	writel(0x00600004, SCPLL_CTL_ADDR);
+
+	/* Ensure register write to initiate calibration has taken
+	effect before reading status flag */
+	dmb();
+
+	/* wait for cal_all_done */
+	while (readl(SCPLL_STATUS_ADDR) & 0x2)
+		;
+
+	/* Start: Set of experimentally derived steps
+	 * to work around a h/w bug. */
+
+	/* Put the pll in normal mode */
+	scpll_apps_enable(1);
+
+	/* SHOT switch to 384 MHz */
+	regval = readl(SCPLL_FSM_CTL_EXT_ADDR);
+	regval &= ~(0x3f << 3);
+	regval |= (L_VAL_384MHZ << 3);
+
+	regval &= ~0x7;
+	regval |= SHOT_SWITCH;
+	writel(regval, SCPLL_FSM_CTL_EXT_ADDR);
+
+	/* Trigger the freq switch by putting pll in normal mode. */
+	regval = readl(SCPLL_CTL_ADDR);
+	regval |= (0x7);
+	writel(regval, SCPLL_CTL_ADDR);
+
+	/* Wait for frequency switch to finish */
+	while (readl(SCPLL_STATUS_ADDR) & 0x1)
+		;
+
+	/* Status bit seems to clear early, using
+	 * 800 microseconds for the worst case. */
+	udelay(800);
+
+	/* HOP switch to 768 MHz. */
+	regval = readl(SCPLL_FSM_CTL_EXT_ADDR);
+	regval &= ~(0x3f << 3);
+	regval |= (L_VAL_768MHZ << 3);
+
+	regval &= ~0x7;
+	regval |= HOP_SWITCH;
+	writel(regval, SCPLL_FSM_CTL_EXT_ADDR);
+
+	/* Trigger the freq switch by putting pll in normal mode. */
+	regval = readl(SCPLL_CTL_ADDR);
+	regval |= (0x7);
+	writel(regval, SCPLL_CTL_ADDR);
+
+	/* Wait for frequency switch to finish */
+	while (readl(SCPLL_STATUS_ADDR) & 0x1)
+		;
+
+	/* Status bit seems to clear early, using
+	 * 100 microseconds for the worst case. */
+	udelay(100);
+
+	/* End: Work around for h/w bug */
+
+	/* Power down scpll */
+	scpll_apps_enable(0);
+}
+
+static void config_pll(struct clkctl_acpu_speed *s)
+{
+	uint32_t regval;
+
+	if (s->pll == ACPU_PLL_3)
+		scpll_set_freq(s->sc_l_value, HOP_SWITCH);
+	/* Configure the PLL divider mux if we plan to use it. */
+	else if (s->sc_core_src_sel_mask == 0) {
+		/* get the current clock source selection */
+		regval = readl(SPSS_CLK_SEL_ADDR) & 0x1;
+
+		/* configure the other clock source, then switch to it,
+		 * using the glitch free mux */
+		switch (regval) {
+		case 0x0:
+			regval = readl(SPSS_CLK_CNTL_ADDR);
+			regval &= ~(0x7 << 4 | 0xf);
+			regval |= (s->acpuclk_src_sel << 4);
+			regval |= (s->acpuclk_src_div << 0);
+			writel(regval, SPSS_CLK_CNTL_ADDR);
+
+			regval = readl(SPSS_CLK_SEL_ADDR);
+			regval |= 0x1;
+			writel(regval, SPSS_CLK_SEL_ADDR);
+			break;
+
+		case 0x1:
+			regval = readl(SPSS_CLK_CNTL_ADDR);
+			regval &= ~(0x7 << 12 | 0xf << 8);
+			regval |= (s->acpuclk_src_sel << 12);
+			regval |= (s->acpuclk_src_div << 8);
+			writel(regval, SPSS_CLK_CNTL_ADDR);
+
+			regval = readl(SPSS_CLK_SEL_ADDR);
+			regval &= ~0x1;
+			writel(regval, SPSS_CLK_SEL_ADDR);
+			break;
+		}
+		dmb();
+	}
+
+	regval = readl(SPSS_CLK_SEL_ADDR);
+	regval &= ~(0x3 << 1);
+	regval |= (s->sc_core_src_sel_mask << 1);
+	writel(regval, SPSS_CLK_SEL_ADDR);
+}
+
+static int acpuclk_set_vdd_level(int vdd)
+{
+	if (drv_state.acpu_set_vdd) {
+		pr_debug("Switching VDD to %d mV\n", 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(int cpu, unsigned long rate, enum setrate_reason reason)
+{
+	struct clkctl_acpu_speed *tgt_s, *strt_s;
+	int res, rc = 0;
+	int freq_index = 0;
+
+	if (reason == SETRATE_CPUFREQ)
+		mutex_lock(&drv_state.lock);
+
+	strt_s = drv_state.current_speed;
+
+	if (rate == strt_s->acpuclk_khz)
+		goto out;
+
+	for (tgt_s = acpu_freq_tbl; tgt_s->acpuclk_khz != 0; tgt_s++) {
+		if (tgt_s->acpuclk_khz == rate)
+			break;
+		freq_index++;
+	}
+
+	if (tgt_s->acpuclk_khz == 0) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (reason == SETRATE_CPUFREQ) {
+#ifdef CONFIG_MSM_CPU_AVS
+		/* Notify avs before changing frequency */
+		rc = avs_adjust_freq(freq_index, 1);
+		if (rc) {
+			pr_err("Unable to increase ACPU vdd (%d)\n", rc);
+			goto out;
+		}
+#endif
+		/* Increase VDD if needed. */
+		if (tgt_s->vdd > strt_s->vdd) {
+			rc = acpuclk_set_vdd_level(tgt_s->vdd);
+			if (rc) {
+				pr_err("Unable to increase ACPU vdd (%d)\n",
+					rc);
+				goto out;
+			}
+		}
+	} else if (reason == SETRATE_PC
+		&& rate != POWER_COLLAPSE_KHZ) {
+		/* Returning from PC. ACPU is running on AXI source.
+		 * Step up to PLL0 before ramping up higher. */
+		config_pll(PLL0_S);
+	}
+
+	pr_debug("Switching from ACPU rate %u KHz -> %u KHz\n",
+		strt_s->acpuclk_khz, tgt_s->acpuclk_khz);
+
+	if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) {
+		config_pll(tgt_s);
+	} else if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll == ACPU_PLL_3) {
+		scpll_apps_enable(1);
+		config_pll(tgt_s);
+	} else if (strt_s->pll == ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) {
+		config_pll(tgt_s);
+		scpll_apps_enable(0);
+	} else {
+		/* Temporarily switch to PLL0 while reconfiguring PLL3. */
+		config_pll(PLL0_S);
+		config_pll(tgt_s);
+	}
+
+	/* Update the driver state with the new clock freq */
+	drv_state.current_speed = tgt_s;
+
+	/* Re-adjust lpj for the new clock speed. */
+	loops_per_jiffy = tgt_s->lpj;
+
+	/* Nothing else to do for SWFI. */
+	if (reason == SETRATE_SWFI)
+		goto out;
+
+	if (strt_s->axiclk_khz != tgt_s->axiclk_khz) {
+		res = clk_set_rate(drv_state.ebi1_clk,
+				tgt_s->axiclk_khz * 1000);
+		if (res < 0)
+			pr_warning("Setting AXI min rate failed (%d)\n", res);
+	}
+
+	/* Nothing else to do for power collapse */
+	if (reason == SETRATE_PC)
+		goto out;
+
+#ifdef CONFIG_MSM_CPU_AVS
+	/* notify avs after changing frequency */
+	res = avs_adjust_freq(freq_index, 0);
+	if (res)
+		pr_warning("Unable to drop ACPU vdd (%d)\n", res);
+#endif
+
+	/* Drop VDD level if we can. */
+	if (tgt_s->vdd < strt_s->vdd) {
+		res = acpuclk_set_vdd_level(tgt_s->vdd);
+		if (res)
+			pr_warning("Unable to drop ACPU vdd (%d)\n", res);
+	}
+
+	pr_debug("ACPU speed change complete\n");
+out:
+	if (reason == SETRATE_CPUFREQ)
+		mutex_unlock(&drv_state.lock);
+	return rc;
+}
+
+static void __init acpuclk_init(void)
+{
+	struct clkctl_acpu_speed *speed;
+	uint32_t div, sel, regval;
+	int res;
+
+	/* Determine the source of the Scorpion clock. */
+	regval = readl(SPSS_CLK_SEL_ADDR);
+	switch ((regval & 0x6) >> 1) {
+	case 0: /* raw source clock */
+	case 3: /* low jitter PLL1 (768Mhz) */
+		if (regval & 0x1) {
+			sel = ((readl(SPSS_CLK_CNTL_ADDR) >> 4) & 0x7);
+			div = ((readl(SPSS_CLK_CNTL_ADDR) >> 0) & 0xf);
+		} else {
+			sel = ((readl(SPSS_CLK_CNTL_ADDR) >> 12) & 0x7);
+			div = ((readl(SPSS_CLK_CNTL_ADDR) >> 8) & 0xf);
+		}
+
+		/* Find the matching clock rate. */
+		for (speed = acpu_freq_tbl; speed->acpuclk_khz != 0; speed++) {
+			if (speed->acpuclk_src_sel == sel &&
+			    speed->acpuclk_src_div == div)
+				break;
+		}
+		break;
+
+	case 1: /* unbuffered scorpion pll (384Mhz to 998.4Mhz) */
+		sel = ((readl(SCPLL_FSM_CTL_EXT_ADDR) >> 3) & 0x3f);
+
+		/* Find the matching clock rate. */
+		for (speed = acpu_freq_tbl; speed->acpuclk_khz != 0; speed++) {
+			if (speed->sc_l_value == sel &&
+			    speed->sc_core_src_sel_mask == 1)
+				break;
+		}
+		break;
+
+	case 2: /* AXI bus clock (128Mhz) */
+		speed = AXI_S;
+		break;
+	default:
+		BUG();
+	}
+
+	/* Initialize scpll only if it wasn't already initialized by the boot
+	 * loader. If the CPU is already running on scpll, then the scpll was
+	 * initialized by the boot loader. */
+	if (speed->pll != ACPU_PLL_3)
+		scpll_init();
+
+	if (speed->acpuclk_khz == 0) {
+		pr_err("Error - ACPU clock reports invalid speed\n");
+		return;
+	}
+
+	drv_state.current_speed = speed;
+	res = clk_set_rate(drv_state.ebi1_clk, speed->axiclk_khz * 1000);
+	if (res < 0)
+		pr_warning("Setting AXI min rate failed (%d)\n", res);
+	res = clk_enable(drv_state.ebi1_clk);
+	if (res < 0)
+		pr_warning("Enabling AXI clock failed (%d)\n", res);
+
+	pr_info("ACPU running at %d KHz\n", speed->acpuclk_khz);
+}
+
+unsigned long acpuclk_get_rate(int cpu)
+{
+	return drv_state.current_speed->acpuclk_khz;
+}
+
+uint32_t acpuclk_get_switch_time(void)
+{
+	return drv_state.acpu_switch_time_us;
+}
+
+unsigned long acpuclk_power_collapse(void)
+{
+	int ret = acpuclk_get_rate(smp_processor_id());
+	acpuclk_set_rate(smp_processor_id(), POWER_COLLAPSE_KHZ, SETRATE_PC);
+	return ret;
+}
+
+unsigned long acpuclk_wait_for_irq(void)
+{
+	int ret = acpuclk_get_rate(smp_processor_id());
+	acpuclk_set_rate(smp_processor_id(), WAIT_FOR_IRQ_KHZ, SETRATE_SWFI);
+	return ret;
+}
+
+/* Spare register populated with efuse data on max ACPU freq. */
+#define CT_CSR_PHYS		0xA8700000
+#define TCSR_SPARE2_ADDR	(ct_csr_base + 0x60)
+
+#define PLL0_M_VAL_ADDR		(MSM_CLK_CTL_BASE + 0x308)
+
+static void __init acpu_freq_tbl_fixup(void)
+{
+	void __iomem *ct_csr_base;
+	uint32_t tcsr_spare2, pll0_m_val;
+	unsigned int max_acpu_khz;
+	unsigned int i;
+
+	ct_csr_base = ioremap(CT_CSR_PHYS, PAGE_SIZE);
+	BUG_ON(ct_csr_base == NULL);
+
+	tcsr_spare2 = readl(TCSR_SPARE2_ADDR);
+
+	/* Check if the register is supported and meaningful. */
+	if ((tcsr_spare2 & 0xF000) != 0xA000) {
+		pr_info("Efuse data on Max ACPU freq not present.\n");
+		goto skip_efuse_fixup;
+	}
+
+	switch (tcsr_spare2 & 0xF0) {
+	case 0x70:
+		acpu_freq_tbl = acpu_freq_tbl_768;
+		max_acpu_khz = 768000;
+		break;
+	case 0x30:
+	case 0x00:
+		max_acpu_khz = 998400;
+		break;
+	case 0x10:
+		max_acpu_khz = 1267200;
+		break;
+	default:
+		pr_warning("Invalid efuse data (%x) on Max ACPU freq!\n",
+				tcsr_spare2);
+		goto skip_efuse_fixup;
+	}
+
+	pr_info("Max ACPU freq from efuse data is %d KHz\n", max_acpu_khz);
+
+	for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0; i++) {
+		if (acpu_freq_tbl[i].acpuclk_khz > max_acpu_khz) {
+			acpu_freq_tbl[i].acpuclk_khz = 0;
+			break;
+		}
+	}
+
+skip_efuse_fixup:
+	iounmap(ct_csr_base);
+	BUG_ON(drv_state.max_vdd == 0);
+
+	/* pll0_m_val will be 36 when PLL0 is run at 235MHz
+	 * instead of the usual 245MHz. */
+	pll0_m_val = readl(PLL0_M_VAL_ADDR) & 0x7FFFF;
+	if (pll0_m_val == 36)
+		PLL0_S->acpuclk_khz = 235930;
+
+	for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0; i++) {
+		if (acpu_freq_tbl[i].vdd > drv_state.max_vdd) {
+			acpu_freq_tbl[i].acpuclk_khz = 0;
+			break;
+		}
+	}
+}
+
+/* Initalize the lpj field in the acpu_freq_tbl. */
+static void __init lpj_init(void)
+{
+	int i;
+	const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
+	for (i = 0; acpu_freq_tbl[i].acpuclk_khz; i++) {
+		acpu_freq_tbl[i].lpj = cpufreq_scale(loops_per_jiffy,
+						base_clk->acpuclk_khz,
+						acpu_freq_tbl[i].acpuclk_khz);
+	}
+}
+
+#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].acpuclk_khz; i++) {
+		freq_count++;
+		if (acpu_freq_tbl[i].acpuclk_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)
+{
+	mutex_init(&drv_state.lock);
+	drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us;
+	drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz;
+	drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us;
+	drv_state.max_vdd = clkdata->max_vdd;
+	drv_state.acpu_set_vdd = clkdata->acpu_set_vdd;
+
+	drv_state.ebi1_clk = clk_get(NULL, "ebi1_acpu_clk");
+	BUG_ON(IS_ERR(drv_state.ebi1_clk));
+
+	acpu_freq_tbl_fixup();
+	acpuclk_init();
+	lpj_init();
+	/* Set a lower bound for ACPU rate for boot. This limits the
+	 * maximum frequency hop caused by the first CPUFREQ switch. */
+	if (drv_state.current_speed->acpuclk_khz < PLL0_S->acpuclk_khz)
+		acpuclk_set_rate(0, PLL0_S->acpuclk_khz, SETRATE_CPUFREQ);
+
+#ifdef CONFIG_CPU_FREQ_MSM
+	cpufreq_table_init();
+	cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
+#endif
+#ifdef CONFIG_MSM_CPU_AVS
+	if (!acpu_avs_init(drv_state.acpu_set_vdd,
+		drv_state.current_speed->acpuclk_khz)) {
+		/* avs init successful. avs will handle voltage changes */
+		drv_state.acpu_set_vdd = NULL;
+	}
+#endif
+}